2018-02-04 19:54:51 +00:00
|
|
|
package core
|
|
|
|
|
|
|
|
import (
|
2020-08-13 10:42:21 +00:00
|
|
|
"errors"
|
2020-07-09 09:57:24 +00:00
|
|
|
"math/big"
|
2020-08-13 10:42:21 +00:00
|
|
|
"math/rand"
|
2018-02-04 19:54:51 +00:00
|
|
|
"testing"
|
2020-05-12 14:20:41 +00:00
|
|
|
"time"
|
2018-02-06 06:43:32 +00:00
|
|
|
|
2020-06-18 09:00:51 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
2020-09-28 14:56:16 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
2020-08-14 10:50:52 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
2020-08-19 16:27:15 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/mempool"
|
2020-09-24 13:33:40 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
2020-05-12 14:20:41 +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-09-24 13:33:40 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
2020-08-13 10:42:21 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/internal/testchain"
|
2020-05-12 14:20:41 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
2020-09-24 13:33:40 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
2020-08-13 15:42:53 +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-07-27 14:57:53 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
2020-05-12 14:20:41 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
2020-08-13 10:42:21 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
2018-03-09 15:55:25 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
2019-02-20 17:39:32 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2018-02-04 19:54:51 +00:00
|
|
|
)
|
|
|
|
|
2020-08-13 10:57:30 +00:00
|
|
|
func TestVerifyHeader(t *testing.T) {
|
|
|
|
bc := newTestChain(t)
|
|
|
|
defer bc.Close()
|
|
|
|
prev := bc.topBlock.Load().(*block.Block).Header()
|
|
|
|
t.Run("Invalid", func(t *testing.T) {
|
|
|
|
t.Run("Hash", func(t *testing.T) {
|
|
|
|
h := prev.Hash()
|
|
|
|
h[0] = ^h[0]
|
|
|
|
hdr := newBlock(bc.config, 1, h).Header()
|
|
|
|
require.True(t, errors.Is(bc.verifyHeader(hdr, prev), ErrHdrHashMismatch))
|
|
|
|
})
|
|
|
|
t.Run("Index", func(t *testing.T) {
|
|
|
|
hdr := newBlock(bc.config, 3, prev.Hash()).Header()
|
|
|
|
require.True(t, errors.Is(bc.verifyHeader(hdr, prev), ErrHdrIndexMismatch))
|
|
|
|
})
|
|
|
|
t.Run("Timestamp", func(t *testing.T) {
|
|
|
|
hdr := newBlock(bc.config, 1, prev.Hash()).Header()
|
|
|
|
hdr.Timestamp = 0
|
|
|
|
require.True(t, errors.Is(bc.verifyHeader(hdr, prev), ErrHdrInvalidTimestamp))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
t.Run("Valid", func(t *testing.T) {
|
|
|
|
hdr := newBlock(bc.config, 1, prev.Hash()).Header()
|
|
|
|
require.NoError(t, bc.verifyHeader(hdr, prev))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-02-06 06:43:32 +00:00
|
|
|
func TestAddHeaders(t *testing.T) {
|
2018-03-17 11:53:21 +00:00
|
|
|
bc := newTestChain(t)
|
2020-02-29 14:53:28 +00:00
|
|
|
defer bc.Close()
|
2020-02-29 14:24:37 +00:00
|
|
|
lastBlock := bc.topBlock.Load().(*block.Block)
|
|
|
|
h1 := newBlock(bc.config, 1, lastBlock.Hash()).Header()
|
|
|
|
h2 := newBlock(bc.config, 2, h1.Hash()).Header()
|
|
|
|
h3 := newBlock(bc.config, 3, h2.Hash()).Header()
|
2018-02-04 19:54:51 +00:00
|
|
|
|
2020-02-29 14:52:09 +00:00
|
|
|
require.NoError(t, bc.AddHeaders())
|
|
|
|
require.NoError(t, bc.AddHeaders(h1, h2))
|
|
|
|
require.NoError(t, bc.AddHeaders(h2, h3))
|
2018-03-09 15:55:25 +00:00
|
|
|
|
|
|
|
assert.Equal(t, h3.Index, bc.HeaderHeight())
|
|
|
|
assert.Equal(t, uint32(0), bc.BlockHeight())
|
|
|
|
assert.Equal(t, h3.Hash(), bc.CurrentHeaderHash())
|
2018-03-10 12:04:06 +00:00
|
|
|
|
|
|
|
// Add them again, they should not be added.
|
2020-03-03 12:34:03 +00:00
|
|
|
require.NoError(t, bc.AddHeaders(h3, h2, h1))
|
2020-02-29 14:52:09 +00:00
|
|
|
|
|
|
|
assert.Equal(t, h3.Index, bc.HeaderHeight())
|
|
|
|
assert.Equal(t, uint32(0), bc.BlockHeight())
|
|
|
|
assert.Equal(t, h3.Hash(), bc.CurrentHeaderHash())
|
|
|
|
|
|
|
|
h4 := newBlock(bc.config, 4, h3.Hash().Reverse()).Header()
|
|
|
|
h5 := newBlock(bc.config, 5, h4.Hash()).Header()
|
|
|
|
|
|
|
|
assert.Error(t, bc.AddHeaders(h4, h5))
|
|
|
|
assert.Equal(t, h3.Index, bc.HeaderHeight())
|
|
|
|
assert.Equal(t, uint32(0), bc.BlockHeight())
|
|
|
|
assert.Equal(t, h3.Hash(), bc.CurrentHeaderHash())
|
2018-03-10 12:04:06 +00:00
|
|
|
|
2020-02-29 14:52:09 +00:00
|
|
|
h6 := newBlock(bc.config, 4, h3.Hash()).Header()
|
|
|
|
h6.Script.InvocationScript = nil
|
|
|
|
assert.Error(t, bc.AddHeaders(h6))
|
2018-03-10 12:04:06 +00:00
|
|
|
assert.Equal(t, h3.Index, bc.HeaderHeight())
|
|
|
|
assert.Equal(t, uint32(0), bc.BlockHeight())
|
|
|
|
assert.Equal(t, h3.Hash(), bc.CurrentHeaderHash())
|
2018-03-09 15:55:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestAddBlock(t *testing.T) {
|
2020-02-29 14:16:13 +00:00
|
|
|
const size = 3
|
2018-03-17 11:53:21 +00:00
|
|
|
bc := newTestChain(t)
|
2020-02-29 14:16:13 +00:00
|
|
|
blocks, err := bc.genBlocks(size)
|
|
|
|
require.NoError(t, err)
|
2018-03-09 15:55:25 +00:00
|
|
|
|
|
|
|
lastBlock := blocks[len(blocks)-1]
|
|
|
|
assert.Equal(t, lastBlock.Index, bc.HeaderHeight())
|
|
|
|
assert.Equal(t, lastBlock.Hash(), bc.CurrentHeaderHash())
|
2018-03-25 10:45:54 +00:00
|
|
|
|
2019-09-24 15:51:20 +00:00
|
|
|
// This one tests persisting blocks, so it does need to persist()
|
2019-10-21 07:04:58 +00:00
|
|
|
require.NoError(t, bc.persist())
|
2018-03-09 15:55:25 +00:00
|
|
|
|
2018-03-25 10:45:54 +00:00
|
|
|
for _, block := range blocks {
|
2019-11-27 09:23:18 +00:00
|
|
|
key := storage.AppendPrefix(storage.DataBlock, block.Hash().BytesLE())
|
2020-04-07 09:41:12 +00:00
|
|
|
_, err := bc.dao.Store.Get(key)
|
2020-02-29 15:55:16 +00:00
|
|
|
require.NoErrorf(t, err, "block %s not persisted", block.Hash())
|
2018-03-25 10:45:54 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 15:55:25 +00:00
|
|
|
assert.Equal(t, lastBlock.Index, bc.BlockHeight())
|
|
|
|
assert.Equal(t, lastBlock.Hash(), bc.CurrentHeaderHash())
|
|
|
|
}
|
|
|
|
|
2020-08-19 11:38:58 +00:00
|
|
|
func TestAddBadBlock(t *testing.T) {
|
|
|
|
bc := newTestChain(t)
|
|
|
|
defer bc.Close()
|
|
|
|
// It has ValidUntilBlock == 0, which is wrong
|
|
|
|
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
|
|
|
tx.Signers = []transaction.Signer{{
|
|
|
|
Account: testchain.MultisigScriptHash(),
|
2020-10-01 12:26:51 +00:00
|
|
|
Scopes: transaction.None,
|
2020-08-19 11:38:58 +00:00
|
|
|
}}
|
|
|
|
require.NoError(t, signTx(bc, tx))
|
|
|
|
b1 := bc.newBlock(tx)
|
|
|
|
|
|
|
|
require.Error(t, bc.AddBlock(b1))
|
|
|
|
bc.config.VerifyTransactions = false
|
|
|
|
require.NoError(t, bc.AddBlock(b1))
|
|
|
|
|
|
|
|
b2 := bc.newBlock()
|
|
|
|
b2.PrevHash = util.Uint256{}
|
|
|
|
|
|
|
|
require.Error(t, bc.AddBlock(b2))
|
|
|
|
bc.config.VerifyBlocks = false
|
|
|
|
require.NoError(t, bc.AddBlock(b2))
|
|
|
|
|
|
|
|
tx = transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
|
|
|
tx.ValidUntilBlock = 128
|
|
|
|
tx.Signers = []transaction.Signer{{
|
|
|
|
Account: testchain.MultisigScriptHash(),
|
2020-10-01 12:26:51 +00:00
|
|
|
Scopes: transaction.None,
|
2020-08-19 11:38:58 +00:00
|
|
|
}}
|
|
|
|
require.NoError(t, signTx(bc, tx))
|
|
|
|
require.NoError(t, bc.PoolTx(tx))
|
|
|
|
bc.config.VerifyTransactions = true
|
|
|
|
bc.config.VerifyBlocks = true
|
|
|
|
b3 := bc.newBlock(tx)
|
|
|
|
require.NoError(t, bc.AddBlock(b3))
|
|
|
|
}
|
|
|
|
|
2018-03-17 11:53:21 +00:00
|
|
|
func TestGetHeader(t *testing.T) {
|
|
|
|
bc := newTestChain(t)
|
2020-06-18 09:00:51 +00:00
|
|
|
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
2020-04-15 06:50:13 +00:00
|
|
|
tx.ValidUntilBlock = bc.BlockHeight() + 1
|
2020-07-29 16:57:38 +00:00
|
|
|
addSigners(tx)
|
2020-04-16 14:10:42 +00:00
|
|
|
assert.Nil(t, signTx(bc, tx))
|
2020-04-15 06:50:13 +00:00
|
|
|
block := bc.newBlock(tx)
|
2018-03-17 11:53:21 +00:00
|
|
|
err := bc.AddBlock(block)
|
|
|
|
assert.Nil(t, err)
|
|
|
|
|
2019-09-24 15:51:20 +00:00
|
|
|
// Test unpersisted and persisted access
|
|
|
|
for i := 0; i < 2; i++ {
|
|
|
|
hash := block.Hash()
|
|
|
|
header, err := bc.GetHeader(hash)
|
|
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, block.Header(), header)
|
|
|
|
|
2020-02-29 14:24:37 +00:00
|
|
|
b2 := bc.newBlock()
|
2019-09-24 15:51:20 +00:00
|
|
|
_, err = bc.GetHeader(b2.Hash())
|
|
|
|
assert.Error(t, err)
|
2019-10-21 07:04:58 +00:00
|
|
|
assert.NoError(t, bc.persist())
|
2019-09-24 15:51:20 +00:00
|
|
|
}
|
2018-03-17 11:53:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetBlock(t *testing.T) {
|
|
|
|
bc := newTestChain(t)
|
2020-02-29 14:16:13 +00:00
|
|
|
blocks, err := bc.genBlocks(100)
|
|
|
|
require.NoError(t, err)
|
2018-03-17 11:53:21 +00:00
|
|
|
|
2019-09-24 15:51:20 +00:00
|
|
|
// Test unpersisted and persisted access
|
|
|
|
for j := 0; j < 2; j++ {
|
|
|
|
for i := 0; i < len(blocks); i++ {
|
|
|
|
block, err := bc.GetBlock(blocks[i].Hash())
|
2020-02-29 15:55:16 +00:00
|
|
|
require.NoErrorf(t, err, "can't get block %d: %s, attempt %d", i, err, j)
|
2019-09-24 15:51:20 +00:00
|
|
|
assert.Equal(t, blocks[i].Index, block.Index)
|
|
|
|
assert.Equal(t, blocks[i].Hash(), block.Hash())
|
2018-03-17 11:53:21 +00:00
|
|
|
}
|
2019-10-21 07:04:58 +00:00
|
|
|
assert.NoError(t, bc.persist())
|
2018-03-17 11:53:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-13 10:42:21 +00:00
|
|
|
func (bc *Blockchain) newTestTx(h util.Uint160, script []byte) *transaction.Transaction {
|
|
|
|
tx := transaction.New(testchain.Network(), script, 1_000_000)
|
|
|
|
tx.Nonce = rand.Uint32()
|
|
|
|
tx.ValidUntilBlock = 100
|
|
|
|
tx.Signers = []transaction.Signer{{
|
|
|
|
Account: h,
|
|
|
|
Scopes: transaction.CalledByEntry,
|
|
|
|
}}
|
|
|
|
tx.NetworkFee = int64(io.GetVarSize(tx)+200 /* witness */) * bc.FeePerByte()
|
|
|
|
tx.NetworkFee += 1_000_000 // verification cost
|
|
|
|
return tx
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestVerifyTx(t *testing.T) {
|
|
|
|
bc := newTestChain(t)
|
|
|
|
defer bc.Close()
|
|
|
|
|
2020-09-30 10:20:40 +00:00
|
|
|
accs := make([]*wallet.Account, 5)
|
2020-08-13 10:42:21 +00:00
|
|
|
for i := range accs {
|
|
|
|
var err error
|
|
|
|
accs[i], err = wallet.NewAccount()
|
|
|
|
require.NoError(t, err)
|
|
|
|
}
|
|
|
|
|
2020-09-24 13:33:40 +00:00
|
|
|
oracleAcc := accs[2]
|
|
|
|
oraclePubs := keys.PublicKeys{oracleAcc.PrivateKey().PublicKey()}
|
|
|
|
require.NoError(t, oracleAcc.ConvertMultisig(1, oraclePubs))
|
|
|
|
|
2020-08-13 10:42:21 +00:00
|
|
|
neoHash := bc.contracts.NEO.Hash
|
|
|
|
gasHash := bc.contracts.GAS.Hash
|
|
|
|
w := io.NewBufBinWriter()
|
|
|
|
for _, sc := range []util.Uint160{neoHash, gasHash} {
|
|
|
|
for _, a := range accs {
|
|
|
|
amount := int64(1_000_000)
|
|
|
|
if sc.Equals(gasHash) {
|
|
|
|
amount = 1_000_000_000
|
|
|
|
}
|
|
|
|
emit.AppCallWithOperationAndArgs(w.BinWriter, sc, "transfer",
|
2020-09-24 13:33:40 +00:00
|
|
|
neoOwner, a.Contract.ScriptHash(), amount)
|
2020-10-02 08:30:15 +00:00
|
|
|
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
2020-08-13 10:42:21 +00:00
|
|
|
}
|
|
|
|
}
|
2020-08-27 15:17:15 +00:00
|
|
|
emit.AppCallWithOperationAndArgs(w.BinWriter, gasHash, "transfer",
|
|
|
|
neoOwner, testchain.CommitteeScriptHash(), int64(1_000_000_000))
|
2020-10-02 08:30:15 +00:00
|
|
|
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
2020-08-13 10:42:21 +00:00
|
|
|
require.NoError(t, w.Err)
|
|
|
|
|
|
|
|
txMove := bc.newTestTx(neoOwner, w.Bytes())
|
|
|
|
txMove.SystemFee = 1_000_000_000
|
|
|
|
require.NoError(t, signTx(bc, txMove))
|
|
|
|
b := bc.newBlock(txMove)
|
|
|
|
require.NoError(t, bc.AddBlock(b))
|
|
|
|
|
|
|
|
aer, err := bc.GetAppExecResult(txMove.Hash())
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, aer.VMState, vm.HaltState)
|
|
|
|
|
|
|
|
res, err := invokeNativePolicyMethod(bc, "blockAccount", accs[1].PrivateKey().GetScriptHash().BytesBE())
|
|
|
|
require.NoError(t, err)
|
|
|
|
checkResult(t, res, stackitem.NewBool(true))
|
|
|
|
|
|
|
|
checkErr := func(t *testing.T, expectedErr error, tx *transaction.Transaction) {
|
2020-08-19 16:27:15 +00:00
|
|
|
err := bc.VerifyTx(tx)
|
2020-08-19 13:19:58 +00:00
|
|
|
require.True(t, errors.Is(err, expectedErr), "expected: %v, got: %v", expectedErr, err)
|
2020-08-13 10:42:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
testScript := []byte{byte(opcode.PUSH1)}
|
|
|
|
h := accs[0].PrivateKey().GetScriptHash()
|
|
|
|
t.Run("Expired", func(t *testing.T) {
|
|
|
|
tx := bc.newTestTx(h, testScript)
|
|
|
|
tx.ValidUntilBlock = 1
|
|
|
|
require.NoError(t, accs[0].SignTx(tx))
|
|
|
|
checkErr(t, ErrTxExpired, tx)
|
|
|
|
})
|
|
|
|
t.Run("BlockedAccount", func(t *testing.T) {
|
|
|
|
tx := bc.newTestTx(accs[1].PrivateKey().GetScriptHash(), testScript)
|
|
|
|
require.NoError(t, accs[1].SignTx(tx))
|
2020-08-19 16:27:15 +00:00
|
|
|
err := bc.VerifyTx(tx)
|
2020-08-13 10:42:21 +00:00
|
|
|
require.True(t, errors.Is(err, ErrPolicy))
|
|
|
|
})
|
|
|
|
t.Run("InsufficientGas", func(t *testing.T) {
|
|
|
|
balance := bc.GetUtilityTokenBalance(h)
|
|
|
|
tx := bc.newTestTx(h, testScript)
|
|
|
|
tx.SystemFee = balance.Int64() + 1
|
|
|
|
require.NoError(t, accs[0].SignTx(tx))
|
|
|
|
checkErr(t, ErrInsufficientFunds, tx)
|
|
|
|
})
|
|
|
|
t.Run("TooBigTx", func(t *testing.T) {
|
|
|
|
script := make([]byte, transaction.MaxTransactionSize)
|
|
|
|
tx := bc.newTestTx(h, script)
|
|
|
|
require.NoError(t, accs[0].SignTx(tx))
|
|
|
|
checkErr(t, ErrTxTooBig, tx)
|
|
|
|
})
|
2020-09-30 10:20:40 +00:00
|
|
|
t.Run("NetworkFee", func(t *testing.T) {
|
|
|
|
t.Run("SmallNetworkFee", func(t *testing.T) {
|
|
|
|
tx := bc.newTestTx(h, testScript)
|
|
|
|
tx.NetworkFee = 1
|
|
|
|
require.NoError(t, accs[0].SignTx(tx))
|
|
|
|
checkErr(t, ErrTxSmallNetworkFee, tx)
|
|
|
|
})
|
2020-09-30 10:50:58 +00:00
|
|
|
t.Run("AlmostEnoughNetworkFee", func(t *testing.T) {
|
|
|
|
tx := bc.newTestTx(h, testScript)
|
2020-09-28 14:56:16 +00:00
|
|
|
verificationNetFee, calcultedScriptSize := fee.Calculate(accs[0].Contract.Script)
|
2020-09-30 10:50:58 +00:00
|
|
|
expectedSize := io.GetVarSize(tx) + calcultedScriptSize
|
|
|
|
calculatedNetFee := verificationNetFee + int64(expectedSize)*bc.FeePerByte()
|
|
|
|
tx.NetworkFee = calculatedNetFee - 1
|
|
|
|
require.NoError(t, accs[0].SignTx(tx))
|
|
|
|
require.Equal(t, expectedSize, io.GetVarSize(tx))
|
|
|
|
checkErr(t, ErrVerificationFailed, tx)
|
|
|
|
})
|
|
|
|
t.Run("EnoughNetworkFee", func(t *testing.T) {
|
|
|
|
tx := bc.newTestTx(h, testScript)
|
2020-09-28 14:56:16 +00:00
|
|
|
verificationNetFee, calcultedScriptSize := fee.Calculate(accs[0].Contract.Script)
|
2020-09-30 10:50:58 +00:00
|
|
|
expectedSize := io.GetVarSize(tx) + calcultedScriptSize
|
|
|
|
calculatedNetFee := verificationNetFee + int64(expectedSize)*bc.FeePerByte()
|
|
|
|
tx.NetworkFee = calculatedNetFee
|
|
|
|
require.NoError(t, accs[0].SignTx(tx))
|
|
|
|
require.Equal(t, expectedSize, io.GetVarSize(tx))
|
|
|
|
require.NoError(t, bc.VerifyTx(tx))
|
|
|
|
})
|
2020-09-30 10:20:40 +00:00
|
|
|
t.Run("CalculateNetworkFee, signature script", func(t *testing.T) {
|
|
|
|
tx := bc.newTestTx(h, testScript)
|
|
|
|
expectedSize := io.GetVarSize(tx)
|
2020-09-28 14:56:16 +00:00
|
|
|
verificationNetFee, calculatedScriptSize := fee.Calculate(accs[0].Contract.Script)
|
2020-09-30 10:20:40 +00:00
|
|
|
expectedSize += calculatedScriptSize
|
|
|
|
expectedNetFee := verificationNetFee + int64(expectedSize)*bc.FeePerByte()
|
|
|
|
tx.NetworkFee = expectedNetFee
|
|
|
|
require.NoError(t, accs[0].SignTx(tx))
|
|
|
|
actualSize := io.GetVarSize(tx)
|
|
|
|
require.Equal(t, expectedSize, actualSize)
|
|
|
|
interopCtx := bc.newInteropContext(trigger.Verification, bc.dao, nil, tx)
|
|
|
|
gasConsumed, err := bc.verifyHashAgainstScript(h, &tx.Scripts[0], interopCtx, -1)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, verificationNetFee, gasConsumed)
|
|
|
|
require.Equal(t, expectedNetFee, bc.FeePerByte()*int64(actualSize)+gasConsumed)
|
|
|
|
})
|
|
|
|
t.Run("CalculateNetworkFee, multisignature script", func(t *testing.T) {
|
|
|
|
multisigAcc := accs[4]
|
|
|
|
pKeys := keys.PublicKeys{multisigAcc.PrivateKey().PublicKey()}
|
|
|
|
require.NoError(t, multisigAcc.ConvertMultisig(1, pKeys))
|
|
|
|
multisigHash := hash.Hash160(multisigAcc.Contract.Script)
|
|
|
|
tx := bc.newTestTx(multisigHash, testScript)
|
2020-09-28 14:56:16 +00:00
|
|
|
verificationNetFee, calculatedScriptSize := fee.Calculate(multisigAcc.Contract.Script)
|
2020-09-30 10:20:40 +00:00
|
|
|
expectedSize := io.GetVarSize(tx) + calculatedScriptSize
|
|
|
|
expectedNetFee := verificationNetFee + int64(expectedSize)*bc.FeePerByte()
|
|
|
|
tx.NetworkFee = expectedNetFee
|
|
|
|
require.NoError(t, multisigAcc.SignTx(tx))
|
|
|
|
actualSize := io.GetVarSize(tx)
|
|
|
|
require.Equal(t, expectedSize, actualSize)
|
|
|
|
interopCtx := bc.newInteropContext(trigger.Verification, bc.dao, nil, tx)
|
|
|
|
gasConsumed, err := bc.verifyHashAgainstScript(multisigHash, &tx.Scripts[0], interopCtx, -1)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, verificationNetFee, gasConsumed)
|
|
|
|
require.Equal(t, expectedNetFee, bc.FeePerByte()*int64(actualSize)+gasConsumed)
|
|
|
|
})
|
2020-08-13 10:42:21 +00:00
|
|
|
})
|
|
|
|
t.Run("Conflict", func(t *testing.T) {
|
|
|
|
balance := bc.GetUtilityTokenBalance(h).Int64()
|
|
|
|
tx := bc.newTestTx(h, testScript)
|
|
|
|
tx.NetworkFee = balance / 2
|
|
|
|
require.NoError(t, accs[0].SignTx(tx))
|
2020-08-19 16:27:15 +00:00
|
|
|
require.NoError(t, bc.PoolTx(tx))
|
2020-08-13 10:42:21 +00:00
|
|
|
|
|
|
|
tx2 := bc.newTestTx(h, testScript)
|
|
|
|
tx2.NetworkFee = balance / 2
|
2020-08-19 16:27:15 +00:00
|
|
|
require.NoError(t, accs[0].SignTx(tx2))
|
|
|
|
err := bc.PoolTx(tx2)
|
|
|
|
require.True(t, errors.Is(err, ErrMemPoolConflict))
|
2020-08-13 10:42:21 +00:00
|
|
|
})
|
|
|
|
t.Run("NotEnoughWitnesses", func(t *testing.T) {
|
|
|
|
tx := bc.newTestTx(h, testScript)
|
|
|
|
checkErr(t, ErrTxInvalidWitnessNum, tx)
|
|
|
|
})
|
|
|
|
t.Run("InvalidWitnessHash", func(t *testing.T) {
|
|
|
|
tx := bc.newTestTx(h, testScript)
|
|
|
|
require.NoError(t, accs[0].SignTx(tx))
|
|
|
|
tx.Scripts[0].VerificationScript = []byte{byte(opcode.PUSHT)}
|
|
|
|
checkErr(t, ErrWitnessHashMismatch, tx)
|
|
|
|
})
|
|
|
|
t.Run("InvalidWitnessSignature", func(t *testing.T) {
|
|
|
|
tx := bc.newTestTx(h, testScript)
|
|
|
|
require.NoError(t, accs[0].SignTx(tx))
|
|
|
|
tx.Scripts[0].InvocationScript[10] = ^tx.Scripts[0].InvocationScript[10]
|
|
|
|
checkErr(t, ErrVerificationFailed, tx)
|
|
|
|
})
|
2020-09-29 13:05:42 +00:00
|
|
|
t.Run("InsufficientNetworkFeeForSecondWitness", func(t *testing.T) {
|
|
|
|
tx := bc.newTestTx(h, testScript)
|
|
|
|
tx.Signers = append(tx.Signers, transaction.Signer{
|
|
|
|
Account: accs[3].PrivateKey().GetScriptHash(),
|
|
|
|
Scopes: transaction.Global,
|
|
|
|
})
|
|
|
|
require.NoError(t, accs[0].SignTx(tx))
|
|
|
|
require.NoError(t, accs[3].SignTx(tx))
|
|
|
|
checkErr(t, ErrVerificationFailed, tx)
|
|
|
|
})
|
2020-08-19 16:27:15 +00:00
|
|
|
t.Run("OldTX", func(t *testing.T) {
|
|
|
|
tx := bc.newTestTx(h, testScript)
|
|
|
|
require.NoError(t, accs[0].SignTx(tx))
|
|
|
|
b := bc.newBlock(tx)
|
|
|
|
require.NoError(t, bc.AddBlock(b))
|
|
|
|
|
|
|
|
err := bc.VerifyTx(tx)
|
|
|
|
require.True(t, errors.Is(err, ErrAlreadyExists))
|
|
|
|
})
|
|
|
|
t.Run("MemPooledTX", func(t *testing.T) {
|
|
|
|
tx := bc.newTestTx(h, testScript)
|
|
|
|
require.NoError(t, accs[0].SignTx(tx))
|
|
|
|
require.NoError(t, bc.PoolTx(tx))
|
|
|
|
|
|
|
|
err := bc.PoolTx(tx)
|
|
|
|
require.True(t, errors.Is(err, ErrAlreadyExists))
|
|
|
|
})
|
|
|
|
t.Run("MemPoolOOM", func(t *testing.T) {
|
|
|
|
bc.memPool = mempool.New(1)
|
|
|
|
tx1 := bc.newTestTx(h, testScript)
|
|
|
|
tx1.NetworkFee += 10000 // Give it more priority.
|
|
|
|
require.NoError(t, accs[0].SignTx(tx1))
|
|
|
|
require.NoError(t, bc.PoolTx(tx1))
|
|
|
|
|
|
|
|
tx2 := bc.newTestTx(h, testScript)
|
|
|
|
require.NoError(t, accs[0].SignTx(tx2))
|
|
|
|
err := bc.PoolTx(tx2)
|
|
|
|
require.True(t, errors.Is(err, ErrOOM))
|
|
|
|
})
|
2020-08-19 13:20:48 +00:00
|
|
|
t.Run("Attribute", func(t *testing.T) {
|
|
|
|
t.Run("InvalidHighPriority", func(t *testing.T) {
|
|
|
|
tx := bc.newTestTx(h, testScript)
|
|
|
|
tx.Attributes = append(tx.Attributes, transaction.Attribute{Type: transaction.HighPriority})
|
|
|
|
require.NoError(t, accs[0].SignTx(tx))
|
|
|
|
checkErr(t, ErrInvalidAttribute, tx)
|
|
|
|
})
|
|
|
|
t.Run("ValidHighPriority", func(t *testing.T) {
|
|
|
|
tx := bc.newTestTx(h, testScript)
|
2020-08-27 15:17:15 +00:00
|
|
|
tx.Attributes = append(tx.Attributes, transaction.Attribute{Type: transaction.HighPriority})
|
2020-08-19 13:20:48 +00:00
|
|
|
tx.NetworkFee += 4_000_000 // multisig check
|
|
|
|
tx.Signers = []transaction.Signer{{
|
2020-08-27 15:17:15 +00:00
|
|
|
Account: testchain.CommitteeScriptHash(),
|
2020-10-01 12:26:51 +00:00
|
|
|
Scopes: transaction.None,
|
2020-08-19 13:20:48 +00:00
|
|
|
}}
|
2020-08-27 15:17:15 +00:00
|
|
|
rawScript := testchain.CommitteeVerificationScript()
|
2020-08-19 13:20:48 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
size := io.GetVarSize(tx)
|
2020-09-28 14:56:16 +00:00
|
|
|
netFee, sizeDelta := fee.Calculate(rawScript)
|
2020-08-19 13:20:48 +00:00
|
|
|
tx.NetworkFee += netFee
|
|
|
|
tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte()
|
|
|
|
data := tx.GetSignedPart()
|
|
|
|
tx.Scripts = []transaction.Witness{{
|
2020-08-27 15:17:15 +00:00
|
|
|
InvocationScript: testchain.SignCommittee(data),
|
2020-08-19 13:20:48 +00:00
|
|
|
VerificationScript: rawScript,
|
|
|
|
}}
|
|
|
|
require.NoError(t, bc.VerifyTx(tx))
|
|
|
|
})
|
2020-09-24 13:33:40 +00:00
|
|
|
t.Run("Oracle", func(t *testing.T) {
|
|
|
|
orc := bc.contracts.Oracle
|
2020-10-07 12:06:10 +00:00
|
|
|
req := &state.OracleRequest{GasForResponse: 1000_0000}
|
2020-09-24 13:33:40 +00:00
|
|
|
require.NoError(t, orc.PutRequestInternal(1, req, bc.dao))
|
|
|
|
|
|
|
|
oracleScript, err := smartcontract.CreateMajorityMultiSigRedeemScript(oraclePubs)
|
|
|
|
require.NoError(t, err)
|
|
|
|
oracleHash := hash.Hash160(oracleScript)
|
|
|
|
|
|
|
|
// We need to create new transaction,
|
|
|
|
// because hashes are cached after signing.
|
|
|
|
getOracleTx := func(t *testing.T) *transaction.Transaction {
|
|
|
|
tx := bc.newTestTx(h, native.GetOracleResponseScript())
|
|
|
|
resp := &transaction.OracleResponse{
|
|
|
|
ID: 1,
|
|
|
|
Code: transaction.Success,
|
|
|
|
Result: []byte{1, 2, 3},
|
|
|
|
}
|
|
|
|
tx.Attributes = []transaction.Attribute{{
|
|
|
|
Type: transaction.OracleResponseT,
|
|
|
|
Value: resp,
|
|
|
|
}}
|
|
|
|
tx.NetworkFee += 4_000_000 // multisig check
|
|
|
|
tx.SystemFee = int64(req.GasForResponse - uint64(tx.NetworkFee))
|
|
|
|
tx.Signers = []transaction.Signer{{
|
|
|
|
Account: oracleHash,
|
2020-10-01 12:26:51 +00:00
|
|
|
Scopes: transaction.None,
|
2020-09-24 13:33:40 +00:00
|
|
|
}}
|
|
|
|
size := io.GetVarSize(tx)
|
2020-09-28 14:56:16 +00:00
|
|
|
netFee, sizeDelta := fee.Calculate(oracleScript)
|
2020-09-24 13:33:40 +00:00
|
|
|
tx.NetworkFee += netFee
|
|
|
|
tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte()
|
|
|
|
return tx
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Run("NoOracleNodes", func(t *testing.T) {
|
|
|
|
tx := getOracleTx(t)
|
|
|
|
require.NoError(t, oracleAcc.SignTx(tx))
|
|
|
|
checkErr(t, ErrInvalidAttribute, tx)
|
|
|
|
})
|
|
|
|
|
|
|
|
txSetOracle := transaction.New(netmode.UnitTestNet, []byte{}, 0)
|
|
|
|
setSigner(txSetOracle, testchain.CommitteeScriptHash())
|
|
|
|
txSetOracle.Scripts = []transaction.Witness{{
|
|
|
|
InvocationScript: testchain.SignCommittee(txSetOracle.GetSignedPart()),
|
|
|
|
VerificationScript: testchain.CommitteeVerificationScript(),
|
|
|
|
}}
|
|
|
|
ic := bc.newInteropContext(trigger.All, bc.dao, nil, txSetOracle)
|
2020-09-29 06:56:19 +00:00
|
|
|
ic.SpawnVM()
|
|
|
|
ic.VM.LoadScript([]byte{byte(opcode.RET)})
|
2020-10-01 15:17:09 +00:00
|
|
|
require.NoError(t, bc.contracts.Designate.DesignateAsRole(ic, native.RoleOracle, oraclePubs))
|
|
|
|
require.NoError(t, bc.contracts.Designate.OnPersistEnd(ic.DAO))
|
2020-09-24 13:33:40 +00:00
|
|
|
_, err = ic.DAO.Persist()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
t.Run("Valid", func(t *testing.T) {
|
|
|
|
tx := getOracleTx(t)
|
|
|
|
require.NoError(t, oracleAcc.SignTx(tx))
|
|
|
|
require.NoError(t, bc.VerifyTx(tx))
|
2020-10-09 10:32:54 +00:00
|
|
|
|
|
|
|
t.Run("NativeVerify", func(t *testing.T) {
|
|
|
|
tx.Signers = append(tx.Signers, transaction.Signer{
|
|
|
|
Account: bc.contracts.Oracle.Hash,
|
|
|
|
Scopes: transaction.None,
|
|
|
|
})
|
|
|
|
tx.Scripts = append(tx.Scripts, transaction.Witness{})
|
|
|
|
t.Run("NonZeroVerification", func(t *testing.T) {
|
|
|
|
tx.Scripts[len(tx.Scripts)-1].VerificationScript = bc.contracts.Oracle.Script
|
|
|
|
err := bc.VerifyTx(tx)
|
|
|
|
require.True(t, errors.Is(err, ErrNativeContractWitness), "got: %v", err)
|
|
|
|
})
|
|
|
|
t.Run("Good", func(t *testing.T) {
|
|
|
|
tx.Scripts[len(tx.Scripts)-1].VerificationScript = nil
|
|
|
|
require.NoError(t, bc.VerifyTx(tx))
|
|
|
|
})
|
|
|
|
})
|
2020-09-24 13:33:40 +00:00
|
|
|
})
|
|
|
|
t.Run("InvalidRequestID", func(t *testing.T) {
|
|
|
|
tx := getOracleTx(t)
|
|
|
|
tx.Attributes[0].Value.(*transaction.OracleResponse).ID = 2
|
|
|
|
require.NoError(t, oracleAcc.SignTx(tx))
|
|
|
|
checkErr(t, ErrInvalidAttribute, tx)
|
|
|
|
})
|
|
|
|
t.Run("InvalidScope", func(t *testing.T) {
|
|
|
|
tx := getOracleTx(t)
|
|
|
|
tx.Signers[0].Scopes = transaction.Global
|
|
|
|
require.NoError(t, oracleAcc.SignTx(tx))
|
|
|
|
checkErr(t, ErrInvalidAttribute, tx)
|
|
|
|
})
|
|
|
|
t.Run("InvalidScript", func(t *testing.T) {
|
|
|
|
tx := getOracleTx(t)
|
|
|
|
tx.Script[0] = ^tx.Script[0]
|
|
|
|
require.NoError(t, oracleAcc.SignTx(tx))
|
|
|
|
checkErr(t, ErrInvalidAttribute, tx)
|
|
|
|
})
|
|
|
|
t.Run("InvalidSigner", func(t *testing.T) {
|
|
|
|
tx := getOracleTx(t)
|
|
|
|
tx.Signers[0].Account = accs[0].Contract.ScriptHash()
|
|
|
|
require.NoError(t, accs[0].SignTx(tx))
|
|
|
|
checkErr(t, ErrInvalidAttribute, tx)
|
|
|
|
})
|
|
|
|
t.Run("SmallFee", func(t *testing.T) {
|
|
|
|
tx := getOracleTx(t)
|
|
|
|
tx.SystemFee = 0
|
|
|
|
require.NoError(t, oracleAcc.SignTx(tx))
|
|
|
|
checkErr(t, ErrInvalidAttribute, tx)
|
|
|
|
})
|
|
|
|
})
|
2020-10-14 16:07:16 +00:00
|
|
|
t.Run("NotValidBefore", func(t *testing.T) {
|
|
|
|
getNVBTx := func(height uint32) *transaction.Transaction {
|
|
|
|
tx := bc.newTestTx(h, testScript)
|
|
|
|
tx.Attributes = append(tx.Attributes, transaction.Attribute{Type: transaction.NotValidBeforeT, Value: &transaction.NotValidBefore{Height: height}})
|
|
|
|
tx.NetworkFee += 4_000_000 // multisig check
|
|
|
|
tx.Signers = []transaction.Signer{{
|
|
|
|
Account: testchain.CommitteeScriptHash(),
|
|
|
|
Scopes: transaction.None,
|
|
|
|
}}
|
|
|
|
rawScript := testchain.CommitteeVerificationScript()
|
|
|
|
require.NoError(t, err)
|
|
|
|
size := io.GetVarSize(tx)
|
|
|
|
netFee, sizeDelta := fee.Calculate(rawScript)
|
|
|
|
tx.NetworkFee += netFee
|
|
|
|
tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte()
|
|
|
|
data := tx.GetSignedPart()
|
|
|
|
tx.Scripts = []transaction.Witness{{
|
|
|
|
InvocationScript: testchain.SignCommittee(data),
|
|
|
|
VerificationScript: rawScript,
|
|
|
|
}}
|
|
|
|
return tx
|
|
|
|
}
|
|
|
|
t.Run("Disabled", func(t *testing.T) {
|
|
|
|
tx := getNVBTx(bc.blockHeight + 1)
|
|
|
|
require.Error(t, bc.VerifyTx(tx))
|
|
|
|
})
|
|
|
|
t.Run("Enabled", func(t *testing.T) {
|
|
|
|
bc.config.P2PSigExtensions = true
|
|
|
|
t.Run("NotYetValid", func(t *testing.T) {
|
|
|
|
tx := getNVBTx(bc.blockHeight + 1)
|
|
|
|
require.True(t, errors.Is(bc.VerifyTx(tx), ErrTxNotYetValid))
|
|
|
|
})
|
|
|
|
t.Run("positive", func(t *testing.T) {
|
|
|
|
tx := getNVBTx(bc.blockHeight)
|
|
|
|
require.NoError(t, bc.VerifyTx(tx))
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
2020-10-15 10:06:22 +00:00
|
|
|
t.Run("Reserved", func(t *testing.T) {
|
|
|
|
getReservedTx := func(attrType transaction.AttrType) *transaction.Transaction {
|
|
|
|
tx := bc.newTestTx(h, testScript)
|
|
|
|
tx.Attributes = append(tx.Attributes, transaction.Attribute{Type: attrType, Value: &transaction.Reserved{Value: []byte{1, 2, 3}}})
|
|
|
|
tx.NetworkFee += 4_000_000 // multisig check
|
|
|
|
tx.Signers = []transaction.Signer{{
|
|
|
|
Account: testchain.CommitteeScriptHash(),
|
|
|
|
Scopes: transaction.None,
|
|
|
|
}}
|
|
|
|
rawScript := testchain.CommitteeVerificationScript()
|
|
|
|
require.NoError(t, err)
|
|
|
|
size := io.GetVarSize(tx)
|
|
|
|
netFee, sizeDelta := fee.Calculate(rawScript)
|
|
|
|
tx.NetworkFee += netFee
|
|
|
|
tx.NetworkFee += int64(size+sizeDelta) * bc.FeePerByte()
|
|
|
|
data := tx.GetSignedPart()
|
|
|
|
tx.Scripts = []transaction.Witness{{
|
|
|
|
InvocationScript: testchain.SignCommittee(data),
|
|
|
|
VerificationScript: rawScript,
|
|
|
|
}}
|
|
|
|
return tx
|
|
|
|
}
|
|
|
|
t.Run("Disabled", func(t *testing.T) {
|
|
|
|
tx := getReservedTx(transaction.ReservedLowerBound + 2)
|
|
|
|
require.Error(t, bc.VerifyTx(tx))
|
|
|
|
})
|
|
|
|
t.Run("Enabled", func(t *testing.T) {
|
|
|
|
bc.config.ReservedAttributes = true
|
|
|
|
tx := getReservedTx(transaction.ReservedLowerBound + 2)
|
|
|
|
require.NoError(t, bc.VerifyTx(tx))
|
|
|
|
})
|
|
|
|
})
|
2020-08-19 13:20:48 +00:00
|
|
|
})
|
2020-08-13 10:42:21 +00:00
|
|
|
}
|
|
|
|
|
2020-08-13 15:42:53 +00:00
|
|
|
func TestVerifyHashAgainstScript(t *testing.T) {
|
|
|
|
bc := newTestChain(t)
|
|
|
|
defer bc.Close()
|
|
|
|
|
|
|
|
cs, csInvalid := getTestContractState()
|
|
|
|
ic := bc.newInteropContext(trigger.Verification, bc.dao, nil, nil)
|
|
|
|
require.NoError(t, ic.DAO.PutContractState(cs))
|
|
|
|
require.NoError(t, ic.DAO.PutContractState(csInvalid))
|
|
|
|
|
|
|
|
gas := bc.contracts.Policy.GetMaxVerificationGas(ic.DAO)
|
|
|
|
t.Run("Contract", func(t *testing.T) {
|
|
|
|
t.Run("Missing", func(t *testing.T) {
|
|
|
|
newH := cs.ScriptHash()
|
|
|
|
newH[0] = ^newH[0]
|
|
|
|
w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH4)}}
|
2020-09-29 13:05:42 +00:00
|
|
|
_, err := bc.verifyHashAgainstScript(newH, w, ic, gas)
|
2020-08-13 15:42:53 +00:00
|
|
|
require.True(t, errors.Is(err, ErrUnknownVerificationContract))
|
|
|
|
})
|
|
|
|
t.Run("Invalid", func(t *testing.T) {
|
|
|
|
w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH4)}}
|
2020-09-29 13:05:42 +00:00
|
|
|
_, err := bc.verifyHashAgainstScript(csInvalid.ScriptHash(), w, ic, gas)
|
2020-08-13 15:42:53 +00:00
|
|
|
require.True(t, errors.Is(err, ErrInvalidVerificationContract))
|
|
|
|
})
|
|
|
|
t.Run("ValidSignature", func(t *testing.T) {
|
|
|
|
w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH4)}}
|
2020-09-29 13:05:42 +00:00
|
|
|
_, err := bc.verifyHashAgainstScript(cs.ScriptHash(), w, ic, gas)
|
2020-08-13 15:42:53 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
})
|
|
|
|
t.Run("InvalidSignature", func(t *testing.T) {
|
|
|
|
w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH3)}}
|
2020-09-29 13:05:42 +00:00
|
|
|
_, err := bc.verifyHashAgainstScript(cs.ScriptHash(), w, ic, gas)
|
2020-08-13 15:42:53 +00:00
|
|
|
require.True(t, errors.Is(err, ErrVerificationFailed))
|
|
|
|
})
|
|
|
|
})
|
2020-08-13 15:53:41 +00:00
|
|
|
t.Run("NotEnoughGas", func(t *testing.T) {
|
|
|
|
verif := []byte{byte(opcode.PUSH1)}
|
|
|
|
w := &transaction.Witness{
|
|
|
|
InvocationScript: []byte{byte(opcode.NOP)},
|
|
|
|
VerificationScript: verif,
|
|
|
|
}
|
2020-09-29 13:05:42 +00:00
|
|
|
_, err := bc.verifyHashAgainstScript(hash.Hash160(verif), w, ic, 1)
|
2020-08-13 15:53:41 +00:00
|
|
|
require.True(t, errors.Is(err, ErrVerificationFailed))
|
|
|
|
})
|
|
|
|
t.Run("NoResult", func(t *testing.T) {
|
|
|
|
verif := []byte{byte(opcode.DROP)}
|
|
|
|
w := &transaction.Witness{
|
|
|
|
InvocationScript: []byte{byte(opcode.PUSH1)},
|
|
|
|
VerificationScript: verif,
|
|
|
|
}
|
2020-09-29 13:05:42 +00:00
|
|
|
_, err := bc.verifyHashAgainstScript(hash.Hash160(verif), w, ic, gas)
|
2020-08-21 18:05:47 +00:00
|
|
|
require.True(t, errors.Is(err, ErrVerificationFailed))
|
|
|
|
})
|
|
|
|
t.Run("BadResult", func(t *testing.T) {
|
|
|
|
verif := make([]byte, 66)
|
|
|
|
verif[0] = byte(opcode.PUSHDATA1)
|
|
|
|
verif[1] = 64
|
|
|
|
w := &transaction.Witness{
|
|
|
|
InvocationScript: []byte{byte(opcode.NOP)},
|
|
|
|
VerificationScript: verif,
|
2020-08-13 15:53:41 +00:00
|
|
|
}
|
2020-09-29 13:05:42 +00:00
|
|
|
_, err := bc.verifyHashAgainstScript(hash.Hash160(verif), w, ic, gas)
|
2020-08-13 15:53:41 +00:00
|
|
|
require.True(t, errors.Is(err, ErrVerificationFailed))
|
|
|
|
})
|
2020-08-17 19:02:15 +00:00
|
|
|
t.Run("TooManyResults", func(t *testing.T) {
|
|
|
|
verif := []byte{byte(opcode.NOP)}
|
|
|
|
w := &transaction.Witness{
|
|
|
|
InvocationScript: []byte{byte(opcode.PUSH1), byte(opcode.PUSH1)},
|
|
|
|
VerificationScript: verif,
|
|
|
|
}
|
2020-09-29 13:05:42 +00:00
|
|
|
_, err := bc.verifyHashAgainstScript(hash.Hash160(verif), w, ic, gas)
|
2020-08-17 19:02:15 +00:00
|
|
|
require.True(t, errors.Is(err, ErrVerificationFailed))
|
|
|
|
})
|
2020-08-13 15:42:53 +00:00
|
|
|
}
|
|
|
|
|
2020-08-19 12:27:13 +00:00
|
|
|
func TestMemPoolRemoval(t *testing.T) {
|
|
|
|
const added = 16
|
|
|
|
const notAdded = 32
|
|
|
|
bc := newTestChain(t)
|
|
|
|
defer bc.Close()
|
|
|
|
addedTxes := make([]*transaction.Transaction, added)
|
|
|
|
notAddedTxes := make([]*transaction.Transaction, notAdded)
|
|
|
|
for i := range addedTxes {
|
|
|
|
addedTxes[i] = bc.newTestTx(testchain.MultisigScriptHash(), []byte{byte(opcode.PUSH1)})
|
|
|
|
require.NoError(t, signTx(bc, addedTxes[i]))
|
|
|
|
require.NoError(t, bc.PoolTx(addedTxes[i]))
|
|
|
|
}
|
|
|
|
for i := range notAddedTxes {
|
|
|
|
notAddedTxes[i] = bc.newTestTx(testchain.MultisigScriptHash(), []byte{byte(opcode.PUSH1)})
|
|
|
|
require.NoError(t, signTx(bc, notAddedTxes[i]))
|
|
|
|
require.NoError(t, bc.PoolTx(notAddedTxes[i]))
|
|
|
|
}
|
|
|
|
b := bc.newBlock(addedTxes...)
|
|
|
|
require.NoError(t, bc.AddBlock(b))
|
|
|
|
mempool := bc.GetMemPool()
|
|
|
|
for _, tx := range addedTxes {
|
|
|
|
require.False(t, mempool.ContainsKey(tx.Hash()))
|
|
|
|
}
|
|
|
|
for _, tx := range notAddedTxes {
|
|
|
|
require.True(t, mempool.ContainsKey(tx.Hash()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-17 11:53:21 +00:00
|
|
|
func TestHasBlock(t *testing.T) {
|
|
|
|
bc := newTestChain(t)
|
2020-02-29 14:16:13 +00:00
|
|
|
blocks, err := bc.genBlocks(50)
|
|
|
|
require.NoError(t, err)
|
2018-03-17 11:53:21 +00:00
|
|
|
|
2019-09-24 15:51:20 +00:00
|
|
|
// Test unpersisted and persisted access
|
|
|
|
for j := 0; j < 2; j++ {
|
|
|
|
for i := 0; i < len(blocks); i++ {
|
|
|
|
assert.True(t, bc.HasBlock(blocks[i].Hash()))
|
|
|
|
}
|
2020-02-29 14:24:37 +00:00
|
|
|
newBlock := bc.newBlock()
|
2019-09-24 15:51:20 +00:00
|
|
|
assert.False(t, bc.HasBlock(newBlock.Hash()))
|
2019-10-21 07:04:58 +00:00
|
|
|
assert.NoError(t, bc.persist())
|
2018-03-17 11:53:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-21 16:11:04 +00:00
|
|
|
func TestGetTransaction(t *testing.T) {
|
|
|
|
bc := newTestChain(t)
|
2020-08-18 12:27:49 +00:00
|
|
|
tx1 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
2020-08-19 10:19:09 +00:00
|
|
|
tx1.ValidUntilBlock = 16
|
2020-08-18 12:27:49 +00:00
|
|
|
tx1.Signers = []transaction.Signer{{
|
|
|
|
Account: testchain.MultisigScriptHash(),
|
|
|
|
Scopes: transaction.CalledByEntry,
|
|
|
|
}}
|
|
|
|
tx2 := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH2)}, 0)
|
2020-08-19 10:19:09 +00:00
|
|
|
tx2.ValidUntilBlock = 16
|
2020-08-18 12:27:49 +00:00
|
|
|
tx2.Signers = []transaction.Signer{{
|
|
|
|
Account: testchain.MultisigScriptHash(),
|
|
|
|
Scopes: transaction.CalledByEntry,
|
|
|
|
}}
|
|
|
|
require.NoError(t, signTx(bc, tx1, tx2))
|
|
|
|
b1 := bc.newBlock(tx1)
|
2018-03-21 16:11:04 +00:00
|
|
|
|
2019-10-21 08:05:10 +00:00
|
|
|
assert.Nil(t, bc.AddBlock(b1))
|
2020-08-18 12:27:49 +00:00
|
|
|
block := bc.newBlock(tx2)
|
2020-08-19 10:19:09 +00:00
|
|
|
txSize := io.GetVarSize(tx2)
|
2019-10-21 08:05:10 +00:00
|
|
|
assert.Nil(t, bc.AddBlock(block))
|
2018-03-21 16:11:04 +00:00
|
|
|
|
2019-09-24 15:51:20 +00:00
|
|
|
// Test unpersisted and persisted access
|
|
|
|
for j := 0; j < 2; j++ {
|
|
|
|
tx, height, err := bc.GetTransaction(block.Transactions[0].Hash())
|
|
|
|
require.Nil(t, err)
|
|
|
|
assert.Equal(t, block.Index, height)
|
2020-09-10 16:28:16 +00:00
|
|
|
assert.Equal(t, txSize, tx.Size())
|
2019-09-24 15:51:20 +00:00
|
|
|
assert.Equal(t, block.Transactions[0], tx)
|
|
|
|
assert.Equal(t, 1, io.GetVarSize(tx.Attributes))
|
|
|
|
assert.Equal(t, 1, io.GetVarSize(tx.Scripts))
|
2019-10-21 07:04:58 +00:00
|
|
|
assert.NoError(t, bc.persist())
|
2018-03-21 16:11:04 +00:00
|
|
|
}
|
|
|
|
}
|
2020-08-18 12:27:49 +00:00
|
|
|
|
2020-02-25 13:15:17 +00:00
|
|
|
func TestGetClaimable(t *testing.T) {
|
|
|
|
bc := newTestChain(t)
|
2020-06-27 09:36:43 +00:00
|
|
|
defer bc.Close()
|
2020-02-25 13:15:17 +00:00
|
|
|
|
2020-02-27 13:48:24 +00:00
|
|
|
_, err := bc.genBlocks(10)
|
2020-02-25 13:15:17 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
t.Run("first generation period", func(t *testing.T) {
|
2020-07-09 09:57:24 +00:00
|
|
|
amount := bc.CalculateClaimable(big.NewInt(1), 0, 2)
|
2020-08-26 09:07:30 +00:00
|
|
|
require.EqualValues(t, big.NewInt(1), amount)
|
2020-02-25 13:15:17 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-11-07 17:47:48 +00:00
|
|
|
func TestClose(t *testing.T) {
|
|
|
|
defer func() {
|
|
|
|
r := recover()
|
|
|
|
assert.NotNil(t, r)
|
|
|
|
}()
|
|
|
|
bc := newTestChain(t)
|
2020-02-29 14:16:13 +00:00
|
|
|
_, err := bc.genBlocks(10)
|
|
|
|
require.NoError(t, err)
|
2019-11-07 17:47:48 +00:00
|
|
|
bc.Close()
|
|
|
|
// It's a hack, but we use internal knowledge of MemoryStore
|
|
|
|
// implementation which makes it completely unusable (up to panicing)
|
|
|
|
// after Close().
|
2020-04-07 09:41:12 +00:00
|
|
|
_ = bc.dao.Store.Put([]byte{0}, []byte{1})
|
2019-11-07 17:47:48 +00:00
|
|
|
|
|
|
|
// This should never be executed.
|
|
|
|
assert.Nil(t, t)
|
|
|
|
}
|
2020-05-12 14:20:41 +00:00
|
|
|
|
|
|
|
func TestSubscriptions(t *testing.T) {
|
|
|
|
// We use buffering here as a substitute for reader goroutines, events
|
|
|
|
// get queued up and we read them one by one here.
|
|
|
|
const chBufSize = 16
|
|
|
|
blockCh := make(chan *block.Block, chBufSize)
|
|
|
|
txCh := make(chan *transaction.Transaction, chBufSize)
|
|
|
|
notificationCh := make(chan *state.NotificationEvent, chBufSize)
|
|
|
|
executionCh := make(chan *state.AppExecResult, chBufSize)
|
|
|
|
|
|
|
|
bc := newTestChain(t)
|
core: fix TestSubscriptions occasional failures
panic: Log in goroutine after TestSubscriptions has completed
goroutine 1079 [running]:
testing.(*common).logDepth(0xc00057a100, 0xc00039e210, 0xa4, 0x3)
/usr/local/go/src/testing/testing.go:634 +0x51a
testing.(*common).log(...)
/usr/local/go/src/testing/testing.go:614
testing.(*common).Logf(0xc00057a100, 0xe32eaa, 0x2, 0xc0009560e0, 0x1, 0x1)
/usr/local/go/src/testing/testing.go:649 +0x91
go.uber.org/zap/zaptest.testingWriter.Write(0xf64120, 0xc00057a100, 0x0, 0xc0003fe400, 0xa5, 0x400, 0xc000958e40, 0xc0009560d0, 0xc000958e60)
/go/pkg/mod/go.uber.org/zap@v1.10.0/zaptest/logger.go:130 +0x120
go.uber.org/zap/zapcore.(*ioCore).Write(0xc0005cd050, 0x0, 0xbfb54ffc0626aba2, 0x916de700, 0x1485500, 0x0, 0x0, 0xe43fb0, 0x1c, 0x0, ...)
/go/pkg/mod/go.uber.org/zap@v1.10.0/zapcore/core.go:90 +0x1c5
go.uber.org/zap/zapcore.(*CheckedEntry).Write(0xc000102d10, 0xc00039a000, 0x5, 0x5)
/go/pkg/mod/go.uber.org/zap@v1.10.0/zapcore/entry.go:215 +0x1e8
go.uber.org/zap.(*Logger).Info(0xc00035eba0, 0xe43fb0, 0x1c, 0xc00039a000, 0x5, 0x5)
/go/pkg/mod/go.uber.org/zap@v1.10.0/logger.go:187 +0x96
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).persist(0xc00000cb40, 0xc00017c2c0, 0xbe8a00)
/go/src/github.com/nspcc-dev/neo-go/pkg/core/blockchain.go:839 +0x6c9
github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).Run.func2(0xc00000cb40, 0xc0005c6c30)
/go/src/github.com/nspcc-dev/neo-go/pkg/core/blockchain.go:302 +0x54
created by github.com/nspcc-dev/neo-go/pkg/core.(*Blockchain).Run
/go/src/github.com/nspcc-dev/neo-go/pkg/core/blockchain.go:301 +0x25d
FAIL github.com/nspcc-dev/neo-go/pkg/core 2.463s
2020-06-25 16:22:38 +00:00
|
|
|
defer bc.Close()
|
2020-05-12 14:20:41 +00:00
|
|
|
bc.SubscribeForBlocks(blockCh)
|
|
|
|
bc.SubscribeForTransactions(txCh)
|
|
|
|
bc.SubscribeForNotifications(notificationCh)
|
|
|
|
bc.SubscribeForExecutions(executionCh)
|
|
|
|
|
|
|
|
assert.Empty(t, notificationCh)
|
|
|
|
assert.Empty(t, executionCh)
|
|
|
|
assert.Empty(t, blockCh)
|
|
|
|
assert.Empty(t, txCh)
|
|
|
|
|
|
|
|
blocks, err := bc.genBlocks(1)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Eventually(t, func() bool { return len(blockCh) != 0 }, time.Second, 10*time.Millisecond)
|
2020-08-26 13:16:57 +00:00
|
|
|
assert.Len(t, notificationCh, 1) // validator bounty
|
2020-09-23 08:48:31 +00:00
|
|
|
assert.Len(t, executionCh, 2)
|
2020-05-12 14:20:41 +00:00
|
|
|
assert.Empty(t, txCh)
|
|
|
|
|
|
|
|
b := <-blockCh
|
|
|
|
assert.Equal(t, blocks[0], b)
|
|
|
|
assert.Empty(t, blockCh)
|
|
|
|
|
2020-06-18 11:19:55 +00:00
|
|
|
aer := <-executionCh
|
|
|
|
assert.Equal(t, b.Hash(), aer.TxHash)
|
2020-09-23 08:48:31 +00:00
|
|
|
aer = <-executionCh
|
|
|
|
assert.Equal(t, b.Hash(), aer.TxHash)
|
2020-06-18 11:19:55 +00:00
|
|
|
|
2020-08-26 13:16:57 +00:00
|
|
|
notif := <-notificationCh
|
|
|
|
require.Equal(t, bc.UtilityTokenHash(), notif.ScriptHash)
|
|
|
|
|
2020-05-12 14:20:41 +00:00
|
|
|
script := io.NewBufBinWriter()
|
|
|
|
emit.Bytes(script.BinWriter, []byte("yay!"))
|
2020-08-14 10:50:52 +00:00
|
|
|
emit.Syscall(script.BinWriter, interopnames.SystemRuntimeNotify)
|
2020-05-12 14:20:41 +00:00
|
|
|
require.NoError(t, script.Err)
|
2020-06-18 09:00:51 +00:00
|
|
|
txGood1 := transaction.New(netmode.UnitTestNet, script.Bytes(), 0)
|
2020-07-29 16:57:38 +00:00
|
|
|
txGood1.Signers = []transaction.Signer{{Account: neoOwner}}
|
2020-05-12 14:20:41 +00:00
|
|
|
txGood1.Nonce = 1
|
|
|
|
txGood1.ValidUntilBlock = 100500
|
|
|
|
require.NoError(t, signTx(bc, txGood1))
|
|
|
|
|
|
|
|
// Reset() reuses the script buffer and we need to keep scripts.
|
|
|
|
script = io.NewBufBinWriter()
|
|
|
|
emit.Bytes(script.BinWriter, []byte("nay!"))
|
2020-08-14 10:50:52 +00:00
|
|
|
emit.Syscall(script.BinWriter, interopnames.SystemRuntimeNotify)
|
2020-10-02 08:30:15 +00:00
|
|
|
emit.Opcodes(script.BinWriter, opcode.THROW)
|
2020-05-12 14:20:41 +00:00
|
|
|
require.NoError(t, script.Err)
|
2020-06-18 09:00:51 +00:00
|
|
|
txBad := transaction.New(netmode.UnitTestNet, script.Bytes(), 0)
|
2020-07-29 16:57:38 +00:00
|
|
|
txBad.Signers = []transaction.Signer{{Account: neoOwner}}
|
2020-05-12 14:20:41 +00:00
|
|
|
txBad.Nonce = 2
|
|
|
|
txBad.ValidUntilBlock = 100500
|
|
|
|
require.NoError(t, signTx(bc, txBad))
|
|
|
|
|
|
|
|
script = io.NewBufBinWriter()
|
|
|
|
emit.Bytes(script.BinWriter, []byte("yay! yay! yay!"))
|
2020-08-14 10:50:52 +00:00
|
|
|
emit.Syscall(script.BinWriter, interopnames.SystemRuntimeNotify)
|
2020-05-12 14:20:41 +00:00
|
|
|
require.NoError(t, script.Err)
|
2020-06-18 09:00:51 +00:00
|
|
|
txGood2 := transaction.New(netmode.UnitTestNet, script.Bytes(), 0)
|
2020-07-29 16:57:38 +00:00
|
|
|
txGood2.Signers = []transaction.Signer{{Account: neoOwner}}
|
2020-05-12 14:20:41 +00:00
|
|
|
txGood2.Nonce = 3
|
|
|
|
txGood2.ValidUntilBlock = 100500
|
|
|
|
require.NoError(t, signTx(bc, txGood2))
|
|
|
|
|
|
|
|
invBlock := newBlock(bc.config, bc.BlockHeight()+1, bc.CurrentHeaderHash(), txGood1, txBad, txGood2)
|
|
|
|
require.NoError(t, bc.AddBlock(invBlock))
|
|
|
|
|
|
|
|
require.Eventually(t, func() bool {
|
|
|
|
return len(blockCh) != 0 && len(txCh) != 0 &&
|
|
|
|
len(notificationCh) != 0 && len(executionCh) != 0
|
|
|
|
}, time.Second, 10*time.Millisecond)
|
|
|
|
|
|
|
|
b = <-blockCh
|
|
|
|
require.Equal(t, invBlock, b)
|
|
|
|
assert.Empty(t, blockCh)
|
|
|
|
|
2020-06-18 11:19:55 +00:00
|
|
|
exec := <-executionCh
|
|
|
|
require.Equal(t, b.Hash(), exec.TxHash)
|
2020-07-27 14:57:53 +00:00
|
|
|
require.Equal(t, exec.VMState, vm.HaltState)
|
2020-06-18 11:19:55 +00:00
|
|
|
|
|
|
|
// 3 burn events for every tx and 1 mint for primary node
|
|
|
|
require.True(t, len(notificationCh) >= 4)
|
|
|
|
for i := 0; i < 4; i++ {
|
|
|
|
notif := <-notificationCh
|
|
|
|
require.Equal(t, bc.contracts.GAS.Hash, notif.ScriptHash)
|
|
|
|
}
|
|
|
|
|
2020-05-12 14:20:41 +00:00
|
|
|
// Follow in-block transaction order.
|
|
|
|
for _, txExpected := range invBlock.Transactions {
|
|
|
|
tx := <-txCh
|
|
|
|
require.Equal(t, txExpected, tx)
|
2020-06-05 13:07:04 +00:00
|
|
|
exec := <-executionCh
|
|
|
|
require.Equal(t, tx.Hash(), exec.TxHash)
|
2020-07-27 14:57:53 +00:00
|
|
|
if exec.VMState == vm.HaltState {
|
2020-06-05 13:07:04 +00:00
|
|
|
notif := <-notificationCh
|
|
|
|
require.Equal(t, hash.Hash160(tx.Script), notif.ScriptHash)
|
2020-05-12 14:20:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
assert.Empty(t, txCh)
|
2020-08-26 13:16:57 +00:00
|
|
|
assert.Len(t, notificationCh, 1)
|
2020-09-23 08:48:31 +00:00
|
|
|
assert.Len(t, executionCh, 1)
|
2020-05-12 14:20:41 +00:00
|
|
|
|
2020-08-26 13:16:57 +00:00
|
|
|
notif = <-notificationCh
|
|
|
|
require.Equal(t, bc.UtilityTokenHash(), notif.ScriptHash)
|
|
|
|
|
2020-09-23 08:48:31 +00:00
|
|
|
exec = <-executionCh
|
|
|
|
require.Equal(t, b.Hash(), exec.TxHash)
|
|
|
|
require.Equal(t, exec.VMState, vm.HaltState)
|
|
|
|
|
2020-05-12 14:20:41 +00:00
|
|
|
bc.UnsubscribeFromBlocks(blockCh)
|
|
|
|
bc.UnsubscribeFromTransactions(txCh)
|
|
|
|
bc.UnsubscribeFromNotifications(notificationCh)
|
|
|
|
bc.UnsubscribeFromExecutions(executionCh)
|
|
|
|
|
|
|
|
// Ensure that new blocks are processed correctly after unsubscription.
|
|
|
|
_, err = bc.genBlocks(2 * chBufSize)
|
|
|
|
require.NoError(t, err)
|
|
|
|
}
|