2018-03-09 15:55:25 +00:00
|
|
|
package core
|
|
|
|
|
|
|
|
import (
|
2020-07-23 14:00:06 +00:00
|
|
|
"bytes"
|
2021-03-05 07:18:03 +00:00
|
|
|
"encoding/base64"
|
2018-03-17 11:53:21 +00:00
|
|
|
"encoding/hex"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
2020-07-09 09:57:24 +00:00
|
|
|
"math/big"
|
2020-02-17 12:04:25 +00:00
|
|
|
"os"
|
2021-02-09 09:05:45 +00:00
|
|
|
"path"
|
2021-02-11 12:41:49 +00:00
|
|
|
"strings"
|
2018-03-17 11:53:21 +00:00
|
|
|
"testing"
|
2018-03-09 15:55:25 +00:00
|
|
|
"time"
|
|
|
|
|
2021-05-17 18:10:09 +00:00
|
|
|
nns "github.com/nspcc-dev/neo-go/examples/nft-nd-nns"
|
2020-11-23 11:09:00 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/internal/testchain"
|
2021-02-11 12:41:49 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
2020-03-25 15:30:21 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
2021-01-26 16:37:19 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
|
2020-11-24 08:36:09 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/chaindump"
|
2020-09-28 14:56:16 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
2020-06-23 14:15:35 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
2021-03-23 10:37:30 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
|
2020-11-19 10:00:46 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
2021-02-09 09:05:45 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
2021-09-16 14:09:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
2020-12-01 08:40:58 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
2020-12-29 10:44:07 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
2021-02-11 12:41:49 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
2020-11-19 10:00:46 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
2020-11-19 10:00:46 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
2020-11-19 10:00:46 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
2019-10-15 09:52:10 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2021-07-12 11:19:13 +00:00
|
|
|
"go.uber.org/zap"
|
2019-12-30 07:43:05 +00:00
|
|
|
"go.uber.org/zap/zaptest"
|
2018-03-09 15:55:25 +00:00
|
|
|
)
|
|
|
|
|
2021-05-12 20:17:03 +00:00
|
|
|
// multisig address which possess all NEO.
|
2020-04-21 15:09:34 +00:00
|
|
|
var neoOwner = testchain.MultisigScriptHash()
|
2020-04-16 14:10:42 +00:00
|
|
|
|
2021-05-17 07:55:26 +00:00
|
|
|
// examplesPrefix is a prefix of the example smart-contracts.
|
|
|
|
const examplesPrefix = "../../examples/"
|
|
|
|
|
|
|
|
// newTestChainWithNS should be called before newBlock invocation to properly setup
|
|
|
|
// global state.
|
|
|
|
func newTestChainWithNS(t *testing.T) (*Blockchain, util.Uint160) {
|
|
|
|
bc := newTestChainWithCustomCfg(t, nil)
|
|
|
|
acc := newAccountWithGAS(t, bc)
|
|
|
|
// Push NameService contract into the chain.
|
|
|
|
nsPath := examplesPrefix + "nft-nd-nns/"
|
|
|
|
nsConfigPath := nsPath + "nns.yml"
|
|
|
|
txDeploy4, _ := newDeployTx(t, bc, acc.PrivateKey().GetScriptHash(), nsPath, nsPath, &nsConfigPath)
|
|
|
|
txDeploy4.Nonce = 123
|
|
|
|
txDeploy4.ValidUntilBlock = bc.BlockHeight() + 1
|
|
|
|
require.NoError(t, addNetworkFee(bc, txDeploy4, acc))
|
|
|
|
require.NoError(t, acc.SignTx(testchain.Network(), txDeploy4))
|
|
|
|
b := bc.newBlock(txDeploy4)
|
|
|
|
require.NoError(t, bc.AddBlock(b))
|
|
|
|
checkTxHalt(t, bc, txDeploy4.Hash())
|
|
|
|
|
|
|
|
h, err := bc.GetContractScriptHash(1)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return bc, h
|
|
|
|
}
|
|
|
|
|
2019-10-15 09:52:10 +00:00
|
|
|
// newTestChain should be called before newBlock invocation to properly setup
|
|
|
|
// global state.
|
2021-07-12 11:19:13 +00:00
|
|
|
func newTestChain(t testing.TB) *Blockchain {
|
2020-11-26 09:33:34 +00:00
|
|
|
return newTestChainWithCustomCfg(t, nil)
|
2020-11-17 12:57:50 +00:00
|
|
|
}
|
|
|
|
|
2021-07-12 11:19:13 +00:00
|
|
|
func newTestChainWithCustomCfg(t testing.TB, f func(*config.Config)) *Blockchain {
|
2020-12-15 10:53:35 +00:00
|
|
|
return newTestChainWithCustomCfgAndStore(t, nil, f)
|
|
|
|
}
|
|
|
|
|
2021-07-12 11:19:13 +00:00
|
|
|
func newTestChainWithCustomCfgAndStore(t testing.TB, st storage.Store, f func(*config.Config)) *Blockchain {
|
2020-09-28 11:58:04 +00:00
|
|
|
chain := initTestChain(t, st, f)
|
|
|
|
go chain.Run()
|
2021-03-01 11:14:15 +00:00
|
|
|
t.Cleanup(chain.Close)
|
2020-09-28 11:58:04 +00:00
|
|
|
return chain
|
|
|
|
}
|
|
|
|
|
2021-07-12 11:19:13 +00:00
|
|
|
func initTestChain(t testing.TB, st storage.Store, f func(*config.Config)) *Blockchain {
|
2020-06-18 09:00:51 +00:00
|
|
|
unitTestNetCfg, err := config.Load("../../config", testchain.Network())
|
2020-02-29 15:55:16 +00:00
|
|
|
require.NoError(t, err)
|
2020-11-26 09:33:34 +00:00
|
|
|
if f != nil {
|
|
|
|
f(&unitTestNetCfg)
|
|
|
|
}
|
2020-12-15 10:53:35 +00:00
|
|
|
if st == nil {
|
|
|
|
st = storage.NewMemoryStore()
|
|
|
|
}
|
2021-07-12 11:19:13 +00:00
|
|
|
log := zaptest.NewLogger(t)
|
|
|
|
if _, ok := t.(*testing.B); ok {
|
|
|
|
log = zap.NewNop()
|
|
|
|
}
|
|
|
|
chain, err := NewBlockchain(st, unitTestNetCfg.ProtocolConfiguration, log)
|
2020-02-29 15:55:16 +00:00
|
|
|
require.NoError(t, err)
|
2019-10-15 09:52:10 +00:00
|
|
|
return chain
|
|
|
|
}
|
|
|
|
|
2020-02-29 14:24:37 +00:00
|
|
|
func (bc *Blockchain) newBlock(txs ...*transaction.Transaction) *block.Block {
|
2020-12-15 10:53:35 +00:00
|
|
|
lastBlock, ok := bc.topBlock.Load().(*block.Block)
|
|
|
|
if !ok {
|
|
|
|
var err error
|
|
|
|
lastBlock, err = bc.GetBlock(bc.GetHeaderHash(int(bc.BlockHeight())))
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
2020-11-17 12:57:50 +00:00
|
|
|
if bc.config.StateRootInHeader {
|
2021-01-29 14:33:24 +00:00
|
|
|
sr, err := bc.GetStateModule().GetStateRoot(bc.BlockHeight())
|
2020-11-17 12:57:50 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return newBlockWithState(bc.config, lastBlock.Index+1, lastBlock.Hash(), &sr.Root, txs...)
|
|
|
|
}
|
2020-02-29 14:24:37 +00:00
|
|
|
return newBlock(bc.config, lastBlock.Index+1, lastBlock.Hash(), txs...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func newBlock(cfg config.ProtocolConfiguration, index uint32, prev util.Uint256, txs ...*transaction.Transaction) *block.Block {
|
2020-11-17 12:57:50 +00:00
|
|
|
return newBlockWithState(cfg, index, prev, nil, txs...)
|
|
|
|
}
|
|
|
|
|
2021-01-22 12:12:09 +00:00
|
|
|
func newBlockCustom(cfg config.ProtocolConfiguration, f func(b *block.Block),
|
|
|
|
txs ...*transaction.Transaction) *block.Block {
|
2020-06-23 15:15:55 +00:00
|
|
|
validators, _ := validatorsFromConfig(cfg)
|
2020-08-10 15:58:11 +00:00
|
|
|
valScript, _ := smartcontract.CreateDefaultMultiSigRedeemScript(validators)
|
2019-12-09 14:14:10 +00:00
|
|
|
witness := transaction.Witness{
|
2019-10-15 09:52:10 +00:00
|
|
|
VerificationScript: valScript,
|
|
|
|
}
|
2020-01-14 12:32:07 +00:00
|
|
|
b := &block.Block{
|
2021-03-01 13:44:47 +00:00
|
|
|
Header: block.Header{
|
2019-10-15 09:52:10 +00:00
|
|
|
NextConsensus: witness.ScriptHash(),
|
|
|
|
Script: witness,
|
2018-03-09 15:55:25 +00:00
|
|
|
},
|
|
|
|
Transactions: txs,
|
|
|
|
}
|
2021-01-22 12:12:09 +00:00
|
|
|
f(b)
|
|
|
|
|
2020-09-15 15:38:15 +00:00
|
|
|
b.RebuildMerkleRoot()
|
2021-03-25 16:18:01 +00:00
|
|
|
b.Script.InvocationScript = testchain.Sign(b)
|
2018-03-09 15:55:25 +00:00
|
|
|
return b
|
|
|
|
}
|
|
|
|
|
2021-01-22 12:12:09 +00:00
|
|
|
func newBlockWithState(cfg config.ProtocolConfiguration, index uint32, prev util.Uint256,
|
|
|
|
prevState *util.Uint256, txs ...*transaction.Transaction) *block.Block {
|
|
|
|
return newBlockCustom(cfg, func(b *block.Block) {
|
|
|
|
b.PrevHash = prev
|
|
|
|
b.Timestamp = uint64(time.Now().UTC().Unix())*1000 + uint64(index)
|
|
|
|
b.Index = index
|
|
|
|
|
|
|
|
if prevState != nil {
|
|
|
|
b.StateRootEnabled = true
|
|
|
|
b.PrevStateRoot = *prevState
|
|
|
|
}
|
|
|
|
}, txs...)
|
|
|
|
}
|
|
|
|
|
2020-02-29 14:16:13 +00:00
|
|
|
func (bc *Blockchain) genBlocks(n int) ([]*block.Block, error) {
|
2020-01-14 12:32:07 +00:00
|
|
|
blocks := make([]*block.Block, n)
|
2020-02-29 14:24:37 +00:00
|
|
|
lastHash := bc.topBlock.Load().(*block.Block).Hash()
|
2020-03-04 14:45:29 +00:00
|
|
|
lastIndex := bc.topBlock.Load().(*block.Block).Index
|
2018-03-17 11:53:21 +00:00
|
|
|
for i := 0; i < n; i++ {
|
2020-04-22 17:42:38 +00:00
|
|
|
blocks[i] = newBlock(bc.config, uint32(i)+lastIndex+1, lastHash)
|
2020-02-29 14:16:13 +00:00
|
|
|
if err := bc.AddBlock(blocks[i]); err != nil {
|
|
|
|
return blocks, err
|
|
|
|
}
|
2020-02-29 14:24:37 +00:00
|
|
|
lastHash = blocks[i].Hash()
|
2018-03-17 11:53:21 +00:00
|
|
|
}
|
2020-02-29 14:16:13 +00:00
|
|
|
return blocks, nil
|
2018-03-17 11:53:21 +00:00
|
|
|
}
|
|
|
|
|
2021-02-11 12:41:49 +00:00
|
|
|
func TestBug1728(t *testing.T) {
|
|
|
|
src := `package example
|
|
|
|
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
|
|
|
func init() { if true { } else { } }
|
|
|
|
func _deploy(_ interface{}, isUpdate bool) {
|
|
|
|
runtime.Log("Deploy")
|
|
|
|
}`
|
|
|
|
b, di, err := compiler.CompileWithDebugInfo("foo", strings.NewReader(src))
|
|
|
|
require.NoError(t, err)
|
|
|
|
m, err := di.ConvertToManifest(&compiler.Options{Name: "TestContract"})
|
|
|
|
require.NoError(t, err)
|
|
|
|
nf, err := nef.NewFile(b)
|
|
|
|
require.NoError(t, err)
|
|
|
|
nf.CalculateChecksum()
|
|
|
|
|
|
|
|
rawManifest, err := json.Marshal(m)
|
|
|
|
require.NoError(t, err)
|
|
|
|
rawNef, err := nf.Bytes()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
bc := newTestChain(t)
|
|
|
|
|
|
|
|
aer, err := invokeContractMethod(bc, 10000000000,
|
|
|
|
bc.contracts.Management.Hash, "deploy", rawNef, rawManifest)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, aer.VMState, vm.HaltState)
|
|
|
|
}
|
|
|
|
|
2020-02-17 12:04:25 +00:00
|
|
|
// This function generates "../rpc/testdata/testblocks.acc" file which contains data
|
2020-03-04 14:45:29 +00:00
|
|
|
// for RPC unit tests. It also is a nice integration test.
|
2020-02-17 12:04:25 +00:00
|
|
|
// To generate new "../rpc/testdata/testblocks.acc", follow the steps:
|
2020-03-04 14:45:29 +00:00
|
|
|
// 1. Set saveChain down below to true
|
|
|
|
// 2. Run tests with `$ make test`
|
|
|
|
func TestCreateBasicChain(t *testing.T) {
|
|
|
|
const saveChain = false
|
2020-02-26 12:37:53 +00:00
|
|
|
const prefix = "../rpc/server/testdata/"
|
2020-11-24 08:36:09 +00:00
|
|
|
|
|
|
|
bc := newTestChain(t)
|
|
|
|
initBasicChain(t, bc)
|
|
|
|
|
|
|
|
if saveChain {
|
|
|
|
outStream, err := os.Create(prefix + "testblocks.acc")
|
|
|
|
require.NoError(t, err)
|
2021-03-01 11:14:15 +00:00
|
|
|
t.Cleanup(func() {
|
|
|
|
outStream.Close()
|
|
|
|
})
|
2020-11-24 08:36:09 +00:00
|
|
|
|
|
|
|
writer := io.NewBinWriterFromIO(outStream)
|
|
|
|
writer.WriteU32LE(bc.BlockHeight())
|
|
|
|
err = chaindump.Dump(bc, writer, 1, bc.BlockHeight())
|
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
priv0 := testchain.PrivateKeyByID(0)
|
|
|
|
priv1 := testchain.PrivateKeyByID(1)
|
|
|
|
priv0ScriptHash := priv0.GetScriptHash()
|
2020-12-04 09:40:22 +00:00
|
|
|
acc0 := wallet.NewAccountFromPrivateKey(priv0)
|
2020-11-24 08:36:09 +00:00
|
|
|
|
|
|
|
// Prepare some transaction for future submission.
|
2020-12-01 08:40:58 +00:00
|
|
|
txSendRaw := newNEP17Transfer(bc.contracts.NEO.Hash, priv0ScriptHash, priv1.GetScriptHash(), int64(fixedn.Fixed8FromInt64(1000)))
|
2021-05-17 08:07:08 +00:00
|
|
|
txSendRaw.ValidUntilBlock = bc.config.MaxValidUntilBlockIncrement
|
2020-11-24 08:36:09 +00:00
|
|
|
txSendRaw.Nonce = 0x1234
|
|
|
|
txSendRaw.Signers = []transaction.Signer{{
|
|
|
|
Account: priv0ScriptHash,
|
|
|
|
Scopes: transaction.CalledByEntry,
|
|
|
|
AllowedContracts: nil,
|
|
|
|
AllowedGroups: nil,
|
|
|
|
}}
|
|
|
|
require.NoError(t, addNetworkFee(bc, txSendRaw, acc0))
|
2021-03-25 16:18:01 +00:00
|
|
|
require.NoError(t, acc0.SignTx(testchain.Network(), txSendRaw))
|
2020-11-24 08:36:09 +00:00
|
|
|
bw := io.NewBufBinWriter()
|
|
|
|
txSendRaw.EncodeBinary(bw.BinWriter)
|
2021-05-11 13:32:09 +00:00
|
|
|
t.Logf("sendrawtransaction: \n\tbase64: %s\n\tHash LE: %s", base64.StdEncoding.EncodeToString(bw.Bytes()), txSendRaw.Hash().StringLE())
|
2020-12-13 16:09:41 +00:00
|
|
|
require.False(t, saveChain)
|
2020-11-24 08:36:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func initBasicChain(t *testing.T, bc *Blockchain) {
|
|
|
|
const prefix = "../rpc/server/testdata/"
|
2020-04-15 06:50:13 +00:00
|
|
|
// Increase in case if you need more blocks
|
2020-11-24 08:36:09 +00:00
|
|
|
const validUntilBlock = 1200
|
2020-02-26 12:37:53 +00:00
|
|
|
|
2020-04-10 10:41:49 +00:00
|
|
|
// To be incremented after each created transaction to keep chain constant.
|
|
|
|
var testNonce uint32 = 1
|
|
|
|
|
|
|
|
// Use as nonce when new transaction is created to avoid random data in tests.
|
|
|
|
getNextNonce := func() uint32 {
|
|
|
|
testNonce++
|
|
|
|
return testNonce
|
|
|
|
}
|
|
|
|
|
2020-06-04 19:21:12 +00:00
|
|
|
const neoAmount = 99999000
|
2020-02-17 12:04:25 +00:00
|
|
|
|
2020-05-08 17:54:24 +00:00
|
|
|
gasHash := bc.contracts.GAS.Hash
|
2020-06-04 19:21:12 +00:00
|
|
|
neoHash := bc.contracts.NEO.Hash
|
2020-07-22 16:03:05 +00:00
|
|
|
policyHash := bc.contracts.Policy.Hash
|
2021-02-09 09:05:45 +00:00
|
|
|
notaryHash := bc.contracts.Notary.Hash
|
2020-05-08 17:54:24 +00:00
|
|
|
t.Logf("native GAS hash: %v", gasHash)
|
2020-06-04 19:21:12 +00:00
|
|
|
t.Logf("native NEO hash: %v", neoHash)
|
2020-07-22 16:03:05 +00:00
|
|
|
t.Logf("native Policy hash: %v", policyHash)
|
2021-02-09 09:05:45 +00:00
|
|
|
t.Logf("native Notary hash: %v", notaryHash)
|
2021-03-09 10:17:51 +00:00
|
|
|
t.Logf("Block0 hash: %s", bc.GetHeaderHash(0).StringLE())
|
2020-05-08 17:54:24 +00:00
|
|
|
|
|
|
|
priv0 := testchain.PrivateKeyByID(0)
|
|
|
|
priv0ScriptHash := priv0.GetScriptHash()
|
|
|
|
|
2020-11-06 07:50:45 +00:00
|
|
|
require.Equal(t, big.NewInt(5000_0000), bc.GetUtilityTokenBalance(priv0ScriptHash)) // gas bounty
|
2020-06-04 19:21:12 +00:00
|
|
|
// Move some NEO to one simple account.
|
2020-11-23 11:09:45 +00:00
|
|
|
txMoveNeo, err := testchain.NewTransferFromOwner(bc, neoHash, priv0ScriptHash, neoAmount, getNextNonce(), validUntilBlock)
|
|
|
|
require.NoError(t, err)
|
2020-06-04 19:21:12 +00:00
|
|
|
// Move some GAS to one simple account.
|
2020-12-01 08:40:58 +00:00
|
|
|
txMoveGas, err := testchain.NewTransferFromOwner(bc, gasHash, priv0ScriptHash, int64(fixedn.Fixed8FromInt64(1000)),
|
2020-11-23 11:09:45 +00:00
|
|
|
getNextNonce(), validUntilBlock)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2020-06-04 19:21:12 +00:00
|
|
|
b := bc.newBlock(txMoveNeo, txMoveGas)
|
2020-02-18 08:09:07 +00:00
|
|
|
require.NoError(t, bc.AddBlock(b))
|
2021-03-22 12:35:22 +00:00
|
|
|
checkTxHalt(t, bc, txMoveGas.Hash())
|
|
|
|
checkTxHalt(t, bc, txMoveNeo.Hash())
|
2020-07-29 16:57:38 +00:00
|
|
|
t.Logf("Block1 hash: %s", b.Hash().StringLE())
|
|
|
|
bw := io.NewBufBinWriter()
|
|
|
|
b.EncodeBinary(bw.BinWriter)
|
|
|
|
require.NoError(t, bw.Err)
|
2021-03-17 13:40:24 +00:00
|
|
|
jsonB, err := b.MarshalJSON()
|
|
|
|
require.NoError(t, err)
|
|
|
|
t.Logf("Block1 base64: %s", base64.StdEncoding.EncodeToString(bw.Bytes()))
|
|
|
|
t.Logf("Block1 JSON: %s", string(jsonB))
|
|
|
|
bw.Reset()
|
|
|
|
b.Header.EncodeBinary(bw.BinWriter)
|
|
|
|
require.NoError(t, bw.Err)
|
|
|
|
jsonH, err := b.Header.MarshalJSON()
|
|
|
|
require.NoError(t, err)
|
|
|
|
t.Logf("Header1 base64: %s", base64.StdEncoding.EncodeToString(bw.Bytes()))
|
|
|
|
t.Logf("Header1 JSON: %s", string(jsonH))
|
|
|
|
jsonTxMoveNeo, err := txMoveNeo.MarshalJSON()
|
|
|
|
require.NoError(t, err)
|
2020-07-29 16:57:38 +00:00
|
|
|
t.Logf("txMoveNeo hash: %s", txMoveNeo.Hash().StringLE())
|
2021-03-17 13:40:24 +00:00
|
|
|
t.Logf("txMoveNeo JSON: %s", string(jsonTxMoveNeo))
|
|
|
|
t.Logf("txMoveNeo base64: %s", base64.StdEncoding.EncodeToString(txMoveNeo.Bytes()))
|
2020-07-29 16:57:38 +00:00
|
|
|
t.Logf("txMoveGas hash: %s", txMoveGas.Hash().StringLE())
|
2020-02-18 08:09:07 +00:00
|
|
|
|
2020-07-09 09:57:24 +00:00
|
|
|
require.True(t, bc.GetUtilityTokenBalance(priv0ScriptHash).Cmp(big.NewInt(1000*native.GASFactor)) >= 0)
|
2020-04-06 06:27:15 +00:00
|
|
|
// info for getblockheader rpc tests
|
|
|
|
t.Logf("header hash: %s", b.Hash().StringLE())
|
|
|
|
buf := io.NewBufBinWriter()
|
2021-03-01 13:44:47 +00:00
|
|
|
b.Header.EncodeBinary(buf.BinWriter)
|
2020-04-06 06:27:15 +00:00
|
|
|
t.Logf("header: %s", hex.EncodeToString(buf.Bytes()))
|
|
|
|
|
2020-12-04 09:40:22 +00:00
|
|
|
acc0 := wallet.NewAccountFromPrivateKey(priv0)
|
2020-03-04 14:45:29 +00:00
|
|
|
|
|
|
|
// Push some contract into the chain.
|
2021-03-22 09:21:48 +00:00
|
|
|
cfgPath := prefix + "test_contract.yml"
|
|
|
|
txDeploy, cHash := newDeployTx(t, bc, priv0ScriptHash, prefix+"test_contract.go", "Rubl", &cfgPath)
|
2020-04-10 10:41:49 +00:00
|
|
|
txDeploy.Nonce = getNextNonce()
|
2020-04-15 06:50:13 +00:00
|
|
|
txDeploy.ValidUntilBlock = validUntilBlock
|
2020-05-08 17:54:24 +00:00
|
|
|
require.NoError(t, addNetworkFee(bc, txDeploy, acc0))
|
2021-03-25 16:18:01 +00:00
|
|
|
require.NoError(t, acc0.SignTx(testchain.Network(), txDeploy))
|
2020-04-22 17:42:38 +00:00
|
|
|
b = bc.newBlock(txDeploy)
|
2020-03-04 14:45:29 +00:00
|
|
|
require.NoError(t, bc.AddBlock(b))
|
2021-03-22 12:35:22 +00:00
|
|
|
checkTxHalt(t, bc, txDeploy.Hash())
|
2020-03-04 14:45:29 +00:00
|
|
|
t.Logf("txDeploy: %s", txDeploy.Hash().StringLE())
|
2020-07-29 16:57:38 +00:00
|
|
|
t.Logf("Block2 hash: %s", b.Hash().StringLE())
|
2020-03-04 14:45:29 +00:00
|
|
|
|
|
|
|
// Now invoke this contract.
|
2020-08-24 11:00:05 +00:00
|
|
|
script := io.NewBufBinWriter()
|
2020-12-29 10:44:07 +00:00
|
|
|
emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "testkey", "testvalue")
|
2020-02-17 12:04:25 +00:00
|
|
|
|
2021-03-25 16:18:01 +00:00
|
|
|
txInv := transaction.New(script.Bytes(), 1*native.GASFactor)
|
2020-04-10 10:41:49 +00:00
|
|
|
txInv.Nonce = getNextNonce()
|
2020-04-15 06:50:13 +00:00
|
|
|
txInv.ValidUntilBlock = validUntilBlock
|
2020-07-29 16:57:38 +00:00
|
|
|
txInv.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
|
2020-05-08 17:54:24 +00:00
|
|
|
require.NoError(t, addNetworkFee(bc, txInv, acc0))
|
2021-03-25 16:18:01 +00:00
|
|
|
require.NoError(t, acc0.SignTx(testchain.Network(), txInv))
|
2020-04-22 17:42:38 +00:00
|
|
|
b = bc.newBlock(txInv)
|
2020-03-04 14:45:29 +00:00
|
|
|
require.NoError(t, bc.AddBlock(b))
|
2021-03-22 12:35:22 +00:00
|
|
|
checkTxHalt(t, bc, txInv.Hash())
|
2020-03-04 14:45:29 +00:00
|
|
|
t.Logf("txInv: %s", txInv.Hash().StringLE())
|
2020-02-17 12:04:25 +00:00
|
|
|
|
2020-04-22 10:15:31 +00:00
|
|
|
priv1 := testchain.PrivateKeyByID(1)
|
2020-11-24 08:14:25 +00:00
|
|
|
txNeo0to1 := newNEP17Transfer(neoHash, priv0ScriptHash, priv1.GetScriptHash(), 1000)
|
2020-04-10 10:41:49 +00:00
|
|
|
txNeo0to1.Nonce = getNextNonce()
|
2020-04-15 06:50:13 +00:00
|
|
|
txNeo0to1.ValidUntilBlock = validUntilBlock
|
2020-07-29 16:57:38 +00:00
|
|
|
txNeo0to1.Signers = []transaction.Signer{
|
2020-06-04 19:21:12 +00:00
|
|
|
{
|
|
|
|
Account: priv0ScriptHash,
|
|
|
|
Scopes: transaction.CalledByEntry,
|
|
|
|
AllowedContracts: nil,
|
|
|
|
AllowedGroups: nil,
|
|
|
|
},
|
|
|
|
}
|
2020-05-08 17:54:24 +00:00
|
|
|
require.NoError(t, addNetworkFee(bc, txNeo0to1, acc0))
|
2021-03-25 16:18:01 +00:00
|
|
|
require.NoError(t, acc0.SignTx(testchain.Network(), txNeo0to1))
|
2020-04-22 17:42:38 +00:00
|
|
|
b = bc.newBlock(txNeo0to1)
|
2020-03-04 14:45:29 +00:00
|
|
|
require.NoError(t, bc.AddBlock(b))
|
2021-03-22 12:35:22 +00:00
|
|
|
checkTxHalt(t, bc, txNeo0to1.Hash())
|
2020-03-04 14:45:29 +00:00
|
|
|
|
2020-03-05 09:28:46 +00:00
|
|
|
w := io.NewBufBinWriter()
|
2020-12-29 10:44:07 +00:00
|
|
|
emit.AppCall(w.BinWriter, cHash, "init", callflag.All)
|
2021-03-25 16:18:01 +00:00
|
|
|
initTx := transaction.New(w.Bytes(), 1*native.GASFactor)
|
2020-04-10 10:41:49 +00:00
|
|
|
initTx.Nonce = getNextNonce()
|
2020-04-15 06:50:13 +00:00
|
|
|
initTx.ValidUntilBlock = validUntilBlock
|
2020-07-29 16:57:38 +00:00
|
|
|
initTx.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
|
2020-05-08 17:54:24 +00:00
|
|
|
require.NoError(t, addNetworkFee(bc, initTx, acc0))
|
2021-03-25 16:18:01 +00:00
|
|
|
require.NoError(t, acc0.SignTx(testchain.Network(), initTx))
|
2020-11-18 20:10:48 +00:00
|
|
|
transferTx := newNEP17Transfer(cHash, cHash, priv0.GetScriptHash(), 1000)
|
2020-04-10 10:41:49 +00:00
|
|
|
transferTx.Nonce = getNextNonce()
|
2020-04-15 06:50:13 +00:00
|
|
|
transferTx.ValidUntilBlock = validUntilBlock
|
2020-07-29 16:57:38 +00:00
|
|
|
transferTx.Signers = []transaction.Signer{
|
2020-05-19 07:19:05 +00:00
|
|
|
{
|
|
|
|
Account: priv0ScriptHash,
|
|
|
|
Scopes: transaction.CalledByEntry,
|
|
|
|
AllowedContracts: nil,
|
|
|
|
AllowedGroups: nil,
|
|
|
|
},
|
|
|
|
}
|
2020-05-08 17:54:24 +00:00
|
|
|
require.NoError(t, addNetworkFee(bc, transferTx, acc0))
|
2021-09-16 14:09:42 +00:00
|
|
|
transferTx.SystemFee += 1000000
|
2021-03-25 16:18:01 +00:00
|
|
|
require.NoError(t, acc0.SignTx(testchain.Network(), transferTx))
|
2020-03-05 09:28:46 +00:00
|
|
|
|
2020-04-22 17:42:38 +00:00
|
|
|
b = bc.newBlock(initTx, transferTx)
|
2020-03-05 09:28:46 +00:00
|
|
|
require.NoError(t, bc.AddBlock(b))
|
2021-03-22 12:35:22 +00:00
|
|
|
checkTxHalt(t, bc, initTx.Hash())
|
|
|
|
checkTxHalt(t, bc, transferTx.Hash())
|
2020-05-27 20:28:20 +00:00
|
|
|
t.Logf("recieveRublesTx: %v", transferTx.Hash().StringLE())
|
2020-03-05 09:28:46 +00:00
|
|
|
|
2020-11-18 20:10:48 +00:00
|
|
|
transferTx = newNEP17Transfer(cHash, priv0.GetScriptHash(), priv1.GetScriptHash(), 123)
|
2020-04-10 10:41:49 +00:00
|
|
|
transferTx.Nonce = getNextNonce()
|
2020-04-15 06:50:13 +00:00
|
|
|
transferTx.ValidUntilBlock = validUntilBlock
|
2020-07-29 16:57:38 +00:00
|
|
|
transferTx.Signers = []transaction.Signer{
|
2020-05-19 07:19:05 +00:00
|
|
|
{
|
|
|
|
Account: priv0ScriptHash,
|
|
|
|
Scopes: transaction.CalledByEntry,
|
|
|
|
AllowedContracts: nil,
|
|
|
|
AllowedGroups: nil,
|
|
|
|
},
|
|
|
|
}
|
2020-05-08 17:54:24 +00:00
|
|
|
require.NoError(t, addNetworkFee(bc, transferTx, acc0))
|
2021-09-16 14:09:42 +00:00
|
|
|
transferTx.SystemFee += 1000000
|
2021-03-25 16:18:01 +00:00
|
|
|
require.NoError(t, acc0.SignTx(testchain.Network(), transferTx))
|
2020-04-16 14:10:42 +00:00
|
|
|
|
2020-04-22 17:42:38 +00:00
|
|
|
b = bc.newBlock(transferTx)
|
2020-03-05 09:28:46 +00:00
|
|
|
require.NoError(t, bc.AddBlock(b))
|
2021-03-22 12:35:22 +00:00
|
|
|
checkTxHalt(t, bc, transferTx.Hash())
|
2020-05-27 20:28:20 +00:00
|
|
|
t.Logf("sendRublesTx: %v", transferTx.Hash().StringLE())
|
2020-03-05 09:28:46 +00:00
|
|
|
|
2020-08-24 11:00:05 +00:00
|
|
|
// Push verification contract into the chain.
|
2021-03-22 09:21:48 +00:00
|
|
|
txDeploy2, _ := newDeployTx(t, bc, priv0ScriptHash, prefix+"verification_contract.go", "Verify", nil)
|
2020-08-24 11:00:05 +00:00
|
|
|
txDeploy2.Nonce = getNextNonce()
|
|
|
|
txDeploy2.ValidUntilBlock = validUntilBlock
|
|
|
|
require.NoError(t, addNetworkFee(bc, txDeploy2, acc0))
|
2021-03-25 16:18:01 +00:00
|
|
|
require.NoError(t, acc0.SignTx(testchain.Network(), txDeploy2))
|
2020-08-24 11:00:05 +00:00
|
|
|
b = bc.newBlock(txDeploy2)
|
|
|
|
require.NoError(t, bc.AddBlock(b))
|
2021-03-22 12:35:22 +00:00
|
|
|
checkTxHalt(t, bc, txDeploy2.Hash())
|
2021-02-09 09:05:45 +00:00
|
|
|
|
|
|
|
// Deposit some GAS to notary contract for priv0
|
|
|
|
transferTx = newNEP17Transfer(gasHash, priv0.GetScriptHash(), notaryHash, 10_0000_0000, priv0.GetScriptHash(), int64(bc.BlockHeight()+1000))
|
|
|
|
transferTx.Nonce = getNextNonce()
|
|
|
|
transferTx.ValidUntilBlock = validUntilBlock
|
|
|
|
transferTx.Signers = []transaction.Signer{
|
|
|
|
{
|
|
|
|
Account: priv0ScriptHash,
|
|
|
|
Scopes: transaction.CalledByEntry,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
require.NoError(t, addNetworkFee(bc, transferTx, acc0))
|
|
|
|
transferTx.SystemFee += 10_0000
|
2021-03-25 16:18:01 +00:00
|
|
|
require.NoError(t, acc0.SignTx(testchain.Network(), transferTx))
|
2021-02-09 09:05:45 +00:00
|
|
|
|
|
|
|
b = bc.newBlock(transferTx)
|
|
|
|
require.NoError(t, bc.AddBlock(b))
|
2021-03-22 12:35:22 +00:00
|
|
|
checkTxHalt(t, bc, transferTx.Hash())
|
2021-02-09 09:05:45 +00:00
|
|
|
t.Logf("notaryDepositTxPriv0: %v", transferTx.Hash().StringLE())
|
|
|
|
|
|
|
|
// Designate new Notary node
|
|
|
|
ntr, err := wallet.NewWalletFromFile(path.Join(notaryModulePath, "./testdata/notary1.json"))
|
|
|
|
require.NoError(t, err)
|
2021-06-04 11:27:22 +00:00
|
|
|
require.NoError(t, ntr.Accounts[0].Decrypt("one", ntr.Scrypt))
|
2021-03-23 10:37:30 +00:00
|
|
|
bc.setNodesByRole(t, true, noderoles.P2PNotary, keys.PublicKeys{ntr.Accounts[0].PrivateKey().PublicKey()})
|
2021-02-09 09:05:45 +00:00
|
|
|
t.Logf("Designated Notary node: %s", hex.EncodeToString(ntr.Accounts[0].PrivateKey().PublicKey().Bytes()))
|
2021-03-05 07:18:03 +00:00
|
|
|
|
2021-03-10 14:43:52 +00:00
|
|
|
// Push verification contract with arguments into the chain.
|
2021-03-22 09:21:48 +00:00
|
|
|
txDeploy3, _ := newDeployTx(t, bc, priv0ScriptHash, prefix+"verification_with_args_contract.go", "VerifyWithArgs", nil)
|
2021-03-10 14:43:52 +00:00
|
|
|
txDeploy3.Nonce = getNextNonce()
|
|
|
|
txDeploy3.ValidUntilBlock = validUntilBlock
|
|
|
|
require.NoError(t, addNetworkFee(bc, txDeploy3, acc0))
|
2021-03-25 16:18:01 +00:00
|
|
|
require.NoError(t, acc0.SignTx(testchain.Network(), txDeploy3))
|
2021-03-10 14:43:52 +00:00
|
|
|
b = bc.newBlock(txDeploy3)
|
2021-05-14 16:38:04 +00:00
|
|
|
require.NoError(t, bc.AddBlock(b)) // block #10
|
2021-03-22 12:35:22 +00:00
|
|
|
checkTxHalt(t, bc, txDeploy3.Hash())
|
2021-03-10 14:43:52 +00:00
|
|
|
|
2021-05-14 16:38:04 +00:00
|
|
|
// Push NameService contract into the chain.
|
|
|
|
nsPath := examplesPrefix + "nft-nd-nns/"
|
|
|
|
nsConfigPath := nsPath + "nns.yml"
|
|
|
|
txDeploy4, _ := newDeployTx(t, bc, priv0ScriptHash, nsPath, nsPath, &nsConfigPath)
|
|
|
|
txDeploy4.Nonce = getNextNonce()
|
|
|
|
txDeploy4.ValidUntilBlock = validUntilBlock
|
|
|
|
require.NoError(t, addNetworkFee(bc, txDeploy4, acc0))
|
|
|
|
require.NoError(t, acc0.SignTx(testchain.Network(), txDeploy4))
|
|
|
|
b = bc.newBlock(txDeploy4)
|
|
|
|
require.NoError(t, bc.AddBlock(b)) // block #11
|
|
|
|
checkTxHalt(t, bc, txDeploy4.Hash())
|
|
|
|
nsHash, err := bc.GetContractScriptHash(4)
|
|
|
|
require.NoError(t, err)
|
|
|
|
t.Logf("contract (%s): \n\tHash: %s\n", nsPath, nsHash.StringLE())
|
|
|
|
|
|
|
|
// register `neo.com` with A record type and priv0 owner via NS
|
|
|
|
transferFundsToCommittee(t, bc) // block #12
|
2021-03-24 12:36:10 +00:00
|
|
|
res, err := invokeContractMethodGeneric(bc, defaultNameServiceSysfee,
|
2021-05-14 16:38:04 +00:00
|
|
|
nsHash, "addRoot", true, "com") // block #13
|
2021-03-24 12:36:10 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
checkResult(t, res, stackitem.Null{})
|
2021-05-14 16:38:04 +00:00
|
|
|
res, err = invokeContractMethodGeneric(bc, defaultNameServiceDomainPrice+defaultNameServiceSysfee+1_0000_000,
|
|
|
|
nsHash, "register", acc0, "neo.com", priv0ScriptHash) // block #14
|
2021-03-24 12:36:10 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
checkResult(t, res, stackitem.NewBool(true))
|
2021-05-14 16:38:04 +00:00
|
|
|
res, err = invokeContractMethodGeneric(bc, defaultNameServiceSysfee, nsHash,
|
2021-05-17 18:10:09 +00:00
|
|
|
"setRecord", acc0, "neo.com", int64(nns.A), "1.2.3.4") // block #15
|
2021-03-24 12:36:10 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
checkResult(t, res, stackitem.Null{})
|
|
|
|
|
2021-10-07 09:03:37 +00:00
|
|
|
// Invoke `test_contract.go`: put new value with the same key to check `getstate` RPC call
|
|
|
|
script.Reset()
|
|
|
|
emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "testkey", "newtestvalue")
|
2021-10-07 13:56:27 +00:00
|
|
|
// Invoke `test_contract.go`: put values to check `findstates` RPC call
|
|
|
|
emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "aa", "v1")
|
|
|
|
emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "aa10", "v2")
|
|
|
|
emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "aa50", "v3")
|
2021-10-07 09:03:37 +00:00
|
|
|
|
|
|
|
txInv = transaction.New(script.Bytes(), 1*native.GASFactor)
|
|
|
|
txInv.Nonce = getNextNonce()
|
|
|
|
txInv.ValidUntilBlock = validUntilBlock
|
|
|
|
txInv.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
|
|
|
|
require.NoError(t, addNetworkFee(bc, txInv, acc0))
|
|
|
|
require.NoError(t, acc0.SignTx(testchain.Network(), txInv))
|
|
|
|
b = bc.newBlock(txInv)
|
|
|
|
require.NoError(t, bc.AddBlock(b))
|
|
|
|
checkTxHalt(t, bc, txInv.Hash())
|
|
|
|
|
2021-03-05 07:18:03 +00:00
|
|
|
// Compile contract to test `invokescript` RPC call
|
2021-03-22 09:21:48 +00:00
|
|
|
_, _ = newDeployTx(t, bc, priv0ScriptHash, prefix+"invokescript_contract.go", "ContractForInvokescriptTest", nil)
|
2020-02-17 12:04:25 +00:00
|
|
|
}
|
2020-03-05 09:28:46 +00:00
|
|
|
|
2020-11-19 10:00:46 +00:00
|
|
|
func newNEP17Transfer(sc, from, to util.Uint160, amount int64, additionalArgs ...interface{}) *transaction.Transaction {
|
core: do not allow NEP17 roundtrip in case of insufficient funds
NEP17 roundtrip is prohibited if from account doesn't have enough funds.
This commit fixes states diff in block 92057 where account
NfuwpaQ1A2xaeVbxWe8FRtaRgaMa8yF3YM initiates two NEO roundtrips with
amount exceeding the account's balance:
block 92057: value mismatch for key +////xTbYWBH3r5qhRKZAPFPHabKfb2vhQ==: QQMhAkwBIQOZZwEA vs QQMhAkwBIQN/ZwEA
block 92057: value mismatch for key +v///ws=: kqlddcitCg== vs tphddcitCg==
block 92057: value mismatch for key +v///xTbYWBH3r5qhRKZAPFPHabKfb2vhQ==: QQEhBUWyDu0W vs QQEhBWmhDu0W
C#'s applog (contains False and False on stack for both transfers):
```
{
"id" : 1,
"jsonrpc" : "2.0",
"result" : {
"executions" : [
{
"gasconsumed" : "11955500",
"exception" : null,
"stack" : [
{
"value" : false,
"type" : "Boolean"
},
{
"value" : false,
"type" : "Boolean"
}
],
"vmstate" : "HALT",
"trigger" : "Application",
"notifications" : []
}
],
"txid" : "0x8e73a7e9a566a514813907272ad65fc965002c3b098eacc5bdda529af19d7688"
}
}
```
Go's applog (both transfers succeeded and GAS minted):
```
{
"result" : {
"executions" : [
{
"gasconsumed" : "11955500",
"trigger" : "Application",
"stack" : [
{
"type" : "Boolean",
"value" : true
},
{
"type" : "Boolean",
"value" : true
}
],
"vmstate" : "HALT",
"notifications" : [
{
"eventname" : "Transfer",
"contract" : "0xd2a4cff31913016155e38e474a2c06d08be276cf",
"state" : {
"value" : [
{
"type" : "Any"
},
{
"value" : "22FgR96+aoUSmQDxTx2myn29r4U=",
"type" : "ByteString"
},
{
"value" : "4316",
"type" : "Integer"
}
],
"type" : "Array"
}
},
{
"state" : {
"type" : "Array",
"value" : [
{
"value" : "22FgR96+aoUSmQDxTx2myn29r4U=",
"type" : "ByteString"
},
{
"type" : "ByteString",
"value" : "22FgR96+aoUSmQDxTx2myn29r4U="
},
{
"type" : "Integer",
"value" : "1111111111"
}
]
},
"contract" : "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5",
"eventname" : "Transfer"
},
{
"contract" : "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5",
"state" : {
"type" : "Array",
"value" : [
{
"value" : "22FgR96+aoUSmQDxTx2myn29r4U=",
"type" : "ByteString"
},
{
"type" : "ByteString",
"value" : "22FgR96+aoUSmQDxTx2myn29r4U="
},
{
"type" : "Integer",
"value" : "1111111"
}
]
},
"eventname" : "Transfer"
}
]
}
],
"txid" : "0x8e73a7e9a566a514813907272ad65fc965002c3b098eacc5bdda529af19d7688"
},
"id" : 1,
"jsonrpc" : "2.0"
}
```
2021-06-09 08:58:58 +00:00
|
|
|
return newNEP17TransferWithAssert(sc, from, to, amount, true, additionalArgs...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func newNEP17TransferWithAssert(sc, from, to util.Uint160, amount int64, needAssert bool, additionalArgs ...interface{}) *transaction.Transaction {
|
2020-03-05 09:28:46 +00:00
|
|
|
w := io.NewBufBinWriter()
|
2020-12-29 10:44:07 +00:00
|
|
|
emit.AppCall(w.BinWriter, sc, "transfer", callflag.All, from, to, amount, additionalArgs)
|
core: do not allow NEP17 roundtrip in case of insufficient funds
NEP17 roundtrip is prohibited if from account doesn't have enough funds.
This commit fixes states diff in block 92057 where account
NfuwpaQ1A2xaeVbxWe8FRtaRgaMa8yF3YM initiates two NEO roundtrips with
amount exceeding the account's balance:
block 92057: value mismatch for key +////xTbYWBH3r5qhRKZAPFPHabKfb2vhQ==: QQMhAkwBIQOZZwEA vs QQMhAkwBIQN/ZwEA
block 92057: value mismatch for key +v///ws=: kqlddcitCg== vs tphddcitCg==
block 92057: value mismatch for key +v///xTbYWBH3r5qhRKZAPFPHabKfb2vhQ==: QQEhBUWyDu0W vs QQEhBWmhDu0W
C#'s applog (contains False and False on stack for both transfers):
```
{
"id" : 1,
"jsonrpc" : "2.0",
"result" : {
"executions" : [
{
"gasconsumed" : "11955500",
"exception" : null,
"stack" : [
{
"value" : false,
"type" : "Boolean"
},
{
"value" : false,
"type" : "Boolean"
}
],
"vmstate" : "HALT",
"trigger" : "Application",
"notifications" : []
}
],
"txid" : "0x8e73a7e9a566a514813907272ad65fc965002c3b098eacc5bdda529af19d7688"
}
}
```
Go's applog (both transfers succeeded and GAS minted):
```
{
"result" : {
"executions" : [
{
"gasconsumed" : "11955500",
"trigger" : "Application",
"stack" : [
{
"type" : "Boolean",
"value" : true
},
{
"type" : "Boolean",
"value" : true
}
],
"vmstate" : "HALT",
"notifications" : [
{
"eventname" : "Transfer",
"contract" : "0xd2a4cff31913016155e38e474a2c06d08be276cf",
"state" : {
"value" : [
{
"type" : "Any"
},
{
"value" : "22FgR96+aoUSmQDxTx2myn29r4U=",
"type" : "ByteString"
},
{
"value" : "4316",
"type" : "Integer"
}
],
"type" : "Array"
}
},
{
"state" : {
"type" : "Array",
"value" : [
{
"value" : "22FgR96+aoUSmQDxTx2myn29r4U=",
"type" : "ByteString"
},
{
"type" : "ByteString",
"value" : "22FgR96+aoUSmQDxTx2myn29r4U="
},
{
"type" : "Integer",
"value" : "1111111111"
}
]
},
"contract" : "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5",
"eventname" : "Transfer"
},
{
"contract" : "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5",
"state" : {
"type" : "Array",
"value" : [
{
"value" : "22FgR96+aoUSmQDxTx2myn29r4U=",
"type" : "ByteString"
},
{
"type" : "ByteString",
"value" : "22FgR96+aoUSmQDxTx2myn29r4U="
},
{
"type" : "Integer",
"value" : "1111111"
}
]
},
"eventname" : "Transfer"
}
]
}
],
"txid" : "0x8e73a7e9a566a514813907272ad65fc965002c3b098eacc5bdda529af19d7688"
},
"id" : 1,
"jsonrpc" : "2.0"
}
```
2021-06-09 08:58:58 +00:00
|
|
|
if needAssert {
|
|
|
|
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
|
|
|
}
|
2021-01-26 16:37:19 +00:00
|
|
|
if w.Err != nil {
|
|
|
|
panic(fmt.Errorf("failed to create nep17 transfer transaction: %w", w.Err))
|
|
|
|
}
|
2020-03-05 09:28:46 +00:00
|
|
|
|
|
|
|
script := w.Bytes()
|
2021-03-25 16:18:01 +00:00
|
|
|
return transaction.New(script, 11000000)
|
2020-03-05 09:28:46 +00:00
|
|
|
}
|
2020-04-16 14:10:42 +00:00
|
|
|
|
2021-03-22 09:21:48 +00:00
|
|
|
func newDeployTx(t *testing.T, bc *Blockchain, sender util.Uint160, name, ctrName string, cfgName *string) (*transaction.Transaction, util.Uint160) {
|
2020-08-24 11:00:05 +00:00
|
|
|
c, err := ioutil.ReadFile(name)
|
2021-05-14 16:38:04 +00:00
|
|
|
var (
|
|
|
|
tx *transaction.Transaction
|
|
|
|
h util.Uint160
|
|
|
|
avm []byte
|
|
|
|
)
|
|
|
|
if err == nil {
|
|
|
|
tx, h, avm, err = testchain.NewDeployTx(bc, ctrName, sender, bytes.NewReader(c), cfgName)
|
|
|
|
} else {
|
|
|
|
tx, h, avm, err = testchain.NewDeployTx(bc, ctrName, sender, nil, cfgName)
|
|
|
|
}
|
2020-08-24 11:00:05 +00:00
|
|
|
require.NoError(t, err)
|
2021-03-05 07:18:03 +00:00
|
|
|
t.Logf("contract (%s): \n\tHash: %s\n\tAVM: %s", name, h.StringLE(), base64.StdEncoding.EncodeToString(avm))
|
2020-11-18 20:10:48 +00:00
|
|
|
return tx, h
|
2020-08-24 11:00:05 +00:00
|
|
|
}
|
|
|
|
|
2021-01-21 09:23:53 +00:00
|
|
|
func addSigners(sender util.Uint160, txs ...*transaction.Transaction) {
|
2020-06-15 18:13:32 +00:00
|
|
|
for _, tx := range txs {
|
2020-07-29 16:57:38 +00:00
|
|
|
tx.Signers = []transaction.Signer{{
|
2021-01-21 09:23:53 +00:00
|
|
|
Account: sender,
|
2020-06-15 18:13:32 +00:00
|
|
|
Scopes: transaction.CalledByEntry,
|
|
|
|
AllowedContracts: nil,
|
|
|
|
AllowedGroups: nil,
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-08 17:54:24 +00:00
|
|
|
func addNetworkFee(bc *Blockchain, tx *transaction.Transaction, sender *wallet.Account) error {
|
|
|
|
size := io.GetVarSize(tx)
|
2020-12-11 12:22:49 +00:00
|
|
|
netFee, sizeDelta := fee.Calculate(bc.GetBaseExecFee(), sender.Contract.Script)
|
2020-05-08 17:54:24 +00:00
|
|
|
tx.NetworkFee += netFee
|
|
|
|
size += sizeDelta
|
2020-07-29 16:57:38 +00:00
|
|
|
for _, cosigner := range tx.Signers {
|
2020-05-08 17:54:24 +00:00
|
|
|
contract := bc.GetContractState(cosigner.Account)
|
|
|
|
if contract != nil {
|
2021-01-13 12:34:10 +00:00
|
|
|
netFee, sizeDelta = fee.Calculate(bc.GetBaseExecFee(), contract.NEF.Script)
|
2020-05-08 17:54:24 +00:00
|
|
|
tx.NetworkFee += netFee
|
|
|
|
size += sizeDelta
|
|
|
|
}
|
|
|
|
}
|
2020-06-23 14:15:35 +00:00
|
|
|
tx.NetworkFee += int64(size) * bc.FeePerByte()
|
2020-05-08 17:54:24 +00:00
|
|
|
return nil
|
|
|
|
}
|
2020-11-19 10:00:46 +00:00
|
|
|
|
2021-01-22 12:11:57 +00:00
|
|
|
// Signer can be either bool or *wallet.Account.
|
|
|
|
// In the first case `true` means sign by committee, `false` means sign by validators.
|
2021-01-21 09:23:53 +00:00
|
|
|
func prepareContractMethodInvokeGeneric(chain *Blockchain, sysfee int64,
|
2021-01-22 12:11:57 +00:00
|
|
|
hash util.Uint160, method string, signer interface{}, args ...interface{}) (*transaction.Transaction, error) {
|
2020-11-19 10:00:46 +00:00
|
|
|
w := io.NewBufBinWriter()
|
2020-12-29 10:44:07 +00:00
|
|
|
emit.AppCall(w.BinWriter, hash, method, callflag.All, args...)
|
2020-11-19 10:00:46 +00:00
|
|
|
if w.Err != nil {
|
|
|
|
return nil, w.Err
|
|
|
|
}
|
|
|
|
script := w.Bytes()
|
2021-03-25 16:18:01 +00:00
|
|
|
tx := transaction.New(script, sysfee)
|
2020-11-19 10:00:46 +00:00
|
|
|
tx.ValidUntilBlock = chain.blockHeight + 1
|
2021-01-21 09:23:53 +00:00
|
|
|
var err error
|
2021-01-22 12:11:57 +00:00
|
|
|
switch s := signer.(type) {
|
|
|
|
case bool:
|
|
|
|
if s {
|
|
|
|
addSigners(testchain.CommitteeScriptHash(), tx)
|
|
|
|
err = testchain.SignTxCommittee(chain, tx)
|
|
|
|
} else {
|
|
|
|
addSigners(neoOwner, tx)
|
|
|
|
err = testchain.SignTx(chain, tx)
|
|
|
|
}
|
|
|
|
case *wallet.Account:
|
|
|
|
signTxWithAccounts(chain, tx, s)
|
|
|
|
case []*wallet.Account:
|
|
|
|
signTxWithAccounts(chain, tx, s...)
|
|
|
|
default:
|
|
|
|
panic("invalid signer")
|
2021-01-21 09:23:53 +00:00
|
|
|
}
|
2020-11-19 10:00:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-12-14 09:18:59 +00:00
|
|
|
return tx, nil
|
|
|
|
}
|
|
|
|
|
2021-01-22 12:11:57 +00:00
|
|
|
func signTxWithAccounts(chain *Blockchain, tx *transaction.Transaction, accs ...*wallet.Account) {
|
|
|
|
scope := transaction.CalledByEntry
|
|
|
|
for _, acc := range accs {
|
2021-09-16 14:09:42 +00:00
|
|
|
accH, _ := address.StringToUint160(acc.Address)
|
2021-01-22 12:11:57 +00:00
|
|
|
tx.Signers = append(tx.Signers, transaction.Signer{
|
2021-09-16 14:09:42 +00:00
|
|
|
Account: accH,
|
2021-01-22 12:11:57 +00:00
|
|
|
Scopes: scope,
|
|
|
|
})
|
|
|
|
scope = transaction.Global
|
|
|
|
}
|
|
|
|
size := io.GetVarSize(tx)
|
|
|
|
for _, acc := range accs {
|
2021-09-16 14:09:42 +00:00
|
|
|
if acc.Contract.Deployed {
|
|
|
|
// don't need precise calculation for tests
|
|
|
|
tx.NetworkFee += 1000_0000
|
|
|
|
continue
|
|
|
|
}
|
2021-01-22 12:11:57 +00:00
|
|
|
netFee, sizeDelta := fee.Calculate(chain.GetBaseExecFee(), acc.Contract.Script)
|
|
|
|
size += sizeDelta
|
|
|
|
tx.NetworkFee += netFee
|
|
|
|
}
|
|
|
|
tx.NetworkFee += int64(size) * chain.FeePerByte()
|
|
|
|
|
|
|
|
for _, acc := range accs {
|
2021-03-25 16:18:01 +00:00
|
|
|
if err := acc.SignTx(testchain.Network(), tx); err != nil {
|
2021-01-22 12:11:57 +00:00
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-21 09:23:53 +00:00
|
|
|
func prepareContractMethodInvoke(chain *Blockchain, sysfee int64,
|
|
|
|
hash util.Uint160, method string, args ...interface{}) (*transaction.Transaction, error) {
|
|
|
|
return prepareContractMethodInvokeGeneric(chain, sysfee, hash,
|
|
|
|
method, false, args...)
|
|
|
|
}
|
|
|
|
|
2020-12-14 09:18:59 +00:00
|
|
|
func persistBlock(chain *Blockchain, txs ...*transaction.Transaction) ([]*state.AppExecResult, error) {
|
|
|
|
b := chain.newBlock(txs...)
|
|
|
|
err := chain.AddBlock(b)
|
2020-11-19 10:00:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-12-14 09:18:59 +00:00
|
|
|
aers := make([]*state.AppExecResult, len(txs))
|
|
|
|
for i, tx := range txs {
|
|
|
|
res, err := chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
aers[i] = &res[0]
|
|
|
|
}
|
|
|
|
return aers, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func invokeContractMethod(chain *Blockchain, sysfee int64, hash util.Uint160, method string, args ...interface{}) (*state.AppExecResult, error) {
|
2021-01-21 09:23:53 +00:00
|
|
|
return invokeContractMethodGeneric(chain, sysfee, hash, method, false, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func invokeContractMethodGeneric(chain *Blockchain, sysfee int64, hash util.Uint160, method string,
|
2021-01-22 12:11:57 +00:00
|
|
|
signer interface{}, args ...interface{}) (*state.AppExecResult, error) {
|
2021-01-21 09:23:53 +00:00
|
|
|
tx, err := prepareContractMethodInvokeGeneric(chain, sysfee, hash,
|
2021-01-22 12:11:57 +00:00
|
|
|
method, signer, args...)
|
2020-12-14 09:18:59 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
aers, err := persistBlock(chain, tx)
|
2020-11-19 10:00:46 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-12-14 09:18:59 +00:00
|
|
|
return aers[0], nil
|
2020-11-19 10:00:46 +00:00
|
|
|
}
|
|
|
|
|
2020-11-27 10:55:48 +00:00
|
|
|
func invokeContractMethodBy(t *testing.T, chain *Blockchain, signer *wallet.Account, hash util.Uint160, method string, args ...interface{}) (*state.AppExecResult, error) {
|
|
|
|
var (
|
|
|
|
netfee int64 = 1000_0000
|
|
|
|
sysfee int64 = 1_0000_0000
|
|
|
|
)
|
|
|
|
transferTx := transferTokenFromMultisigAccount(t, chain, signer.PrivateKey().PublicKey().GetScriptHash(), chain.contracts.GAS.Hash, sysfee+netfee+1000_0000, nil)
|
|
|
|
res, err := chain.GetAppExecResults(transferTx.Hash(), trigger.Application)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, vm.HaltState, res[0].VMState)
|
|
|
|
require.Equal(t, 0, len(res[0].Stack))
|
2021-01-22 12:11:57 +00:00
|
|
|
return invokeContractMethodGeneric(chain, sysfee, hash, method, signer, args...)
|
2020-11-27 10:55:48 +00:00
|
|
|
}
|
|
|
|
|
2020-12-30 08:01:13 +00:00
|
|
|
func transferTokenFromMultisigAccountCheckOK(t *testing.T, chain *Blockchain, to, tokenHash util.Uint160, amount int64, additionalArgs ...interface{}) {
|
|
|
|
transferTx := transferTokenFromMultisigAccount(t, chain, to, tokenHash, amount, additionalArgs...)
|
|
|
|
res, err := chain.GetAppExecResults(transferTx.Hash(), trigger.Application)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, vm.HaltState, res[0].VMState)
|
|
|
|
require.Equal(t, 0, len(res[0].Stack))
|
|
|
|
}
|
|
|
|
|
2020-11-19 10:00:46 +00:00
|
|
|
func transferTokenFromMultisigAccount(t *testing.T, chain *Blockchain, to, tokenHash util.Uint160, amount int64, additionalArgs ...interface{}) *transaction.Transaction {
|
core: do not allow NEP17 roundtrip in case of insufficient funds
NEP17 roundtrip is prohibited if from account doesn't have enough funds.
This commit fixes states diff in block 92057 where account
NfuwpaQ1A2xaeVbxWe8FRtaRgaMa8yF3YM initiates two NEO roundtrips with
amount exceeding the account's balance:
block 92057: value mismatch for key +////xTbYWBH3r5qhRKZAPFPHabKfb2vhQ==: QQMhAkwBIQOZZwEA vs QQMhAkwBIQN/ZwEA
block 92057: value mismatch for key +v///ws=: kqlddcitCg== vs tphddcitCg==
block 92057: value mismatch for key +v///xTbYWBH3r5qhRKZAPFPHabKfb2vhQ==: QQEhBUWyDu0W vs QQEhBWmhDu0W
C#'s applog (contains False and False on stack for both transfers):
```
{
"id" : 1,
"jsonrpc" : "2.0",
"result" : {
"executions" : [
{
"gasconsumed" : "11955500",
"exception" : null,
"stack" : [
{
"value" : false,
"type" : "Boolean"
},
{
"value" : false,
"type" : "Boolean"
}
],
"vmstate" : "HALT",
"trigger" : "Application",
"notifications" : []
}
],
"txid" : "0x8e73a7e9a566a514813907272ad65fc965002c3b098eacc5bdda529af19d7688"
}
}
```
Go's applog (both transfers succeeded and GAS minted):
```
{
"result" : {
"executions" : [
{
"gasconsumed" : "11955500",
"trigger" : "Application",
"stack" : [
{
"type" : "Boolean",
"value" : true
},
{
"type" : "Boolean",
"value" : true
}
],
"vmstate" : "HALT",
"notifications" : [
{
"eventname" : "Transfer",
"contract" : "0xd2a4cff31913016155e38e474a2c06d08be276cf",
"state" : {
"value" : [
{
"type" : "Any"
},
{
"value" : "22FgR96+aoUSmQDxTx2myn29r4U=",
"type" : "ByteString"
},
{
"value" : "4316",
"type" : "Integer"
}
],
"type" : "Array"
}
},
{
"state" : {
"type" : "Array",
"value" : [
{
"value" : "22FgR96+aoUSmQDxTx2myn29r4U=",
"type" : "ByteString"
},
{
"type" : "ByteString",
"value" : "22FgR96+aoUSmQDxTx2myn29r4U="
},
{
"type" : "Integer",
"value" : "1111111111"
}
]
},
"contract" : "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5",
"eventname" : "Transfer"
},
{
"contract" : "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5",
"state" : {
"type" : "Array",
"value" : [
{
"value" : "22FgR96+aoUSmQDxTx2myn29r4U=",
"type" : "ByteString"
},
{
"type" : "ByteString",
"value" : "22FgR96+aoUSmQDxTx2myn29r4U="
},
{
"type" : "Integer",
"value" : "1111111"
}
]
},
"eventname" : "Transfer"
}
]
}
],
"txid" : "0x8e73a7e9a566a514813907272ad65fc965002c3b098eacc5bdda529af19d7688"
},
"id" : 1,
"jsonrpc" : "2.0"
}
```
2021-06-09 08:58:58 +00:00
|
|
|
return transferTokenFromMultisigAccountWithAssert(t, chain, to, tokenHash, amount, true, additionalArgs...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func transferTokenFromMultisigAccountWithAssert(t *testing.T, chain *Blockchain, to, tokenHash util.Uint160, amount int64, needAssert bool, additionalArgs ...interface{}) *transaction.Transaction {
|
|
|
|
transferTx := newNEP17TransferWithAssert(tokenHash, testchain.MultisigScriptHash(), to, amount, needAssert, additionalArgs...)
|
2020-11-19 10:00:46 +00:00
|
|
|
transferTx.SystemFee = 100000000
|
|
|
|
transferTx.ValidUntilBlock = chain.BlockHeight() + 1
|
2021-01-21 09:23:53 +00:00
|
|
|
addSigners(neoOwner, transferTx)
|
2020-11-19 10:00:46 +00:00
|
|
|
require.NoError(t, testchain.SignTx(chain, transferTx))
|
|
|
|
b := chain.newBlock(transferTx)
|
|
|
|
require.NoError(t, chain.AddBlock(b))
|
|
|
|
return transferTx
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkResult(t *testing.T, result *state.AppExecResult, expected stackitem.Item) {
|
2020-11-27 10:55:48 +00:00
|
|
|
require.Equal(t, vm.HaltState, result.VMState, result.FaultException)
|
2020-11-19 10:00:46 +00:00
|
|
|
require.Equal(t, 1, len(result.Stack))
|
|
|
|
require.Equal(t, expected, result.Stack[0])
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkFAULTState(t *testing.T, result *state.AppExecResult) {
|
|
|
|
require.Equal(t, vm.FaultState, result.VMState)
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkBalanceOf(t *testing.T, chain *Blockchain, addr util.Uint160, expected int) {
|
2021-07-25 12:00:44 +00:00
|
|
|
balance := chain.GetUtilityTokenBalance(addr)
|
|
|
|
require.Equal(t, int64(expected), balance.Int64())
|
2020-11-19 10:00:46 +00:00
|
|
|
}
|
2021-01-26 16:37:19 +00:00
|
|
|
|
|
|
|
type NotaryFeerStub struct {
|
|
|
|
bc blockchainer.Blockchainer
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f NotaryFeerStub) FeePerByte() int64 { return f.bc.FeePerByte() }
|
|
|
|
func (f NotaryFeerStub) GetUtilityTokenBalance(acc util.Uint160) *big.Int {
|
|
|
|
return f.bc.GetNotaryBalance(acc)
|
|
|
|
}
|
|
|
|
func (f NotaryFeerStub) BlockHeight() uint32 { return f.bc.BlockHeight() }
|
|
|
|
func (f NotaryFeerStub) P2PSigExtensionsEnabled() bool { return f.bc.P2PSigExtensionsEnabled() }
|
|
|
|
func NewNotaryFeerStub(bc blockchainer.Blockchainer) NotaryFeerStub {
|
|
|
|
return NotaryFeerStub{
|
|
|
|
bc: bc,
|
|
|
|
}
|
|
|
|
}
|