From c113d682bd65033be3c7d31c6714191d2e3f7774 Mon Sep 17 00:00:00 2001
From: Anna Shaleva <shaleva.ann@nspcc.ru>
Date: Thu, 16 Sep 2021 17:09:42 +0300
Subject: [PATCH] core: fix NEO balance state handler

We need to store NEO balance's LastUpdateHeight before GAS mint,
because mint can call onNEP17Payment and onNEP17Payment can call NEO
transfer which also calls GAS mint. Storing balance height allows to
avoid recursion.
---
 pkg/core/helper_test.go                   |  11 +++++++-
 pkg/core/native/native_neo.go             |   9 ++++++
 pkg/core/native_neo_test.go               |  33 ++++++++++++++++++++++
 pkg/rpc/server/server_test.go             |   6 ++--
 pkg/rpc/server/testdata/test_contract.go  |  25 ++++++++++++++++
 pkg/rpc/server/testdata/test_contract.yml |   2 ++
 pkg/rpc/server/testdata/testblocks.acc    | Bin 23193 -> 23934 bytes
 7 files changed, 82 insertions(+), 4 deletions(-)

diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go
index 4f0f2f566..e91c28db3 100644
--- a/pkg/core/helper_test.go
+++ b/pkg/core/helper_test.go
@@ -28,6 +28,7 @@ import (
 	"github.com/nspcc-dev/neo-go/pkg/core/storage"
 	"github.com/nspcc-dev/neo-go/pkg/core/transaction"
 	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
+	"github.com/nspcc-dev/neo-go/pkg/encoding/address"
 	"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
 	"github.com/nspcc-dev/neo-go/pkg/io"
 	"github.com/nspcc-dev/neo-go/pkg/smartcontract"
@@ -395,6 +396,7 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
 		},
 	}
 	require.NoError(t, addNetworkFee(bc, transferTx, acc0))
+	transferTx.SystemFee += 1000000
 	require.NoError(t, acc0.SignTx(testchain.Network(), transferTx))
 
 	b = bc.newBlock(initTx, transferTx)
@@ -415,6 +417,7 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
 		},
 	}
 	require.NoError(t, addNetworkFee(bc, transferTx, acc0))
+	transferTx.SystemFee += 1000000
 	require.NoError(t, acc0.SignTx(testchain.Network(), transferTx))
 
 	b = bc.newBlock(transferTx)
