github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/services/notary/core_test.go (about) 1 package notary_test 2 3 import ( 4 "errors" 5 "fmt" 6 "math/big" 7 "math/rand" 8 "sync" 9 "testing" 10 "time" 11 12 "github.com/nspcc-dev/neo-go/pkg/config" 13 "github.com/nspcc-dev/neo-go/pkg/config/netmode" 14 "github.com/nspcc-dev/neo-go/pkg/core" 15 "github.com/nspcc-dev/neo-go/pkg/core/block" 16 "github.com/nspcc-dev/neo-go/pkg/core/mempool" 17 "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" 18 "github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" 19 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 20 "github.com/nspcc-dev/neo-go/pkg/crypto/hash" 21 "github.com/nspcc-dev/neo-go/pkg/crypto/keys" 22 "github.com/nspcc-dev/neo-go/pkg/io" 23 "github.com/nspcc-dev/neo-go/pkg/neotest" 24 "github.com/nspcc-dev/neo-go/pkg/neotest/chain" 25 "github.com/nspcc-dev/neo-go/pkg/network" 26 "github.com/nspcc-dev/neo-go/pkg/network/payload" 27 "github.com/nspcc-dev/neo-go/pkg/services/notary" 28 "github.com/nspcc-dev/neo-go/pkg/smartcontract" 29 "github.com/nspcc-dev/neo-go/pkg/util" 30 "github.com/nspcc-dev/neo-go/pkg/vm/opcode" 31 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 32 "github.com/nspcc-dev/neo-go/pkg/wallet" 33 "github.com/stretchr/testify/require" 34 "go.uber.org/zap/zaptest" 35 ) 36 37 func getTestNotary(t *testing.T, bc *core.Blockchain, walletPath, pass string, onTx func(tx *transaction.Transaction) error) (*wallet.Account, *notary.Notary, *mempool.Pool) { 38 mainCfg := config.P2PNotary{ 39 Enabled: true, 40 UnlockWallet: config.Wallet{ 41 Path: walletPath, 42 Password: pass, 43 }, 44 } 45 cfg := notary.Config{ 46 MainCfg: mainCfg, 47 Chain: bc, 48 Log: zaptest.NewLogger(t), 49 } 50 mp := mempool.New(10, 1, true, nil) 51 ntr, err := notary.NewNotary(cfg, netmode.UnitTestNet, mp, onTx) 52 require.NoError(t, err) 53 54 w, err := wallet.NewWalletFromFile(walletPath) 55 require.NoError(t, err) 56 require.NoError(t, w.Accounts[0].Decrypt(pass, w.Scrypt)) 57 return w.Accounts[0], ntr, mp 58 } 59 60 // dupNotaryRequest duplicates notary request by serializing/deserializing it. Use 61 // it to avoid data races when reusing the same payload. Normal OnNewRequest handler 62 // never receives the same (as in the same pointer) payload multiple times, even if 63 // the contents is the same it would be a separate buffer. 64 func dupNotaryRequest(t *testing.T, p *payload.P2PNotaryRequest) *payload.P2PNotaryRequest { 65 b, err := p.Bytes() 66 require.NoError(t, err) 67 r, err := payload.NewP2PNotaryRequestFromBytes(b) 68 require.NoError(t, err) 69 return r 70 } 71 72 func TestNotary(t *testing.T) { 73 bc, validators, committee := chain.NewMultiWithCustomConfig(t, func(c *config.Blockchain) { 74 c.P2PSigExtensions = true 75 }) 76 e := neotest.NewExecutor(t, bc, validators, committee) 77 notaryHash := e.NativeHash(t, nativenames.Notary) 78 designationSuperInvoker := e.NewInvoker(e.NativeHash(t, nativenames.Designation), validators, committee) 79 gasValidatorInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas)) 80 81 var ( 82 nonce uint32 83 nvbDiffFallback uint32 = 20 84 ) 85 86 mtx := sync.RWMutex{} 87 completedTxes := make(map[util.Uint256]*transaction.Transaction) 88 var unluckies []*payload.P2PNotaryRequest 89 var ( 90 finalizeWithError bool 91 choosy bool 92 ) 93 setFinalizeWithError := func(v bool) { 94 mtx.Lock() 95 finalizeWithError = v 96 mtx.Unlock() 97 } 98 setChoosy := func(v bool) { 99 mtx.Lock() 100 choosy = v 101 mtx.Unlock() 102 } 103 onTransaction := func(tx *transaction.Transaction) error { 104 mtx.Lock() 105 defer mtx.Unlock() 106 if !choosy { 107 if completedTxes[tx.Hash()] != nil { 108 panic("transaction was completed twice") 109 } 110 if finalizeWithError { 111 return errors.New("error while finalizing transaction") 112 } 113 completedTxes[tx.Hash()] = tx 114 return nil 115 } 116 for _, unl := range unluckies { 117 if tx.Hash().Equals(unl.FallbackTransaction.Hash()) { 118 return errors.New("error while finalizing transaction") 119 } 120 } 121 completedTxes[tx.Hash()] = tx 122 return nil 123 } 124 getCompletedTx := func(t *testing.T, waitForNonNil bool, h util.Uint256) *transaction.Transaction { 125 if !waitForNonNil { 126 mtx.RLock() 127 defer mtx.RUnlock() 128 return completedTxes[h] 129 } 130 var completedTx *transaction.Transaction 131 require.Eventually(t, func() bool { 132 mtx.RLock() 133 defer mtx.RUnlock() 134 completedTx = completedTxes[h] 135 return completedTx != nil 136 }, time.Second*3, time.Millisecond*50, errors.New("transaction expected to be completed")) 137 return completedTx 138 } 139 140 acc1, ntr1, mp1 := getTestNotary(t, bc, "./testdata/notary1.json", "one", onTransaction) 141 acc2, _, _ := getTestNotary(t, bc, "./testdata/notary2.json", "two", onTransaction) 142 randomAcc, err := keys.NewPrivateKey() 143 require.NoError(t, err) 144 145 bc.SetNotary(ntr1) 146 bc.RegisterPostBlock(func(f func(*transaction.Transaction, *mempool.Pool, bool) bool, pool *mempool.Pool, b *block.Block) { 147 ntr1.PostPersist() 148 }) 149 150 mp1.RunSubscriptions() 151 ntr1.Start() 152 t.Cleanup(func() { 153 ntr1.Shutdown() 154 mp1.StopSubscriptions() 155 }) 156 157 notaryNodes := []any{acc1.PublicKey().Bytes(), acc2.PrivateKey().PublicKey().Bytes()} 158 designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole", 159 int64(noderoles.P2PNotary), notaryNodes) 160 161 type requester struct { 162 accounts []*wallet.Account 163 m int 164 typ notary.RequestType 165 } 166 createFallbackTx := func(requester *wallet.Account, mainTx *transaction.Transaction, nvbIncrement ...uint32) *transaction.Transaction { 167 fallback := transaction.New([]byte{byte(opcode.RET)}, 2000_0000) 168 fallback.Nonce = nonce 169 nonce++ 170 fallback.SystemFee = 1_0000_0000 171 fallback.ValidUntilBlock = bc.BlockHeight() + 2*nvbDiffFallback 172 fallback.Signers = []transaction.Signer{ 173 { 174 Account: bc.GetNotaryContractScriptHash(), 175 Scopes: transaction.None, 176 }, 177 { 178 Account: requester.ScriptHash(), 179 Scopes: transaction.None, 180 }, 181 } 182 nvb := bc.BlockHeight() + nvbDiffFallback 183 if len(nvbIncrement) != 0 { 184 nvb += nvbIncrement[0] 185 } 186 fallback.Attributes = []transaction.Attribute{ 187 { 188 Type: transaction.NotaryAssistedT, 189 Value: &transaction.NotaryAssisted{NKeys: 0}, 190 }, 191 { 192 Type: transaction.NotValidBeforeT, 193 Value: &transaction.NotValidBefore{Height: nvb}, 194 }, 195 { 196 Type: transaction.ConflictsT, 197 Value: &transaction.Conflicts{Hash: mainTx.Hash()}, 198 }, 199 } 200 fallback.Scripts = []transaction.Witness{ 201 { 202 InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, make([]byte, keys.SignatureLen)...), 203 VerificationScript: []byte{}, 204 }, 205 } 206 err = requester.SignTx(netmode.UnitTestNet, fallback) 207 require.NoError(t, err) 208 return fallback 209 } 210 createMixedRequest := func(requesters []requester, NVBincrements ...uint32) []*payload.P2PNotaryRequest { 211 mainTx := *transaction.New([]byte{byte(opcode.RET)}, 11000000) 212 mainTx.Nonce = nonce 213 nonce++ 214 mainTx.SystemFee = 100000000 215 mainTx.ValidUntilBlock = bc.BlockHeight() + 2*nvbDiffFallback 216 signers := make([]transaction.Signer, len(requesters)+1) 217 var ( 218 nKeys uint8 219 verificationScripts [][]byte 220 ) 221 for i := range requesters { 222 var script []byte 223 switch requesters[i].typ { 224 case notary.Signature: 225 script = requesters[i].accounts[0].PublicKey().GetVerificationScript() 226 nKeys++ 227 case notary.MultiSignature: 228 pubs := make(keys.PublicKeys, len(requesters[i].accounts)) 229 for j, r := range requesters[i].accounts { 230 pubs[j] = r.PublicKey() 231 } 232 script, err = smartcontract.CreateMultiSigRedeemScript(requesters[i].m, pubs) 233 require.NoError(t, err) 234 nKeys += uint8(len(requesters[i].accounts)) 235 } 236 signers[i] = transaction.Signer{ 237 Account: hash.Hash160(script), 238 Scopes: transaction.None, 239 } 240 verificationScripts = append(verificationScripts, script) 241 } 242 signers[len(signers)-1] = transaction.Signer{ 243 Account: bc.GetNotaryContractScriptHash(), 244 Scopes: transaction.None, 245 } 246 mainTx.Signers = signers 247 mainTx.Attributes = []transaction.Attribute{ 248 { 249 Type: transaction.NotaryAssistedT, 250 Value: &transaction.NotaryAssisted{NKeys: nKeys}, 251 }, 252 } 253 payloads := make([]*payload.P2PNotaryRequest, nKeys) 254 plIndex := 0 255 // 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) 256 for i, r := range requesters { 257 for _, acc := range r.accounts { 258 cp := mainTx 259 main := &cp 260 main.Scripts = make([]transaction.Witness, len(requesters)) 261 for j := range main.Scripts { 262 main.Scripts[j].VerificationScript = verificationScripts[j] 263 if i == j { 264 main.Scripts[j].InvocationScript = append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, acc.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), main)...) 265 } 266 } 267 main.Scripts = append(main.Scripts, transaction.Witness{}) // empty Notary witness 268 269 _ = main.Size() // for size update test 270 271 var fallback *transaction.Transaction 272 if len(NVBincrements) == int(nKeys) { 273 fallback = createFallbackTx(acc, main, NVBincrements[plIndex]) 274 } else { 275 fallback = createFallbackTx(acc, main) 276 } 277 278 _ = fallback.Size() // for size update test 279 280 payloads[plIndex] = &payload.P2PNotaryRequest{ 281 MainTransaction: main, 282 FallbackTransaction: fallback, 283 } 284 plIndex++ 285 } 286 } 287 return payloads 288 } 289 checkMainTx := func(t *testing.T, requesters []requester, requests []*payload.P2PNotaryRequest, sentCount int, shouldComplete bool) { 290 nSigs := 0 291 for _, r := range requesters { 292 switch r.typ { 293 case notary.Signature: 294 nSigs++ 295 case notary.MultiSignature: 296 nSigs += r.m 297 } 298 } 299 nSigners := len(requesters) + 1 300 if sentCount >= nSigs && shouldComplete { 301 completedTx := getCompletedTx(t, true, requests[0].MainTransaction.Hash()) 302 require.Equal(t, nSigners, len(completedTx.Signers)) 303 require.Equal(t, nSigners, len(completedTx.Scripts)) 304 305 // check that tx size was updated 306 require.Equal(t, io.GetVarSize(completedTx), completedTx.Size()) 307 308 for i := 0; i < len(completedTx.Scripts)-1; i++ { 309 _, err := bc.VerifyWitness(completedTx.Signers[i].Account, completedTx, &completedTx.Scripts[i], -1) 310 require.NoError(t, err) 311 } 312 require.Equal(t, transaction.Witness{ 313 InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, acc1.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), requests[0].MainTransaction)...), 314 VerificationScript: []byte{}, 315 }, completedTx.Scripts[len(completedTx.Scripts)-1]) 316 } else { 317 completedTx := getCompletedTx(t, false, requests[0].MainTransaction.Hash()) 318 require.Nil(t, completedTx, fmt.Errorf("main transaction shouldn't be completed: sent %d out of %d requests", sentCount, nSigs)) 319 } 320 } 321 checkFallbackTxs := func(t *testing.T, requests []*payload.P2PNotaryRequest, shouldComplete bool) { 322 for i, req := range requests { 323 if shouldComplete { 324 completedTx := getCompletedTx(t, true, req.FallbackTransaction.Hash()) 325 require.Equal(t, 2, len(completedTx.Signers)) 326 require.Equal(t, 2, len(completedTx.Scripts)) 327 require.Equal(t, transaction.Witness{ 328 InvocationScript: append([]byte{byte(opcode.PUSHDATA1), keys.SignatureLen}, acc1.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), req.FallbackTransaction)...), 329 VerificationScript: []byte{}, 330 }, completedTx.Scripts[0]) 331 332 // check that tx size was updated 333 require.Equal(t, io.GetVarSize(completedTx), completedTx.Size()) 334 335 _, err := bc.VerifyWitness(completedTx.Signers[1].Account, completedTx, &completedTx.Scripts[1], -1) 336 require.NoError(t, err) 337 } else { 338 completedTx := getCompletedTx(t, false, req.FallbackTransaction.Hash()) 339 require.Nil(t, completedTx, fmt.Errorf("fallback transaction for request #%d shouldn't be completed", i)) 340 } 341 } 342 } 343 checkCompleteStandardRequest := func(t *testing.T, nKeys int, shouldComplete bool, nvbIncrements ...uint32) ([]*payload.P2PNotaryRequest, []requester) { 344 requesters := make([]requester, nKeys) 345 for i := range requesters { 346 acc, _ := wallet.NewAccount() 347 requesters[i] = requester{ 348 accounts: []*wallet.Account{acc}, 349 typ: notary.Signature, 350 } 351 } 352 353 requests := createMixedRequest(requesters, nvbIncrements...) 354 sendOrder := make([]int, nKeys) 355 for i := range sendOrder { 356 sendOrder[i] = i 357 } 358 rand.Shuffle(nKeys, func(i, j int) { 359 sendOrder[j], sendOrder[i] = sendOrder[i], sendOrder[j] 360 }) 361 for i := range requests { 362 ntr1.OnNewRequest(requests[sendOrder[i]]) 363 checkMainTx(t, requesters, requests, i+1, shouldComplete) 364 completedCount := len(completedTxes) 365 366 // check that the same request won't be processed twice 367 ntr1.OnNewRequest(dupNotaryRequest(t, requests[sendOrder[i]])) 368 checkMainTx(t, requesters, requests, i+1, shouldComplete) 369 require.Equal(t, completedCount, len(completedTxes)) 370 } 371 return requests, requesters 372 } 373 checkCompleteMultisigRequest := func(t *testing.T, nSigs int, nKeys int, shouldComplete bool) ([]*payload.P2PNotaryRequest, []requester) { 374 accounts := make([]*wallet.Account, nKeys) 375 for i := range accounts { 376 accounts[i], _ = wallet.NewAccount() 377 } 378 requesters := []requester{ 379 { 380 accounts: accounts, 381 m: nSigs, 382 typ: notary.MultiSignature, 383 }, 384 } 385 requests := createMixedRequest(requesters) 386 sendOrder := make([]int, nKeys) 387 for i := range sendOrder { 388 sendOrder[i] = i 389 } 390 rand.Shuffle(nKeys, func(i, j int) { 391 sendOrder[j], sendOrder[i] = sendOrder[i], sendOrder[j] 392 }) 393 394 var submittedRequests []*payload.P2PNotaryRequest 395 // sent only nSigs (m out of n) requests - it should be enough to complete min tx 396 for i := 0; i < nSigs; i++ { 397 submittedRequests = append(submittedRequests, requests[sendOrder[i]]) 398 399 ntr1.OnNewRequest(requests[sendOrder[i]]) 400 checkMainTx(t, requesters, submittedRequests, i+1, shouldComplete) 401 402 // check that the same request won't be processed twice 403 ntr1.OnNewRequest(dupNotaryRequest(t, requests[sendOrder[i]])) 404 checkMainTx(t, requesters, submittedRequests, i+1, shouldComplete) 405 } 406 407 // sent the rest (n-m) out of n requests: main tx is already collected, so only fallbacks should be applied 408 completedCount := len(completedTxes) 409 for i := nSigs; i < nKeys; i++ { 410 submittedRequests = append(submittedRequests, requests[sendOrder[i]]) 411 412 ntr1.OnNewRequest(requests[sendOrder[i]]) 413 checkMainTx(t, requesters, submittedRequests, i+1, shouldComplete) 414 require.Equal(t, completedCount, len(completedTxes)) 415 } 416 417 return submittedRequests, requesters 418 } 419 420 checkCompleteMixedRequest := func(t *testing.T, nSigSigners int, shouldComplete bool) ([]*payload.P2PNotaryRequest, []requester) { 421 requesters := make([]requester, nSigSigners) 422 for i := range requesters { 423 acc, _ := wallet.NewAccount() 424 requesters[i] = requester{ 425 accounts: []*wallet.Account{acc}, 426 typ: notary.Signature, 427 } 428 } 429 multisigAccounts := make([]*wallet.Account, 3) 430 for i := range multisigAccounts { 431 multisigAccounts[i], _ = wallet.NewAccount() 432 } 433 434 requesters = append(requesters, requester{ 435 accounts: multisigAccounts, 436 m: 2, 437 typ: notary.MultiSignature, 438 }) 439 440 requests := createMixedRequest(requesters) 441 for i := range requests { 442 ntr1.OnNewRequest(requests[i]) 443 checkMainTx(t, requesters, requests, i+1, shouldComplete) 444 completedCount := len(completedTxes) 445 446 // check that the same request won't be processed twice 447 ntr1.OnNewRequest(dupNotaryRequest(t, requests[i])) 448 checkMainTx(t, requesters, requests, i+1, shouldComplete) 449 require.Equal(t, completedCount, len(completedTxes)) 450 } 451 return requests, requesters 452 } 453 454 // OnNewRequest: missing account 455 ntr1.UpdateNotaryNodes(keys.PublicKeys{randomAcc.PublicKey()}) 456 r, _ := checkCompleteStandardRequest(t, 1, false) 457 checkFallbackTxs(t, r, false) 458 // set account back for the next tests 459 ntr1.UpdateNotaryNodes(keys.PublicKeys{acc1.PublicKey()}) 460 461 // OnNewRequest: signature request 462 for _, i := range []int{1, 2, 3, 10} { 463 r, _ := checkCompleteStandardRequest(t, i, true) 464 checkFallbackTxs(t, r, false) 465 } 466 467 // OnNewRequest: multisignature request 468 r, _ = checkCompleteMultisigRequest(t, 1, 1, true) 469 checkFallbackTxs(t, r, false) 470 r, _ = checkCompleteMultisigRequest(t, 1, 2, true) 471 checkFallbackTxs(t, r, false) 472 r, _ = checkCompleteMultisigRequest(t, 1, 3, true) 473 checkFallbackTxs(t, r, false) 474 r, _ = checkCompleteMultisigRequest(t, 3, 3, true) 475 checkFallbackTxs(t, r, false) 476 r, _ = checkCompleteMultisigRequest(t, 3, 4, true) 477 checkFallbackTxs(t, r, false) 478 r, _ = checkCompleteMultisigRequest(t, 3, 10, true) 479 checkFallbackTxs(t, r, false) 480 481 // OnNewRequest: mixed request 482 r, _ = checkCompleteMixedRequest(t, 1, true) 483 checkFallbackTxs(t, r, false) 484 r, _ = checkCompleteMixedRequest(t, 2, true) 485 checkFallbackTxs(t, r, false) 486 r, _ = checkCompleteMixedRequest(t, 3, true) 487 checkFallbackTxs(t, r, false) 488 // PostPersist: missing account 489 setFinalizeWithError(true) 490 r, requesters := checkCompleteStandardRequest(t, 1, false) 491 checkFallbackTxs(t, r, false) 492 ntr1.UpdateNotaryNodes(keys.PublicKeys{randomAcc.PublicKey()}) 493 setFinalizeWithError(false) 494 495 e.AddNewBlock(t) 496 // Allow a single-block slippage since PostPersist is handled by Notary service via block notification routine. 497 e.AddNewBlock(t) 498 checkMainTx(t, requesters, r, 1, false) 499 checkFallbackTxs(t, r, false) 500 // set account back for the next tests 501 ntr1.UpdateNotaryNodes(keys.PublicKeys{acc1.PublicKey()}) 502 503 // PostPersist: complete main transaction, signature request 504 setFinalizeWithError(true) 505 requests, requesters := checkCompleteStandardRequest(t, 3, false) 506 // check PostPersist with finalisation error 507 setFinalizeWithError(true) 508 e.AddNewBlock(t) 509 // Allow a single-block slippage since PostPersist is handled by Notary service via block notification routine. 510 e.AddNewBlock(t) 511 checkMainTx(t, requesters, requests, len(requests), false) 512 // check PostPersist without finalisation error 513 setFinalizeWithError(false) 514 e.AddNewBlock(t) 515 // Allow a single-block slippage since PostPersist is handled by Notary service via block notification routine. 516 e.AddNewBlock(t) 517 checkMainTx(t, requesters, requests, len(requests), true) 518 519 // PostPersist: complete main transaction, multisignature account 520 setFinalizeWithError(true) 521 requests, requesters = checkCompleteMultisigRequest(t, 3, 4, false) 522 checkFallbackTxs(t, requests, false) 523 // check PostPersist with finalisation error 524 setFinalizeWithError(true) 525 e.AddNewBlock(t) 526 // Allow a single-block slippage since PostPersist is handled by Notary service via block notification routine. 527 e.AddNewBlock(t) 528 checkMainTx(t, requesters, requests, len(requests), false) 529 checkFallbackTxs(t, requests, false) 530 // check PostPersist without finalisation error 531 setFinalizeWithError(false) 532 e.AddNewBlock(t) 533 // Allow a single-block slippage since PostPersist is handled by Notary service via block notification routine. 534 e.AddNewBlock(t) 535 checkMainTx(t, requesters, requests, len(requests), true) 536 checkFallbackTxs(t, requests, false) 537 538 // PostPersist: complete fallback, signature request 539 setFinalizeWithError(true) 540 requests, requesters = checkCompleteStandardRequest(t, 3, false) 541 checkFallbackTxs(t, requests, false) 542 // make fallbacks valid 543 e.GenerateNewBlocks(t, int(nvbDiffFallback+1)) 544 require.NoError(t, err) 545 // check PostPersist for valid fallbacks with finalisation error 546 e.AddNewBlock(t) 547 // Allow a single-block slippage since PostPersist is handled by Notary service via block notification routine. 548 e.AddNewBlock(t) 549 checkMainTx(t, requesters, requests, len(requests), false) 550 checkFallbackTxs(t, requests, false) 551 // check PostPersist for valid fallbacks without finalisation error 552 setFinalizeWithError(false) 553 e.AddNewBlock(t) 554 // Allow a single-block slippage since PostPersist is handled by Notary service via block notification routine. 555 e.AddNewBlock(t) 556 checkMainTx(t, requesters, requests, len(requests), false) 557 checkFallbackTxs(t, requests, true) 558 559 // PostPersist: complete fallback, multisignature request 560 nSigs, nKeys := 3, 5 561 // check OnNewRequest with finalization error 562 setFinalizeWithError(true) 563 requests, requesters = checkCompleteMultisigRequest(t, nSigs, nKeys, false) 564 checkFallbackTxs(t, requests, false) 565 // make fallbacks valid 566 e.GenerateNewBlocks(t, int(nvbDiffFallback+1)) 567 require.NoError(t, err) 568 // check PostPersist for valid fallbacks with finalisation error 569 e.AddNewBlock(t) 570 // Allow a single-block slippage since PostPersist is handled by Notary service via block notification routine. 571 e.AddNewBlock(t) 572 checkMainTx(t, requesters, requests, len(requests), false) 573 checkFallbackTxs(t, requests, false) 574 // check PostPersist for valid fallbacks without finalisation error 575 setFinalizeWithError(false) 576 e.AddNewBlock(t) 577 // Allow a single-block slippage since PostPersist is handled by Notary service via block notification routine. 578 e.AddNewBlock(t) 579 checkMainTx(t, requesters, requests, len(requests), false) 580 checkFallbackTxs(t, requests[:nSigs], true) 581 // the rest of fallbacks should also be applied even if the main tx was already constructed by the moment they were sent 582 checkFallbackTxs(t, requests[nSigs:], true) 583 584 // PostPersist: partial fallbacks completion due to finalisation errors 585 setFinalizeWithError(true) 586 requests, requesters = checkCompleteStandardRequest(t, 5, false) 587 checkFallbackTxs(t, requests, false) 588 // make fallbacks valid 589 e.GenerateNewBlocks(t, int(nvbDiffFallback+1)) 590 require.NoError(t, err) 591 // some of fallbacks should fail finalisation 592 unluckies = []*payload.P2PNotaryRequest{requests[0], requests[4]} 593 lucky := requests[1:4] 594 setChoosy(true) 595 // check PostPersist for lucky fallbacks 596 e.AddNewBlock(t) 597 // Allow a single-block slippage since PostPersist is handled by Notary service via block notification routine. 598 e.AddNewBlock(t) 599 checkMainTx(t, requesters, requests, len(requests), false) 600 checkFallbackTxs(t, lucky, true) 601 checkFallbackTxs(t, unluckies, false) 602 // reset finalisation function for unlucky fallbacks to finalise without an error 603 setChoosy(false) 604 setFinalizeWithError(false) 605 // check PostPersist for unlucky fallbacks 606 e.AddNewBlock(t) 607 // Allow a single-block slippage since PostPersist is handled by Notary service via block notification routine. 608 e.AddNewBlock(t) 609 checkMainTx(t, requesters, requests, len(requests), false) 610 checkFallbackTxs(t, lucky, true) 611 checkFallbackTxs(t, unluckies, true) 612 613 // PostPersist: different NVBs 614 // check OnNewRequest with finalization error and different NVBs 615 setFinalizeWithError(true) 616 // Introduce some slippage between first and second fallback NVBs in order to avoid possible race caused by early 617 // first fallback transaction acceptance. The rest of fallbacks follow X+4 NVB pattern for testing code shortness. 618 requests, requesters = checkCompleteStandardRequest(t, 5, false, 1, 7, 11, 15, 19) 619 checkFallbackTxs(t, requests, false) 620 // generate blocks to reach the most earlier fallback's NVB 621 // Here and below add +1 slippage to ensure that PostPersist for (nvbDiffFallback+1) height is properly handled, i.e. 622 // to exclude race condition when main transaction is finalized between `finalizeWithError` disabling and new block addition. 623 e.GenerateNewBlocks(t, int((nvbDiffFallback+1)+1)) 624 require.NoError(t, err) 625 // check PostPersist for valid fallbacks without finalisation error 626 setFinalizeWithError(false) 627 for i := range requests { 628 e.AddNewBlock(t) 629 e.AddNewBlock(t) 630 e.AddNewBlock(t) 631 e.AddNewBlock(t) 632 633 checkMainTx(t, requesters, requests, len(requests), false) 634 checkFallbackTxs(t, requests[:i+1], true) 635 checkFallbackTxs(t, requests[i+1:], false) 636 } 637 638 // OnRequestRemoval: missing account 639 // check OnNewRequest with finalization error 640 setFinalizeWithError(true) 641 requests, requesters = checkCompleteStandardRequest(t, 4, false) 642 checkFallbackTxs(t, requests, false) 643 // make fallbacks valid and remove one fallback 644 e.GenerateNewBlocks(t, int(nvbDiffFallback+1)) 645 require.NoError(t, err) 646 ntr1.UpdateNotaryNodes(keys.PublicKeys{randomAcc.PublicKey()}) 647 ntr1.OnRequestRemoval(requests[3]) 648 // non of the fallbacks should be completed 649 setFinalizeWithError(false) 650 e.AddNewBlock(t) 651 // Allow a single-block slippage since PostPersist is handled by Notary service via block notification routine. 652 e.AddNewBlock(t) 653 checkMainTx(t, requesters, requests, len(requests), false) 654 checkFallbackTxs(t, requests, false) 655 // set account back for the next tests 656 ntr1.UpdateNotaryNodes(keys.PublicKeys{acc1.PublicKey()}) 657 658 // OnRequestRemoval: signature request, remove one fallback 659 // check OnNewRequest with finalization error 660 setFinalizeWithError(true) 661 requests, requesters = checkCompleteStandardRequest(t, 4, false) 662 checkFallbackTxs(t, requests, false) 663 // make fallbacks valid and remove one fallback 664 e.GenerateNewBlocks(t, int(nvbDiffFallback+1)) 665 require.NoError(t, err) 666 unlucky := requests[3] 667 ntr1.OnRequestRemoval(unlucky) 668 // rest of the fallbacks should be completed 669 setFinalizeWithError(false) 670 e.AddNewBlock(t) 671 // Allow a single-block slippage since PostPersist is handled by Notary service via block notification routine. 672 e.AddNewBlock(t) 673 checkMainTx(t, requesters, requests, len(requests), false) 674 checkFallbackTxs(t, requests[:3], true) 675 require.Nil(t, completedTxes[unlucky.FallbackTransaction.Hash()]) 676 677 // OnRequestRemoval: signature request, remove all fallbacks 678 setFinalizeWithError(true) 679 requests, requesters = checkCompleteStandardRequest(t, 4, false) 680 // remove all fallbacks 681 e.GenerateNewBlocks(t, int(nvbDiffFallback+1)) 682 require.NoError(t, err) 683 for i := range requests { 684 ntr1.OnRequestRemoval(requests[i]) 685 } 686 // then the whole request should be removed, i.e. there are no completed transactions 687 setFinalizeWithError(false) 688 e.AddNewBlock(t) 689 // Allow a single-block slippage since PostPersist is handled by Notary service via block notification routine. 690 e.AddNewBlock(t) 691 checkMainTx(t, requesters, requests, len(requests), false) 692 checkFallbackTxs(t, requests, false) 693 694 // OnRequestRemoval: signature request, remove unexisting fallback 695 ntr1.OnRequestRemoval(requests[0]) 696 e.AddNewBlock(t) 697 // Allow a single-block slippage since PostPersist is handled by Notary service via block notification routine. 698 e.AddNewBlock(t) 699 checkMainTx(t, requesters, requests, len(requests), false) 700 checkFallbackTxs(t, requests, false) 701 702 // OnRequestRemoval: multisignature request, remove one fallback 703 nSigs, nKeys = 3, 5 704 // check OnNewRequest with finalization error 705 setFinalizeWithError(true) 706 requests, requesters = checkCompleteMultisigRequest(t, nSigs, nKeys, false) 707 checkMainTx(t, requesters, requests, len(requests), false) 708 checkFallbackTxs(t, requests, false) 709 // make fallbacks valid and remove the last fallback 710 e.GenerateNewBlocks(t, int(nvbDiffFallback+1)) 711 require.NoError(t, err) 712 unlucky = requests[nSigs-1] 713 ntr1.OnRequestRemoval(unlucky) 714 // then (m-1) out of n fallbacks should be completed 715 setFinalizeWithError(false) 716 e.AddNewBlock(t) 717 // Allow a single-block slippage since PostPersist is handled by Notary service via block notification routine. 718 e.AddNewBlock(t) 719 checkMainTx(t, requesters, requests, len(requests), false) 720 checkFallbackTxs(t, requests[:nSigs-1], true) 721 require.Nil(t, completedTxes[unlucky.FallbackTransaction.Hash()]) 722 // 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 723 checkFallbackTxs(t, requests[nSigs:], true) 724 725 // OnRequestRemoval: multisignature request, remove all fallbacks 726 setFinalizeWithError(true) 727 requests, requesters = checkCompleteMultisigRequest(t, nSigs, nKeys, false) 728 // make fallbacks valid and then remove all of them 729 e.GenerateNewBlocks(t, int(nvbDiffFallback+1)) 730 require.NoError(t, err) 731 for i := range requests { 732 ntr1.OnRequestRemoval(requests[i]) 733 } 734 // then the whole request should be removed, i.e. there are no completed transactions 735 setFinalizeWithError(false) 736 e.AddNewBlock(t) 737 // Allow a single-block slippage since PostPersist is handled by Notary service via block notification routine. 738 e.AddNewBlock(t) 739 checkMainTx(t, requesters, requests, len(requests), false) 740 checkFallbackTxs(t, requests, false) 741 742 // // OnRequestRemoval: multisignature request, remove unexisting fallbac, i.e. there still shouldn't be any completed transactions after this 743 ntr1.OnRequestRemoval(requests[0]) 744 e.AddNewBlock(t) 745 // Allow a single-block slippage since PostPersist is handled by Notary service via block notification routine. 746 e.AddNewBlock(t) 747 checkMainTx(t, requesters, requests, len(requests), false) 748 checkFallbackTxs(t, requests, false) 749 750 // Subscriptions test 751 setFinalizeWithError(false) 752 requester1, _ := wallet.NewAccount() 753 requester2, _ := wallet.NewAccount() 754 amount := int64(100_0000_0000) 755 gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), bc.GetNotaryContractScriptHash(), amount, []any{requester1.ScriptHash(), int64(bc.BlockHeight() + 50)}) 756 e.CheckGASBalance(t, notaryHash, big.NewInt(amount)) 757 gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), bc.GetNotaryContractScriptHash(), amount, []any{requester2.ScriptHash(), int64(bc.BlockHeight() + 50)}) 758 e.CheckGASBalance(t, notaryHash, big.NewInt(2*amount)) 759 760 // create request for 2 standard signatures => main tx should be completed after the second request is added to the pool 761 requests = createMixedRequest([]requester{ 762 { 763 accounts: []*wallet.Account{requester1}, 764 typ: notary.Signature, 765 }, 766 { 767 accounts: []*wallet.Account{requester2}, 768 typ: notary.Signature, 769 }, 770 }) 771 feer := network.NewNotaryFeer(bc) 772 require.NoError(t, mp1.Add(requests[0].FallbackTransaction, feer, requests[0])) 773 require.NoError(t, mp1.Add(requests[1].FallbackTransaction, feer, requests[1])) 774 require.Eventually(t, func() bool { 775 mtx.RLock() 776 defer mtx.RUnlock() 777 return completedTxes[requests[0].MainTransaction.Hash()] != nil 778 }, 3*time.Second, 100*time.Millisecond) 779 checkFallbackTxs(t, requests, false) 780 } 781 782 func TestNotary_GenesisRoles(t *testing.T) { 783 const ( 784 notaryPath = "./testdata/notary1.json" 785 notaryPass = "one" 786 ) 787 788 w, err := wallet.NewWalletFromFile(notaryPath) 789 require.NoError(t, err) 790 require.NoError(t, w.Accounts[0].Decrypt(notaryPass, w.Scrypt)) 791 acc := w.Accounts[0] 792 793 bc, _, _ := chain.NewMultiWithCustomConfig(t, func(c *config.Blockchain) { 794 c.P2PSigExtensions = true 795 c.Genesis.Roles = map[noderoles.Role]keys.PublicKeys{ 796 noderoles.P2PNotary: {acc.PublicKey()}, 797 } 798 }) 799 800 _, ntr, _ := getTestNotary(t, bc, "./testdata/notary1.json", "one", func(tx *transaction.Transaction) error { return nil }) 801 require.False(t, ntr.IsAuthorized()) 802 803 bc.SetNotary(ntr) 804 require.True(t, ntr.IsAuthorized()) 805 }