2018-03-09 15:55:25 +00:00
|
|
|
package core
|
|
|
|
|
|
|
|
import (
|
2018-03-17 11:53:21 +00:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
2020-07-09 09:57:24 +00:00
|
|
|
"math/big"
|
2021-12-29 10:36:10 +00:00
|
|
|
"path/filepath"
|
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"
|
|
|
|
|
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-09-28 14:56:16 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
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-09-16 14:09:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
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"
|
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
|
|
|
|
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-12-29 10:36:10 +00:00
|
|
|
func newLevelDBForTesting(t testing.TB) storage.Store {
|
2022-03-10 14:12:04 +00:00
|
|
|
dbPath := t.TempDir()
|
2022-02-25 09:14:17 +00:00
|
|
|
dbOptions := storage.LevelDBOptions{
|
2022-02-25 15:43:05 +00:00
|
|
|
DataDirectoryPath: dbPath,
|
2021-12-29 10:36:10 +00:00
|
|
|
}
|
2022-02-25 09:14:17 +00:00
|
|
|
newLevelStore, err := storage.NewLevelDBStore(dbOptions)
|
2021-12-29 10:36:10 +00:00
|
|
|
require.Nil(t, err, "NewLevelDBStore error")
|
2022-03-10 14:12:04 +00:00
|
|
|
return newLevelStore
|
2021-12-29 10:36:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func newBoltStoreForTesting(t testing.TB) storage.Store {
|
2022-03-10 14:12:04 +00:00
|
|
|
d := t.TempDir()
|
|
|
|
dbPath := filepath.Join(d, "test_bolt_db")
|
2022-02-25 15:43:05 +00:00
|
|
|
boltDBStore, err := storage.NewBoltDBStore(storage.BoltDBOptions{FilePath: dbPath})
|
|
|
|
require.NoError(t, err)
|
2022-03-10 14:12:04 +00:00
|
|
|
return boltDBStore
|
2022-02-25 15:43:05 +00:00
|
|
|
}
|
|
|
|
|
2021-07-12 11:19:13 +00:00
|
|
|
func initTestChain(t testing.TB, st storage.Store, f func(*config.Config)) *Blockchain {
|
2022-02-25 15:43:05 +00:00
|
|
|
chain, err := initTestChainNoCheck(t, st, f)
|
|
|
|
require.NoError(t, err)
|
|
|
|
return chain
|
|
|
|
}
|
|
|
|
|
|
|
|
func initTestChainNoCheck(t testing.TB, st storage.Store, f func(*config.Config)) (*Blockchain, error) {
|
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()
|
|
|
|
}
|
2022-02-25 15:43:05 +00:00
|
|
|
return NewBlockchain(st, unitTestNetCfg.ProtocolConfiguration, log)
|
2019-10-15 09:52:10 +00:00
|
|
|
}
|
|
|
|
|
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")
|
|
|
|
}`
|
2021-12-02 13:36:29 +00:00
|
|
|
nf, di, err := compiler.CompileWithOptions("foo.go", strings.NewReader(src), nil)
|
2021-02-11 12:41:49 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
m, err := di.ConvertToManifest(&compiler.Options{Name: "TestContract"})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
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-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 {
|
2021-11-18 13:37:42 +00:00
|
|
|
panic(fmt.Errorf("failed to create NEP-17 transfer transaction: %w", w.Err))
|
2021-01-26 16:37:19 +00:00
|
|
|
}
|
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-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,
|
2021-11-15 16:29:26 +00:00
|
|
|
Scopes: transaction.Global,
|
2020-06-15 18:13:32 +00:00
|
|
|
AllowedContracts: nil,
|
|
|
|
AllowedGroups: nil,
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-11-11 07:42:44 +00:00
|
|
|
tx := transaction.New(script, 0)
|
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)
|
2021-11-11 07:42:44 +00:00
|
|
|
setTxSystemFee(chain, sysfee, tx)
|
2021-01-22 12:11:57 +00:00
|
|
|
err = testchain.SignTxCommittee(chain, tx)
|
|
|
|
} else {
|
|
|
|
addSigners(neoOwner, tx)
|
2021-11-11 07:42:44 +00:00
|
|
|
setTxSystemFee(chain, sysfee, tx)
|
2021-01-22 12:11:57 +00:00
|
|
|
err = testchain.SignTx(chain, tx)
|
|
|
|
}
|
|
|
|
case *wallet.Account:
|
2021-11-11 07:42:44 +00:00
|
|
|
signTxWithAccounts(chain, sysfee, tx, s)
|
2021-01-22 12:11:57 +00:00
|
|
|
case []*wallet.Account:
|
2021-11-11 07:42:44 +00:00
|
|
|
signTxWithAccounts(chain, sysfee, tx, s...)
|
2021-01-22 12:11:57 +00:00
|
|
|
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-11-11 07:42:44 +00:00
|
|
|
func setTxSystemFee(bc *Blockchain, sysFee int64, tx *transaction.Transaction) {
|
|
|
|
if sysFee >= 0 {
|
|
|
|
tx.SystemFee = sysFee
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
lastBlock := bc.topBlock.Load().(*block.Block)
|
|
|
|
b := &block.Block{
|
|
|
|
Header: block.Header{
|
|
|
|
Index: lastBlock.Index + 1,
|
|
|
|
Timestamp: lastBlock.Timestamp + 1000,
|
|
|
|
},
|
|
|
|
Transactions: []*transaction.Transaction{tx},
|
|
|
|
}
|
|
|
|
|
|
|
|
ttx := *tx // prevent setting 'hash' field
|
2022-01-12 22:20:08 +00:00
|
|
|
ic := bc.GetTestVM(trigger.Application, &ttx, b)
|
|
|
|
defer ic.Finalize()
|
2021-11-11 07:42:44 +00:00
|
|
|
|
2022-01-12 22:20:08 +00:00
|
|
|
ic.VM.LoadWithFlags(tx.Script, callflag.All)
|
|
|
|
_ = ic.VM.Run()
|
|
|
|
tx.SystemFee = ic.VM.GasConsumed()
|
2021-11-11 07:42:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func signTxWithAccounts(chain *Blockchain, sysFee int64, tx *transaction.Transaction, accs ...*wallet.Account) {
|
2021-01-22 12:11:57 +00:00
|
|
|
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
|
|
|
|
}
|
2021-11-11 07:42:44 +00:00
|
|
|
setTxSystemFee(chain, sysFee, tx)
|
2021-01-22 12:11:57 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-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])
|
|
|
|
}
|
|
|
|
|
2021-12-09 17:23:58 +00:00
|
|
|
func checkTxHalt(t testing.TB, bc *Blockchain, h util.Uint256) {
|
|
|
|
aer, err := bc.GetAppExecResults(h, trigger.Application)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 1, len(aer))
|
|
|
|
require.Equal(t, vm.HaltState, aer[0].VMState, aer[0].FaultException)
|
|
|
|
}
|
|
|
|
|
2020-11-19 10:00:46 +00:00
|
|
|
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,
|
|
|
|
}
|
|
|
|
}
|