2018-02-04 19:54:51 +00:00
package core
import (
2021-08-31 15:39:19 +00:00
"encoding/binary"
2020-08-13 10:42:21 +00:00
"errors"
2021-02-17 07:45:39 +00:00
"fmt"
2020-07-09 09:57:24 +00:00
"math/big"
2020-08-13 10:42:21 +00:00
"math/rand"
2021-11-17 11:14:22 +00:00
"path/filepath"
2021-02-17 07:45:39 +00:00
"strings"
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-11-23 11:09:00 +00:00
"github.com/nspcc-dev/neo-go/internal/random"
"github.com/nspcc-dev/neo-go/internal/testchain"
2020-11-26 09:33:34 +00:00
"github.com/nspcc-dev/neo-go/pkg/config"
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-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-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"
2021-03-23 10:49:27 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/native/nativeprices"
2021-03-23 10:37:30 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
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"
2021-02-15 13:40:44 +00:00
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
2020-05-12 14:20:41 +00:00
"github.com/nspcc-dev/neo-go/pkg/io"
2021-09-24 09:15:25 +00:00
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result/subscriptions"
2020-09-24 13:33:40 +00:00
"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-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"
2021-08-31 15:39:19 +00:00
"github.com/nspcc-dev/neo-go/pkg/util/slice"
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"
2021-08-31 15:39:19 +00:00
"go.uber.org/zap/zaptest"
2018-02-04 19:54:51 +00:00
)
2020-08-13 10:57:30 +00:00
func TestVerifyHeader ( t * testing . T ) {
bc := newTestChain ( t )
2021-03-01 13:44:47 +00:00
prev := bc . topBlock . Load ( ) . ( * block . Block ) . Header
2020-08-13 10:57:30 +00:00
t . Run ( "Invalid" , func ( t * testing . T ) {
t . Run ( "Hash" , func ( t * testing . T ) {
h := prev . Hash ( )
h [ 0 ] = ^ h [ 0 ]
2021-03-01 13:44:47 +00:00
hdr := newBlock ( bc . config , 1 , h ) . Header
require . True ( t , errors . Is ( bc . verifyHeader ( & hdr , & prev ) , ErrHdrHashMismatch ) )
2020-08-13 10:57:30 +00:00
} )
t . Run ( "Index" , func ( t * testing . T ) {
2021-03-01 13:44:47 +00:00
hdr := newBlock ( bc . config , 3 , prev . Hash ( ) ) . Header
require . True ( t , errors . Is ( bc . verifyHeader ( & hdr , & prev ) , ErrHdrIndexMismatch ) )
2020-08-13 10:57:30 +00:00
} )
t . Run ( "Timestamp" , func ( t * testing . T ) {
2021-03-01 13:44:47 +00:00
hdr := newBlock ( bc . config , 1 , prev . Hash ( ) ) . Header
2020-08-13 10:57:30 +00:00
hdr . Timestamp = 0
2021-03-01 13:44:47 +00:00
require . True ( t , errors . Is ( bc . verifyHeader ( & hdr , & prev ) , ErrHdrInvalidTimestamp ) )
2020-08-13 10:57:30 +00:00
} )
} )
t . Run ( "Valid" , func ( t * testing . T ) {
2021-03-01 13:44:47 +00:00
hdr := newBlock ( bc . config , 1 , prev . Hash ( ) ) . Header
require . NoError ( t , bc . verifyHeader ( & hdr , & prev ) )
2020-08-13 10:57:30 +00:00
} )
}
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:24:37 +00:00
lastBlock := bc . topBlock . Load ( ) . ( * block . Block )
2021-03-01 13:44:47 +00:00
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 ( ) )
2021-03-01 13:44:47 +00:00
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.
2021-03-01 13:44:47 +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 ( ) )
2021-03-01 13:44:47 +00:00
h4 := newBlock ( bc . config , 4 , h3 . Hash ( ) . Reverse ( ) ) . Header
h5 := newBlock ( bc . config , 5 , h4 . Hash ( ) ) . Header
2020-02-29 14:52:09 +00:00
2021-03-01 13:44:47 +00:00
assert . Error ( t , bc . AddHeaders ( & h4 , & h5 ) )
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 ( ) )
2018-03-10 12:04:06 +00:00
2021-03-01 13:44:47 +00:00
h6 := newBlock ( bc . config , 4 , h3 . Hash ( ) ) . Header
2020-02-29 14:52:09 +00:00
h6 . Script . InvocationScript = nil
2021-03-01 13:44:47 +00:00
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()
2021-11-22 07:41:40 +00:00
_ , err = bc . persist ( false )
2021-07-30 20:47:48 +00:00
require . NoError ( t , err )
2018-03-09 15:55:25 +00:00
2018-03-25 10:45:54 +00:00
for _ , block := range blocks {
2021-12-07 20:05:28 +00:00
key := storage . AppendPrefix ( storage . DataExecutable , block . Hash ( ) . BytesBE ( ) )
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-11-17 12:57:50 +00:00
func TestAddBlockStateRoot ( t * testing . T ) {
2020-11-26 09:33:34 +00:00
bc := newTestChainWithCustomCfg ( t , func ( c * config . Config ) {
c . ProtocolConfiguration . StateRootInHeader = true
} )
2020-11-17 12:57:50 +00:00
2021-01-29 14:33:24 +00:00
sr , err := bc . GetStateModule ( ) . GetStateRoot ( bc . BlockHeight ( ) )
2020-11-17 12:57:50 +00:00
require . NoError ( t , err )
2020-11-24 08:14:25 +00:00
tx := newNEP17Transfer ( bc . contracts . NEO . Hash , neoOwner , util . Uint160 { } , 1 )
2020-11-17 12:57:50 +00:00
tx . ValidUntilBlock = bc . BlockHeight ( ) + 1
2021-01-21 09:23:53 +00:00
addSigners ( neoOwner , tx )
2020-11-23 11:09:45 +00:00
require . NoError ( t , testchain . SignTx ( bc , tx ) )
2020-11-17 12:57:50 +00:00
lastBlock := bc . topBlock . Load ( ) . ( * block . Block )
b := newBlock ( bc . config , lastBlock . Index + 1 , lastBlock . Hash ( ) , tx )
err = bc . AddBlock ( b )
require . True ( t , errors . Is ( err , ErrHdrStateRootSetting ) , "got: %v" , err )
u := sr . Root
u [ 0 ] ^ = 0xFF
b = newBlockWithState ( bc . config , lastBlock . Index + 1 , lastBlock . Hash ( ) , & u , tx )
err = bc . AddBlock ( b )
require . True ( t , errors . Is ( err , ErrHdrInvalidStateRoot ) , "got: %v" , err )
b = bc . newBlock ( tx )
require . NoError ( t , bc . AddBlock ( b ) )
}
2021-06-29 15:28:44 +00:00
func TestAddHeadersStateRoot ( t * testing . T ) {
bc := newTestChainWithCustomCfg ( t , func ( c * config . Config ) {
c . ProtocolConfiguration . StateRootInHeader = true
} )
r := bc . stateRoot . CurrentLocalStateRoot ( )
h1 := bc . newBlock ( ) . Header
// invalid stateroot
h1 . PrevStateRoot [ 0 ] ^ = 0xFF
require . True ( t , errors . Is ( bc . AddHeaders ( & h1 ) , ErrHdrInvalidStateRoot ) )
// valid stateroot
h1 . PrevStateRoot = r
require . NoError ( t , bc . AddHeaders ( & h1 ) )
// unable to verify stateroot (stateroot is computed for block #0 only => can
// verify stateroot of header #1 only) => just store the header
h2 := newBlockWithState ( bc . config , 2 , h1 . Hash ( ) , nil ) . Header
require . NoError ( t , bc . AddHeaders ( & h2 ) )
}
2020-08-19 11:38:58 +00:00
func TestAddBadBlock ( t * testing . T ) {
bc := newTestChain ( t )
// It has ValidUntilBlock == 0, which is wrong
2021-03-25 16:18:01 +00:00
tx := transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-08-19 11:38:58 +00:00
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
} }
2020-11-23 11:09:45 +00:00
require . NoError ( t , testchain . SignTx ( bc , tx ) )
2020-08-19 11:38:58 +00:00
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 ) )
2021-03-25 16:18:01 +00:00
tx = transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-08-19 11:38:58 +00:00
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
} }
2020-11-23 11:09:45 +00:00
require . NoError ( t , testchain . SignTx ( bc , tx ) )
2020-08-19 11:38:58 +00:00
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 )
2021-03-25 16:18:01 +00:00
tx := transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-04-15 06:50:13 +00:00
tx . ValidUntilBlock = bc . BlockHeight ( ) + 1
2021-01-21 09:23:53 +00:00
addSigners ( neoOwner , tx )
2020-11-23 11:09:45 +00:00
assert . Nil ( t , testchain . 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 )
2021-03-01 13:44:47 +00:00
assert . Equal ( t , & block . Header , header )
2019-09-24 15:51:20 +00:00
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 )
2021-11-22 07:41:40 +00:00
_ , err = bc . persist ( false )
2021-07-30 20:47:48 +00:00
assert . NoError ( t , err )
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
}
2021-11-22 07:41:40 +00:00
_ , err = bc . persist ( false )
2021-07-30 20:47:48 +00:00
assert . NoError ( t , err )
2018-03-17 11:53:21 +00:00
}
2021-03-10 09:29:56 +00:00
t . Run ( "store only header" , func ( t * testing . T ) {
t . Run ( "non-empty block" , func ( t * testing . T ) {
tx , err := testchain . NewTransferFromOwner ( bc , bc . contracts . NEO . Hash ,
random . Uint160 ( ) , 1 , 1 , 1000 )
2021-05-12 16:37:14 +00:00
require . NoError ( t , err )
2021-03-10 09:29:56 +00:00
b := bc . newBlock ( tx )
require . NoError ( t , bc . AddHeaders ( & b . Header ) )
_ , err = bc . GetBlock ( b . Hash ( ) )
require . Error ( t , err )
_ , err = bc . GetHeader ( b . Hash ( ) )
require . NoError ( t , err )
require . NoError ( t , bc . AddBlock ( b ) )
_ , err = bc . GetBlock ( b . Hash ( ) )
require . NoError ( t , err )
} )
t . Run ( "empty block" , func ( t * testing . T ) {
b := bc . newBlock ( )
require . NoError ( t , bc . AddHeaders ( & b . Header ) )
_ , err = bc . GetBlock ( b . Hash ( ) )
require . NoError ( t , err )
} )
} )
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 {
2021-03-25 16:18:01 +00:00
tx := transaction . New ( script , 1_000_000 )
2020-08-13 10:42:21 +00:00
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 )
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
}
2020-12-29 10:44:07 +00:00
emit . AppCall ( w . BinWriter , sc , "transfer" , callflag . All ,
2020-11-19 15:01:42 +00:00
neoOwner , a . Contract . ScriptHash ( ) , amount , nil )
2020-10-02 08:30:15 +00:00
emit . Opcodes ( w . BinWriter , opcode . ASSERT )
2020-08-13 10:42:21 +00:00
}
}
2020-12-29 10:44:07 +00:00
emit . AppCall ( w . BinWriter , gasHash , "transfer" , callflag . All ,
2020-11-19 15:01:42 +00:00
neoOwner , testchain . CommitteeScriptHash ( ) , int64 ( 1_000_000_000 ) , nil )
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
2020-11-23 11:09:45 +00:00
require . NoError ( t , testchain . SignTx ( bc , txMove ) )
2020-08-13 10:42:21 +00:00
b := bc . newBlock ( txMove )
require . NoError ( t , bc . AddBlock ( b ) )
2020-11-11 15:43:28 +00:00
aer , err := bc . GetAppExecResults ( txMove . Hash ( ) , trigger . Application )
2020-08-13 10:42:21 +00:00
require . NoError ( t , err )
2020-11-11 15:43:28 +00:00
require . Equal ( t , 1 , len ( aer ) )
require . Equal ( t , aer [ 0 ] . VMState , vm . HaltState )
2020-08-13 10:42:21 +00:00
2021-01-21 12:05:15 +00:00
res , err := invokeContractMethodGeneric ( bc , 100000000 , bc . contracts . Policy . Hash , "blockAccount" , true , accs [ 1 ] . PrivateKey ( ) . GetScriptHash ( ) . BytesBE ( ) )
2020-08-13 10:42:21 +00:00
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
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx ) )
2020-08-13 10:42:21 +00:00
checkErr ( t , ErrTxExpired , tx )
} )
t . Run ( "BlockedAccount" , func ( t * testing . T ) {
tx := bc . newTestTx ( accs [ 1 ] . PrivateKey ( ) . GetScriptHash ( ) , testScript )
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 1 ] . SignTx ( netmode . UnitTestNet , 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
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx ) )
2020-08-13 10:42:21 +00:00
checkErr ( t , ErrInsufficientFunds , tx )
} )
t . Run ( "TooBigTx" , func ( t * testing . T ) {
script := make ( [ ] byte , transaction . MaxTransactionSize )
tx := bc . newTestTx ( h , script )
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx ) )
2020-08-13 10:42:21 +00:00
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
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx ) )
2020-09-30 10:20:40 +00:00
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-12-11 12:22:49 +00:00
verificationNetFee , calcultedScriptSize := fee . Calculate ( bc . GetBaseExecFee ( ) , 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
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx ) )
2020-09-30 10:50:58 +00:00
require . Equal ( t , expectedSize , io . GetVarSize ( tx ) )
checkErr ( t , ErrVerificationFailed , tx )
} )
t . Run ( "EnoughNetworkFee" , func ( t * testing . T ) {
tx := bc . newTestTx ( h , testScript )
2020-12-11 12:22:49 +00:00
verificationNetFee , calcultedScriptSize := fee . Calculate ( bc . GetBaseExecFee ( ) , 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
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx ) )
2020-09-30 10:50:58 +00:00
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-12-11 12:22:49 +00:00
verificationNetFee , calculatedScriptSize := fee . Calculate ( bc . GetBaseExecFee ( ) , accs [ 0 ] . Contract . Script )
2020-09-30 10:20:40 +00:00
expectedSize += calculatedScriptSize
expectedNetFee := verificationNetFee + int64 ( expectedSize ) * bc . FeePerByte ( )
tx . NetworkFee = expectedNetFee
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx ) )
2020-09-30 10:20:40 +00:00
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-12-11 12:22:49 +00:00
verificationNetFee , calculatedScriptSize := fee . Calculate ( bc . GetBaseExecFee ( ) , 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
2021-03-25 16:18:01 +00:00
require . NoError ( t , multisigAcc . SignTx ( netmode . UnitTestNet , tx ) )
2020-09-30 10:20:40 +00:00
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
} )
2021-02-09 19:28:36 +00:00
t . Run ( "InvalidTxScript" , func ( t * testing . T ) {
tx := bc . newTestTx ( h , testScript )
tx . Script = append ( tx . Script , 0xff )
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx ) )
2021-02-09 19:28:36 +00:00
checkErr ( t , ErrInvalidScript , tx )
} )
t . Run ( "InvalidVerificationScript" , func ( t * testing . T ) {
tx := bc . newTestTx ( h , testScript )
verif := [ ] byte { byte ( opcode . JMP ) , 3 , 0xff , byte ( opcode . PUSHT ) }
tx . Signers = append ( tx . Signers , transaction . Signer {
Account : hash . Hash160 ( verif ) ,
Scopes : transaction . Global ,
} )
tx . NetworkFee += 1000000
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx ) )
2021-02-09 19:28:36 +00:00
tx . Scripts = append ( tx . Scripts , transaction . Witness {
InvocationScript : [ ] byte { } ,
VerificationScript : verif ,
} )
checkErr ( t , ErrInvalidVerification , tx )
} )
t . Run ( "InvalidInvocationScript" , func ( t * testing . T ) {
tx := bc . newTestTx ( h , testScript )
verif := [ ] byte { byte ( opcode . PUSHT ) }
tx . Signers = append ( tx . Signers , transaction . Signer {
Account : hash . Hash160 ( verif ) ,
Scopes : transaction . Global ,
} )
tx . NetworkFee += 1000000
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx ) )
2021-02-09 19:28:36 +00:00
tx . Scripts = append ( tx . Scripts , transaction . Witness {
InvocationScript : [ ] byte { byte ( opcode . JMP ) , 3 , 0xff } ,
VerificationScript : verif ,
} )
checkErr ( t , ErrInvalidInvocation , tx )
} )
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
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , 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
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx2 ) )
2020-08-19 16:27:15 +00:00
err := bc . PoolTx ( tx2 )
require . True ( t , errors . Is ( err , ErrMemPoolConflict ) )
2020-08-13 10:42:21 +00:00
} )
t . Run ( "InvalidWitnessHash" , func ( t * testing . T ) {
tx := bc . newTestTx ( h , testScript )
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx ) )
2020-08-13 10:42:21 +00:00
tx . Scripts [ 0 ] . VerificationScript = [ ] byte { byte ( opcode . PUSHT ) }
checkErr ( t , ErrWitnessHashMismatch , tx )
} )
t . Run ( "InvalidWitnessSignature" , func ( t * testing . T ) {
tx := bc . newTestTx ( h , testScript )
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx ) )
2020-08-13 10:42:21 +00:00
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 ,
} )
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx ) )
require . NoError ( t , accs [ 3 ] . SignTx ( netmode . UnitTestNet , tx ) )
2020-09-29 13:05:42 +00:00
checkErr ( t , ErrVerificationFailed , tx )
} )
2020-08-19 16:27:15 +00:00
t . Run ( "OldTX" , func ( t * testing . T ) {
tx := bc . newTestTx ( h , testScript )
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx ) )
2020-08-19 16:27:15 +00:00
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 )
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx ) )
2020-08-19 16:27:15 +00:00
require . NoError ( t , bc . PoolTx ( tx ) )
err := bc . PoolTx ( tx )
require . True ( t , errors . Is ( err , ErrAlreadyExists ) )
} )
t . Run ( "MemPoolOOM" , func ( t * testing . T ) {
2021-01-15 12:40:15 +00:00
bc . memPool = mempool . New ( 1 , 0 , false )
2020-08-19 16:27:15 +00:00
tx1 := bc . newTestTx ( h , testScript )
tx1 . NetworkFee += 10000 // Give it more priority.
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx1 ) )
2020-08-19 16:27:15 +00:00
require . NoError ( t , bc . PoolTx ( tx1 ) )
tx2 := bc . newTestTx ( h , testScript )
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx2 ) )
2020-08-19 16:27:15 +00:00
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 } )
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx ) )
2020-08-19 13:20:48 +00:00
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-12-11 12:22:49 +00:00
netFee , sizeDelta := fee . Calculate ( bc . GetBaseExecFee ( ) , rawScript )
2020-08-19 13:20:48 +00:00
tx . NetworkFee += netFee
tx . NetworkFee += int64 ( size + sizeDelta ) * bc . FeePerByte ( )
tx . Scripts = [ ] transaction . Witness { {
2021-03-25 16:18:01 +00:00
InvocationScript : testchain . SignCommittee ( tx ) ,
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 {
2021-01-22 08:28:13 +00:00
tx := bc . newTestTx ( h , orc . GetOracleResponseScript ( ) )
2020-09-24 13:33:40 +00:00
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-12-11 12:22:49 +00:00
netFee , sizeDelta := fee . Calculate ( bc . GetBaseExecFee ( ) , 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 )
2021-03-25 16:18:01 +00:00
require . NoError ( t , oracleAcc . SignTx ( netmode . UnitTestNet , tx ) )
2020-09-24 13:33:40 +00:00
checkErr ( t , ErrInvalidAttribute , tx )
} )
2021-03-25 16:18:01 +00:00
txSetOracle := transaction . New ( [ ] byte { byte ( opcode . RET ) } , 0 ) // it's a hack, so we don't need a real script
2020-09-24 13:33:40 +00:00
setSigner ( txSetOracle , testchain . CommitteeScriptHash ( ) )
txSetOracle . Scripts = [ ] transaction . Witness { {
2021-03-25 16:18:01 +00:00
InvocationScript : testchain . SignCommittee ( txSetOracle ) ,
2020-09-24 13:33:40 +00:00
VerificationScript : testchain . CommitteeVerificationScript ( ) ,
} }
2021-03-25 18:46:52 +00:00
bl := block . New ( bc . config . StateRootInHeader )
2020-11-05 16:34:48 +00:00
bl . Index = bc . BlockHeight ( ) + 1
ic := bc . newInteropContext ( trigger . All , bc . dao , bl , txSetOracle )
2020-09-29 06:56:19 +00:00
ic . SpawnVM ( )
ic . VM . LoadScript ( [ ] byte { byte ( opcode . RET ) } )
2021-03-23 10:37:30 +00:00
require . NoError ( t , bc . contracts . Designate . DesignateAsRole ( ic , noderoles . Oracle , oraclePubs ) )
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 )
2021-03-25 16:18:01 +00:00
require . NoError ( t , oracleAcc . SignTx ( netmode . UnitTestNet , tx ) )
2020-09-24 13:33:40 +00:00
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 ) {
2020-12-14 11:11:24 +00:00
w := io . NewBufBinWriter ( )
emit . Opcodes ( w . BinWriter , opcode . ABORT )
emit . Bytes ( w . BinWriter , util . Uint160 { } . BytesBE ( ) )
2021-02-15 13:40:44 +00:00
emit . Int ( w . BinWriter , 0 )
2021-01-22 09:22:48 +00:00
emit . String ( w . BinWriter , orc . Manifest . Name )
2020-12-14 11:11:24 +00:00
tx . Scripts [ len ( tx . Scripts ) - 1 ] . VerificationScript = w . Bytes ( )
2020-10-09 10:32:54 +00:00
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
2021-03-25 16:18:01 +00:00
require . NoError ( t , oracleAcc . SignTx ( netmode . UnitTestNet , tx ) )
2020-09-24 13:33:40 +00:00
checkErr ( t , ErrInvalidAttribute , tx )
} )
t . Run ( "InvalidScope" , func ( t * testing . T ) {
tx := getOracleTx ( t )
tx . Signers [ 0 ] . Scopes = transaction . Global
2021-03-25 16:18:01 +00:00
require . NoError ( t , oracleAcc . SignTx ( netmode . UnitTestNet , tx ) )
2020-09-24 13:33:40 +00:00
checkErr ( t , ErrInvalidAttribute , tx )
} )
t . Run ( "InvalidScript" , func ( t * testing . T ) {
tx := getOracleTx ( t )
2021-02-09 19:28:36 +00:00
tx . Script = append ( tx . Script , byte ( opcode . NOP ) )
2021-03-25 16:18:01 +00:00
require . NoError ( t , oracleAcc . SignTx ( netmode . UnitTestNet , tx ) )
2020-09-24 13:33:40 +00:00
checkErr ( t , ErrInvalidAttribute , tx )
} )
t . Run ( "InvalidSigner" , func ( t * testing . T ) {
tx := getOracleTx ( t )
tx . Signers [ 0 ] . Account = accs [ 0 ] . Contract . ScriptHash ( )
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx ) )
2020-09-24 13:33:40 +00:00
checkErr ( t , ErrInvalidAttribute , tx )
} )
t . Run ( "SmallFee" , func ( t * testing . T ) {
tx := getOracleTx ( t )
tx . SystemFee = 0
2021-03-25 16:18:01 +00:00
require . NoError ( t , oracleAcc . SignTx ( netmode . UnitTestNet , tx ) )
2020-09-24 13:33:40 +00:00
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 )
2020-12-11 12:22:49 +00:00
netFee , sizeDelta := fee . Calculate ( bc . GetBaseExecFee ( ) , rawScript )
2020-10-14 16:07:16 +00:00
tx . NetworkFee += netFee
tx . NetworkFee += int64 ( size + sizeDelta ) * bc . FeePerByte ( )
tx . Scripts = [ ] transaction . Witness { {
2021-03-25 16:18:01 +00:00
InvocationScript : testchain . SignCommittee ( tx ) ,
2020-10-14 16:07:16 +00:00
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 )
2020-11-20 10:25:19 +00:00
require . True ( t , errors . Is ( bc . VerifyTx ( tx ) , ErrInvalidAttribute ) )
2020-10-14 16:07:16 +00:00
} )
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 )
2020-12-11 12:22:49 +00:00
netFee , sizeDelta := fee . Calculate ( bc . GetBaseExecFee ( ) , rawScript )
2020-10-15 10:06:22 +00:00
tx . NetworkFee += netFee
tx . NetworkFee += int64 ( size + sizeDelta ) * bc . FeePerByte ( )
tx . Scripts = [ ] transaction . Witness { {
2021-03-25 16:18:01 +00:00
InvocationScript : testchain . SignCommittee ( tx ) ,
2020-10-15 10:06:22 +00:00
VerificationScript : rawScript ,
} }
return tx
}
t . Run ( "Disabled" , func ( t * testing . T ) {
2020-11-18 11:26:13 +00:00
tx := getReservedTx ( transaction . ReservedLowerBound + 3 )
2020-10-15 10:06:22 +00:00
require . Error ( t , bc . VerifyTx ( tx ) )
} )
t . Run ( "Enabled" , func ( t * testing . T ) {
bc . config . ReservedAttributes = true
2020-11-18 11:26:13 +00:00
tx := getReservedTx ( transaction . ReservedLowerBound + 3 )
2020-10-15 10:06:22 +00:00
require . NoError ( t , bc . VerifyTx ( tx ) )
} )
} )
2020-10-15 11:45:29 +00:00
t . Run ( "Conflicts" , func ( t * testing . T ) {
getConflictsTx := func ( hashes ... util . Uint256 ) * transaction . Transaction {
tx := bc . newTestTx ( h , testScript )
tx . Attributes = make ( [ ] transaction . Attribute , len ( hashes ) )
for i , h := range hashes {
tx . Attributes [ i ] = transaction . Attribute {
Type : transaction . ConflictsT ,
Value : & transaction . Conflicts {
Hash : h ,
} ,
}
}
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 )
2020-12-11 12:22:49 +00:00
netFee , sizeDelta := fee . Calculate ( bc . GetBaseExecFee ( ) , rawScript )
2020-10-15 11:45:29 +00:00
tx . NetworkFee += netFee
tx . NetworkFee += int64 ( size + sizeDelta ) * bc . FeePerByte ( )
tx . Scripts = [ ] transaction . Witness { {
2021-03-25 16:18:01 +00:00
InvocationScript : testchain . SignCommittee ( tx ) ,
2020-10-15 11:45:29 +00:00
VerificationScript : rawScript ,
} }
return tx
}
t . Run ( "disabled" , func ( t * testing . T ) {
bc . config . P2PSigExtensions = false
tx := getConflictsTx ( util . Uint256 { 1 , 2 , 3 } )
require . Error ( t , bc . VerifyTx ( tx ) )
} )
t . Run ( "enabled" , func ( t * testing . T ) {
bc . config . P2PSigExtensions = true
t . Run ( "dummy on-chain conflict" , func ( t * testing . T ) {
tx := bc . newTestTx ( h , testScript )
2021-03-25 16:18:01 +00:00
require . NoError ( t , accs [ 0 ] . SignTx ( netmode . UnitTestNet , tx ) )
2021-08-17 15:33:09 +00:00
conflicting := transaction . New ( [ ] byte { byte ( opcode . RET ) } , 1 )
conflicting . Attributes = [ ] transaction . Attribute {
{
Type : transaction . ConflictsT ,
Value : & transaction . Conflicts {
Hash : tx . Hash ( ) ,
} ,
} ,
}
2021-12-07 20:05:28 +00:00
require . NoError ( t , bc . dao . StoreAsTransaction ( conflicting , bc . blockHeight , nil , nil ) )
2020-10-15 11:45:29 +00:00
require . True ( t , errors . Is ( bc . VerifyTx ( tx ) , ErrHasConflicts ) )
} )
t . Run ( "attribute on-chain conflict" , func ( t * testing . T ) {
2021-03-25 16:18:01 +00:00
tx := transaction . New ( [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
2020-12-08 15:28:00 +00:00
tx . ValidUntilBlock = 4242
tx . Signers = [ ] transaction . Signer { {
Account : testchain . MultisigScriptHash ( ) ,
Scopes : transaction . None ,
} }
require . NoError ( t , testchain . SignTx ( bc , tx ) )
b := bc . newBlock ( tx )
require . NoError ( t , bc . AddBlock ( b ) )
txConflict := getConflictsTx ( tx . Hash ( ) )
require . Error ( t , bc . VerifyTx ( txConflict ) )
2020-10-15 11:45:29 +00:00
} )
t . Run ( "positive" , func ( t * testing . T ) {
tx := getConflictsTx ( random . Uint256 ( ) )
require . NoError ( t , bc . VerifyTx ( tx ) )
} )
} )
} )
2020-11-18 11:26:13 +00:00
t . Run ( "NotaryAssisted" , func ( t * testing . T ) {
2020-11-19 10:00:46 +00:00
notary , err := wallet . NewAccount ( )
require . NoError ( t , err )
2021-03-25 16:18:01 +00:00
txSetNotary := transaction . New ( [ ] byte { byte ( opcode . RET ) } , 0 )
2020-11-19 10:00:46 +00:00
setSigner ( txSetNotary , testchain . CommitteeScriptHash ( ) )
txSetNotary . Scripts = [ ] transaction . Witness { {
2021-03-25 16:18:01 +00:00
InvocationScript : testchain . SignCommittee ( txSetNotary ) ,
2020-11-19 10:00:46 +00:00
VerificationScript : testchain . CommitteeVerificationScript ( ) ,
} }
2021-03-25 18:46:52 +00:00
bl := block . New ( false )
2020-11-19 10:00:46 +00:00
bl . Index = bc . BlockHeight ( ) + 1
ic := bc . newInteropContext ( trigger . All , bc . dao , bl , txSetNotary )
ic . SpawnVM ( )
ic . VM . LoadScript ( [ ] byte { byte ( opcode . RET ) } )
2021-03-23 10:37:30 +00:00
require . NoError ( t , bc . contracts . Designate . DesignateAsRole ( ic , noderoles . P2PNotary , keys . PublicKeys { notary . PrivateKey ( ) . PublicKey ( ) } ) )
2020-11-19 10:00:46 +00:00
_ , err = ic . DAO . Persist ( )
require . NoError ( t , err )
2020-11-18 11:26:13 +00:00
getNotaryAssistedTx := func ( signaturesCount uint8 , serviceFee int64 ) * transaction . Transaction {
tx := bc . newTestTx ( h , testScript )
tx . Attributes = append ( tx . Attributes , transaction . Attribute { Type : transaction . NotaryAssistedT , Value : & transaction . NotaryAssisted {
NKeys : signaturesCount ,
} } )
tx . NetworkFee += serviceFee // additional fee for NotaryAssisted attribute
tx . NetworkFee += 4_000_000 // multisig check
tx . Signers = [ ] transaction . Signer { {
Account : testchain . CommitteeScriptHash ( ) ,
Scopes : transaction . None ,
2020-11-19 10:00:46 +00:00
} ,
{
Account : bc . contracts . Notary . Hash ,
Scopes : transaction . None ,
} ,
}
2020-11-18 11:26:13 +00:00
rawScript := testchain . CommitteeVerificationScript ( )
size := io . GetVarSize ( tx )
2020-12-11 12:22:49 +00:00
netFee , sizeDelta := fee . Calculate ( bc . GetBaseExecFee ( ) , rawScript )
2020-11-18 11:26:13 +00:00
tx . NetworkFee += netFee
tx . NetworkFee += int64 ( size + sizeDelta ) * bc . FeePerByte ( )
2020-11-19 10:00:46 +00:00
tx . Scripts = [ ] transaction . Witness {
{
2021-03-25 16:18:01 +00:00
InvocationScript : testchain . SignCommittee ( tx ) ,
2020-11-19 10:00:46 +00:00
VerificationScript : rawScript ,
} ,
{
2021-03-25 16:18:01 +00:00
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , notary . PrivateKey ( ) . SignHashable ( uint32 ( testchain . Network ( ) ) , tx ) ... ) ,
2020-11-19 10:00:46 +00:00
} ,
}
2020-11-18 11:26:13 +00:00
return tx
}
t . Run ( "Disabled" , func ( t * testing . T ) {
bc . config . P2PSigExtensions = false
tx := getNotaryAssistedTx ( 0 , 0 )
2020-11-19 10:00:46 +00:00
require . True ( t , errors . Is ( bc . VerifyTx ( tx ) , ErrInvalidAttribute ) )
2020-11-18 11:26:13 +00:00
} )
t . Run ( "Enabled, insufficient network fee" , func ( t * testing . T ) {
bc . config . P2PSigExtensions = true
tx := getNotaryAssistedTx ( 1 , 0 )
require . Error ( t , bc . VerifyTx ( tx ) )
} )
2020-11-19 10:00:46 +00:00
t . Run ( "Test verify" , func ( t * testing . T ) {
2020-11-18 11:26:13 +00:00
bc . config . P2PSigExtensions = true
2020-11-19 10:00:46 +00:00
t . Run ( "no NotaryAssisted attribute" , func ( t * testing . T ) {
tx := getNotaryAssistedTx ( 1 , ( 1 + 1 ) * transaction . NotaryServiceFeePerKey )
tx . Attributes = [ ] transaction . Attribute { }
tx . Signers = [ ] transaction . Signer {
{
Account : testchain . CommitteeScriptHash ( ) ,
Scopes : transaction . None ,
} ,
{
Account : bc . contracts . Notary . Hash ,
Scopes : transaction . None ,
} ,
}
tx . Scripts = [ ] transaction . Witness {
{
2021-03-25 16:18:01 +00:00
InvocationScript : testchain . SignCommittee ( tx ) ,
2020-11-19 10:00:46 +00:00
VerificationScript : testchain . CommitteeVerificationScript ( ) ,
} ,
{
2021-03-25 16:18:01 +00:00
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , notary . PrivateKey ( ) . SignHashable ( uint32 ( testchain . Network ( ) ) , tx ) ... ) ,
2020-11-19 10:00:46 +00:00
} ,
}
require . Error ( t , bc . VerifyTx ( tx ) )
} )
t . Run ( "no deposit" , func ( t * testing . T ) {
tx := getNotaryAssistedTx ( 1 , ( 1 + 1 ) * transaction . NotaryServiceFeePerKey )
tx . Signers = [ ] transaction . Signer {
{
Account : bc . contracts . Notary . Hash ,
Scopes : transaction . None ,
} ,
{
Account : testchain . CommitteeScriptHash ( ) ,
Scopes : transaction . None ,
} ,
}
tx . Scripts = [ ] transaction . Witness {
{
2021-03-25 16:18:01 +00:00
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , notary . PrivateKey ( ) . SignHashable ( uint32 ( testchain . Network ( ) ) , tx ) ... ) ,
2020-11-19 10:00:46 +00:00
} ,
{
2021-03-25 16:18:01 +00:00
InvocationScript : testchain . SignCommittee ( tx ) ,
2020-11-19 10:00:46 +00:00
VerificationScript : testchain . CommitteeVerificationScript ( ) ,
} ,
}
require . Error ( t , bc . VerifyTx ( tx ) )
} )
t . Run ( "bad Notary signer scope" , func ( t * testing . T ) {
tx := getNotaryAssistedTx ( 1 , ( 1 + 1 ) * transaction . NotaryServiceFeePerKey )
tx . Signers = [ ] transaction . Signer {
{
Account : testchain . CommitteeScriptHash ( ) ,
Scopes : transaction . None ,
} ,
{
Account : bc . contracts . Notary . Hash ,
Scopes : transaction . CalledByEntry ,
} ,
}
tx . Scripts = [ ] transaction . Witness {
{
2021-03-25 16:18:01 +00:00
InvocationScript : testchain . SignCommittee ( tx ) ,
2020-11-19 10:00:46 +00:00
VerificationScript : testchain . CommitteeVerificationScript ( ) ,
} ,
{
2021-03-25 16:18:01 +00:00
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , notary . PrivateKey ( ) . SignHashable ( uint32 ( testchain . Network ( ) ) , tx ) ... ) ,
2020-11-19 10:00:46 +00:00
} ,
}
require . Error ( t , bc . VerifyTx ( tx ) )
} )
t . Run ( "not signed by Notary" , func ( t * testing . T ) {
tx := getNotaryAssistedTx ( 1 , ( 1 + 1 ) * transaction . NotaryServiceFeePerKey )
tx . Signers = [ ] transaction . Signer {
{
Account : testchain . CommitteeScriptHash ( ) ,
Scopes : transaction . None ,
} ,
}
tx . Scripts = [ ] transaction . Witness {
{
2021-03-25 16:18:01 +00:00
InvocationScript : testchain . SignCommittee ( tx ) ,
2020-11-19 10:00:46 +00:00
VerificationScript : testchain . CommitteeVerificationScript ( ) ,
} ,
}
require . Error ( t , bc . VerifyTx ( tx ) )
} )
t . Run ( "bad Notary node witness" , func ( t * testing . T ) {
tx := getNotaryAssistedTx ( 1 , ( 1 + 1 ) * transaction . NotaryServiceFeePerKey )
tx . Signers = [ ] transaction . Signer {
{
Account : testchain . CommitteeScriptHash ( ) ,
Scopes : transaction . None ,
} ,
{
Account : bc . contracts . Notary . Hash ,
Scopes : transaction . None ,
} ,
}
acc , err := keys . NewPrivateKey ( )
require . NoError ( t , err )
tx . Scripts = [ ] transaction . Witness {
{
2021-03-25 16:18:01 +00:00
InvocationScript : testchain . SignCommittee ( tx ) ,
2020-11-19 10:00:46 +00:00
VerificationScript : testchain . CommitteeVerificationScript ( ) ,
} ,
{
2021-03-25 16:18:01 +00:00
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , acc . SignHashable ( uint32 ( testchain . Network ( ) ) , tx ) ... ) ,
2020-11-19 10:00:46 +00:00
} ,
}
require . Error ( t , bc . VerifyTx ( tx ) )
} )
t . Run ( "missing payer" , func ( t * testing . T ) {
tx := getNotaryAssistedTx ( 1 , ( 1 + 1 ) * transaction . NotaryServiceFeePerKey )
tx . Signers = [ ] transaction . Signer {
{
Account : bc . contracts . Notary . Hash ,
Scopes : transaction . None ,
} ,
}
tx . Scripts = [ ] transaction . Witness {
{
2021-03-25 16:18:01 +00:00
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , notary . PrivateKey ( ) . SignHashable ( uint32 ( testchain . Network ( ) ) , tx ) ... ) ,
2020-11-19 10:00:46 +00:00
} ,
}
require . Error ( t , bc . VerifyTx ( tx ) )
} )
t . Run ( "positive" , func ( t * testing . T ) {
tx := getNotaryAssistedTx ( 1 , ( 1 + 1 ) * transaction . NotaryServiceFeePerKey )
require . NoError ( t , bc . VerifyTx ( tx ) )
} )
2020-11-18 11:26:13 +00:00
} )
} )
2020-08-19 13:20:48 +00:00
} )
2020-11-27 10:55:48 +00:00
t . Run ( "Partially-filled transaction" , func ( t * testing . T ) {
bc . config . P2PSigExtensions = true
getPartiallyFilledTx := func ( nvb uint32 , validUntil uint32 ) * transaction . Transaction {
tx := bc . newTestTx ( h , testScript )
tx . ValidUntilBlock = validUntil
tx . Attributes = [ ] transaction . Attribute {
{
Type : transaction . NotValidBeforeT ,
Value : & transaction . NotValidBefore { Height : nvb } ,
} ,
{
Type : transaction . NotaryAssistedT ,
Value : & transaction . NotaryAssisted { NKeys : 0 } ,
} ,
}
tx . Signers = [ ] transaction . Signer {
{
Account : bc . contracts . Notary . Hash ,
Scopes : transaction . None ,
} ,
{
Account : testchain . MultisigScriptHash ( ) ,
Scopes : transaction . None ,
} ,
}
size := io . GetVarSize ( tx )
2020-12-11 12:22:49 +00:00
netFee , sizeDelta := fee . Calculate ( bc . GetBaseExecFee ( ) , testchain . MultisigVerificationScript ( ) )
2020-11-27 10:55:48 +00:00
tx . NetworkFee = netFee + // multisig witness verification price
int64 ( size ) * bc . FeePerByte ( ) + // fee for unsigned size
2022-01-10 13:55:57 +00:00
int64 ( sizeDelta ) * bc . FeePerByte ( ) + // fee for multisig size
2020-11-27 10:55:48 +00:00
66 * bc . FeePerByte ( ) + // fee for Notary signature size (66 bytes for Invocation script and 0 bytes for Verification script)
2 * bc . FeePerByte ( ) + // fee for the length of each script in Notary witness (they are nil, so we did not take them into account during `size` calculation)
transaction . NotaryServiceFeePerKey + // fee for Notary attribute
2020-12-11 12:22:49 +00:00
fee . Opcode ( bc . GetBaseExecFee ( ) , // Notary verification script
2020-11-27 10:55:48 +00:00
opcode . PUSHDATA1 , opcode . RET , // invocation script
2021-02-15 13:40:44 +00:00
opcode . PUSH0 , opcode . SYSCALL , opcode . RET ) + // Neo.Native.Call
2021-03-23 10:49:27 +00:00
nativeprices . NotaryVerificationPrice * bc . GetBaseExecFee ( ) // Notary witness verification price
2020-11-27 10:55:48 +00:00
tx . Scripts = [ ] transaction . Witness {
{
2021-05-12 15:32:11 +00:00
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , make ( [ ] byte , 64 ) ... ) ,
2020-11-27 10:55:48 +00:00
VerificationScript : [ ] byte { } ,
} ,
{
2021-03-25 16:18:01 +00:00
InvocationScript : testchain . Sign ( tx ) ,
2020-11-27 10:55:48 +00:00
VerificationScript : testchain . MultisigVerificationScript ( ) ,
} ,
}
return tx
}
2021-01-15 12:40:15 +00:00
mp := mempool . New ( 10 , 1 , false )
2022-01-14 01:09:54 +00:00
verificationF := func ( tx * transaction . Transaction , data interface { } ) error {
2020-11-27 10:55:48 +00:00
if data . ( int ) > 5 {
return errors . New ( "bad data" )
}
return nil
}
t . Run ( "failed pre-verification" , func ( t * testing . T ) {
tx := getPartiallyFilledTx ( bc . blockHeight , bc . blockHeight + 1 )
require . Error ( t , bc . PoolTxWithData ( tx , 6 , mp , bc , verificationF ) ) // here and below let's use `bc` instead of proper NotaryFeer for the test simplicity.
} )
t . Run ( "GasLimitExceeded during witness verification" , func ( t * testing . T ) {
tx := getPartiallyFilledTx ( bc . blockHeight , bc . blockHeight + 1 )
tx . NetworkFee -- // to check that NetworkFee was set correctly in getPartiallyFilledTx
tx . Scripts = [ ] transaction . Witness {
{
2021-05-12 15:32:11 +00:00
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , make ( [ ] byte , 64 ) ... ) ,
2020-11-27 10:55:48 +00:00
VerificationScript : [ ] byte { } ,
} ,
{
2021-03-25 16:18:01 +00:00
InvocationScript : testchain . Sign ( tx ) ,
2020-11-27 10:55:48 +00:00
VerificationScript : testchain . MultisigVerificationScript ( ) ,
} ,
}
require . Error ( t , bc . PoolTxWithData ( tx , 5 , mp , bc , verificationF ) )
} )
t . Run ( "bad NVB: too big" , func ( t * testing . T ) {
tx := getPartiallyFilledTx ( bc . blockHeight + bc . contracts . Notary . GetMaxNotValidBeforeDelta ( bc . dao ) + 1 , bc . blockHeight + 1 )
require . True ( t , errors . Is ( bc . PoolTxWithData ( tx , 5 , mp , bc , verificationF ) , ErrInvalidAttribute ) )
} )
t . Run ( "bad ValidUntilBlock: too small" , func ( t * testing . T ) {
tx := getPartiallyFilledTx ( bc . blockHeight , bc . blockHeight + bc . contracts . Notary . GetMaxNotValidBeforeDelta ( bc . dao ) + 1 )
require . True ( t , errors . Is ( bc . PoolTxWithData ( tx , 5 , mp , bc , verificationF ) , ErrInvalidAttribute ) )
} )
t . Run ( "good" , func ( t * testing . T ) {
tx := getPartiallyFilledTx ( bc . blockHeight , bc . blockHeight + 1 )
require . NoError ( t , bc . PoolTxWithData ( tx , 5 , mp , bc , verificationF ) )
} )
} )
2020-08-13 10:42:21 +00:00
}
2020-08-13 15:42:53 +00:00
func TestVerifyHashAgainstScript ( t * testing . T ) {
bc := newTestChain ( t )
2021-12-09 17:12:15 +00:00
cs , csInvalid := getTestContractState ( t , 4 , 5 , random . Uint160 ( ) ) // sender and IDs are not important for the test
2022-02-03 08:46:09 +00:00
ic := bc . newInteropContext ( trigger . Verification , bc . dao . GetWrapped ( ) , nil , nil )
2020-12-13 15:26:35 +00:00
require . NoError ( t , bc . contracts . Management . PutContractState ( bc . dao , cs ) )
require . NoError ( t , bc . contracts . Management . PutContractState ( bc . dao , csInvalid ) )
2020-08-13 15:42:53 +00:00
gas := bc . contracts . Policy . GetMaxVerificationGas ( ic . DAO )
t . Run ( "Contract" , func ( t * testing . T ) {
t . Run ( "Missing" , func ( t * testing . T ) {
2020-11-18 20:10:48 +00:00
newH := cs . Hash
2020-08-13 15:42:53 +00:00
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-11-18 20:10:48 +00:00
_ , err := bc . verifyHashAgainstScript ( csInvalid . Hash , 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-11-18 20:10:48 +00:00
_ , err := bc . verifyHashAgainstScript ( cs . Hash , 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-11-18 20:10:48 +00:00
_ , err := bc . verifyHashAgainstScript ( cs . Hash , 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-11-26 13:02:00 +00:00
func TestIsTxStillRelevant ( t * testing . T ) {
bc := newTestChain ( t )
mp := bc . GetMemPool ( )
newTx := func ( t * testing . T ) * transaction . Transaction {
2021-03-25 16:18:01 +00:00
tx := transaction . New ( [ ] byte { byte ( opcode . RET ) } , 100 )
2020-11-26 13:02:00 +00:00
tx . ValidUntilBlock = bc . BlockHeight ( ) + 1
tx . Signers = [ ] transaction . Signer { {
Account : neoOwner ,
Scopes : transaction . CalledByEntry ,
} }
return tx
}
t . Run ( "small ValidUntilBlock" , func ( t * testing . T ) {
tx := newTx ( t )
require . NoError ( t , testchain . SignTx ( bc , tx ) )
2020-11-27 10:55:48 +00:00
require . True ( t , bc . IsTxStillRelevant ( tx , nil , false ) )
2020-11-26 13:02:00 +00:00
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
2020-11-27 10:55:48 +00:00
require . False ( t , bc . IsTxStillRelevant ( tx , nil , false ) )
2020-11-26 13:02:00 +00:00
} )
t . Run ( "tx is already persisted" , func ( t * testing . T ) {
tx := newTx ( t )
tx . ValidUntilBlock = bc . BlockHeight ( ) + 2
require . NoError ( t , testchain . SignTx ( bc , tx ) )
2020-11-27 10:55:48 +00:00
require . True ( t , bc . IsTxStillRelevant ( tx , nil , false ) )
2020-11-26 13:02:00 +00:00
require . NoError ( t , bc . AddBlock ( bc . newBlock ( tx ) ) )
2020-11-27 10:55:48 +00:00
require . False ( t , bc . IsTxStillRelevant ( tx , nil , false ) )
2020-11-26 13:02:00 +00:00
} )
t . Run ( "tx with Conflicts attribute" , func ( t * testing . T ) {
tx1 := newTx ( t )
require . NoError ( t , testchain . SignTx ( bc , tx1 ) )
tx2 := newTx ( t )
tx2 . Attributes = [ ] transaction . Attribute { {
Type : transaction . ConflictsT ,
Value : & transaction . Conflicts { Hash : tx1 . Hash ( ) } ,
} }
require . NoError ( t , testchain . SignTx ( bc , tx2 ) )
2020-11-27 10:55:48 +00:00
require . True ( t , bc . IsTxStillRelevant ( tx1 , mp , false ) )
require . NoError ( t , bc . verifyAndPoolTx ( tx2 , mp , bc ) )
require . False ( t , bc . IsTxStillRelevant ( tx1 , mp , false ) )
2020-11-26 13:02:00 +00:00
} )
t . Run ( "NotValidBefore" , func ( t * testing . T ) {
tx3 := newTx ( t )
tx3 . Attributes = [ ] transaction . Attribute { {
Type : transaction . NotValidBeforeT ,
Value : & transaction . NotValidBefore { Height : bc . BlockHeight ( ) + 1 } ,
} }
tx3 . ValidUntilBlock = bc . BlockHeight ( ) + 2
require . NoError ( t , testchain . SignTx ( bc , tx3 ) )
2020-11-27 10:55:48 +00:00
require . False ( t , bc . IsTxStillRelevant ( tx3 , nil , false ) )
2020-11-26 13:02:00 +00:00
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
2020-11-27 10:55:48 +00:00
require . True ( t , bc . IsTxStillRelevant ( tx3 , nil , false ) )
2020-11-26 13:02:00 +00:00
} )
t . Run ( "contract witness check fails" , func ( t * testing . T ) {
src := fmt . Sprintf ( ` package verify
2021-02-03 19:01:20 +00:00
import (
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/interop/util"
)
2020-11-26 13:02:00 +00:00
func Verify ( ) bool {
2021-02-15 13:40:44 +00:00
addr := util . FromAddress ( "`+address.Uint160ToString(bc.contracts.Ledger.Hash)+`" )
currentHeight := contract . Call ( addr , "currentIndex" , contract . ReadStates )
2021-02-03 19:01:20 +00:00
return currentHeight . ( int ) < % d
2020-11-26 13:02:00 +00:00
} ` , bc . BlockHeight ( ) + 2 ) // deploy + next block
2021-12-02 13:36:29 +00:00
txDeploy , h , _ , err := testchain . NewDeployTx ( bc , "TestVerify.go" , neoOwner , strings . NewReader ( src ) , nil )
2020-11-26 13:02:00 +00:00
require . NoError ( t , err )
txDeploy . ValidUntilBlock = bc . BlockHeight ( ) + 1
2021-01-21 09:23:53 +00:00
addSigners ( neoOwner , txDeploy )
2020-11-26 13:02:00 +00:00
require . NoError ( t , testchain . SignTx ( bc , txDeploy ) )
require . NoError ( t , bc . AddBlock ( bc . newBlock ( txDeploy ) ) )
tx := newTx ( t )
tx . Signers = append ( tx . Signers , transaction . Signer {
2020-11-18 20:10:48 +00:00
Account : h ,
2020-11-26 13:02:00 +00:00
Scopes : transaction . None ,
} )
2021-02-03 19:01:20 +00:00
tx . NetworkFee += 10_000_000
2020-11-26 13:02:00 +00:00
require . NoError ( t , testchain . SignTx ( bc , tx ) )
tx . Scripts = append ( tx . Scripts , transaction . Witness { } )
2020-11-27 10:55:48 +00:00
require . True ( t , bc . IsTxStillRelevant ( tx , mp , false ) )
2020-11-26 13:02:00 +00:00
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
2020-11-27 10:55:48 +00:00
require . False ( t , bc . IsTxStillRelevant ( tx , mp , false ) )
2020-11-26 13:02:00 +00:00
} )
}
2020-08-19 12:27:13 +00:00
func TestMemPoolRemoval ( t * testing . T ) {
const added = 16
const notAdded = 32
bc := newTestChain ( t )
addedTxes := make ( [ ] * transaction . Transaction , added )
notAddedTxes := make ( [ ] * transaction . Transaction , notAdded )
for i := range addedTxes {
addedTxes [ i ] = bc . newTestTx ( testchain . MultisigScriptHash ( ) , [ ] byte { byte ( opcode . PUSH1 ) } )
2020-11-23 11:09:45 +00:00
require . NoError ( t , testchain . SignTx ( bc , addedTxes [ i ] ) )
2020-08-19 12:27:13 +00:00
require . NoError ( t , bc . PoolTx ( addedTxes [ i ] ) )
}
for i := range notAddedTxes {
notAddedTxes [ i ] = bc . newTestTx ( testchain . MultisigScriptHash ( ) , [ ] byte { byte ( opcode . PUSH1 ) } )
2020-11-23 11:09:45 +00:00
require . NoError ( t , testchain . SignTx ( bc , notAddedTxes [ i ] ) )
2020-08-19 12:27:13 +00:00
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 ( ) ) )
2021-11-22 07:41:40 +00:00
_ , err = bc . persist ( true )
2021-07-30 20:47:48 +00:00
assert . NoError ( t , err )
2018-03-17 11:53:21 +00:00
}
}
2018-03-21 16:11:04 +00:00
func TestGetTransaction ( t * testing . T ) {
bc := newTestChain ( t )
2021-03-25 16:18:01 +00:00
tx1 := transaction . New ( [ ] 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 ,
} }
2021-03-25 16:18:01 +00:00
tx2 := transaction . New ( [ ] 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 ,
} }
2020-11-23 11:09:45 +00:00
require . NoError ( t , testchain . SignTx ( bc , tx1 , tx2 ) )
2020-08-18 12:27:49 +00:00
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 ) )
2021-11-22 07:41:40 +00:00
_ , err = bc . persist ( true )
2021-07-30 20:47:48 +00:00
assert . NoError ( t , err )
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-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-11-06 09:27:05 +00:00
amount , err := bc . CalculateClaimable ( neoOwner , 1 )
require . NoError ( t , err )
require . EqualValues ( t , big . NewInt ( 5 * native . GASFactor / 10 ) , amount )
2020-02-25 13:15:17 +00:00
} )
}
2019-11-07 17:47:48 +00:00
func TestClose ( t * testing . T ) {
2021-03-01 11:14:15 +00:00
bc := initTestChain ( t , nil , nil )
go bc . Run ( )
2022-02-02 11:30:46 +00:00
hash0 := bc . GetHeaderHash ( 0 )
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 ( )
2022-02-02 11:30:46 +00:00
_ , err = bc . GetBlock ( hash0 ) // DB is closed, so this will fail.
require . Error ( t , err )
2019-11-07 17:47:48 +00:00
}
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 )
2021-09-24 09:15:25 +00:00
notificationCh := make ( chan * subscriptions . NotificationEvent , chBufSize )
2020-05-12 14:20:41 +00:00
executionCh := make ( chan * state . AppExecResult , chBufSize )
bc := newTestChain ( t )
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
2020-11-11 15:43:28 +00:00
assert . Equal ( t , b . Hash ( ) , aer . Container )
2020-09-23 08:48:31 +00:00
aer = <- executionCh
2020-11-11 15:43:28 +00:00
assert . Equal ( t , b . Hash ( ) , aer . Container )
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 )
2021-03-25 16:18:01 +00:00
txGood1 := transaction . New ( 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
2020-11-05 19:08:02 +00:00
txGood1 . ValidUntilBlock = 1024
2020-11-23 11:09:45 +00:00
require . NoError ( t , testchain . SignTx ( bc , txGood1 ) )
2020-05-12 14:20:41 +00:00
// 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 )
2021-03-25 16:18:01 +00:00
txBad := transaction . New ( 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
2020-11-05 19:08:02 +00:00
txBad . ValidUntilBlock = 1024
2020-11-23 11:09:45 +00:00
require . NoError ( t , testchain . SignTx ( bc , txBad ) )
2020-05-12 14:20:41 +00:00
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 )
2021-03-25 16:18:01 +00:00
txGood2 := transaction . New ( 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
2020-11-05 19:08:02 +00:00
txGood2 . ValidUntilBlock = 1024
2020-11-23 11:09:45 +00:00
require . NoError ( t , testchain . SignTx ( bc , txGood2 ) )
2020-05-12 14:20:41 +00:00
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
2020-11-11 15:43:28 +00:00
require . Equal ( t , b . Hash ( ) , exec . Container )
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
2020-11-11 15:43:28 +00:00
require . Equal ( t , tx . Hash ( ) , exec . Container )
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
2020-11-11 15:43:28 +00:00
require . Equal ( t , b . Hash ( ) , exec . Container )
2020-09-23 08:48:31 +00:00
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 )
}
2020-11-24 08:36:09 +00:00
2020-11-26 09:33:34 +00:00
func testDumpAndRestore ( t * testing . T , dumpF , restoreF func ( c * config . Config ) ) {
if restoreF == nil {
restoreF = dumpF
}
bc := newTestChainWithCustomCfg ( t , dumpF )
2020-11-24 08:36:09 +00:00
initBasicChain ( t , bc )
require . True ( t , bc . BlockHeight ( ) > 5 ) // ensure that test is valid
w := io . NewBufBinWriter ( )
require . NoError ( t , chaindump . Dump ( bc , w . BinWriter , 0 , bc . BlockHeight ( ) + 1 ) )
require . NoError ( t , w . Err )
buf := w . Bytes ( )
t . Run ( "invalid start" , func ( t * testing . T ) {
2020-11-26 09:33:34 +00:00
bc2 := newTestChainWithCustomCfg ( t , restoreF )
2020-11-24 08:36:09 +00:00
r := io . NewBinReaderFromBuf ( buf )
require . Error ( t , chaindump . Restore ( bc2 , r , 2 , 1 , nil ) )
} )
t . Run ( "good" , func ( t * testing . T ) {
2020-11-26 09:33:34 +00:00
bc2 := newTestChainWithCustomCfg ( t , restoreF )
2020-11-24 08:36:09 +00:00
r := io . NewBinReaderFromBuf ( buf )
require . NoError ( t , chaindump . Restore ( bc2 , r , 0 , 2 , nil ) )
require . Equal ( t , uint32 ( 1 ) , bc2 . BlockHeight ( ) )
r = io . NewBinReaderFromBuf ( buf ) // new reader because start is relative to dump
require . NoError ( t , chaindump . Restore ( bc2 , r , 2 , 1 , nil ) )
t . Run ( "check handler" , func ( t * testing . T ) {
lastIndex := uint32 ( 0 )
errStopped := errors . New ( "stopped" )
f := func ( b * block . Block ) error {
lastIndex = b . Index
if b . Index >= bc . BlockHeight ( ) - 1 {
return errStopped
}
return nil
}
require . NoError ( t , chaindump . Restore ( bc2 , r , 0 , 1 , f ) )
require . Equal ( t , bc2 . BlockHeight ( ) , lastIndex )
r = io . NewBinReaderFromBuf ( buf )
err := chaindump . Restore ( bc2 , r , 4 , bc . BlockHeight ( ) - bc2 . BlockHeight ( ) , f )
require . True ( t , errors . Is ( err , errStopped ) )
require . Equal ( t , bc . BlockHeight ( ) - 1 , lastIndex )
} )
} )
}
func TestDumpAndRestore ( t * testing . T ) {
t . Run ( "no state root" , func ( t * testing . T ) {
2020-11-26 09:33:34 +00:00
testDumpAndRestore ( t , func ( c * config . Config ) {
c . ProtocolConfiguration . StateRootInHeader = false
} , nil )
2020-11-24 08:36:09 +00:00
} )
t . Run ( "with state root" , func ( t * testing . T ) {
2020-11-26 09:33:34 +00:00
testDumpAndRestore ( t , func ( c * config . Config ) {
c . ProtocolConfiguration . StateRootInHeader = false
} , nil )
} )
t . Run ( "remove untraceable" , func ( t * testing . T ) {
// Dump can only be created if all blocks and transactions are present.
testDumpAndRestore ( t , nil , func ( c * config . Config ) {
c . ProtocolConfiguration . MaxTraceableBlocks = 2
c . ProtocolConfiguration . RemoveUntraceableBlocks = true
} )
2020-11-24 08:36:09 +00:00
} )
}
2020-11-26 09:33:34 +00:00
func TestRemoveUntraceable ( t * testing . T ) {
2021-06-25 10:24:24 +00:00
check := func ( t * testing . T , bc * Blockchain , tHash , bHash util . Uint256 , errorExpected bool ) {
_ , _ , err := bc . GetTransaction ( tHash )
if errorExpected {
require . Error ( t , err )
} else {
require . NoError ( t , err )
}
_ , err = bc . GetAppExecResults ( tHash , trigger . Application )
if errorExpected {
require . Error ( t , err )
} else {
require . NoError ( t , err )
}
_ , err = bc . GetBlock ( bHash )
if errorExpected {
require . Error ( t , err )
} else {
require . NoError ( t , err )
}
_ , err = bc . GetHeader ( bHash )
require . NoError ( t , err )
}
t . Run ( "P2PStateExchangeExtensions off" , func ( t * testing . T ) {
bc := newTestChainWithCustomCfg ( t , func ( c * config . Config ) {
c . ProtocolConfiguration . MaxTraceableBlocks = 2
c . ProtocolConfiguration . RemoveUntraceableBlocks = true
} )
tx1 , err := testchain . NewTransferFromOwner ( bc , bc . contracts . NEO . Hash , util . Uint160 { } , 1 , 0 , bc . BlockHeight ( ) + 1 )
require . NoError ( t , err )
b1 := bc . newBlock ( tx1 )
require . NoError ( t , bc . AddBlock ( b1 ) )
tx1Height := bc . BlockHeight ( )
tx2 , err := testchain . NewTransferFromOwner ( bc , bc . contracts . NEO . Hash , util . Uint160 { } , 1 , 0 , bc . BlockHeight ( ) + 1 )
require . NoError ( t , err )
require . NoError ( t , bc . AddBlock ( bc . newBlock ( tx2 ) ) )
_ , h1 , err := bc . GetTransaction ( tx1 . Hash ( ) )
require . NoError ( t , err )
require . Equal ( t , tx1Height , h1 )
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
check ( t , bc , tx1 . Hash ( ) , b1 . Hash ( ) , true )
2020-11-26 09:33:34 +00:00
} )
2021-06-25 10:24:24 +00:00
t . Run ( "P2PStateExchangeExtensions on" , func ( t * testing . T ) {
bc := newTestChainWithCustomCfg ( t , func ( c * config . Config ) {
c . ProtocolConfiguration . MaxTraceableBlocks = 2
c . ProtocolConfiguration . RemoveUntraceableBlocks = true
c . ProtocolConfiguration . P2PStateExchangeExtensions = true
c . ProtocolConfiguration . StateSyncInterval = 2
c . ProtocolConfiguration . StateRootInHeader = true
} )
2020-11-26 09:33:34 +00:00
2021-06-25 10:24:24 +00:00
tx1 , err := testchain . NewTransferFromOwner ( bc , bc . contracts . NEO . Hash , util . Uint160 { } , 1 , 0 , bc . BlockHeight ( ) + 1 )
require . NoError ( t , err )
b1 := bc . newBlock ( tx1 )
require . NoError ( t , bc . AddBlock ( b1 ) )
tx1Height := bc . BlockHeight ( )
2020-11-26 09:33:34 +00:00
2021-06-25 10:24:24 +00:00
tx2 , err := testchain . NewTransferFromOwner ( bc , bc . contracts . NEO . Hash , util . Uint160 { } , 1 , 0 , bc . BlockHeight ( ) + 1 )
require . NoError ( t , err )
b2 := bc . newBlock ( tx2 )
require . NoError ( t , bc . AddBlock ( b2 ) )
tx2Height := bc . BlockHeight ( )
2020-11-26 09:33:34 +00:00
2021-06-25 10:24:24 +00:00
_ , h1 , err := bc . GetTransaction ( tx1 . Hash ( ) )
require . NoError ( t , err )
require . Equal ( t , tx1Height , h1 )
2020-11-26 09:33:34 +00:00
2021-06-25 10:24:24 +00:00
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
2020-11-26 09:33:34 +00:00
2021-06-25 10:24:24 +00:00
check ( t , bc , tx1 . Hash ( ) , b1 . Hash ( ) , false )
check ( t , bc , tx2 . Hash ( ) , b2 . Hash ( ) , false )
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
check ( t , bc , tx1 . Hash ( ) , b1 . Hash ( ) , true )
check ( t , bc , tx2 . Hash ( ) , b2 . Hash ( ) , false )
_ , h2 , err := bc . GetTransaction ( tx2 . Hash ( ) )
require . NoError ( t , err )
require . Equal ( t , tx2Height , h2 )
} )
2020-11-26 09:33:34 +00:00
}
2020-12-18 09:28:01 +00:00
func TestInvalidNotification ( t * testing . T ) {
bc := newTestChain ( t )
2021-12-09 17:12:15 +00:00
cs , _ := getTestContractState ( t , 4 , 5 , random . Uint160 ( ) ) // sender and IDs are not important for the test
2020-12-18 09:28:01 +00:00
require . NoError ( t , bc . contracts . Management . PutContractState ( bc . dao , cs ) )
aer , err := invokeContractMethod ( bc , 1_00000000 , cs . Hash , "invalidStack" )
require . NoError ( t , err )
require . Equal ( t , 2 , len ( aer . Stack ) )
require . Nil ( t , aer . Stack [ 0 ] )
require . Equal ( t , stackitem . InteropT , aer . Stack [ 1 ] . Type ( ) )
}
2021-02-17 09:26:32 +00:00
// Test that deletion of non-existent doesn't result in error in tx or block addition.
func TestMPTDeleteNoKey ( t * testing . T ) {
bc := newTestChain ( t )
2021-12-09 17:12:15 +00:00
cs , _ := getTestContractState ( t , 4 , 5 , random . Uint160 ( ) ) // sender and IDs are not important for the test
2021-02-17 09:26:32 +00:00
require . NoError ( t , bc . contracts . Management . PutContractState ( bc . dao , cs ) )
aer , err := invokeContractMethod ( bc , 1_00000000 , cs . Hash , "delValue" , "non-existent-key" )
require . NoError ( t , err )
require . Equal ( t , vm . HaltState , aer . VMState )
}
2021-03-11 11:39:51 +00:00
// Test that UpdateHistory is added to ProtocolConfiguration for all native contracts
// for all default configurations. If UpdateHistory is not added to config, then
// native contract is disabled. It's easy to forget about config while adding new
// native contract.
func TestConfigNativeUpdateHistory ( t * testing . T ) {
2021-11-17 11:14:22 +00:00
var prefixPath = filepath . Join ( ".." , ".." , "config" )
2021-03-11 11:39:51 +00:00
check := func ( t * testing . T , cfgFileSuffix interface { } ) {
2021-11-17 11:14:22 +00:00
cfgPath := filepath . Join ( prefixPath , fmt . Sprintf ( "protocol.%s.yml" , cfgFileSuffix ) )
2021-03-11 11:39:51 +00:00
cfg , err := config . LoadFile ( cfgPath )
require . NoError ( t , err , fmt . Errorf ( "failed to load %s" , cfgPath ) )
2021-07-20 12:54:56 +00:00
natives := native . NewContracts ( cfg . ProtocolConfiguration )
2021-03-11 11:39:51 +00:00
assert . Equal ( t , len ( natives . Contracts ) ,
len ( cfg . ProtocolConfiguration . NativeUpdateHistories ) ,
fmt . Errorf ( "protocol configuration file %s: extra or missing NativeUpdateHistory in NativeActivations section" , cfgPath ) )
for _ , c := range natives . Contracts {
assert . NotNil ( t , cfg . ProtocolConfiguration . NativeUpdateHistories [ c . Metadata ( ) . Name ] ,
fmt . Errorf ( "protocol configuration file %s: configuration for %s native contract is missing in NativeActivations section; " +
"edit the test if the contract should be disabled" , cfgPath , c . Metadata ( ) . Name ) )
}
}
testCases := [ ] interface { } {
netmode . MainNet ,
netmode . PrivNet ,
netmode . TestNet ,
netmode . UnitTestNet ,
"privnet.docker.one" ,
"privnet.docker.two" ,
"privnet.docker.three" ,
"privnet.docker.four" ,
"privnet.docker.single" ,
"unit_testnet.single" ,
}
for _ , tc := range testCases {
check ( t , tc )
}
}
2021-08-31 15:39:19 +00:00
func TestBlockchain_InitWithIncompleteStateJump ( t * testing . T ) {
var (
stateSyncInterval = 4
maxTraceable uint32 = 6
)
spountCfg := func ( c * config . Config ) {
c . ProtocolConfiguration . RemoveUntraceableBlocks = true
c . ProtocolConfiguration . StateRootInHeader = true
c . ProtocolConfiguration . P2PStateExchangeExtensions = true
c . ProtocolConfiguration . StateSyncInterval = stateSyncInterval
c . ProtocolConfiguration . MaxTraceableBlocks = maxTraceable
}
bcSpout := newTestChainWithCustomCfg ( t , spountCfg )
initBasicChain ( t , bcSpout )
// reach next to the latest state sync point and pretend that we've just restored
stateSyncPoint := ( int ( bcSpout . BlockHeight ( ) ) / stateSyncInterval + 1 ) * stateSyncInterval
for i := bcSpout . BlockHeight ( ) + 1 ; i <= uint32 ( stateSyncPoint ) ; i ++ {
require . NoError ( t , bcSpout . AddBlock ( bcSpout . newBlock ( ) ) )
}
require . Equal ( t , uint32 ( stateSyncPoint ) , bcSpout . BlockHeight ( ) )
b := bcSpout . newBlock ( )
require . NoError ( t , bcSpout . AddHeaders ( & b . Header ) )
// put storage items with STTemp prefix
batch := bcSpout . dao . Store . Batch ( )
2021-09-27 13:35:25 +00:00
tempPrefix := storage . STTempStorage
2021-10-22 07:58:53 +00:00
if bcSpout . dao . Version . StoragePrefix == tempPrefix {
2021-09-27 13:35:25 +00:00
tempPrefix = storage . STStorage
}
2022-01-17 17:41:51 +00:00
bcSpout . dao . Store . Seek ( storage . SeekRange { Prefix : bcSpout . dao . Version . StoragePrefix . Bytes ( ) } , func ( k , v [ ] byte ) bool {
2021-08-31 15:39:19 +00:00
key := slice . Copy ( k )
2021-09-27 13:35:25 +00:00
key [ 0 ] = byte ( tempPrefix )
2021-08-31 15:39:19 +00:00
value := slice . Copy ( v )
batch . Put ( key , value )
2022-01-17 17:41:51 +00:00
return true
2021-08-31 15:39:19 +00:00
} )
require . NoError ( t , bcSpout . dao . Store . PutBatch ( batch ) )
checkNewBlockchainErr := func ( t * testing . T , cfg func ( c * config . Config ) , store storage . Store , shouldFail bool ) {
unitTestNetCfg , err := config . Load ( "../../config" , testchain . Network ( ) )
require . NoError ( t , err )
cfg ( & unitTestNetCfg )
log := zaptest . NewLogger ( t )
_ , err = NewBlockchain ( store , unitTestNetCfg . ProtocolConfiguration , log )
if shouldFail {
require . Error ( t , err )
} else {
require . NoError ( t , err )
}
}
boltCfg := func ( c * config . Config ) {
spountCfg ( c )
c . ProtocolConfiguration . KeepOnlyLatestState = true
}
// manually store statejump stage to check statejump recover process
t . Run ( "invalid RemoveUntraceableBlocks setting" , func ( t * testing . T ) {
require . NoError ( t , bcSpout . dao . Store . Put ( storage . SYSStateJumpStage . Bytes ( ) , [ ] byte { byte ( stateJumpStarted ) } ) )
checkNewBlockchainErr ( t , func ( c * config . Config ) {
boltCfg ( c )
c . ProtocolConfiguration . RemoveUntraceableBlocks = false
} , bcSpout . dao . Store , true )
} )
t . Run ( "invalid state jump stage format" , func ( t * testing . T ) {
require . NoError ( t , bcSpout . dao . Store . Put ( storage . SYSStateJumpStage . Bytes ( ) , [ ] byte { 0x01 , 0x02 } ) )
checkNewBlockchainErr ( t , boltCfg , bcSpout . dao . Store , true )
} )
t . Run ( "missing state sync point" , func ( t * testing . T ) {
require . NoError ( t , bcSpout . dao . Store . Put ( storage . SYSStateJumpStage . Bytes ( ) , [ ] byte { byte ( stateJumpStarted ) } ) )
checkNewBlockchainErr ( t , boltCfg , bcSpout . dao . Store , true )
} )
t . Run ( "invalid state sync point" , func ( t * testing . T ) {
require . NoError ( t , bcSpout . dao . Store . Put ( storage . SYSStateJumpStage . Bytes ( ) , [ ] byte { byte ( stateJumpStarted ) } ) )
point := make ( [ ] byte , 4 )
binary . LittleEndian . PutUint32 ( point , uint32 ( len ( bcSpout . headerHashes ) ) )
require . NoError ( t , bcSpout . dao . Store . Put ( storage . SYSStateSyncPoint . Bytes ( ) , point ) )
checkNewBlockchainErr ( t , boltCfg , bcSpout . dao . Store , true )
} )
2021-11-09 12:13:53 +00:00
for _ , stage := range [ ] stateJumpStage { stateJumpStarted , newStorageItemsAdded , genesisStateRemoved , 0x03 } {
2021-08-31 15:39:19 +00:00
t . Run ( fmt . Sprintf ( "state jump stage %d" , stage ) , func ( t * testing . T ) {
require . NoError ( t , bcSpout . dao . Store . Put ( storage . SYSStateJumpStage . Bytes ( ) , [ ] byte { byte ( stage ) } ) )
point := make ( [ ] byte , 4 )
binary . LittleEndian . PutUint32 ( point , uint32 ( stateSyncPoint ) )
require . NoError ( t , bcSpout . dao . Store . Put ( storage . SYSStateSyncPoint . Bytes ( ) , point ) )
shouldFail := stage == 0x03 // unknown stage
2021-10-22 08:09:47 +00:00
checkNewBlockchainErr ( t , spountCfg , bcSpout . dao . Store , shouldFail )
2021-08-31 15:39:19 +00:00
} )
}
}
2021-12-09 17:23:58 +00:00
2022-01-21 02:33:06 +00:00
func TestChainWithVolatileNumOfValidators ( t * testing . T ) {
bc := newTestChainWithCustomCfg ( t , func ( c * config . Config ) {
c . ProtocolConfiguration . ValidatorsCount = 0
c . ProtocolConfiguration . CommitteeHistory = map [ uint32 ] int {
0 : 1 ,
4 : 4 ,
24 : 6 ,
}
c . ProtocolConfiguration . ValidatorsHistory = map [ uint32 ] int {
0 : 1 ,
4 : 4 ,
}
require . NoError ( t , c . ProtocolConfiguration . Validate ( ) )
} )
require . Equal ( t , uint32 ( 0 ) , bc . BlockHeight ( ) )
priv0 := testchain . PrivateKeyByID ( 0 )
vals , err := bc . GetValidators ( )
require . NoError ( t , err )
script , err := smartcontract . CreateDefaultMultiSigRedeemScript ( vals )
require . NoError ( t , err )
curWit := transaction . Witness {
VerificationScript : script ,
}
for i := 1 ; i < 26 ; i ++ {
comm , err := bc . GetCommittee ( )
require . NoError ( t , err )
if i < 5 {
require . Equal ( t , 1 , len ( comm ) )
} else if i < 25 {
require . Equal ( t , 4 , len ( comm ) )
} else {
require . Equal ( t , 6 , len ( comm ) )
}
// Mimic consensus.
if bc . config . ShouldUpdateCommitteeAt ( uint32 ( i ) ) {
vals , err = bc . GetValidators ( )
} else {
vals , err = bc . GetNextBlockValidators ( )
}
require . NoError ( t , err )
if i < 4 {
require . Equalf ( t , 1 , len ( vals ) , "at %d" , i )
} else {
require . Equalf ( t , 4 , len ( vals ) , "at %d" , i )
}
require . NoError ( t , err )
script , err := smartcontract . CreateDefaultMultiSigRedeemScript ( vals )
require . NoError ( t , err )
nextWit := transaction . Witness {
VerificationScript : script ,
}
b := & block . Block {
Header : block . Header {
NextConsensus : nextWit . ScriptHash ( ) ,
Script : curWit ,
} ,
}
curWit = nextWit
b . PrevHash = bc . GetHeaderHash ( i - 1 )
b . Timestamp = uint64 ( time . Now ( ) . UTC ( ) . Unix ( ) ) * 1000 + uint64 ( i )
b . Index = uint32 ( i )
b . RebuildMerkleRoot ( )
if i < 5 {
signa := priv0 . SignHashable ( uint32 ( bc . config . Magic ) , b )
b . Script . InvocationScript = append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , byte ( len ( signa ) ) } , signa ... )
} else {
b . Script . InvocationScript = testchain . Sign ( b )
}
err = bc . AddBlock ( b )
require . NoErrorf ( t , err , "at %d" , i )
}
}
2021-12-09 17:23:58 +00:00
func setSigner ( tx * transaction . Transaction , h util . Uint160 ) {
tx . Signers = [ ] transaction . Signer { {
Account : h ,
Scopes : transaction . Global ,
} }
}