github.com/iotexproject/iotex-core@v1.14.1-rc1/state/account_test.go (about)

     1  // Copyright (c) 2019 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package state
     7  
     8  import (
     9  	"encoding/hex"
    10  	"math"
    11  	"math/big"
    12  	"testing"
    13  
    14  	"github.com/golang/mock/gomock"
    15  	"github.com/iotexproject/go-pkgs/hash"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  func TestNonce(t *testing.T) {
    20  	require := require.New(t)
    21  	t.Run("legacy account type", func(t *testing.T) {
    22  		acct, err := NewAccount(LegacyNonceAccountTypeOption())
    23  		require.NoError(err)
    24  		require.Equal(uint64(1), acct.PendingNonce())
    25  		require.Error(acct.SetPendingNonce(0))
    26  		require.Error(acct.SetPendingNonce(3))
    27  		require.NoError(acct.SetPendingNonce(2))
    28  		require.Equal(uint64(2), acct.PendingNonce())
    29  	})
    30  	t.Run("zero nonce account type", func(t *testing.T) {
    31  		acct, err := NewAccount()
    32  		require.NoError(err)
    33  		require.Equal(uint64(0), acct.PendingNonce())
    34  		require.Error(acct.SetPendingNonce(2))
    35  		require.NoError(acct.SetPendingNonce(1))
    36  		require.Equal(uint64(1), acct.PendingNonce())
    37  	})
    38  	t.Run("legacy fresh account type", func(t *testing.T) {
    39  		acct, err := NewAccount(LegacyNonceAccountTypeOption())
    40  		require.NoError(err)
    41  		require.Equal(uint64(1), acct.PendingNonce())
    42  		require.Error(acct.SetPendingNonce(0))
    43  		require.Error(acct.SetPendingNonce(3))
    44  		require.NoError(acct.SetPendingNonce(1))
    45  		require.Equal(uint64(1), acct.PendingNonce())
    46  	})
    47  }
    48  
    49  func TestNonceOverflow(t *testing.T) {
    50  	require := require.New(t)
    51  	t.Run("account nonce uint64 max", func(t *testing.T) {
    52  		acct, err := NewAccount()
    53  		require.NoError(err)
    54  		var target uint64 = math.MaxUint64
    55  		acct.nonce = uint64(target)
    56  		require.ErrorIs(acct.SetPendingNonce(target+1), ErrNonceOverflow)
    57  		require.Equal(target, acct.PendingNonce())
    58  	})
    59  	t.Run("account nonce uint64 max-1", func(t *testing.T) {
    60  		acct, err := NewAccount()
    61  		require.NoError(err)
    62  		var target uint64 = math.MaxUint64 - 1
    63  		acct.nonce = uint64(target)
    64  		require.NoError(acct.SetPendingNonce(target + 1))
    65  		require.Equal(target+1, acct.PendingNonce())
    66  	})
    67  	t.Run("legacy account nonce uint64 max", func(t *testing.T) {
    68  		acct, err := NewAccount(LegacyNonceAccountTypeOption())
    69  		require.NoError(err)
    70  		var target uint64 = math.MaxUint64
    71  		acct.nonce = uint64(target)
    72  		require.ErrorIs(acct.SetPendingNonce(target+2), ErrNonceOverflow)
    73  		require.Equal(target+1, acct.PendingNonce())
    74  	})
    75  	t.Run("legacy account nonce uint64 max-1", func(t *testing.T) {
    76  		acct, err := NewAccount(LegacyNonceAccountTypeOption())
    77  		require.NoError(err)
    78  		var target uint64 = math.MaxUint64 - 1
    79  		acct.nonce = uint64(target)
    80  		require.ErrorIs(acct.SetPendingNonce(target+2), ErrNonceOverflow)
    81  		require.Equal(target+1, acct.PendingNonce())
    82  	})
    83  	t.Run("legacy account nonce uint64 max-2", func(t *testing.T) {
    84  		acct, err := NewAccount(LegacyNonceAccountTypeOption())
    85  		require.NoError(err)
    86  		var target uint64 = math.MaxUint64 - 2
    87  		acct.nonce = uint64(target)
    88  		require.NoError(acct.SetPendingNonce(target + 2))
    89  		require.Equal(target+2, acct.PendingNonce())
    90  	})
    91  }
    92  
    93  func TestEncodeDecode(t *testing.T) {
    94  	require := require.New(t)
    95  
    96  	for _, test := range []struct {
    97  		accountType int32
    98  		expectedLen int
    99  	}{
   100  		{
   101  			1, 66,
   102  		},
   103  		{
   104  			0, 64,
   105  		},
   106  	} {
   107  		acc := Account{
   108  			accountType: test.accountType,
   109  			Balance:     big.NewInt(20000000),
   110  			nonce:       0x10,
   111  			CodeHash:    []byte("testing codehash"),
   112  		}
   113  		ss, err := acc.Serialize()
   114  		require.NoError(err)
   115  		require.NotEmpty(ss)
   116  		require.Equal(test.expectedLen, len(ss))
   117  
   118  		s2 := Account{}
   119  		require.NoError(s2.Deserialize(ss))
   120  		require.Equal(acc.accountType, s2.accountType)
   121  		require.Equal(acc.Balance, s2.Balance)
   122  		require.Equal(acc.nonce, s2.nonce)
   123  		require.Equal(hash.ZeroHash256, s2.Root)
   124  		require.Equal(acc.CodeHash, s2.CodeHash)
   125  	}
   126  }
   127  
   128  func TestProto(t *testing.T) {
   129  	require := require.New(t)
   130  
   131  	for _, test := range []struct {
   132  		accountType int32
   133  		raw         string
   134  	}{
   135  		{
   136  			0, "1201301a200000000000000000000000000000000000000000000000000000000000000000",
   137  		},
   138  		{
   139  			1, "1201301a2000000000000000000000000000000000000000000000000000000000000000003801",
   140  		},
   141  	} {
   142  		acc := Account{accountType: test.accountType}
   143  		ss, _ := hex.DecodeString(test.raw)
   144  		require.NoError(acc.Deserialize(ss))
   145  		bytes, err := acc.Serialize()
   146  		require.NoError(err)
   147  		require.Equal(test.raw, hex.EncodeToString(bytes))
   148  	}
   149  }
   150  
   151  func TestBalance(t *testing.T) {
   152  	require := require.New(t)
   153  	ctrl := gomock.NewController(t)
   154  	defer ctrl.Finish()
   155  
   156  	state := &Account{Balance: big.NewInt(20)}
   157  	// Add 10 to the balance
   158  	require.NoError(state.AddBalance(big.NewInt(10)))
   159  	// Balance should == 30 now
   160  	require.Equal(0, state.Balance.Cmp(big.NewInt(30)))
   161  	// Sub 40 to the balance
   162  	require.Equal(ErrNotEnoughBalance, state.SubBalance(big.NewInt(40)))
   163  
   164  	require.True(state.HasSufficientBalance(big.NewInt(30)))
   165  	require.False(state.HasSufficientBalance(big.NewInt(31)))
   166  
   167  	require.Contains(state.AddBalance(big.NewInt(-1)).Error(), ErrInvalidAmount.Error())
   168  	require.Contains(state.SubBalance(big.NewInt(-1)).Error(), ErrInvalidAmount.Error())
   169  	require.Contains(state.AddBalance(nil).Error(), ErrInvalidAmount.Error())
   170  	require.Contains(state.SubBalance(nil).Error(), ErrInvalidAmount.Error())
   171  }
   172  
   173  func TestClone(t *testing.T) {
   174  	require := require.New(t)
   175  	ss := &Account{
   176  		nonce:   0x10,
   177  		Balance: big.NewInt(200),
   178  	}
   179  	account := ss.Clone()
   180  	require.Equal(big.NewInt(200), account.Balance)
   181  
   182  	require.NoError(account.AddBalance(big.NewInt(100)))
   183  	require.Equal(big.NewInt(200), ss.Balance)
   184  	require.Equal(big.NewInt(200+100), account.Balance)
   185  }
   186  
   187  func TestConvertFreshAddress(t *testing.T) {
   188  	// TODO: fix this test @dustinxie
   189  	t.Skip()
   190  	require := require.New(t)
   191  
   192  	var (
   193  		s1, _ = NewAccount(LegacyNonceAccountTypeOption())
   194  		s2, _ = NewAccount(LegacyNonceAccountTypeOption())
   195  		s3, _ = NewAccount()
   196  		s4, _ = NewAccount()
   197  	)
   198  	s2.nonce, s4.nonce = 1, 1
   199  
   200  	for i, v := range []struct {
   201  		s                    *Account
   202  		accType, cvtType     int32
   203  		first, second, third uint64
   204  	}{
   205  		{s1, 0, 1, 1, 0, 1},
   206  		{s2, 0, 0, 2, 2, 3},
   207  		{s3, 1, 1, 0, 0, 1},
   208  		{s4, 1, 1, 1, 1, 2},
   209  	} {
   210  		require.Equal(v.accType, v.s.accountType)
   211  		require.Equal(v.first, v.s.PendingNonce())
   212  		require.Equal(v.second, v.s.PendingNonceConsideringFreshAccount())
   213  		// trying convert using pending nonce does not take effect
   214  		// require.False(v.s.ConvertFreshAccountToZeroNonceType(v.first))
   215  		require.Equal(v.accType, v.s.accountType)
   216  		// only adjusted nonce can convert legacy fresh address to zero-nonce type
   217  		// require.Equal(v.s.IsLegacyFreshAccount() && v.second == 0, v.s.ConvertFreshAccountToZeroNonceType(v.second))
   218  		require.Equal(v.cvtType, v.s.accountType)
   219  		// after conversion, fresh address is still fresh
   220  		require.Equal(i == 0 || i == 2, v.s.IsNewbieAccount())
   221  		// after conversion, 2 pending nonces become the same
   222  		require.Equal(v.second, v.s.PendingNonce())
   223  		require.Equal(v.second, v.s.PendingNonceConsideringFreshAccount())
   224  		require.NoError(v.s.SetPendingNonce(v.second + 1))
   225  		// for dirty address, 2 pending nonces are the same
   226  		require.Equal(v.third, v.s.PendingNonce())
   227  		require.Equal(v.third, v.s.PendingNonceConsideringFreshAccount())
   228  	}
   229  }