@@ -603,14 +606,20 @@ func prepareContractMethodInvokeGeneric(chain *Blockchain, sysfee int64,
 func signTxWithAccounts(chain *Blockchain, tx *transaction.Transaction, accs ...*wallet.Account) {
 	scope := transaction.CalledByEntry
 	for _, acc := range accs {
+		accH, _ := address.StringToUint160(acc.Address)
 		tx.Signers = append(tx.Signers, transaction.Signer{
-			Account: acc.PrivateKey().GetScriptHash(),
+			Account: accH,
 			Scopes:  scope,
 		})
 		scope = transaction.Global
 	}
 	size := io.GetVarSize(tx)
 	for _, acc := range accs {
+		if acc.Contract.Deployed {
+			// don't need precise calculation for tests
+			tx.NetworkFee += 1000_0000
+			continue
+		}
 		netFee, sizeDelta := fee.Calculate(chain.GetBaseExecFee(), acc.Contract.Script)
 		size += sizeDelta
 		tx.NetworkFee += netFee
diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go
index 391525f1f..58c3a6517 100644
--- a/pkg/core/native/native_neo.go
+++ b/pkg/core/native/native_neo.go
@@ -442,6 +442,15 @@ func (n *NEO) distributeGas(ic *interop.Context, h util.Uint160, acc *state.NEOB
 		return err
 	}
 	acc.BalanceHeight = ic.Block.Index
+
+	// Must store acc before GAS distribution to fix acc's BalanceHeight value in the storage for
+	// further acc's queries from `onNEP17Payment` if so, see https://github.com/nspcc-dev/neo-go/pull/2181.
+	key := makeAccountKey(h)
+	err = ic.DAO.PutStorageItem(n.ID, key, acc.Bytes())
+	if err != nil {
+		return fmt.Errorf("failed to store acc before gas distribution: %w", err)
+	}
+
 	n.GAS.mint(ic, h, gen, true)
 	return nil
 }
diff --git a/pkg/core/native_neo_test.go b/pkg/core/native_neo_test.go
index 089543113..4d2fbf2f1 100644
--- a/pkg/core/native_neo_test.go
+++ b/pkg/core/native_neo_test.go
@@ -195,6 +195,39 @@ func TestNEO_Vote(t *testing.T) {
 	}
 }
 
+// TestNEO_RecursiveDistribution is a test for https://github.com/nspcc-dev/neo-go/pull/2181.
+func TestNEO_RecursiveGASMint(t *testing.T) {
+	bc := newTestChain(t)
+	initBasicChain(t, bc)
+
+	contractHash, err := bc.GetContractScriptHash(1) // deployed rpc/server/testdata/test_contract.go contract
+	require.NoError(t, err)
+	tx := transferTokenFromMultisigAccount(t, bc, contractHash, bc.contracts.GAS.Hash, 2_0000_0000)
+	checkTxHalt(t, bc, tx.Hash())
+
+	// Transfer 10 NEO to test contract, the contract should earn some GAS by owning this NEO.
+	tx = transferTokenFromMultisigAccount(t, bc, contractHash, bc.contracts.NEO.Hash, 10)
+	res, err := bc.GetAppExecResults(tx.Hash(), trigger.Application)
+	require.NoError(t, err)
+	require.Equal(t, vm.HaltState, res[0].VMState)
+
+	// Add blocks to be able to trigger NEO transfer from contract address to owner
+	// address inside onNEP17Payment (the contract starts NEO transfers from chain height = 100).
+	for i := bc.BlockHeight(); i < 100; i++ {
+		require.NoError(t, bc.AddBlock(bc.newBlock()))
+	}
+
+	// Transfer 1 more NEO to the contract. Transfer will trigger onNEP17Payment. OnNEP17Payment will
+	// trigger transfer of 11 NEO to the contract owner (based on the contract code). 11 NEO Transfer will
+	// trigger GAS distribution. GAS transfer will trigger OnNEP17Payment one more time. The recursion
+	// shouldn't occur here, because contract's balance LastUpdated height has already been updated in
+	// this block.
+	tx = transferTokenFromMultisigAccount(t, bc, contractHash, bc.contracts.NEO.Hash, 1)
+	res, err = bc.GetAppExecResults(tx.Hash(), trigger.Application)
+	require.NoError(t, err)
+	require.Equal(t, vm.HaltState, res[0].VMState, res[0].FaultException)
+}
+
 func TestNEO_SetGasPerBlock(t *testing.T) {
 	bc := newTestChain(t)
 
diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go
index 30c58984f..76ca0e202 100644
--- a/pkg/rpc/server/server_test.go
+++ b/pkg/rpc/server/server_test.go
@@ -54,8 +54,8 @@ type rpcTestCase struct {
 	check  func(t *testing.T, e *executor, result interface{})
 }
 
-const testContractHash = "bb6a679438ce0fc6cb0ed1aa85ce83cf96cd3aeb"
-const deploymentTxHash = "4c631654b04f6a3b25af45082d260b555de4d0eeba6b7697e3a0f18b3f96434f"
+const testContractHash = "5c9e40a12055c6b9e3f72271c9779958c842135d"
+const deploymentTxHash = "fefc10d2f7e323282cb50838174b68979b1794c1e5131f2b4737acbc5dde5932"
 const genesisBlockHash = "0f8fb4e17d2ab9f3097af75ca7fd16064160fb8043db94909e00dd4e257b9dc4"
 
 const verifyContractHash = "f68822e4ecd93de334bdf1f7c409eda3431bcbd0"
@@ -1650,7 +1650,7 @@ func checkNep17Balances(t *testing.T, e *executor, acc interface{}) {
 			},
 			{
 				Asset:       e.chain.UtilityTokenHash(),
-				Amount:      "57900879260",
+				Amount:      "57898138260",
 				LastUpdated: 15,
 			}},
 		Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(),
diff --git a/pkg/rpc/server/testdata/test_contract.go b/pkg/rpc/server/testdata/test_contract.go
index 01e6a63f4..11f9f9a46 100644
--- a/pkg/rpc/server/testdata/test_contract.go
+++ b/pkg/rpc/server/testdata/test_contract.go
@@ -3,9 +3,12 @@ package testdata
 import (
 	"github.com/nspcc-dev/neo-go/pkg/interop"
 	"github.com/nspcc-dev/neo-go/pkg/interop/contract"
+	"github.com/nspcc-dev/neo-go/pkg/interop/native/ledger"
 	"github.com/nspcc-dev/neo-go/pkg/interop/native/management"
+	"github.com/nspcc-dev/neo-go/pkg/interop/native/neo"
 	"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
 	"github.com/nspcc-dev/neo-go/pkg/interop/storage"
+	"github.com/nspcc-dev/neo-go/pkg/interop/util"
 )
 
 const (
@@ -13,6 +16,8 @@ const (
 	decimals    = 2
 )
 
+var owner = util.FromAddress("NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB")
+
 func Init() bool {
 	ctx := storage.GetContext()
 	h := runtime.GetExecutingScriptHash()
@@ -22,6 +27,26 @@ func Init() bool {
 	return true
 }
 
+func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) {
+	curr := runtime.GetExecutingScriptHash()
+	balance := neo.BalanceOf(curr)
+	if ledger.CurrentIndex() >= 100 {
+		ok := neo.Transfer(curr, owner, balance, nil)
+		if !ok {
+			panic("owner transfer failed")
+		}
+		ok = neo.Transfer(curr, owner, 0, nil)
+		if !ok {
+			panic("owner transfer failed")
+		}
+	}
+}
+
+// Verify always returns true and is aimed to serve the TestNEO_RecursiveGASMint.
+func Verify() bool {
+	return true
+}
+
 func Transfer(from, to interop.Hash160, amount int, data interface{}) bool {
 	ctx := storage.GetContext()
 	if len(from) != 20 {
diff --git a/pkg/rpc/server/testdata/test_contract.yml b/pkg/rpc/server/testdata/test_contract.yml
index 2761f0c7e..e0a5ce927 100644
--- a/pkg/rpc/server/testdata/test_contract.yml
+++ b/pkg/rpc/server/testdata/test_contract.yml
@@ -11,4 +11,6 @@ events:
       - name: amount
         type: Integer
 permissions:
+  - hash: ef4073a0f2b305a38ec4050e4d3d28bc40ea63f5
+    methods: ["transfer"]
   - methods: ["onNEP17Payment"]
\ No newline at end of file
diff --git a/pkg/rpc/server/testdata/testblocks.acc b/pkg/rpc/server/testdata/testblocks.acc
index 155622bacf2762b6cc0afb04d147b861069ed32d..0f48ffd7870602f42558a1405234f506559d94f7 100644
GIT binary patch
delta 6567
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
b)5a>%wo5FgPCDilHi_3(eZlbRwxj(wP5kQ3

delta 6204
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
EKLn#p<NyEw