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 }