2018-03-09 15:55:25 +00:00
|
|
|
package core
|
|
|
|
|
|
|
|
import (
|
2020-07-23 14:00:06 +00:00
|
|
|
"bytes"
|
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"
|
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"
|
|
|
|
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
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"
|
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"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
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"
|
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"
|
2019-12-30 07:43:05 +00:00
|
|
|
"go.uber.org/zap/zaptest"
|
2018-03-09 15:55:25 +00:00
|
|
|
)
|
|
|
|
|
2020-04-16 14:10:42 +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.
|
|
|
|
func newTestChain(t *testing.T) *Blockchain {
|
2020-11-26 09:33:34 +00:00
|
|
|
return newTestChainWithCustomCfg(t, nil)
|
2020-11-17 12:57:50 +00:00
|
|
|
}
|
|
|
|
|
2020-11-26 09:33:34 +00:00
|
|
|
func newTestChainWithCustomCfg(t *testing.T, f func(*config.Config)) *Blockchain {
|
2020-12-15 10:53:35 +00:00
|
|
|
return newTestChainWithCustomCfgAndStore(t, nil, f)
|
|
|
|
}
|
|
|
|
|
|
|
|
func newTestChainWithCustomCfgAndStore(t *testing.T, st storage.Store, f func(*config.Config)) *Blockchain {
|
2020-09-28 11:58:04 +00:00
|
|
|
chain := initTestChain(t, st, f)
|
|
|
|
go chain.Run()
|
|
|
|
return chain
|
|
|
|
}
|
|
|
|
|
|
|
|
func initTestChain(t *testing.T, 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()
|
|
|
|
}
|
|
|
|
chain, err := NewBlockchain(st, unitTestNetCfg.ProtocolConfiguration, zaptest.NewLogger(t))
|
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 {
|
|
|
|
sr, err := bc.GetStateRoot(bc.BlockHeight())
|
|
|
|
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{
|
2020-01-15 08:29:50 +00:00
|
|
|
Base: block.Base{
|
2020-06-18 09:00:51 +00:00
|
|
|
Network: testchain.Network(),
|
2019-10-15 09:52:10 +00:00
|
|
|
NextConsensus: witness.ScriptHash(),
|
|
|
|
Script: witness,
|
2018-03-09 15:55:25 +00:00
|
|
|
},
|
2020-04-22 05:57:55 +00:00
|
|
|
ConsensusData: block.ConsensusData{
|
|
|
|
PrimaryIndex: 0,
|
|
|
|
Nonce: 1111,
|
|
|
|
},
|
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()
|
2020-04-23 13:11:40 +00:00
|
|
|
b.Script.InvocationScript = testchain.Sign(b.GetSignedPart())
|
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
|
|
|
}
|
|
|
|
|
2020-01-14 12:32:07 +00:00
|
|
|
func getDecodedBlock(t *testing.T, i int) *block.Block {
|
2018-03-17 11:53:21 +00:00
|
|
|
data, err := getBlockData(i)
|
2020-02-29 15:55:16 +00:00
|
|
|
require.NoError(t, err)
|
2018-03-17 11:53:21 +00:00
|
|
|
|
|
|
|
b, err := hex.DecodeString(data["raw"].(string))
|
2020-02-29 15:55:16 +00:00
|
|
|
require.NoError(t, err)
|
2018-03-17 11:53:21 +00:00
|
|
|
|
2020-11-17 12:57:50 +00:00
|
|
|
block := block.New(testchain.Network(), false)
|
2020-03-26 14:43:24 +00:00
|
|
|
require.NoError(t, testserdes.DecodeBinary(b, block))
|
2018-03-17 11:53:21 +00:00
|
|
|
|
|
|
|
return block
|
|
|
|
}
|
|
|
|
|
|
|
|
func getBlockData(i int) (map[string]interface{}, error) {
|
|
|
|
b, err := ioutil.ReadFile(fmt.Sprintf("test_data/block_%d.json", i))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
var data map[string]interface{}
|
|
|
|
if err := json.Unmarshal(b, &data); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return data, err
|
|
|
|
}
|
2020-01-14 12:32:07 +00:00
|
|
|
|
|
|
|
func newDumbBlock() *block.Block {
|
|
|
|
return &block.Block{
|
2020-01-15 08:29:50 +00:00
|
|
|
Base: block.Base{
|
2020-06-18 09:00:51 +00:00
|
|
|
Network: testchain.Network(),
|
2020-01-14 12:32:07 +00:00
|
|
|
Version: 0,
|
|
|
|
PrevHash: hash.Sha256([]byte("a")),
|
|
|
|
MerkleRoot: hash.Sha256([]byte("b")),
|
2020-04-21 11:26:57 +00:00
|
|
|
Timestamp: 100500,
|
2020-01-14 12:32:07 +00:00
|
|
|
Index: 1,
|
|
|
|
NextConsensus: hash.Hash160([]byte("a")),
|
|
|
|
Script: transaction.Witness{
|
|
|
|
VerificationScript: []byte{0x51}, // PUSH1
|
|
|
|
InvocationScript: []byte{0x61}, // NOP
|
|
|
|
},
|
|
|
|
},
|
2020-04-22 05:57:55 +00:00
|
|
|
ConsensusData: block.ConsensusData{
|
|
|
|
PrimaryIndex: 0,
|
|
|
|
Nonce: 1111,
|
|
|
|
},
|
2020-01-14 12:32:07 +00:00
|
|
|
Transactions: []*transaction.Transaction{
|
2020-06-18 09:00:51 +00:00
|
|
|
transaction.New(testchain.Network(), []byte{byte(opcode.PUSH1)}, 0),
|
2020-01-14 12:32:07 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
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)
|
|
|
|
defer bc.Close()
|
|
|
|
initBasicChain(t, bc)
|
|
|
|
|
|
|
|
if saveChain {
|
|
|
|
outStream, err := os.Create(prefix + "testblocks.acc")
|
|
|
|
require.NoError(t, err)
|
|
|
|
defer outStream.Close()
|
|
|
|
|
|
|
|
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)))
|
2020-11-24 08:36:09 +00:00
|
|
|
txSendRaw.ValidUntilBlock = transaction.MaxValidUntilBlockIncrement
|
|
|
|
txSendRaw.Nonce = 0x1234
|
|
|
|
txSendRaw.Signers = []transaction.Signer{{
|
|
|
|
Account: priv0ScriptHash,
|
|
|
|
Scopes: transaction.CalledByEntry,
|
|
|
|
AllowedContracts: nil,
|
|
|
|
AllowedGroups: nil,
|
|
|
|
}}
|
|
|
|
require.NoError(t, addNetworkFee(bc, txSendRaw, acc0))
|
|
|
|
require.NoError(t, acc0.SignTx(txSendRaw))
|
|
|
|
bw := io.NewBufBinWriter()
|
|
|
|
txSendRaw.EncodeBinary(bw.BinWriter)
|
|
|
|
t.Logf("sendrawtransaction: %s", hex.EncodeToString(bw.Bytes()))
|
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
|
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)
|
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))
|
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)
|
2020-09-30 10:50:58 +00:00
|
|
|
t.Logf("Block1 hex: %s", hex.EncodeToString(bw.Bytes()))
|
2020-07-29 16:57:38 +00:00
|
|
|
t.Logf("txMoveNeo hash: %s", txMoveNeo.Hash().StringLE())
|
|
|
|
t.Logf("txMoveNeo hex: %s", hex.EncodeToString(txMoveNeo.Bytes()))
|
|
|
|
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()
|
|
|
|
b.Header().EncodeBinary(buf.BinWriter)
|
|
|
|
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.
|
2020-12-13 15:26:35 +00:00
|
|
|
txDeploy, cHash := newDeployTx(t, bc, priv0ScriptHash, prefix+"test_contract.go", "Rubl")
|
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))
|
2020-03-04 14:45:29 +00:00
|
|
|
require.NoError(t, acc0.SignTx(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))
|
|
|
|
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
|
|
|
|
2020-07-13 17:12:13 +00:00
|
|
|
txInv := transaction.New(testchain.Network(), 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))
|
2020-04-16 14:10:42 +00:00
|
|
|
require.NoError(t, acc0.SignTx(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))
|
|
|
|
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))
|
2020-03-04 14:45:29 +00:00
|
|
|
require.NoError(t, acc0.SignTx(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))
|
|
|
|
|
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)
|
2020-07-13 17:12:13 +00:00
|
|
|
initTx := transaction.New(testchain.Network(), 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))
|
2020-04-16 14:10:42 +00:00
|
|
|
require.NoError(t, acc0.SignTx(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))
|
2020-04-16 14:10:42 +00:00
|
|
|
require.NoError(t, acc0.SignTx(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))
|
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))
|
2020-04-16 14:10:42 +00:00
|
|
|
require.NoError(t, acc0.SignTx(transferTx))
|
|
|
|
|
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))
|
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.
|
2020-12-13 15:26:35 +00:00
|
|
|
txDeploy2, _ := newDeployTx(t, bc, priv0ScriptHash, prefix+"verification_contract.go", "Verify")
|
2020-08-24 11:00:05 +00:00
|
|
|
txDeploy2.Nonce = getNextNonce()
|
|
|
|
txDeploy2.ValidUntilBlock = validUntilBlock
|
|
|
|
require.NoError(t, addNetworkFee(bc, txDeploy2, acc0))
|
|
|
|
require.NoError(t, acc0.SignTx(txDeploy2))
|
|
|
|
b = bc.newBlock(txDeploy2)
|
|
|
|
require.NoError(t, bc.AddBlock(b))
|
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 {
|
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)
|
2020-10-02 08:30:15 +00:00
|
|
|
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()
|
2020-12-15 09:04:08 +00:00
|
|
|
return transaction.New(testchain.Network(), script, 11000000)
|
2020-03-05 09:28:46 +00:00
|
|
|
}
|
2020-04-16 14:10:42 +00:00
|
|
|
|
2020-12-13 15:26:35 +00:00
|
|
|
func newDeployTx(t *testing.T, bc *Blockchain, sender util.Uint160, name, ctrName string) (*transaction.Transaction, util.Uint160) {
|
2020-08-24 11:00:05 +00:00
|
|
|
c, err := ioutil.ReadFile(name)
|
|
|
|
require.NoError(t, err)
|
2020-12-13 15:26:35 +00:00
|
|
|
tx, h, err := testchain.NewDeployTx(bc, ctrName, sender, bytes.NewReader(c))
|
2020-08-24 11:00:05 +00:00
|
|
|
require.NoError(t, err)
|
2020-11-18 20:10:48 +00:00
|
|
|
t.Logf("contractHash (%s): %s", name, h.StringLE())
|
|
|
|
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()
|
|
|
|
tx := transaction.New(chain.GetConfig().Magic, script, sysfee)
|
|
|
|
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 {
|
|
|
|
tx.Signers = append(tx.Signers, transaction.Signer{
|
|
|
|
Account: acc.PrivateKey().GetScriptHash(),
|
|
|
|
Scopes: scope,
|
|
|
|
})
|
|
|
|
scope = transaction.Global
|
|
|
|
}
|
|
|
|
size := io.GetVarSize(tx)
|
|
|
|
for _, acc := range accs {
|
|
|
|
netFee, sizeDelta := fee.Calculate(chain.GetBaseExecFee(), acc.Contract.Script)
|
|
|
|
size += sizeDelta
|
|
|
|
tx.NetworkFee += netFee
|
|
|
|
}
|
|
|
|
tx.NetworkFee += int64(size) * chain.FeePerByte()
|
|
|
|
|
|
|
|
for _, acc := range accs {
|
|
|
|
if err := acc.SignTx(tx); err != nil {
|
|
|
|
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 {
|
|
|
|
transferTx := newNEP17Transfer(tokenHash, testchain.MultisigScriptHash(), to, amount, additionalArgs...)
|
|
|
|
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) {
|
|
|
|
balance := chain.GetNEP17Balances(addr).Trackers[chain.contracts.GAS.ContractID]
|
|
|
|
require.Equal(t, int64(expected), balance.Balance.Int64())
|
|
|
|
}
|
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,
|
|
|
|
}
|
|
|
|
}
|