github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/native/native_test/neo_test.go (about) 1 package native_test 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "math" 8 "math/big" 9 "sort" 10 "strings" 11 "testing" 12 13 "github.com/nspcc-dev/neo-go/internal/contracts" 14 "github.com/nspcc-dev/neo-go/internal/random" 15 "github.com/nspcc-dev/neo-go/pkg/compiler" 16 "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" 17 "github.com/nspcc-dev/neo-go/pkg/core/native" 18 "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" 19 "github.com/nspcc-dev/neo-go/pkg/core/state" 20 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 21 "github.com/nspcc-dev/neo-go/pkg/crypto/hash" 22 "github.com/nspcc-dev/neo-go/pkg/crypto/keys" 23 "github.com/nspcc-dev/neo-go/pkg/io" 24 "github.com/nspcc-dev/neo-go/pkg/neotest" 25 "github.com/nspcc-dev/neo-go/pkg/neotest/chain" 26 "github.com/nspcc-dev/neo-go/pkg/smartcontract" 27 "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" 28 "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" 29 "github.com/nspcc-dev/neo-go/pkg/util" 30 "github.com/nspcc-dev/neo-go/pkg/vm/emit" 31 "github.com/nspcc-dev/neo-go/pkg/vm/opcode" 32 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 33 "github.com/stretchr/testify/assert" 34 "github.com/stretchr/testify/require" 35 ) 36 37 func newNeoCommitteeClient(t *testing.T, expectedGASBalance int) *neotest.ContractInvoker { 38 bc, validators, committee := chain.NewMulti(t) 39 e := neotest.NewExecutor(t, bc, validators, committee) 40 41 if expectedGASBalance > 0 { 42 e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas)).Invoke(t, true, "transfer", e.Validator.ScriptHash(), e.CommitteeHash, expectedGASBalance, nil) 43 } 44 45 return e.CommitteeInvoker(e.NativeHash(t, nativenames.Neo)) 46 } 47 48 func newNeoValidatorsClient(t *testing.T) *neotest.ContractInvoker { 49 c := newNeoCommitteeClient(t, 100_0000_0000) 50 return c.ValidatorInvoker(c.NativeHash(t, nativenames.Neo)) 51 } 52 53 func TestNEO_GasPerBlock(t *testing.T) { 54 testGetSet(t, newNeoCommitteeClient(t, 100_0000_0000), "GasPerBlock", 5*native.GASFactor, 0, 10*native.GASFactor) 55 } 56 57 func TestNEO_GasPerBlockCache(t *testing.T) { 58 testGetSetCache(t, newNeoCommitteeClient(t, 100_0000_0000), "GasPerBlock", 5*native.GASFactor) 59 } 60 61 func TestNEO_RegisterPrice(t *testing.T) { 62 testGetSet(t, newNeoCommitteeClient(t, 100_0000_0000), "RegisterPrice", native.DefaultRegisterPrice, 1, math.MaxInt64) 63 } 64 65 func TestNEO_RegisterPriceCache(t *testing.T) { 66 testGetSetCache(t, newNeoCommitteeClient(t, 100_0000_0000), "RegisterPrice", native.DefaultRegisterPrice) 67 } 68 69 func TestNEO_CandidateEvents(t *testing.T) { 70 c := newNativeClient(t, nativenames.Neo) 71 singleSigner := c.Signers[0].(neotest.MultiSigner).Single(0) 72 cc := c.WithSigners(c.Signers[0], singleSigner) 73 e := c.Executor 74 pkb := singleSigner.Account().PublicKey().Bytes() 75 76 // Register 1 -> event 77 tx := cc.Invoke(t, true, "registerCandidate", pkb) 78 e.CheckTxNotificationEvent(t, tx, 0, state.NotificationEvent{ 79 ScriptHash: c.Hash, 80 Name: "CandidateStateChanged", 81 Item: stackitem.NewArray([]stackitem.Item{ 82 stackitem.NewByteArray(pkb), 83 stackitem.NewBool(true), 84 stackitem.Make(0), 85 }), 86 }) 87 88 // Register 2 -> no event 89 tx = cc.Invoke(t, true, "registerCandidate", pkb) 90 aer := e.GetTxExecResult(t, tx) 91 require.Equal(t, 0, len(aer.Events)) 92 93 // Vote -> event 94 tx = c.Invoke(t, true, "vote", c.Signers[0].ScriptHash().BytesBE(), pkb) 95 e.CheckTxNotificationEvent(t, tx, 0, state.NotificationEvent{ 96 ScriptHash: c.Hash, 97 Name: "Vote", 98 Item: stackitem.NewArray([]stackitem.Item{ 99 stackitem.NewByteArray(c.Signers[0].ScriptHash().BytesBE()), 100 stackitem.Null{}, 101 stackitem.NewByteArray(pkb), 102 stackitem.Make(100000000), 103 }), 104 }) 105 106 // Unregister 1 -> event 107 tx = cc.Invoke(t, true, "unregisterCandidate", pkb) 108 e.CheckTxNotificationEvent(t, tx, 0, state.NotificationEvent{ 109 ScriptHash: c.Hash, 110 Name: "CandidateStateChanged", 111 Item: stackitem.NewArray([]stackitem.Item{ 112 stackitem.NewByteArray(pkb), 113 stackitem.NewBool(false), 114 stackitem.Make(100000000), 115 }), 116 }) 117 118 // Unregister 2 -> no event 119 tx = cc.Invoke(t, true, "unregisterCandidate", pkb) 120 aer = e.GetTxExecResult(t, tx) 121 require.Equal(t, 0, len(aer.Events)) 122 } 123 124 func TestNEO_CommitteeEvents(t *testing.T) { 125 neoCommitteeInvoker := newNeoCommitteeClient(t, 100_0000_0000) 126 neoValidatorsInvoker := neoCommitteeInvoker.WithSigners(neoCommitteeInvoker.Validator) 127 e := neoCommitteeInvoker.Executor 128 129 cfg := e.Chain.GetConfig() 130 committeeSize := cfg.GetCommitteeSize(0) 131 132 voters := make([]neotest.Signer, committeeSize) 133 candidates := make([]neotest.Signer, committeeSize) 134 for i := 0; i < committeeSize; i++ { 135 voters[i] = e.NewAccount(t, 10_0000_0000) 136 candidates[i] = e.NewAccount(t, 2000_0000_0000) // enough for one registration 137 } 138 txes := make([]*transaction.Transaction, 0, committeeSize*3) 139 for i := 0; i < committeeSize; i++ { 140 transferTx := neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), int64(committeeSize-i)*1000000, nil) 141 txes = append(txes, transferTx) 142 143 registerTx := neoValidatorsInvoker.WithSigners(candidates[i]).PrepareInvoke(t, "registerCandidate", candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes()) 144 txes = append(txes, registerTx) 145 146 voteTx := neoValidatorsInvoker.WithSigners(voters[i]).PrepareInvoke(t, "vote", voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes()) 147 txes = append(txes, voteTx) 148 } 149 block := neoValidatorsInvoker.AddNewBlock(t, txes...) 150 for _, tx := range txes { 151 e.CheckHalt(t, tx.Hash(), stackitem.Make(true)) 152 } 153 154 // Advance the chain to trigger committee recalculation and potential change. 155 for (block.Index)%uint32(committeeSize) != 0 { 156 block = neoCommitteeInvoker.AddNewBlock(t) 157 } 158 159 // Check for CommitteeChanged event in the last persisted block's AER. 160 blockHash := e.Chain.CurrentBlockHash() 161 aer, err := e.Chain.GetAppExecResults(blockHash, trigger.OnPersist) 162 require.NoError(t, err) 163 require.Equal(t, 1, len(aer)) 164 165 require.Equal(t, aer[0].Events[0].Name, "CommitteeChanged") 166 require.Equal(t, 2, len(aer[0].Events[0].Item.Value().([]stackitem.Item))) 167 168 expectedOldCommitteePublicKeys, err := keys.NewPublicKeysFromStrings(cfg.StandbyCommittee) 169 require.NoError(t, err) 170 expectedOldCommitteeStackItems := make([]stackitem.Item, len(expectedOldCommitteePublicKeys)) 171 for i, pubKey := range expectedOldCommitteePublicKeys { 172 expectedOldCommitteeStackItems[i] = stackitem.NewByteArray(pubKey.Bytes()) 173 } 174 oldCommitteeStackItem := aer[0].Events[0].Item.Value().([]stackitem.Item)[0].(*stackitem.Array) 175 for i, item := range oldCommitteeStackItem.Value().([]stackitem.Item) { 176 assert.Equal(t, expectedOldCommitteeStackItems[i].(*stackitem.ByteArray).Value().([]byte), item.Value().([]byte)) 177 } 178 expectedNewCommitteeStackItems := make([]stackitem.Item, 0, committeeSize) 179 for _, candidate := range candidates { 180 expectedNewCommitteeStackItems = append(expectedNewCommitteeStackItems, stackitem.NewByteArray(candidate.(neotest.SingleSigner).Account().PublicKey().Bytes())) 181 } 182 newCommitteeStackItem := aer[0].Events[0].Item.Value().([]stackitem.Item)[1].(*stackitem.Array) 183 for i, item := range newCommitteeStackItem.Value().([]stackitem.Item) { 184 assert.Equal(t, expectedNewCommitteeStackItems[i].(*stackitem.ByteArray).Value().([]byte), item.Value().([]byte)) 185 } 186 } 187 188 func TestNEO_Vote(t *testing.T) { 189 neoCommitteeInvoker := newNeoCommitteeClient(t, 100_0000_0000) 190 neoValidatorsInvoker := neoCommitteeInvoker.WithSigners(neoCommitteeInvoker.Validator) 191 policyInvoker := neoCommitteeInvoker.CommitteeInvoker(neoCommitteeInvoker.NativeHash(t, nativenames.Policy)) 192 e := neoCommitteeInvoker.Executor 193 194 cfg := e.Chain.GetConfig() 195 committeeSize := cfg.GetCommitteeSize(0) 196 validatorsCount := cfg.GetNumOfCNs(0) 197 freq := validatorsCount + committeeSize 198 advanceChain := func(t *testing.T) { 199 for i := 0; i < freq; i++ { 200 neoCommitteeInvoker.AddNewBlock(t) 201 } 202 } 203 204 standBySorted, err := keys.NewPublicKeysFromStrings(e.Chain.GetConfig().StandbyCommittee) 205 require.NoError(t, err) 206 standBySorted = standBySorted[:validatorsCount] 207 sort.Sort(standBySorted) 208 pubs := e.Chain.ComputeNextBlockValidators() 209 require.Equal(t, standBySorted, keys.PublicKeys(pubs)) 210 211 // voters vote for candidates. The aim of this test is to check if voting 212 // reward is proportional to the NEO balance. 213 voters := make([]neotest.Signer, committeeSize+1) 214 // referenceAccounts perform the same actions as voters except voting, i.e. we 215 // will transfer the same amount of NEO to referenceAccounts and see how much 216 // GAS they receive for NEO ownership. We need these values to be able to define 217 // how much GAS voters receive for NEO ownership. 218 referenceAccounts := make([]neotest.Signer, committeeSize+1) 219 candidates := make([]neotest.Signer, committeeSize+1) 220 for i := 0; i < committeeSize+1; i++ { 221 voters[i] = e.NewAccount(t, 10_0000_0000) 222 referenceAccounts[i] = e.NewAccount(t, 10_0000_0000) 223 candidates[i] = e.NewAccount(t, 2000_0000_0000) // enough for one registration 224 } 225 txes := make([]*transaction.Transaction, 0, committeeSize*4-2) 226 for i := 0; i < committeeSize+1; i++ { 227 transferTx := neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), int64(committeeSize+1-i)*1000000, nil) 228 txes = append(txes, transferTx) 229 transferTx = neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), referenceAccounts[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), int64(committeeSize+1-i)*1000000, nil) 230 txes = append(txes, transferTx) 231 if i > 0 { 232 registerTx := neoValidatorsInvoker.WithSigners(candidates[i]).PrepareInvoke(t, "registerCandidate", candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes()) 233 txes = append(txes, registerTx) 234 voteTx := neoValidatorsInvoker.WithSigners(voters[i]).PrepareInvoke(t, "vote", voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes()) 235 txes = append(txes, voteTx) 236 } 237 } 238 txes = append(txes, policyInvoker.PrepareInvoke(t, "blockAccount", candidates[len(candidates)-1].(neotest.SingleSigner).Account().ScriptHash())) 239 neoValidatorsInvoker.AddNewBlock(t, txes...) 240 for _, tx := range txes { 241 e.CheckHalt(t, tx.Hash(), stackitem.Make(true)) // luckily, both `transfer`, `registerCandidate` and `vote` return boolean values 242 } 243 244 // We still haven't voted enough validators in. 245 pubs = e.Chain.ComputeNextBlockValidators() 246 require.NoError(t, err) 247 require.Equal(t, standBySorted, keys.PublicKeys(pubs)) 248 249 advanceChain(t) 250 pubs, err = e.Chain.GetNextBlockValidators() 251 require.NoError(t, err) 252 require.EqualValues(t, standBySorted, keys.PublicKeys(pubs)) 253 254 // Register and give some value to the last validator. 255 txes = txes[:0] 256 registerTx := neoValidatorsInvoker.WithSigners(candidates[0]).PrepareInvoke(t, "registerCandidate", candidates[0].(neotest.SingleSigner).Account().PublicKey().Bytes()) 257 txes = append(txes, registerTx) 258 voteTx := neoValidatorsInvoker.WithSigners(voters[0]).PrepareInvoke(t, "vote", voters[0].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), candidates[0].(neotest.SingleSigner).Account().PublicKey().Bytes()) 259 txes = append(txes, voteTx) 260 neoValidatorsInvoker.AddNewBlock(t, txes...) 261 for _, tx := range txes { 262 e.CheckHalt(t, tx.Hash(), stackitem.Make(true)) // luckily, both `transfer`, `registerCandidate` and `vote` return boolean values 263 } 264 265 advanceChain(t) 266 pubs, err = neoCommitteeInvoker.Chain.GetNextBlockValidators() 267 require.NoError(t, err) 268 sortedCandidates := make(keys.PublicKeys, validatorsCount) 269 for i := range candidates[:validatorsCount] { 270 sortedCandidates[i] = candidates[i].(neotest.SingleSigner).Account().PublicKey() 271 } 272 sort.Sort(sortedCandidates) 273 require.EqualValues(t, sortedCandidates, keys.PublicKeys(pubs)) 274 275 pubs, err = neoCommitteeInvoker.Chain.GetNextBlockValidators() 276 require.NoError(t, err) 277 require.EqualValues(t, sortedCandidates, pubs) 278 279 t.Run("check voter rewards", func(t *testing.T) { 280 gasBalance := make([]*big.Int, len(voters)-1) 281 referenceGASBalance := make([]*big.Int, len(referenceAccounts)-1) 282 neoBalance := make([]*big.Int, len(voters)-1) 283 txes = make([]*transaction.Transaction, 0, len(voters)-1) 284 var refTxFee int64 285 for i := range voters[:len(voters)-1] { 286 h := voters[i].ScriptHash() 287 refH := referenceAccounts[i].ScriptHash() 288 gasBalance[i] = e.Chain.GetUtilityTokenBalance(h) 289 neoBalance[i], _ = e.Chain.GetGoverningTokenBalance(h) 290 referenceGASBalance[i] = e.Chain.GetUtilityTokenBalance(refH) 291 292 tx := neoCommitteeInvoker.WithSigners(voters[i]).PrepareInvoke(t, "transfer", h.BytesBE(), h.BytesBE(), int64(1), nil) 293 txes = append(txes, tx) 294 tx = neoCommitteeInvoker.WithSigners(referenceAccounts[i]).PrepareInvoke(t, "transfer", refH.BytesBE(), refH.BytesBE(), int64(1), nil) 295 txes = append(txes, tx) 296 refTxFee = tx.SystemFee + tx.NetworkFee 297 } 298 neoCommitteeInvoker.AddNewBlock(t, txes...) 299 for _, tx := range txes { 300 e.CheckHalt(t, tx.Hash(), stackitem.Make(true)) 301 } 302 303 // Define reference reward for NEO holding for each voter account. 304 for i := range referenceGASBalance { 305 newBalance := e.Chain.GetUtilityTokenBalance(referenceAccounts[i].ScriptHash()) 306 referenceGASBalance[i].Sub(newBalance, referenceGASBalance[i]) 307 referenceGASBalance[i].Add(referenceGASBalance[i], big.NewInt(refTxFee)) 308 } 309 310 // GAS increase consists of 2 parts: NEO holding + voting for committee nodes. 311 // Here we check that 2-nd part exists and is proportional to the amount of NEO given. 312 for i := range voters[:len(voters)-1] { 313 newGAS := e.Chain.GetUtilityTokenBalance(voters[i].ScriptHash()) 314 newGAS.Sub(newGAS, gasBalance[i]) 315 gasForHold := referenceGASBalance[i] 316 newGAS.Sub(newGAS, gasForHold) 317 require.True(t, newGAS.Sign() > 0) 318 gasBalance[i] = newGAS 319 } 320 // First account voted later than the others. 321 require.Equal(t, -1, gasBalance[0].Cmp(gasBalance[1])) 322 for i := 2; i < validatorsCount; i++ { 323 require.Equal(t, 0, gasBalance[i].Cmp(gasBalance[1])) 324 } 325 require.Equal(t, 1, gasBalance[1].Cmp(gasBalance[validatorsCount])) 326 for i := validatorsCount; i < committeeSize; i++ { 327 require.Equal(t, 0, gasBalance[i].Cmp(gasBalance[validatorsCount])) 328 } 329 }) 330 331 neoCommitteeInvoker.WithSigners(candidates[0]).Invoke(t, true, "unregisterCandidate", candidates[0].(neotest.SingleSigner).Account().PublicKey().Bytes()) 332 neoCommitteeInvoker.WithSigners(voters[0]).Invoke(t, false, "vote", voters[0].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), candidates[0].(neotest.SingleSigner).Account().PublicKey().Bytes()) 333 334 advanceChain(t) 335 336 pubs = e.Chain.ComputeNextBlockValidators() 337 for i := range pubs { 338 require.NotEqual(t, candidates[0], pubs[i]) 339 require.NotEqual(t, candidates[len(candidates)-1], pubs[i]) 340 } 341 // LastGasPerVote should be 0 after unvoting 342 getAccountState := func(t *testing.T, account util.Uint160) *state.NEOBalance { 343 stack, err := neoCommitteeInvoker.TestInvoke(t, "getAccountState", account) 344 require.NoError(t, err) 345 res := stack.Pop().Item() 346 // (s *NEOBalance) FromStackItem is able to handle both 3 and 4 subitems. 347 // The forth optional subitem is LastGasPerVote. 348 require.Equal(t, 4, len(res.Value().([]stackitem.Item))) 349 as := new(state.NEOBalance) 350 err = as.FromStackItem(res) 351 require.NoError(t, err) 352 return as 353 } 354 registerTx = neoValidatorsInvoker.WithSigners(candidates[0]).PrepareInvoke(t, "registerCandidate", candidates[0].(neotest.SingleSigner).Account().PublicKey().Bytes()) 355 voteTx = neoValidatorsInvoker.WithSigners(voters[0]).PrepareInvoke(t, "vote", voters[0].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), candidates[0].(neotest.SingleSigner).Account().PublicKey().Bytes()) 356 neoValidatorsInvoker.AddNewBlock(t, registerTx, voteTx) 357 e.CheckHalt(t, registerTx.Hash(), stackitem.Make(true)) 358 e.CheckHalt(t, voteTx.Hash(), stackitem.Make(true)) 359 360 stateBeforeUnvote := getAccountState(t, voters[0].ScriptHash()) 361 require.NotEqual(t, uint64(0), stateBeforeUnvote.LastGasPerVote.Uint64()) 362 // Unvote 363 unvoteTx := neoValidatorsInvoker.WithSigners(voters[0]).PrepareInvoke(t, "vote", voters[0].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), nil) 364 neoValidatorsInvoker.AddNewBlock(t, unvoteTx) 365 e.CheckHalt(t, unvoteTx.Hash(), stackitem.Make(true)) 366 advanceChain(t) 367 368 stateAfterUnvote := getAccountState(t, voters[0].ScriptHash()) 369 require.Equal(t, uint64(0), stateAfterUnvote.LastGasPerVote.Uint64()) 370 } 371 372 // TestNEO_RecursiveDistribution is a test for https://github.com/nspcc-dev/neo-go/pull/2181. 373 func TestNEO_RecursiveGASMint(t *testing.T) { 374 neoCommitteeInvoker := newNeoCommitteeClient(t, 100_0000_0000) 375 neoValidatorInvoker := neoCommitteeInvoker.WithSigners(neoCommitteeInvoker.Validator) 376 e := neoCommitteeInvoker.Executor 377 gasValidatorInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas)) 378 379 c := neotest.CompileFile(t, e.Validator.ScriptHash(), "../../../../internal/basicchain/testdata/test_contract.go", "../../../../internal/basicchain/testdata/test_contract.yml") 380 e.DeployContract(t, c, nil) 381 382 gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), c.Hash, int64(2_0000_0000), nil) 383 384 // Transfer 10 NEO to test contract, the contract should earn some GAS by owning this NEO. 385 neoValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), c.Hash, int64(10), nil) 386 387 // Add blocks to be able to trigger NEO transfer from contract address to owner 388 // address inside onNEP17Payment (the contract starts NEO transfers from chain height = 100). 389 for i := e.Chain.BlockHeight(); i < 100; i++ { 390 e.AddNewBlock(t) 391 } 392 393 // Transfer 1 more NEO to the contract. Transfer will trigger onNEP17Payment. OnNEP17Payment will 394 // trigger transfer of 11 NEO to the contract owner (based on the contract code). 11 NEO Transfer will 395 // trigger GAS distribution. GAS transfer will trigger OnNEP17Payment one more time. The recursion 396 // shouldn't occur here, because contract's balance LastUpdated height has already been updated in 397 // this block. 398 neoValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), c.Hash, int64(1), nil) 399 } 400 401 func TestNEO_GetCommitteeAddress(t *testing.T) { 402 neoValidatorInvoker := newNeoValidatorsClient(t) 403 e := neoValidatorInvoker.Executor 404 standByCommitteePublicKeys, err := keys.NewPublicKeysFromStrings(e.Chain.GetConfig().StandbyCommittee) 405 require.NoError(t, err) 406 sort.Sort(standByCommitteePublicKeys) 407 expectedCommitteeAddress, err := smartcontract.CreateMajorityMultiSigRedeemScript(standByCommitteePublicKeys) 408 require.NoError(t, err) 409 stack, err := neoValidatorInvoker.TestInvoke(t, "getCommitteeAddress") 410 require.NoError(t, err) 411 require.Equal(t, hash.Hash160(expectedCommitteeAddress).BytesBE(), stack.Pop().Item().Value().([]byte)) 412 } 413 414 func TestNEO_GetAccountState(t *testing.T) { 415 neoValidatorInvoker := newNeoValidatorsClient(t) 416 e := neoValidatorInvoker.Executor 417 418 cfg := e.Chain.GetConfig() 419 committeeSize := cfg.GetCommitteeSize(0) 420 validatorSize := cfg.GetNumOfCNs(0) 421 advanceChain := func(t *testing.T) { 422 for i := 0; i < committeeSize; i++ { 423 neoValidatorInvoker.AddNewBlock(t) 424 } 425 } 426 427 t.Run("empty", func(t *testing.T) { 428 neoValidatorInvoker.Invoke(t, stackitem.Null{}, "getAccountState", util.Uint160{}) 429 }) 430 431 t.Run("with funds", func(t *testing.T) { 432 amount := int64(1) 433 acc := e.NewAccount(t) 434 neoValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), acc.ScriptHash(), amount, nil) 435 lub := e.Chain.BlockHeight() 436 neoValidatorInvoker.Invoke(t, stackitem.NewStruct([]stackitem.Item{ 437 stackitem.Make(amount), 438 stackitem.Make(lub), 439 stackitem.Null{}, 440 stackitem.Make(0), 441 }), "getAccountState", acc.ScriptHash()) 442 }) 443 444 t.Run("lastGasPerVote", func(t *testing.T) { 445 const ( 446 GasPerBlock = 5 447 VoterRewardRatio = 80 448 ) 449 getAccountState := func(t *testing.T, account util.Uint160) *state.NEOBalance { 450 stack, err := neoValidatorInvoker.TestInvoke(t, "getAccountState", account) 451 require.NoError(t, err) 452 as := new(state.NEOBalance) 453 err = as.FromStackItem(stack.Pop().Item()) 454 require.NoError(t, err) 455 return as 456 } 457 458 amount := int64(1000) 459 acc := e.NewAccount(t) 460 neoValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), acc.ScriptHash(), amount, nil) 461 as := getAccountState(t, acc.ScriptHash()) 462 require.Equal(t, uint64(amount), as.Balance.Uint64()) 463 require.Equal(t, e.Chain.BlockHeight(), as.BalanceHeight) 464 require.Equal(t, uint64(0), as.LastGasPerVote.Uint64()) 465 committee, _ := e.Chain.GetCommittee() 466 neoValidatorInvoker.WithSigners(e.Validator, e.Validator.(neotest.MultiSigner).Single(0)).Invoke(t, true, "registerCandidate", committee[0].Bytes()) 467 neoValidatorInvoker.WithSigners(acc).Invoke(t, true, "vote", acc.ScriptHash(), committee[0].Bytes()) 468 as = getAccountState(t, acc.ScriptHash()) 469 require.Equal(t, uint64(0), as.LastGasPerVote.Uint64()) 470 advanceChain(t) 471 neoValidatorInvoker.WithSigners(acc).Invoke(t, true, "transfer", acc.ScriptHash(), acc.ScriptHash(), amount, nil) 472 as = getAccountState(t, acc.ScriptHash()) 473 expect := GasPerBlock * native.GASFactor * VoterRewardRatio / 100 * (uint64(e.Chain.BlockHeight()) / uint64(committeeSize)) 474 expect = expect * uint64(committeeSize) / uint64(validatorSize+committeeSize) * native.NEOTotalSupply / as.Balance.Uint64() 475 require.Equal(t, e.Chain.BlockHeight(), as.BalanceHeight) 476 require.Equal(t, expect, as.LastGasPerVote.Uint64()) 477 }) 478 } 479 480 func TestNEO_GetAccountStateInteropAPI(t *testing.T) { 481 neoValidatorInvoker := newNeoValidatorsClient(t) 482 e := neoValidatorInvoker.Executor 483 484 cfg := e.Chain.GetConfig() 485 committeeSize := cfg.GetCommitteeSize(0) 486 validatorSize := cfg.GetNumOfCNs(0) 487 advanceChain := func(t *testing.T) { 488 for i := 0; i < committeeSize; i++ { 489 neoValidatorInvoker.AddNewBlock(t) 490 } 491 } 492 493 amount := int64(1000) 494 acc := e.NewAccount(t) 495 neoValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), acc.ScriptHash(), amount, nil) 496 committee, _ := e.Chain.GetCommittee() 497 neoValidatorInvoker.WithSigners(e.Validator, e.Validator.(neotest.MultiSigner).Single(0)).Invoke(t, true, "registerCandidate", committee[0].Bytes()) 498 neoValidatorInvoker.WithSigners(acc).Invoke(t, true, "vote", acc.ScriptHash(), committee[0].Bytes()) 499 advanceChain(t) 500 neoValidatorInvoker.WithSigners(acc).Invoke(t, true, "transfer", acc.ScriptHash(), acc.ScriptHash(), amount, nil) 501 502 var hashAStr string 503 for i := 0; i < util.Uint160Size; i++ { 504 hashAStr += fmt.Sprintf("%#x", acc.ScriptHash()[i]) 505 if i != util.Uint160Size-1 { 506 hashAStr += ", " 507 } 508 } 509 src := `package testaccountstate 510 import ( 511 "github.com/nspcc-dev/neo-go/pkg/interop/native/neo" 512 "github.com/nspcc-dev/neo-go/pkg/interop" 513 ) 514 func GetLastGasPerVote() int { 515 accState := neo.GetAccountState(interop.Hash160{` + hashAStr + `}) 516 if accState == nil { 517 panic("nil state") 518 } 519 return accState.LastGasPerVote 520 }` 521 ctr := neotest.CompileSource(t, e.Validator.ScriptHash(), strings.NewReader(src), &compiler.Options{ 522 Name: "testaccountstate_contract", 523 }) 524 e.DeployContract(t, ctr, nil) 525 526 const ( 527 GasPerBlock = 5 528 VoterRewardRatio = 80 529 ) 530 expect := GasPerBlock * native.GASFactor * VoterRewardRatio / 100 * (uint64(e.Chain.BlockHeight()) / uint64(committeeSize)) 531 expect = expect * uint64(committeeSize) / uint64(validatorSize+committeeSize) * native.NEOTotalSupply / uint64(amount) 532 ctrInvoker := e.NewInvoker(ctr.Hash, e.Committee) 533 ctrInvoker.Invoke(t, stackitem.Make(expect), "getLastGasPerVote") 534 } 535 536 func TestNEO_CommitteeBountyOnPersist(t *testing.T) { 537 neoCommitteeInvoker := newNeoCommitteeClient(t, 0) 538 e := neoCommitteeInvoker.Executor 539 540 hs, err := keys.NewPublicKeysFromStrings(e.Chain.GetConfig().StandbyCommittee) 541 require.NoError(t, err) 542 committeeSize := len(hs) 543 544 const singleBounty = 50000000 545 bs := map[int]int64{0: singleBounty} 546 checkBalances := func() { 547 for i := 0; i < committeeSize; i++ { 548 require.EqualValues(t, bs[i], e.Chain.GetUtilityTokenBalance(hs[i].GetScriptHash()).Int64(), i) 549 } 550 } 551 for i := 0; i < committeeSize*2; i++ { 552 e.AddNewBlock(t) 553 bs[(i+1)%committeeSize] += singleBounty 554 checkBalances() 555 } 556 } 557 558 func TestNEO_TransferOnPayment(t *testing.T) { 559 neoValidatorsInvoker := newNeoValidatorsClient(t) 560 e := neoValidatorsInvoker.Executor 561 managementValidatorsInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management)) 562 563 cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, e.CommitteeHash) 564 cs.Hash = state.CreateContractHash(e.Validator.ScriptHash(), cs.NEF.Checksum, cs.Manifest.Name) // set proper hash 565 manifB, err := json.Marshal(cs.Manifest) 566 require.NoError(t, err) 567 nefB, err := cs.NEF.Bytes() 568 require.NoError(t, err) 569 si, err := cs.ToStackItem() 570 require.NoError(t, err) 571 managementValidatorsInvoker.Invoke(t, si, "deploy", nefB, manifB) 572 573 const amount int64 = 2 574 575 h := neoValidatorsInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), cs.Hash, amount, nil) 576 aer := e.GetTxExecResult(t, h) 577 require.Equal(t, 3, len(aer.Events)) // transfer + GAS claim for sender + onPayment 578 e.CheckTxNotificationEvent(t, h, 1, state.NotificationEvent{ 579 ScriptHash: cs.Hash, 580 Name: "LastPaymentNEP17", 581 Item: stackitem.NewArray([]stackitem.Item{ 582 stackitem.NewByteArray(neoValidatorsInvoker.Hash.BytesBE()), 583 stackitem.NewByteArray(e.Validator.ScriptHash().BytesBE()), 584 stackitem.NewBigInteger(big.NewInt(amount)), 585 stackitem.Null{}, 586 }), 587 }) 588 589 h = neoValidatorsInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), cs.Hash, amount, nil) 590 aer = e.GetTxExecResult(t, h) 591 require.Equal(t, 5, len(aer.Events)) // Now we must also have GAS claim for contract and corresponding `onPayment`. 592 e.CheckTxNotificationEvent(t, h, 1, state.NotificationEvent{ // onPayment for NEO transfer 593 ScriptHash: cs.Hash, 594 Name: "LastPaymentNEP17", 595 Item: stackitem.NewArray([]stackitem.Item{ 596 stackitem.NewByteArray(e.NativeHash(t, nativenames.Neo).BytesBE()), 597 stackitem.NewByteArray(e.Validator.ScriptHash().BytesBE()), 598 stackitem.NewBigInteger(big.NewInt(amount)), 599 stackitem.Null{}, 600 }), 601 }) 602 e.CheckTxNotificationEvent(t, h, 4, state.NotificationEvent{ // onPayment for GAS claim 603 ScriptHash: cs.Hash, 604 Name: "LastPaymentNEP17", 605 Item: stackitem.NewArray([]stackitem.Item{ 606 stackitem.NewByteArray(e.NativeHash(t, nativenames.Gas).BytesBE()), 607 stackitem.Null{}, 608 stackitem.NewBigInteger(big.NewInt(1)), 609 stackitem.Null{}, 610 }), 611 }) 612 } 613 614 func TestNEO_Roundtrip(t *testing.T) { 615 neoValidatorsInvoker := newNeoValidatorsClient(t) 616 e := neoValidatorsInvoker.Executor 617 validatorH := neoValidatorsInvoker.Validator.ScriptHash() 618 619 initialBalance, initialHeight := e.Chain.GetGoverningTokenBalance(validatorH) 620 require.NotNil(t, initialBalance) 621 622 t.Run("bad: amount > initial balance", func(t *testing.T) { 623 h := neoValidatorsInvoker.Invoke(t, false, "transfer", validatorH, validatorH, initialBalance.Int64()+1, nil) 624 aer, err := e.Chain.GetAppExecResults(h, trigger.Application) 625 require.NoError(t, err) 626 require.Equal(t, 0, len(aer[0].Events)) // failed transfer => no events 627 // check balance and height were not changed 628 updatedBalance, updatedHeight := e.Chain.GetGoverningTokenBalance(validatorH) 629 require.Equal(t, initialBalance, updatedBalance) 630 require.Equal(t, initialHeight, updatedHeight) 631 }) 632 633 t.Run("good: amount == initial balance", func(t *testing.T) { 634 h := neoValidatorsInvoker.Invoke(t, true, "transfer", validatorH, validatorH, initialBalance.Int64(), nil) 635 aer, err := e.Chain.GetAppExecResults(h, trigger.Application) 636 require.NoError(t, err) 637 require.Equal(t, 2, len(aer[0].Events)) // roundtrip + GAS claim 638 // check balance wasn't changed and height was updated 639 updatedBalance, updatedHeight := e.Chain.GetGoverningTokenBalance(validatorH) 640 require.Equal(t, initialBalance, updatedBalance) 641 require.Equal(t, e.Chain.BlockHeight(), updatedHeight) 642 }) 643 } 644 645 func TestNEO_TransferZeroWithZeroBalance(t *testing.T) { 646 neoValidatorsInvoker := newNeoValidatorsClient(t) 647 e := neoValidatorsInvoker.Executor 648 649 check := func(t *testing.T, roundtrip bool) { 650 acc := neoValidatorsInvoker.WithSigners(e.NewAccount(t)) 651 accH := acc.Signers[0].ScriptHash() 652 to := accH 653 if !roundtrip { 654 to = random.Uint160() 655 } 656 h := acc.Invoke(t, true, "transfer", accH, to, int64(0), nil) 657 aer, err := e.Chain.GetAppExecResults(h, trigger.Application) 658 require.NoError(t, err) 659 require.Equal(t, 1, len(aer[0].Events)) // roundtrip/transfer only, no GAS claim 660 require.Equal(t, stackitem.NewBigInteger(big.NewInt(0)), aer[0].Events[0].Item.Value().([]stackitem.Item)[2]) // amount is 0 661 // check balance wasn't changed and height was updated 662 updatedBalance, updatedHeight := e.Chain.GetGoverningTokenBalance(accH) 663 require.Equal(t, int64(0), updatedBalance.Int64()) 664 require.Equal(t, uint32(0), updatedHeight) 665 } 666 t.Run("roundtrip: amount == initial balance == 0", func(t *testing.T) { 667 check(t, true) 668 }) 669 t.Run("non-roundtrip: amount == initial balance == 0", func(t *testing.T) { 670 check(t, false) 671 }) 672 } 673 674 func TestNEO_TransferZeroWithNonZeroBalance(t *testing.T) { 675 neoValidatorsInvoker := newNeoValidatorsClient(t) 676 e := neoValidatorsInvoker.Executor 677 678 check := func(t *testing.T, roundtrip bool) { 679 acc := e.NewAccount(t) 680 neoValidatorsInvoker.Invoke(t, true, "transfer", neoValidatorsInvoker.Validator.ScriptHash(), acc.ScriptHash(), int64(100), nil) 681 neoAccInvoker := neoValidatorsInvoker.WithSigners(acc) 682 initialBalance, _ := e.Chain.GetGoverningTokenBalance(acc.ScriptHash()) 683 require.True(t, initialBalance.Sign() > 0) 684 to := acc.ScriptHash() 685 if !roundtrip { 686 to = random.Uint160() 687 } 688 h := neoAccInvoker.Invoke(t, true, "transfer", acc.ScriptHash(), to, int64(0), nil) 689 690 aer, err := e.Chain.GetAppExecResults(h, trigger.Application) 691 require.NoError(t, err) 692 require.Equal(t, 2, len(aer[0].Events)) // roundtrip + GAS claim 693 require.Equal(t, stackitem.NewBigInteger(big.NewInt(0)), aer[0].Events[0].Item.Value().([]stackitem.Item)[2]) // amount is 0 694 // check balance wasn't changed and height was updated 695 updatedBalance, updatedHeight := e.Chain.GetGoverningTokenBalance(acc.ScriptHash()) 696 require.Equal(t, initialBalance, updatedBalance) 697 require.Equal(t, e.Chain.BlockHeight(), updatedHeight) 698 } 699 t.Run("roundtrip", func(t *testing.T) { 700 check(t, true) 701 }) 702 t.Run("non-roundtrip", func(t *testing.T) { 703 check(t, false) 704 }) 705 } 706 707 // https://github.com/nspcc-dev/neo-go/issues/3190 708 func TestNEO_TransferNonZeroWithZeroBalance(t *testing.T) { 709 neoValidatorsInvoker := newNeoValidatorsClient(t) 710 e := neoValidatorsInvoker.Executor 711 712 acc := neoValidatorsInvoker.WithSigners(e.NewAccount(t)) 713 accH := acc.Signers[0].ScriptHash() 714 h := acc.Invoke(t, false, "transfer", accH, accH, int64(5), nil) 715 aer := e.CheckHalt(t, h, stackitem.Make(false)) 716 require.Equal(t, 0, len(aer.Events)) 717 // check balance wasn't changed and height was not updated 718 updatedBalance, updatedHeight := e.Chain.GetGoverningTokenBalance(accH) 719 require.Equal(t, int64(0), updatedBalance.Int64()) 720 require.Equal(t, uint32(0), updatedHeight) 721 } 722 723 func TestNEO_CalculateBonus(t *testing.T) { 724 neoCommitteeInvoker := newNeoCommitteeClient(t, 10_0000_0000) 725 e := neoCommitteeInvoker.Executor 726 neoValidatorsInvoker := neoCommitteeInvoker.WithSigners(e.Validator) 727 728 acc := neoValidatorsInvoker.WithSigners(e.NewAccount(t)) 729 accH := acc.Signers[0].ScriptHash() 730 rewardDistance := 10 731 732 t.Run("Zero", func(t *testing.T) { 733 initialGASBalance := e.Chain.GetUtilityTokenBalance(accH) 734 for i := 0; i < rewardDistance; i++ { 735 e.AddNewBlock(t) 736 } 737 // Claim GAS, but there's no NEO on the account, so no GAS should be earned. 738 h := acc.Invoke(t, true, "transfer", accH, accH, 0, nil) 739 claimTx, _ := e.GetTransaction(t, h) 740 741 e.CheckGASBalance(t, accH, big.NewInt(initialGASBalance.Int64()-claimTx.SystemFee-claimTx.NetworkFee)) 742 }) 743 744 t.Run("Many blocks", func(t *testing.T) { 745 amount := 100 746 defaultGASParBlock := 5 747 newGASPerBlock := 1 748 749 initialGASBalance := e.Chain.GetUtilityTokenBalance(accH) 750 751 // Five blocks of NEO owning with default GasPerBlockValue. 752 neoValidatorsInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), accH, amount, nil) 753 for i := 0; i < rewardDistance/2-2; i++ { 754 e.AddNewBlock(t) 755 } 756 neoCommitteeInvoker.Invoke(t, stackitem.Null{}, "setGasPerBlock", newGASPerBlock*native.GASFactor) 757 758 // Five blocks more with modified GasPerBlock value. 759 for i := 0; i < rewardDistance/2; i++ { 760 e.AddNewBlock(t) 761 } 762 763 // GAS claim for the last 10 blocks of NEO owning. 764 h := acc.Invoke(t, true, "transfer", accH, accH, amount, nil) 765 claimTx, _ := e.GetTransaction(t, h) 766 767 firstPart := int64(amount*rewardDistance/2*defaultGASParBlock) / int64(rewardDistance) 768 secondPart := int64(amount*rewardDistance/2*newGASPerBlock) / int64(rewardDistance) 769 e.CheckGASBalance(t, accH, big.NewInt(initialGASBalance.Int64()- 770 claimTx.SystemFee-claimTx.NetworkFee + +firstPart + secondPart)) 771 }) 772 } 773 774 func TestNEO_GetCandidates(t *testing.T) { 775 neoCommitteeInvoker := newNeoCommitteeClient(t, 100_0000_0000) 776 neoValidatorsInvoker := neoCommitteeInvoker.WithSigners(neoCommitteeInvoker.Validator) 777 policyInvoker := neoCommitteeInvoker.CommitteeInvoker(neoCommitteeInvoker.NativeHash(t, nativenames.Policy)) 778 e := neoCommitteeInvoker.Executor 779 780 cfg := e.Chain.GetConfig() 781 candidatesCount := cfg.GetCommitteeSize(0) - 1 782 783 // Register a set of candidates and vote for them. 784 voters := make([]neotest.Signer, candidatesCount) 785 candidates := make([]neotest.Signer, candidatesCount) 786 for i := 0; i < candidatesCount; i++ { 787 voters[i] = e.NewAccount(t, 10_0000_0000) 788 candidates[i] = e.NewAccount(t, 2000_0000_0000) // enough for one registration 789 } 790 txes := make([]*transaction.Transaction, 0, candidatesCount*3) 791 for i := 0; i < candidatesCount; i++ { 792 transferTx := neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), int64(candidatesCount+1-i)*1000000, nil) 793 txes = append(txes, transferTx) 794 registerTx := neoValidatorsInvoker.WithSigners(candidates[i]).PrepareInvoke(t, "registerCandidate", candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes()) 795 txes = append(txes, registerTx) 796 voteTx := neoValidatorsInvoker.WithSigners(voters[i]).PrepareInvoke(t, "vote", voters[i].(neotest.SingleSigner).Account().PrivateKey().GetScriptHash(), candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes()) 797 txes = append(txes, voteTx) 798 } 799 800 neoValidatorsInvoker.AddNewBlock(t, txes...) 801 for _, tx := range txes { 802 e.CheckHalt(t, tx.Hash(), stackitem.Make(true)) // luckily, both `transfer`, `registerCandidate` and `vote` return boolean values 803 } 804 expected := make([]stackitem.Item, candidatesCount) 805 for i := range expected { 806 pub := candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes() 807 v := stackitem.NewBigInteger(big.NewInt(int64(candidatesCount-i+1) * 1000000)) 808 expected[i] = stackitem.NewStruct([]stackitem.Item{ 809 stackitem.NewByteArray(pub), 810 v, 811 }) 812 neoCommitteeInvoker.Invoke(t, v, "getCandidateVote", pub) 813 } 814 sort.Slice(expected, func(i, j int) bool { 815 return bytes.Compare(expected[i].Value().([]stackitem.Item)[0].Value().([]byte), expected[j].Value().([]stackitem.Item)[0].Value().([]byte)) < 0 816 }) 817 neoCommitteeInvoker.Invoke(t, stackitem.NewArray(expected), "getCandidates") 818 819 // Check that GetAllCandidates works the same way as GetCandidates. 820 checkGetAllCandidates := func(t *testing.T, expected []stackitem.Item) { 821 for i := 0; i < len(expected)+1; i++ { 822 w := io.NewBufBinWriter() 823 emit.AppCall(w.BinWriter, neoCommitteeInvoker.Hash, "getAllCandidates", callflag.All) 824 for j := 0; j < i+1; j++ { 825 emit.Opcodes(w.BinWriter, opcode.DUP) 826 emit.Syscall(w.BinWriter, interopnames.SystemIteratorNext) 827 emit.Opcodes(w.BinWriter, opcode.DROP) // drop the value returned from Next. 828 } 829 emit.Syscall(w.BinWriter, interopnames.SystemIteratorValue) 830 require.NoError(t, w.Err) 831 h := neoCommitteeInvoker.InvokeScript(t, w.Bytes(), neoCommitteeInvoker.Signers) 832 if i < len(expected) { 833 e.CheckHalt(t, h, expected[i]) 834 } else { 835 e.CheckFault(t, h, "iterator index out of range") // ensure there are no extra elements. 836 } 837 w.Reset() 838 } 839 } 840 checkGetAllCandidates(t, expected) 841 842 // Block candidate and check it won't be returned from getCandidates and getAllCandidates. 843 unlucky := candidates[len(candidates)-1].(neotest.SingleSigner).Account().PublicKey() 844 policyInvoker.Invoke(t, true, "blockAccount", unlucky.GetScriptHash()) 845 for i := range expected { 846 if bytes.Equal(expected[i].Value().([]stackitem.Item)[0].Value().([]byte), unlucky.Bytes()) { 847 if i != len(expected)-1 { 848 expected = append(expected[:i], expected[i+1:]...) 849 } else { 850 expected = expected[:i] 851 } 852 break 853 } 854 } 855 neoCommitteeInvoker.Invoke(t, expected, "getCandidates") 856 checkGetAllCandidates(t, expected) 857 }