From 02727b14b76476be3c2f9a73bbe1c70b6be464b6 Mon Sep 17 00:00:00 2001 From: Ekaterina Pavlova Date: Mon, 30 Sep 2024 15:21:19 +0300 Subject: [PATCH] vm: fix unclaimedGas calculation Fix difference with C#: ``` (base) ekaterinapavlova@MacBook-Air-4 neo-go % curl -X POST http://seed1t5.neo.org:20332 -H 'Content-Type: application/json' -d '{ "jsonrpc": "2.0", "method": "getapplicationlog", "params": ["61681ce24dffea5481e9a50b10159b43b7ebfc21967b0b06fee7ff69c7123e3f"], "id": 1 }' | json_pp % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 429 0 269 100 160 582 346 --:--:-- --:--:-- --:--:-- 930 { "id" : 1, "jsonrpc" : "2.0", "result" : { "executions" : [ { "exception" : null, "gasconsumed" : "198754", "notifications" : [], "stack" : [ { "type" : "Integer", "value" : "0" } ], "trigger" : "Application", "vmstate" : "HALT" } ], "txid" : "0x61681ce24dffea5481e9a50b10159b43b7ebfc21967b0b06fee7ff69c7123e3 f" } } ``` (base) ekaterinapavlova@MacBook-Air-4 neo-go % curl -X POST https://rpc .t5.n3.nspcc.ru:20331 -H 'Content-Type: application/json' -d '{ "jsonrpc": "2.0", "method": "getapplicationlog", "params": ["61681ce24dffea5481e9a50b10159b43b7ebfc21967b0b06fee7ff69c7123e3f"], "id": 1 }' | json_pp % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 583 100 423 100 160 1424 538 --:--:-- --:--:-- --:--:-- 1969 { "id" : 1, "jsonrpc" : "2.0", "result" : { "executions" : [ { "exception" : "at instruction 120 (SYSCALL): can't calculate bonus of height unequal (BlockHeight + 1)", "gasconsumed" : "198754", "notifications" : [], "stack" : [ { "type" : "Integer", "value" : "4704605" }, { "type" : "ByteString", "value" : "KfYYlDe/fxqqqm1yr7o5XLnQ7uk=" } ], "trigger" : "Application", "vmstate" : "FAULT" } ], "txid" : "0x61681ce24dffea5481e9a50b10159b43b7ebfc21967b0b06fee7ff69c7123e3 f" } } ``` ``` (base) ekaterinapavlova@MacBook-Air-4 neo-go % ./bin/neo-go contract invokefunction -r https://rpc.t5.n3.nspcc.ru:20331 -w ./testnet_wallet .json 1e6f88377a6c6bc4f683a5fc61eed5645ec5f123 unclaimedGas e9eed0b95c39baaf726daaaa1a7fbf379418f629 4704605 Enter account NWtk9HYWsf1njtSzA3XNgwZXRtriACcJ9G password > Warning: FAULT VM state returned from the RPC node: at instruction 120 (SYSCALL): can't calculate bonus of height unequal (BlockHeight + 1). Use --force flag to send the transaction anyway. ``` Close #3589 Signed-off-by: Ekaterina Pavlova --- pkg/core/native/native_neo.go | 11 ++++++---- pkg/core/native/native_test/neo_test.go | 29 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index 7a7211183..9978e0b97 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -750,18 +750,21 @@ func makeVoterKey(pub []byte, prealloc ...[]byte) []byte { // CalculateBonus calculates amount of gas generated for holding value NEO from start to end block // and having voted for active committee member. func (n *NEO) CalculateBonus(ic *interop.Context, acc util.Uint160, end uint32) (*big.Int, error) { - if ic.Block == nil || end != ic.Block.Index { - return nil, errors.New("can't calculate bonus of height unequal (BlockHeight + 1)") - } key := makeAccountKey(acc) si := ic.DAO.GetStorageItem(n.ID, key) if si == nil { - return nil, storage.ErrKeyNotFound + return big.NewInt(0), nil } st, err := state.NEOBalanceFromBytes(si) if err != nil { return nil, err } + if st.Balance.Sign() == 0 { + return big.NewInt(0), nil + } + if ic.Block == nil || end != ic.Block.Index { + return nil, errors.New("can't calculate bonus of height unequal (BlockHeight + 1)") + } return n.calculateBonus(ic.DAO, st, end) } diff --git a/pkg/core/native/native_test/neo_test.go b/pkg/core/native/native_test/neo_test.go index e25e0b91f..f0ef78ed7 100644 --- a/pkg/core/native/native_test/neo_test.go +++ b/pkg/core/native/native_test/neo_test.go @@ -776,6 +776,35 @@ func TestNEO_CalculateBonus(t *testing.T) { }) } +func TestNEO_UnclaimedGas(t *testing.T) { + neoValidatorsInvoker := newNeoValidatorsClient(t) + e := neoValidatorsInvoker.Executor + + acc := neoValidatorsInvoker.WithSigners(e.NewAccount(t)) + accH := acc.Signers[0].ScriptHash() + + t.Run("non-existing account", func(t *testing.T) { + // non-existing account, should return zero unclaimed GAS. + acc.Invoke(t, 0, "unclaimedGas", util.Uint160{}, 1) + }) + + t.Run("non-zero balance", func(t *testing.T) { + amount := 100 + defaultGASPerBlock := 5 + neoHolderRewardRatio := 10 + rewardDistance := 10 + neoValidatorsInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), accH, amount, nil) + + for range rewardDistance - 1 { + e.AddNewBlock(t) + } + expectedGas := int64(amount * neoHolderRewardRatio / 100 * defaultGASPerBlock * rewardDistance) + acc.Invoke(t, expectedGas, "unclaimedGas", accH, e.Chain.BlockHeight()+1) + + acc.InvokeFail(t, "can't calculate bonus of height unequal (BlockHeight + 1)", "unclaimedGas", accH, e.Chain.BlockHeight()) + }) +} + func TestNEO_GetCandidates(t *testing.T) { neoCommitteeInvoker := newNeoCommitteeClient(t, 100_0000_0000) neoValidatorsInvoker := neoCommitteeInvoker.WithSigners(neoCommitteeInvoker.Validator)