2018-02-04 19:54:51 +00:00
package core
import (
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-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-27 10:55:48 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
2020-11-24 08:36:09 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/chaindump"
2020-09-28 14:56:16 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/fee"
2020-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"
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"
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"
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 )
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()
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 {
2020-11-25 10:59:30 +00:00
key := storage . AppendPrefix ( storage . DataBlock , 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 ) )
}
2020-08-19 11:38:58 +00:00
func TestAddBadBlock ( t * testing . T ) {
bc := newTestChain ( t )
// 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
} }
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 ) )
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
} }
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 )
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
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 )
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
}
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 )
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 {
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 )
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
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-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
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-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
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-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
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-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
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
} )
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 )
require . NoError ( t , accs [ 0 ] . SignTx ( tx ) )
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
require . NoError ( t , accs [ 0 ] . SignTx ( tx ) )
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
require . NoError ( t , accs [ 0 ] . SignTx ( tx ) )
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
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 ( "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 ) {
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.
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-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 ( )
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 {
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 )
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 ( ) ,
} }
2020-11-17 12:57:50 +00:00
bl := block . New ( netmode . UnitTestNet , 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 ) } )
2020-10-01 15:17:09 +00:00
require . NoError ( t , bc . contracts . Designate . DesignateAsRole ( ic , native . RoleOracle , 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 )
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 ) {
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
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 )
2021-02-09 19:28:36 +00:00
tx . Script = append ( tx . Script , byte ( opcode . NOP ) )
2020-09-24 13:33:40 +00:00
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 )
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 ( )
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 )
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 ( )
data := tx . GetSignedPart ( )
tx . Scripts = [ ] transaction . Witness { {
InvocationScript : testchain . SignCommittee ( data ) ,
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 ( )
data := tx . GetSignedPart ( )
tx . Scripts = [ ] transaction . Witness { {
InvocationScript : testchain . SignCommittee ( data ) ,
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 )
require . NoError ( t , accs [ 0 ] . SignTx ( tx ) )
dummyTx := transaction . NewTrimmedTX ( tx . Hash ( ) )
dummyTx . Version = transaction . DummyVersion
require . NoError ( t , bc . dao . StoreAsTransaction ( dummyTx , bc . blockHeight , nil ) )
require . True ( t , errors . Is ( bc . VerifyTx ( tx ) , ErrHasConflicts ) )
} )
t . Run ( "attribute on-chain conflict" , func ( t * testing . T ) {
2020-12-08 15:28:00 +00:00
tx := transaction . New ( netmode . UnitTestNet , [ ] byte { byte ( opcode . PUSH1 ) } , 0 )
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 )
txSetNotary := transaction . New ( netmode . UnitTestNet , [ ] byte { } , 0 )
setSigner ( txSetNotary , testchain . CommitteeScriptHash ( ) )
txSetNotary . Scripts = [ ] transaction . Witness { {
InvocationScript : testchain . SignCommittee ( txSetNotary . GetSignedPart ( ) ) ,
VerificationScript : testchain . CommitteeVerificationScript ( ) ,
} }
bl := block . New ( netmode . UnitTestNet , false )
bl . Index = bc . BlockHeight ( ) + 1
ic := bc . newInteropContext ( trigger . All , bc . dao , bl , txSetNotary )
ic . SpawnVM ( )
ic . VM . LoadScript ( [ ] byte { byte ( opcode . RET ) } )
require . NoError ( t , bc . contracts . Designate . DesignateAsRole ( ic , native . RoleP2PNotary , keys . PublicKeys { notary . PrivateKey ( ) . PublicKey ( ) } ) )
_ , 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 ( )
data := tx . GetSignedPart ( )
2020-11-19 10:00:46 +00:00
tx . Scripts = [ ] transaction . Witness {
{
InvocationScript : testchain . SignCommittee ( data ) ,
VerificationScript : rawScript ,
} ,
{
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , notary . PrivateKey ( ) . Sign ( data ) ... ) ,
} ,
}
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 ,
} ,
}
data := tx . GetSignedPart ( )
tx . Scripts = [ ] transaction . Witness {
{
InvocationScript : testchain . SignCommittee ( data ) ,
VerificationScript : testchain . CommitteeVerificationScript ( ) ,
} ,
{
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , notary . PrivateKey ( ) . Sign ( data ) ... ) ,
} ,
}
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 ,
} ,
}
data := tx . GetSignedPart ( )
tx . Scripts = [ ] transaction . Witness {
{
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , notary . PrivateKey ( ) . Sign ( data ) ... ) ,
} ,
{
InvocationScript : testchain . SignCommittee ( data ) ,
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 ,
} ,
}
data := tx . GetSignedPart ( )
tx . Scripts = [ ] transaction . Witness {
{
InvocationScript : testchain . SignCommittee ( data ) ,
VerificationScript : testchain . CommitteeVerificationScript ( ) ,
} ,
{
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , notary . PrivateKey ( ) . Sign ( data ) ... ) ,
} ,
}
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 ,
} ,
}
data := tx . GetSignedPart ( )
tx . Scripts = [ ] transaction . Witness {
{
InvocationScript : testchain . SignCommittee ( data ) ,
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 ,
} ,
}
data := tx . GetSignedPart ( )
acc , err := keys . NewPrivateKey ( )
require . NoError ( t , err )
tx . Scripts = [ ] transaction . Witness {
{
InvocationScript : testchain . SignCommittee ( data ) ,
VerificationScript : testchain . CommitteeVerificationScript ( ) ,
} ,
{
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , acc . Sign ( data ) ... ) ,
} ,
}
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 ,
} ,
}
data := tx . GetSignedPart ( )
tx . Scripts = [ ] transaction . Witness {
{
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , notary . PrivateKey ( ) . Sign ( data ) ... ) ,
} ,
}
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
int64 ( sizeDelta ) * bc . FeePerByte ( ) + //fee for multisig size
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-05 10:30:16 +00:00
native . NotaryVerificationPrice * bc . GetBaseExecFee ( ) // Notary witness verification price
2020-11-27 10:55:48 +00:00
tx . Scripts = [ ] transaction . Witness {
{
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , make ( [ ] byte , 64 , 64 ) ... ) ,
VerificationScript : [ ] byte { } ,
} ,
{
InvocationScript : testchain . Sign ( tx . GetSignedPart ( ) ) ,
VerificationScript : testchain . MultisigVerificationScript ( ) ,
} ,
}
return tx
}
2021-01-15 12:40:15 +00:00
mp := mempool . New ( 10 , 1 , false )
2020-11-27 10:55:48 +00:00
verificationF := func ( bc blockchainer . Blockchainer , tx * transaction . Transaction , data interface { } ) error {
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 {
{
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , make ( [ ] byte , 64 , 64 ) ... ) ,
VerificationScript : [ ] byte { } ,
} ,
{
InvocationScript : testchain . Sign ( tx . GetSignedPart ( ) ) ,
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 )
2020-12-13 15:26:35 +00:00
cs , csInvalid := getTestContractState ( bc )
2020-08-13 15:42:53 +00:00
ic := bc . newInteropContext ( trigger . Verification , bc . dao , 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 {
tx := transaction . New ( netmode . UnitTestNet , [ ] byte { byte ( opcode . RET ) } , 100 )
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-03-05 07:18:03 +00:00
txDeploy , h , _ , err := testchain . NewDeployTx ( bc , "TestVerify" , neoOwner , strings . NewReader ( src ) )
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 ( ) ) )
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 ,
} }
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 ) )
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-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 ) {
defer func ( ) {
r := recover ( )
assert . NotNil ( t , r )
} ( )
2021-03-01 11:14:15 +00:00
bc := initTestChain ( t , nil , nil )
go bc . Run ( )
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 )
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 )
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
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 )
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
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 )
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
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 ) {
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 ( ) ) )
_ , _ , err = bc . GetTransaction ( tx1 . Hash ( ) )
require . Error ( t , err )
_ , err = bc . GetAppExecResults ( tx1 . Hash ( ) , trigger . Application )
require . Error ( t , err )
2021-03-10 09:29:56 +00:00
_ , err = bc . GetBlock ( b1 . Hash ( ) )
require . Error ( t , err )
_ , err = bc . GetHeader ( b1 . Hash ( ) )
2020-11-26 09:33:34 +00:00
require . NoError ( t , err )
}
2020-12-18 09:28:01 +00:00
func TestInvalidNotification ( t * testing . T ) {
bc := newTestChain ( t )
cs , _ := getTestContractState ( bc )
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 )
cs , _ := getTestContractState ( bc )
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 )
}