2021-01-26 16:37:19 +00:00
package core
import (
"bytes"
"errors"
"fmt"
"math/rand"
"path"
"sync"
"testing"
"time"
"github.com/nspcc-dev/neo-go/internal/testchain"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
"github.com/nspcc-dev/neo-go/pkg/core/mempool"
"github.com/nspcc-dev/neo-go/pkg/core/native"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/network/payload"
"github.com/nspcc-dev/neo-go/pkg/services/notary"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
)
const notaryModulePath = "../services/notary/"
func getTestNotary ( t * testing . T , bc * Blockchain , walletPath , pass string , onTx func ( tx * transaction . Transaction ) error ) ( * wallet . Account , * notary . Notary , * mempool . Pool ) {
bc . config . P2PNotary = config . P2PNotary {
Enabled : true ,
UnlockWallet : config . Wallet {
Path : path . Join ( notaryModulePath , walletPath ) ,
Password : pass ,
} ,
}
mp := mempool . New ( 10 , 1 , true )
ntr , err := notary . NewNotary ( bc , mp , zaptest . NewLogger ( t ) , onTx )
require . NoError ( t , err )
w , err := wallet . NewWalletFromFile ( path . Join ( notaryModulePath , walletPath ) )
require . NoError ( t , err )
require . NoError ( t , w . Accounts [ 0 ] . Decrypt ( pass ) )
return w . Accounts [ 0 ] , ntr , mp
}
func TestNotary ( t * testing . T ) {
bc := newTestChain ( t )
defer bc . Close ( )
var (
nonce uint32
nvbDiffFallback uint32 = 20
)
mtx := sync . RWMutex { }
completedTxes := make ( map [ util . Uint256 ] * transaction . Transaction )
var unluckies [ ] * payload . P2PNotaryRequest
var (
finalizeWithError bool
choosy bool
)
onTransaction := func ( tx * transaction . Transaction ) error {
mtx . Lock ( )
defer mtx . Unlock ( )
if ! choosy {
if completedTxes [ tx . Hash ( ) ] != nil {
panic ( "transaction was completed twice" )
}
if finalizeWithError {
return errors . New ( "error while finalizing transaction" )
}
completedTxes [ tx . Hash ( ) ] = tx
return nil
}
for _ , unl := range unluckies {
if tx . Hash ( ) . Equals ( unl . FallbackTransaction . Hash ( ) ) {
return errors . New ( "error while finalizing transaction" )
}
}
completedTxes [ tx . Hash ( ) ] = tx
return nil
}
acc1 , ntr1 , mp1 := getTestNotary ( t , bc , "./testdata/notary1.json" , "one" , onTransaction )
acc2 , _ , _ := getTestNotary ( t , bc , "./testdata/notary2.json" , "two" , onTransaction )
randomAcc , err := keys . NewPrivateKey ( )
require . NoError ( t , err )
bc . SetNotary ( ntr1 )
bc . RegisterPostBlock ( func ( bc blockchainer . Blockchainer , pool * mempool . Pool , b * block . Block ) {
2021-02-09 13:03:06 +00:00
ntr1 . PostPersist ( )
2021-01-26 16:37:19 +00:00
} )
notaryNodes := keys . PublicKeys { acc1 . PrivateKey ( ) . PublicKey ( ) , acc2 . PrivateKey ( ) . PublicKey ( ) }
bc . setNodesByRole ( t , true , native . RoleP2PNotary , notaryNodes )
createFallbackTx := func ( requester * wallet . Account , mainTx * transaction . Transaction , nvbIncrement ... uint32 ) * transaction . Transaction {
fallback := transaction . New ( testchain . Network ( ) , [ ] byte { byte ( opcode . RET ) } , 2000_0000 )
fallback . Nonce = nonce
nonce ++
fallback . SystemFee = 1_0000_0000
fallback . ValidUntilBlock = bc . BlockHeight ( ) + 50
fallback . Signers = [ ] transaction . Signer {
{
Account : bc . GetNotaryContractScriptHash ( ) ,
Scopes : transaction . None ,
} ,
{
Account : requester . PrivateKey ( ) . PublicKey ( ) . GetScriptHash ( ) ,
Scopes : transaction . None ,
} ,
}
nvb := bc . BlockHeight ( ) + nvbDiffFallback
if len ( nvbIncrement ) != 0 {
nvb += nvbIncrement [ 0 ]
}
fallback . Attributes = [ ] transaction . Attribute {
{
Type : transaction . NotaryAssistedT ,
Value : & transaction . NotaryAssisted { NKeys : 0 } ,
} ,
{
Type : transaction . NotValidBeforeT ,
Value : & transaction . NotValidBefore { Height : nvb } ,
} ,
{
Type : transaction . ConflictsT ,
Value : & transaction . Conflicts { Hash : mainTx . Hash ( ) } ,
} ,
}
fallback . Scripts = [ ] transaction . Witness {
{
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , make ( [ ] byte , 64 ) ... ) ,
VerificationScript : [ ] byte { } ,
} ,
}
requester . SignTx ( fallback )
return fallback
}
createStandardRequest := func ( requesters [ ] * wallet . Account , NVBincrements ... uint32 ) [ ] * payload . P2PNotaryRequest {
mainTx := * transaction . New ( testchain . Network ( ) , [ ] byte { byte ( opcode . RET ) } , 11000000 )
mainTx . Nonce = nonce
nonce ++
mainTx . SystemFee = 100000000
mainTx . ValidUntilBlock = bc . BlockHeight ( ) + 2 * nvbDiffFallback
signers := make ( [ ] transaction . Signer , len ( requesters ) + 1 )
for i := range requesters {
signers [ i ] = transaction . Signer {
Account : requesters [ i ] . PrivateKey ( ) . PublicKey ( ) . GetScriptHash ( ) ,
Scopes : transaction . None ,
}
}
signers [ len ( signers ) - 1 ] = transaction . Signer {
Account : bc . GetNotaryContractScriptHash ( ) ,
Scopes : transaction . None ,
}
mainTx . Signers = signers
mainTx . Attributes = [ ] transaction . Attribute {
{
Type : transaction . NotaryAssistedT ,
Value : & transaction . NotaryAssisted { NKeys : uint8 ( len ( requesters ) ) } ,
} ,
}
payloads := make ( [ ] * payload . P2PNotaryRequest , len ( requesters ) )
for i := range payloads {
cp := mainTx
main := & cp
scripts := make ( [ ] transaction . Witness , len ( requesters ) + 1 )
for j := range requesters {
scripts [ j ] . VerificationScript = requesters [ j ] . PrivateKey ( ) . PublicKey ( ) . GetVerificationScript ( )
}
scripts [ i ] . InvocationScript = append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , requesters [ i ] . PrivateKey ( ) . Sign ( main . GetSignedPart ( ) ) ... )
main . Scripts = scripts
var fallback * transaction . Transaction
if len ( NVBincrements ) == len ( requesters ) {
fallback = createFallbackTx ( requesters [ i ] , main , NVBincrements [ i ] )
} else {
fallback = createFallbackTx ( requesters [ i ] , main )
}
payloads [ i ] = & payload . P2PNotaryRequest {
MainTransaction : main ,
FallbackTransaction : fallback ,
Network : netmode . UnitTestNet ,
}
}
return payloads
}
createMultisigRequest := func ( m int , requesters [ ] * wallet . Account ) [ ] * payload . P2PNotaryRequest {
mainTx := * transaction . New ( testchain . Network ( ) , [ ] byte { byte ( opcode . RET ) } , 11000000 )
mainTx . Nonce = nonce
nonce ++
mainTx . SystemFee = 100000000
mainTx . ValidUntilBlock = bc . BlockHeight ( ) + 2 * nvbDiffFallback
pubs := make ( keys . PublicKeys , len ( requesters ) )
for i , r := range requesters {
pubs [ i ] = r . PrivateKey ( ) . PublicKey ( )
}
script , err := smartcontract . CreateMultiSigRedeemScript ( m , pubs )
require . NoError ( t , err )
mainTx . Signers = [ ] transaction . Signer {
{
Account : hash . Hash160 ( script ) ,
Scopes : transaction . None ,
} ,
{
Account : bc . GetNotaryContractScriptHash ( ) ,
Scopes : transaction . None ,
} ,
}
mainTx . Attributes = [ ] transaction . Attribute {
{
Type : transaction . NotaryAssistedT ,
Value : & transaction . NotaryAssisted { NKeys : uint8 ( len ( requesters ) ) } ,
} ,
}
payloads := make ( [ ] * payload . P2PNotaryRequest , len ( requesters ) )
// we'll collect only m signatures out of n (so only m payloads are needed), but let's create payloads for all requesters (for the next tests)
for i := range payloads {
cp := mainTx
main := & cp
main . Scripts = [ ] transaction . Witness {
{
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , requesters [ i ] . PrivateKey ( ) . Sign ( main . GetSignedPart ( ) ) ... ) ,
VerificationScript : script ,
} ,
{ } , // empty Notary witness
}
fallback := createFallbackTx ( requesters [ i ] , main )
payloads [ i ] = & payload . P2PNotaryRequest {
MainTransaction : main ,
FallbackTransaction : fallback ,
Network : netmode . UnitTestNet ,
}
}
return payloads
}
checkSigTx := func ( t * testing . T , requests [ ] * payload . P2PNotaryRequest , sentCount int , shouldComplete bool ) {
nKeys := len ( requests )
completedTx := completedTxes [ requests [ 0 ] . MainTransaction . Hash ( ) ]
if sentCount == nKeys && shouldComplete {
require . NotNil ( t , completedTx , errors . New ( "main transaction expected to be completed" ) )
require . Equal ( t , nKeys + 1 , len ( completedTx . Signers ) )
require . Equal ( t , nKeys + 1 , len ( completedTx . Scripts ) )
interopCtx := bc . newInteropContext ( trigger . Verification , bc . dao , nil , completedTx )
for i , req := range requests {
require . Equal ( t , req . MainTransaction . Scripts [ i ] , completedTx . Scripts [ i ] )
_ , err := bc . verifyHashAgainstScript ( completedTx . Signers [ i ] . Account , & completedTx . Scripts [ i ] , interopCtx , - 1 )
require . NoError ( t , err )
}
require . Equal ( t , transaction . Witness {
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , acc1 . PrivateKey ( ) . Sign ( requests [ 0 ] . MainTransaction . GetSignedPart ( ) ) ... ) ,
VerificationScript : [ ] byte { } ,
} , completedTx . Scripts [ nKeys ] )
} else {
require . Nil ( t , completedTx , fmt . Errorf ( "main transaction shouldn't be completed: sent %d out of %d requests" , sentCount , nKeys ) )
}
}
checkMultisigTx := func ( t * testing . T , nSigs int , requests [ ] * payload . P2PNotaryRequest , sentCount int , shouldComplete bool ) {
completedTx := completedTxes [ requests [ 0 ] . MainTransaction . Hash ( ) ]
if sentCount >= nSigs && shouldComplete {
require . NotNil ( t , completedTx , errors . New ( "main transaction expected to be completed" ) )
require . Equal ( t , 2 , len ( completedTx . Signers ) )
require . Equal ( t , 2 , len ( completedTx . Scripts ) )
interopCtx := bc . newInteropContext ( trigger . Verification , bc . dao , nil , completedTx )
_ , err := bc . verifyHashAgainstScript ( completedTx . Signers [ 0 ] . Account , & completedTx . Scripts [ 0 ] , interopCtx , - 1 )
require . NoError ( t , err )
require . Equal ( t , transaction . Witness {
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , acc1 . PrivateKey ( ) . Sign ( requests [ 0 ] . MainTransaction . GetSignedPart ( ) ) ... ) ,
VerificationScript : [ ] byte { } ,
} , completedTx . Scripts [ 1 ] )
// check that only nSigs out of nKeys signatures are presented in the invocation script
for i , req := range requests [ : nSigs ] {
require . True ( t , bytes . Contains ( completedTx . Scripts [ 0 ] . InvocationScript , req . MainTransaction . Scripts [ 0 ] . InvocationScript ) , fmt . Errorf ( "signature from extra request #%d shouldn't be presented in the main tx" , i ) )
}
// the rest (nKeys-nSigs) out of nKeys shouldn't be presented in the invocation script
for i , req := range requests [ nSigs : ] {
require . False ( t , bytes . Contains ( completedTx . Scripts [ 0 ] . InvocationScript , req . MainTransaction . Scripts [ 0 ] . InvocationScript ) , fmt . Errorf ( "signature from extra request #%d shouldn't be presented in the main tx" , i ) )
}
} else {
require . Nil ( t , completedTx , fmt . Errorf ( "main transaction shouldn't be completed: sent %d out of %d requests" , sentCount , nSigs ) )
}
}
checkFallbackTxs := func ( t * testing . T , requests [ ] * payload . P2PNotaryRequest , shouldComplete bool ) {
for i , req := range requests {
completedTx := completedTxes [ req . FallbackTransaction . Hash ( ) ]
if shouldComplete {
require . NotNil ( t , completedTx , fmt . Errorf ( "fallback transaction for request #%d expected to be completed" , i ) )
require . Equal ( t , 2 , len ( completedTx . Signers ) )
require . Equal ( t , 2 , len ( completedTx . Scripts ) )
require . Equal ( t , transaction . Witness {
InvocationScript : append ( [ ] byte { byte ( opcode . PUSHDATA1 ) , 64 } , acc1 . PrivateKey ( ) . Sign ( req . FallbackTransaction . GetSignedPart ( ) ) ... ) ,
VerificationScript : [ ] byte { } ,
} , completedTx . Scripts [ 0 ] )
interopCtx := bc . newInteropContext ( trigger . Verification , bc . dao , nil , completedTx )
_ , err := bc . verifyHashAgainstScript ( completedTx . Signers [ 1 ] . Account , & completedTx . Scripts [ 1 ] , interopCtx , - 1 )
require . NoError ( t , err )
} else {
require . Nil ( t , completedTx , fmt . Errorf ( "fallback transaction for request #%d shouldn't be completed" , i ) )
}
}
}
checkCompleteStandardRequest := func ( t * testing . T , nKeys int , shouldComplete bool , nvbIncrements ... uint32 ) [ ] * payload . P2PNotaryRequest {
requesters := make ( [ ] * wallet . Account , nKeys )
for i := range requesters {
requesters [ i ] , _ = wallet . NewAccount ( )
}
requests := createStandardRequest ( requesters , nvbIncrements ... )
sendOrder := make ( [ ] int , nKeys )
for i := range sendOrder {
sendOrder [ i ] = i
}
rand . Shuffle ( nKeys , func ( i , j int ) {
sendOrder [ j ] , sendOrder [ i ] = sendOrder [ i ] , sendOrder [ j ]
} )
for i := range requests {
ntr1 . OnNewRequest ( requests [ sendOrder [ i ] ] )
checkSigTx ( t , requests , i + 1 , shouldComplete )
completedCount := len ( completedTxes )
// check that the same request won't be processed twice
ntr1 . OnNewRequest ( requests [ sendOrder [ i ] ] )
checkSigTx ( t , requests , i + 1 , shouldComplete )
require . Equal ( t , completedCount , len ( completedTxes ) )
}
return requests
}
checkCompleteMultisigRequest := func ( t * testing . T , nSigs int , nKeys int , shouldComplete bool ) [ ] * payload . P2PNotaryRequest {
requesters := make ( [ ] * wallet . Account , nKeys )
for i := range requesters {
requesters [ i ] , _ = wallet . NewAccount ( )
}
requests := createMultisigRequest ( nSigs , requesters )
sendOrder := make ( [ ] int , nKeys )
for i := range sendOrder {
sendOrder [ i ] = i
}
rand . Shuffle ( nKeys , func ( i , j int ) {
sendOrder [ j ] , sendOrder [ i ] = sendOrder [ i ] , sendOrder [ j ]
} )
var submittedRequests [ ] * payload . P2PNotaryRequest
// sent only nSigs (m out of n) requests - it should be enough to complete min tx
for i := 0 ; i < nSigs ; i ++ {
submittedRequests = append ( submittedRequests , requests [ sendOrder [ i ] ] )
ntr1 . OnNewRequest ( requests [ sendOrder [ i ] ] )
checkMultisigTx ( t , nSigs , submittedRequests , i + 1 , shouldComplete )
// check that the same request won't be processed twice
ntr1 . OnNewRequest ( requests [ sendOrder [ i ] ] )
checkMultisigTx ( t , nSigs , submittedRequests , i + 1 , shouldComplete )
}
// sent the rest (n-m) out of n requests: main tx is already collected, so only fallbacks should be applied
completedCount := len ( completedTxes )
for i := nSigs ; i < nKeys ; i ++ {
submittedRequests = append ( submittedRequests , requests [ sendOrder [ i ] ] )
ntr1 . OnNewRequest ( requests [ sendOrder [ i ] ] )
checkMultisigTx ( t , nSigs , submittedRequests , i + 1 , shouldComplete )
require . Equal ( t , completedCount , len ( completedTxes ) )
}
return submittedRequests
}
// OnNewRequest: missing account
ntr1 . UpdateNotaryNodes ( keys . PublicKeys { randomAcc . PublicKey ( ) } )
r := checkCompleteStandardRequest ( t , 1 , false )
checkFallbackTxs ( t , r , false )
// set account back for the next tests
ntr1 . UpdateNotaryNodes ( keys . PublicKeys { acc1 . PrivateKey ( ) . PublicKey ( ) } )
// OnNewRequest: signature request
for _ , i := range [ ] int { 1 , 2 , 3 , 10 } {
r := checkCompleteStandardRequest ( t , i , true )
checkFallbackTxs ( t , r , false )
}
// OnNewRequest: multisignature request
r = checkCompleteMultisigRequest ( t , 1 , 1 , true )
checkFallbackTxs ( t , r , false )
r = checkCompleteMultisigRequest ( t , 1 , 2 , true )
checkFallbackTxs ( t , r , false )
r = checkCompleteMultisigRequest ( t , 1 , 3 , true )
checkFallbackTxs ( t , r , false )
r = checkCompleteMultisigRequest ( t , 3 , 3 , true )
checkFallbackTxs ( t , r , false )
r = checkCompleteMultisigRequest ( t , 3 , 4 , true )
checkFallbackTxs ( t , r , false )
r = checkCompleteMultisigRequest ( t , 3 , 10 , true )
checkFallbackTxs ( t , r , false )
// PostPersist: missing account
finalizeWithError = true
r = checkCompleteStandardRequest ( t , 1 , false )
checkFallbackTxs ( t , r , false )
finalizeWithError = false
ntr1 . UpdateNotaryNodes ( keys . PublicKeys { randomAcc . PublicKey ( ) } )
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
checkSigTx ( t , r , 1 , false )
checkFallbackTxs ( t , r , false )
// set account back for the next tests
ntr1 . UpdateNotaryNodes ( keys . PublicKeys { acc1 . PrivateKey ( ) . PublicKey ( ) } )
// PostPersist: complete main transaction, signature request
finalizeWithError = true
requests := checkCompleteStandardRequest ( t , 3 , false )
// check PostPersist with finalisation error
finalizeWithError = true
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
checkSigTx ( t , requests , len ( requests ) , false )
// check PostPersist without finalisation error
finalizeWithError = false
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
checkSigTx ( t , requests , len ( requests ) , true )
// PostPersist: complete main transaction, multisignature account
finalizeWithError = true
requests = checkCompleteMultisigRequest ( t , 3 , 4 , false )
checkFallbackTxs ( t , requests , false )
// check PostPersist with finalisation error
finalizeWithError = true
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
checkMultisigTx ( t , 3 , requests , len ( requests ) , false )
checkFallbackTxs ( t , requests , false )
// check PostPersist without finalisation error
finalizeWithError = false
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
checkMultisigTx ( t , 3 , requests , len ( requests ) , true )
checkFallbackTxs ( t , requests , false )
// PostPersist: complete fallback, signature request
finalizeWithError = true
requests = checkCompleteStandardRequest ( t , 3 , false )
checkFallbackTxs ( t , requests , false )
// make fallbacks valid
_ , err = bc . genBlocks ( int ( nvbDiffFallback ) )
require . NoError ( t , err )
// check PostPersist for valid fallbacks with finalisation error
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
checkSigTx ( t , requests , len ( requests ) , false )
checkFallbackTxs ( t , requests , false )
// check PostPersist for valid fallbacks without finalisation error
finalizeWithError = false
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
checkSigTx ( t , requests , len ( requests ) , false )
checkFallbackTxs ( t , requests , true )
// PostPersist: complete fallback, multisignature request
nSigs , nKeys := 3 , 5
// check OnNewRequest with finalization error
finalizeWithError = true
requests = checkCompleteMultisigRequest ( t , nSigs , nKeys , false )
checkFallbackTxs ( t , requests , false )
// make fallbacks valid
_ , err = bc . genBlocks ( int ( nvbDiffFallback ) )
require . NoError ( t , err )
// check PostPersist for valid fallbacks with finalisation error
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
checkMultisigTx ( t , nSigs , requests , len ( requests ) , false )
checkFallbackTxs ( t , requests , false )
// check PostPersist for valid fallbacks without finalisation error
finalizeWithError = false
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
checkMultisigTx ( t , nSigs , requests , len ( requests ) , false )
checkFallbackTxs ( t , requests [ : nSigs ] , true )
// the rest of fallbacks should also be applied even if the main tx was already constructed by the moment they were sent
checkFallbackTxs ( t , requests [ nSigs : ] , true )
// PostPersist: partial fallbacks completion due to finalisation errors
finalizeWithError = true
requests = checkCompleteStandardRequest ( t , 5 , false )
checkFallbackTxs ( t , requests , false )
// make fallbacks valid
_ , err = bc . genBlocks ( int ( nvbDiffFallback ) )
require . NoError ( t , err )
// some of fallbacks should fail finalisation
unluckies = [ ] * payload . P2PNotaryRequest { requests [ 0 ] , requests [ 4 ] }
lucky := requests [ 1 : 4 ]
choosy = true
// check PostPersist for lucky fallbacks
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
checkSigTx ( t , requests , len ( requests ) , false )
checkFallbackTxs ( t , lucky , true )
checkFallbackTxs ( t , unluckies , false )
// reset finalisation function for unlucky fallbacks to finalise without an error
choosy = false
finalizeWithError = false
// check PostPersist for unlucky fallbacks
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
checkSigTx ( t , requests , len ( requests ) , false )
checkFallbackTxs ( t , lucky , true )
checkFallbackTxs ( t , unluckies , true )
// PostPersist: different NVBs
// check OnNewRequest with finalization error and different NVBs
finalizeWithError = true
requests = checkCompleteStandardRequest ( t , 5 , false , 1 , 2 , 3 , 4 , 5 )
checkFallbackTxs ( t , requests , false )
// generate blocks to reach the most earlier fallback's NVB
_ , err = bc . genBlocks ( int ( nvbDiffFallback ) )
require . NoError ( t , err )
// check PostPersist for valid fallbacks without finalisation error
finalizeWithError = false
for i := range requests {
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
checkSigTx ( t , requests , len ( requests ) , false )
checkFallbackTxs ( t , requests [ : i + 1 ] , true )
checkFallbackTxs ( t , requests [ i + 1 : ] , false )
}
// OnRequestRemoval: missing account
// check OnNewRequest with finalization error
finalizeWithError = true
requests = checkCompleteStandardRequest ( t , 4 , false )
checkFallbackTxs ( t , requests , false )
// make fallbacks valid and remove one fallback
_ , err = bc . genBlocks ( int ( nvbDiffFallback ) )
require . NoError ( t , err )
ntr1 . UpdateNotaryNodes ( keys . PublicKeys { randomAcc . PublicKey ( ) } )
ntr1 . OnRequestRemoval ( requests [ 3 ] )
// non of the fallbacks should be completed
finalizeWithError = false
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
checkSigTx ( t , requests , len ( requests ) , false )
checkFallbackTxs ( t , requests , false )
// set account back for the next tests
ntr1 . UpdateNotaryNodes ( keys . PublicKeys { acc1 . PrivateKey ( ) . PublicKey ( ) } )
// OnRequestRemoval: signature request, remove one fallback
// check OnNewRequest with finalization error
finalizeWithError = true
requests = checkCompleteStandardRequest ( t , 4 , false )
checkFallbackTxs ( t , requests , false )
// make fallbacks valid and remove one fallback
_ , err = bc . genBlocks ( int ( nvbDiffFallback ) )
require . NoError ( t , err )
unlucky := requests [ 3 ]
ntr1 . OnRequestRemoval ( unlucky )
// rest of the fallbacks should be completed
finalizeWithError = false
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
checkSigTx ( t , requests , len ( requests ) , false )
checkFallbackTxs ( t , requests [ : 3 ] , true )
require . Nil ( t , completedTxes [ unlucky . FallbackTransaction . Hash ( ) ] )
// OnRequestRemoval: signature request, remove all fallbacks
finalizeWithError = true
requests = checkCompleteStandardRequest ( t , 4 , false )
// remove all fallbacks
_ , err = bc . genBlocks ( int ( nvbDiffFallback ) )
require . NoError ( t , err )
for i := range requests {
ntr1 . OnRequestRemoval ( requests [ i ] )
}
// then the whole request should be removed, i.e. there are no completed transactions
finalizeWithError = false
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
checkSigTx ( t , requests , len ( requests ) , false )
checkFallbackTxs ( t , requests , false )
// OnRequestRemoval: signature request, remove unexisting fallback
ntr1 . OnRequestRemoval ( requests [ 0 ] )
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
checkSigTx ( t , requests , len ( requests ) , false )
checkFallbackTxs ( t , requests , false )
// OnRequestRemoval: multisignature request, remove one fallback
nSigs , nKeys = 3 , 5
// check OnNewRequest with finalization error
finalizeWithError = true
requests = checkCompleteMultisigRequest ( t , nSigs , nKeys , false )
checkMultisigTx ( t , nSigs , requests , len ( requests ) , false )
checkFallbackTxs ( t , requests , false )
// make fallbacks valid and remove the last fallback
_ , err = bc . genBlocks ( int ( nvbDiffFallback ) )
require . NoError ( t , err )
unlucky = requests [ nSigs - 1 ]
ntr1 . OnRequestRemoval ( unlucky )
// then (m-1) out of n fallbacks should be completed
finalizeWithError = false
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
checkMultisigTx ( t , nSigs , requests , len ( requests ) , false )
checkFallbackTxs ( t , requests [ : nSigs - 1 ] , true )
require . Nil ( t , completedTxes [ unlucky . FallbackTransaction . Hash ( ) ] )
// the rest (n-(m-1)) out of n fallbacks should also be completed even if main tx has been collected by the moment they were sent
checkFallbackTxs ( t , requests [ nSigs : ] , true )
// OnRequestRemoval: multisignature request, remove all fallbacks
finalizeWithError = true
requests = checkCompleteMultisigRequest ( t , nSigs , nKeys , false )
// make fallbacks valid and then remove all of them
_ , err = bc . genBlocks ( int ( nvbDiffFallback ) )
require . NoError ( t , err )
for i := range requests {
ntr1 . OnRequestRemoval ( requests [ i ] )
}
// then the whole request should be removed, i.e. there are no completed transactions
finalizeWithError = false
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
checkMultisigTx ( t , nSigs , requests , len ( requests ) , false )
checkFallbackTxs ( t , requests , false )
// // OnRequestRemoval: multisignature request, remove unexisting fallbac, i.e. there still shouldn't be any completed transactions after this
ntr1 . OnRequestRemoval ( requests [ 0 ] )
require . NoError ( t , bc . AddBlock ( bc . newBlock ( ) ) )
checkMultisigTx ( t , nSigs , requests , len ( requests ) , false )
checkFallbackTxs ( t , requests , false )
// Subscriptions test
mp1 . RunSubscriptions ( )
go ntr1 . Run ( )
defer func ( ) {
ntr1 . Stop ( )
mp1 . StopSubscriptions ( )
} ( )
finalizeWithError = false
requester1 , _ := wallet . NewAccount ( )
requester2 , _ := wallet . NewAccount ( )
amount := int64 ( 100_0000_0000 )
feer := NewNotaryFeerStub ( bc )
transferTokenFromMultisigAccountCheckOK ( t , bc , bc . GetNotaryContractScriptHash ( ) , bc . contracts . GAS . Hash , amount , requester1 . PrivateKey ( ) . PublicKey ( ) . GetScriptHash ( ) , int64 ( bc . BlockHeight ( ) + 50 ) )
checkBalanceOf ( t , bc , bc . contracts . Notary . Hash , int ( amount ) )
transferTokenFromMultisigAccountCheckOK ( t , bc , bc . GetNotaryContractScriptHash ( ) , bc . contracts . GAS . Hash , amount , requester2 . PrivateKey ( ) . PublicKey ( ) . GetScriptHash ( ) , int64 ( bc . BlockHeight ( ) + 50 ) )
checkBalanceOf ( t , bc , bc . contracts . Notary . Hash , int ( 2 * amount ) )
// create request for 2 standard signatures => main tx should be completed after the second request is added to the pool
requests = createStandardRequest ( [ ] * wallet . Account { requester1 , requester2 } )
require . NoError ( t , mp1 . Add ( requests [ 0 ] . FallbackTransaction , feer , requests [ 0 ] ) )
require . NoError ( t , mp1 . Add ( requests [ 1 ] . FallbackTransaction , feer , requests [ 1 ] ) )
require . Eventually ( t , func ( ) bool {
mtx . RLock ( )
defer mtx . RUnlock ( )
return completedTxes [ requests [ 0 ] . MainTransaction . Hash ( ) ] != nil
} , time . Second , time . Millisecond )
checkFallbackTxs ( t , requests , false )
}