github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/rpcclient/notary/actor_test.go (about) 1 package notary 2 3 import ( 4 "context" 5 "errors" 6 "testing" 7 8 "github.com/google/uuid" 9 "github.com/nspcc-dev/neo-go/pkg/config/netmode" 10 "github.com/nspcc-dev/neo-go/pkg/core/state" 11 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 12 "github.com/nspcc-dev/neo-go/pkg/crypto/keys" 13 "github.com/nspcc-dev/neo-go/pkg/encoding/address" 14 "github.com/nspcc-dev/neo-go/pkg/neorpc/result" 15 "github.com/nspcc-dev/neo-go/pkg/network/payload" 16 "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" 17 "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" 18 "github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter" 19 "github.com/nspcc-dev/neo-go/pkg/smartcontract" 20 "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" 21 "github.com/nspcc-dev/neo-go/pkg/util" 22 "github.com/nspcc-dev/neo-go/pkg/vm/opcode" 23 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 24 "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" 25 "github.com/nspcc-dev/neo-go/pkg/wallet" 26 "github.com/stretchr/testify/require" 27 ) 28 29 type RPCClient struct { 30 err error 31 invRes *result.Invoke 32 netFee int64 33 bCount uint32 34 version *result.Version 35 hash util.Uint256 36 nhash util.Uint256 37 mirror bool 38 applog *result.ApplicationLog 39 } 40 41 func (r *RPCClient) InvokeContractVerify(contract util.Uint160, params []smartcontract.Parameter, signers []transaction.Signer, witnesses ...transaction.Witness) (*result.Invoke, error) { 42 return r.invRes, r.err 43 } 44 func (r *RPCClient) InvokeFunction(contract util.Uint160, operation string, params []smartcontract.Parameter, signers []transaction.Signer) (*result.Invoke, error) { 45 return r.invRes, r.err 46 } 47 func (r *RPCClient) InvokeScript(script []byte, signers []transaction.Signer) (*result.Invoke, error) { 48 return r.invRes, r.err 49 } 50 func (r *RPCClient) CalculateNetworkFee(tx *transaction.Transaction) (int64, error) { 51 return r.netFee, r.err 52 } 53 func (r *RPCClient) GetBlockCount() (uint32, error) { 54 return r.bCount, r.err 55 } 56 func (r *RPCClient) GetVersion() (*result.Version, error) { 57 verCopy := *r.version 58 return &verCopy, r.err 59 } 60 func (r *RPCClient) SendRawTransaction(tx *transaction.Transaction) (util.Uint256, error) { 61 return r.hash, r.err 62 } 63 func (r *RPCClient) SubmitP2PNotaryRequest(req *payload.P2PNotaryRequest) (util.Uint256, error) { 64 if r.mirror { 65 return req.FallbackTransaction.Hash(), nil 66 } 67 return r.nhash, r.err 68 } 69 func (r *RPCClient) TerminateSession(sessionID uuid.UUID) (bool, error) { 70 return false, nil // Just a stub, unused by actor. 71 } 72 func (r *RPCClient) TraverseIterator(sessionID, iteratorID uuid.UUID, maxItemsCount int) ([]stackitem.Item, error) { 73 return nil, nil // Just a stub, unused by actor. 74 } 75 func (r *RPCClient) Context() context.Context { 76 return context.Background() 77 } 78 func (r *RPCClient) GetApplicationLog(hash util.Uint256, trig *trigger.Type) (*result.ApplicationLog, error) { 79 return r.applog, nil 80 } 81 82 var _ = waiter.RPCPollingBased(&RPCClient{}) 83 84 func TestNewActor(t *testing.T) { 85 rc := &RPCClient{ 86 version: &result.Version{ 87 Protocol: result.Protocol{ 88 Network: netmode.UnitTestNet, 89 MillisecondsPerBlock: 1000, 90 ValidatorsCount: 7, 91 }, 92 }, 93 } 94 95 _, err := NewActor(rc, nil, nil) 96 require.Error(t, err) 97 98 var ( 99 keyz [4]*keys.PrivateKey 100 accs [4]*wallet.Account 101 faccs [4]*wallet.Account 102 pkeys [4]*keys.PublicKey 103 ) 104 for i := range accs { 105 keyz[i], err = keys.NewPrivateKey() 106 require.NoError(t, err) 107 accs[i] = wallet.NewAccountFromPrivateKey(keyz[i]) 108 pkeys[i] = keyz[i].PublicKey() 109 faccs[i] = FakeSimpleAccount(pkeys[i]) 110 } 111 var multiAccs [4]*wallet.Account 112 for i := range accs { 113 multiAccs[i] = &wallet.Account{} 114 *multiAccs[i] = *accs[i] 115 require.NoError(t, multiAccs[i].ConvertMultisig(smartcontract.GetDefaultHonestNodeCount(len(pkeys)), pkeys[:])) 116 } 117 118 // nil Contract 119 badMultiAcc0 := &wallet.Account{} 120 *badMultiAcc0 = *multiAccs[0] 121 badMultiAcc0.Contract = nil 122 _, err = NewActor(rc, []actor.SignerAccount{{ 123 Signer: transaction.Signer{ 124 Account: multiAccs[0].Contract.ScriptHash(), 125 Scopes: transaction.CalledByEntry, 126 }, 127 Account: badMultiAcc0, 128 }}, accs[0]) 129 require.Error(t, err) 130 131 // Non-standard script. 132 badMultiAcc0.Contract = &wallet.Contract{} 133 *badMultiAcc0.Contract = *multiAccs[0].Contract 134 badMultiAcc0.Contract.Script = append(badMultiAcc0.Contract.Script, byte(opcode.NOP)) 135 badMultiAcc0.Address = address.Uint160ToString(badMultiAcc0.Contract.ScriptHash()) 136 _, err = NewActor(rc, []actor.SignerAccount{{ 137 Signer: transaction.Signer{ 138 Account: badMultiAcc0.Contract.ScriptHash(), 139 Scopes: transaction.CalledByEntry, 140 }, 141 Account: badMultiAcc0, 142 }}, accs[0]) 143 require.Error(t, err) 144 145 // Too many keys 146 var ( 147 manyKeys [256]*keys.PrivateKey 148 manyPkeys [256]*keys.PublicKey 149 ) 150 for i := range manyKeys { 151 manyKeys[i], err = keys.NewPrivateKey() 152 require.NoError(t, err) 153 manyPkeys[i] = manyKeys[i].PublicKey() 154 } 155 bigMultiAcc := &wallet.Account{} 156 *bigMultiAcc = *wallet.NewAccountFromPrivateKey(manyKeys[0]) 157 require.NoError(t, bigMultiAcc.ConvertMultisig(129, manyPkeys[:])) 158 159 _, err = NewActor(rc, []actor.SignerAccount{{ 160 Signer: transaction.Signer{ 161 Account: bigMultiAcc.Contract.ScriptHash(), 162 Scopes: transaction.CalledByEntry, 163 }, 164 Account: bigMultiAcc, 165 }}, wallet.NewAccountFromPrivateKey(manyKeys[0])) 166 require.Error(t, err) 167 168 // No contract in the simple account. 169 badSimple0 := &wallet.Account{} 170 *badSimple0 = *accs[0] 171 badSimple0.Contract = nil 172 _, err = NewActor(rc, []actor.SignerAccount{{ 173 Signer: transaction.Signer{ 174 Account: multiAccs[0].Contract.ScriptHash(), 175 Scopes: transaction.CalledByEntry, 176 }, 177 Account: multiAccs[0], 178 }}, badSimple0) 179 require.Error(t, err) 180 181 // Simple account that can't sign. 182 badSimple0 = FakeSimpleAccount(pkeys[0]) 183 _, err = NewActor(rc, []actor.SignerAccount{{ 184 Signer: transaction.Signer{ 185 Account: multiAccs[0].Contract.ScriptHash(), 186 Scopes: transaction.CalledByEntry, 187 }, 188 Account: multiAccs[0], 189 }}, badSimple0) 190 require.Error(t, err) 191 192 // Multisig account instead of simple one. 193 _, err = NewActor(rc, []actor.SignerAccount{{ 194 Signer: transaction.Signer{ 195 Account: multiAccs[0].Contract.ScriptHash(), 196 Scopes: transaction.CalledByEntry, 197 }, 198 Account: multiAccs[0], 199 }}, multiAccs[0]) 200 require.Error(t, err) 201 202 // Main actor freaking out on hash mismatch. 203 _, err = NewActor(rc, []actor.SignerAccount{{ 204 Signer: transaction.Signer{ 205 Account: accs[0].Contract.ScriptHash(), 206 Scopes: transaction.CalledByEntry, 207 }, 208 Account: multiAccs[0], 209 }}, accs[0]) 210 require.Error(t, err) 211 212 // FB actor freaking out on hash mismatch. 213 opts := NewDefaultActorOptions(NewReader(invoker.New(rc, nil)), accs[0]) 214 opts.FbSigner.Signer.Account = multiAccs[0].Contract.ScriptHash() 215 _, err = NewTunedActor(rc, []actor.SignerAccount{{ 216 Signer: transaction.Signer{ 217 Account: multiAccs[0].Contract.ScriptHash(), 218 Scopes: transaction.CalledByEntry, 219 }, 220 Account: multiAccs[0], 221 }}, opts) 222 require.Error(t, err) 223 224 // Good, one multisig. 225 multi0, err := NewActor(rc, []actor.SignerAccount{{ 226 Signer: transaction.Signer{ 227 Account: multiAccs[0].Contract.ScriptHash(), 228 Scopes: transaction.CalledByEntry, 229 }, 230 Account: multiAccs[0], 231 }}, accs[0]) 232 require.NoError(t, err) 233 234 script := []byte{byte(opcode.RET)} 235 rc.invRes = &result.Invoke{ 236 State: "HALT", 237 GasConsumed: 3, 238 Script: script, 239 Stack: []stackitem.Item{stackitem.Make(42)}, 240 } 241 tx, err := multi0.MakeRun(script) 242 require.NoError(t, err) 243 require.Equal(t, 1, len(tx.Attributes)) 244 require.Equal(t, transaction.NotaryAssistedT, tx.Attributes[0].Type) 245 require.Equal(t, &transaction.NotaryAssisted{NKeys: 4}, tx.Attributes[0].Value) 246 247 // Good, 4 single sigs with one that can sign and one contract. 248 single4, err := NewActor(rc, []actor.SignerAccount{{ 249 Signer: transaction.Signer{ 250 Account: accs[0].Contract.ScriptHash(), 251 Scopes: transaction.CalledByEntry, 252 }, 253 Account: accs[0], 254 }, { 255 Signer: transaction.Signer{ 256 Account: faccs[1].Contract.ScriptHash(), 257 Scopes: transaction.CalledByEntry, 258 }, 259 Account: faccs[1], 260 }, { 261 Signer: transaction.Signer{ 262 Account: faccs[2].Contract.ScriptHash(), 263 Scopes: transaction.CalledByEntry, 264 }, 265 Account: faccs[2], 266 }, { 267 Signer: transaction.Signer{ 268 Account: accs[3].Contract.ScriptHash(), 269 Scopes: transaction.CalledByEntry, 270 }, 271 Account: faccs[3], 272 }, { 273 Signer: transaction.Signer{ 274 Account: util.Uint160{1, 2, 3}, 275 Scopes: transaction.CalledByEntry, 276 }, 277 Account: FakeContractAccount(util.Uint160{1, 2, 3}), 278 }}, accs[0]) 279 require.NoError(t, err) 280 281 tx, err = single4.MakeRun(script) 282 require.NoError(t, err) 283 require.Equal(t, 1, len(tx.Attributes)) 284 require.Equal(t, transaction.NotaryAssistedT, tx.Attributes[0].Type) 285 require.Equal(t, &transaction.NotaryAssisted{NKeys: 4}, tx.Attributes[0].Value) // One account can sign, three need to collect additional sigs. 286 } 287 288 func TestSendRequestExactly(t *testing.T) { 289 rc := &RPCClient{ 290 version: &result.Version{ 291 Protocol: result.Protocol{ 292 Network: netmode.UnitTestNet, 293 MillisecondsPerBlock: 1000, 294 ValidatorsCount: 7, 295 }, 296 }, 297 } 298 299 key0, err := keys.NewPrivateKey() 300 require.NoError(t, err) 301 key1, err := keys.NewPrivateKey() 302 require.NoError(t, err) 303 304 acc0 := wallet.NewAccountFromPrivateKey(key0) 305 facc1 := FakeSimpleAccount(key1.PublicKey()) 306 307 act, err := NewActor(rc, []actor.SignerAccount{{ 308 Signer: transaction.Signer{ 309 Account: acc0.Contract.ScriptHash(), 310 Scopes: transaction.None, 311 }, 312 Account: acc0, 313 }, { 314 Signer: transaction.Signer{ 315 Account: facc1.Contract.ScriptHash(), 316 Scopes: transaction.CalledByEntry, 317 }, 318 Account: facc1, 319 }}, acc0) 320 require.NoError(t, err) 321 322 script := []byte{byte(opcode.RET)} 323 mainTx := transaction.New(script, 1) 324 fbTx := transaction.New(script, 1) 325 326 // Hashes mismatch 327 _, _, _, err = act.SendRequestExactly(mainTx, fbTx) 328 require.Error(t, err) 329 330 // Error returned 331 rc.err = errors.New("") 332 _, _, _, err = act.SendRequestExactly(mainTx, fbTx) 333 require.Error(t, err) 334 335 // OK returned 336 rc.err = nil 337 rc.nhash = fbTx.Hash() 338 mHash, fbHash, vub, err := act.SendRequestExactly(mainTx, fbTx) 339 require.NoError(t, err) 340 require.Equal(t, mainTx.Hash(), mHash) 341 require.Equal(t, fbTx.Hash(), fbHash) 342 require.Equal(t, mainTx.ValidUntilBlock, vub) 343 } 344 345 func TestSendRequest(t *testing.T) { 346 rc := &RPCClient{ 347 version: &result.Version{ 348 Protocol: result.Protocol{ 349 Network: netmode.UnitTestNet, 350 MillisecondsPerBlock: 1000, 351 ValidatorsCount: 7, 352 }, 353 }, 354 bCount: 42, 355 } 356 357 key0, err := keys.NewPrivateKey() 358 require.NoError(t, err) 359 key1, err := keys.NewPrivateKey() 360 require.NoError(t, err) 361 362 acc0 := wallet.NewAccountFromPrivateKey(key0) 363 facc0 := FakeSimpleAccount(key0.PublicKey()) 364 facc1 := FakeSimpleAccount(key1.PublicKey()) 365 366 act, err := NewActor(rc, []actor.SignerAccount{{ 367 Signer: transaction.Signer{ 368 Account: acc0.Contract.ScriptHash(), 369 Scopes: transaction.None, 370 }, 371 Account: acc0, 372 }, { 373 Signer: transaction.Signer{ 374 Account: facc1.Contract.ScriptHash(), 375 Scopes: transaction.CalledByEntry, 376 }, 377 Account: facc1, 378 }}, acc0) 379 require.NoError(t, err) 380 381 script := []byte{byte(opcode.RET)} 382 rc.invRes = &result.Invoke{ 383 State: "HALT", 384 GasConsumed: 3, 385 Script: script, 386 Stack: []stackitem.Item{stackitem.Make(42)}, 387 } 388 389 mainTx, err := act.MakeRun(script) 390 require.NoError(t, err) 391 392 // No attributes. 393 fbTx, err := act.FbActor.MakeUnsignedRun(script, nil) 394 require.NoError(t, err) 395 fbTx.Attributes = nil 396 _, _, _, err = act.SendRequest(mainTx, fbTx) 397 require.Error(t, err) 398 399 // Bad NVB. 400 fbTx, err = act.FbActor.MakeUnsignedRun(script, nil) 401 require.NoError(t, err) 402 fbTx.Attributes[1].Type = transaction.HighPriority 403 fbTx.Attributes[1].Value = nil 404 _, _, _, err = act.SendRequest(mainTx, fbTx) 405 require.Error(t, err) 406 407 // Bad Conflicts. 408 fbTx, err = act.FbActor.MakeUnsignedRun(script, nil) 409 require.NoError(t, err) 410 fbTx.Attributes[2].Type = transaction.HighPriority 411 fbTx.Attributes[2].Value = nil 412 _, _, _, err = act.SendRequest(mainTx, fbTx) 413 require.Error(t, err) 414 415 // GetBlockCount error. 416 fbTx, err = act.FbActor.MakeUnsignedRun(script, nil) 417 require.NoError(t, err) 418 rc.err = errors.New("") 419 _, _, _, err = act.SendRequest(mainTx, fbTx) 420 require.Error(t, err) 421 422 // Can't sign suddenly. 423 rc.err = nil 424 acc0Backup := &wallet.Account{} 425 *acc0Backup = *acc0 426 *acc0 = *facc0 427 fbTx, err = act.FbActor.MakeUnsignedRun(script, nil) 428 require.NoError(t, err) 429 _, _, _, err = act.SendRequest(mainTx, fbTx) 430 require.Error(t, err) 431 432 // Good. 433 *acc0 = *acc0Backup 434 fbTx, err = act.FbActor.MakeUnsignedRun(script, nil) 435 require.NoError(t, err) 436 _, _, _, err = act.SendRequest(mainTx, fbTx) 437 require.Error(t, err) 438 } 439 440 func TestNotarize(t *testing.T) { 441 rc := &RPCClient{ 442 version: &result.Version{ 443 Protocol: result.Protocol{ 444 Network: netmode.UnitTestNet, 445 MillisecondsPerBlock: 1000, 446 ValidatorsCount: 7, 447 }, 448 }, 449 bCount: 42, 450 } 451 452 key0, err := keys.NewPrivateKey() 453 require.NoError(t, err) 454 key1, err := keys.NewPrivateKey() 455 require.NoError(t, err) 456 457 acc0 := wallet.NewAccountFromPrivateKey(key0) 458 facc1 := FakeSimpleAccount(key1.PublicKey()) 459 460 act, err := NewActor(rc, []actor.SignerAccount{{ 461 Signer: transaction.Signer{ 462 Account: acc0.Contract.ScriptHash(), 463 Scopes: transaction.None, 464 }, 465 Account: acc0, 466 }, { 467 Signer: transaction.Signer{ 468 Account: facc1.Contract.ScriptHash(), 469 Scopes: transaction.CalledByEntry, 470 }, 471 Account: facc1, 472 }}, acc0) 473 require.NoError(t, err) 474 475 script := []byte{byte(opcode.RET)} 476 477 // Immediate error from MakeRun. 478 rc.invRes = &result.Invoke{ 479 State: "FAULT", 480 GasConsumed: 3, 481 Script: script, 482 Stack: []stackitem.Item{stackitem.Make(42)}, 483 } 484 _, _, _, err = act.Notarize(act.MakeRun(script)) 485 require.Error(t, err) 486 487 // Explicitly good transaction. but failure to create a fallback. 488 rc.invRes.State = "HALT" 489 tx, err := act.MakeRun(script) 490 require.NoError(t, err) 491 492 rc.invRes.State = "FAULT" 493 _, _, _, err = act.Notarize(tx, nil) 494 require.Error(t, err) 495 496 // FB hash mismatch from SendRequestExactly. 497 rc.invRes.State = "HALT" 498 _, _, _, err = act.Notarize(act.MakeRun(script)) 499 require.Error(t, err) 500 501 // Good. 502 rc.mirror = true 503 mHash, fbHash, vub, err := act.Notarize(act.MakeRun(script)) 504 require.NoError(t, err) 505 require.NotEqual(t, util.Uint256{}, mHash) 506 require.NotEqual(t, util.Uint256{}, fbHash) 507 require.Equal(t, uint32(92), vub) 508 } 509 510 func TestDefaultActorOptions(t *testing.T) { 511 rc := &RPCClient{ 512 version: &result.Version{ 513 Protocol: result.Protocol{ 514 Network: netmode.UnitTestNet, 515 MillisecondsPerBlock: 1000, 516 ValidatorsCount: 7, 517 }, 518 }, 519 } 520 acc, err := wallet.NewAccount() 521 require.NoError(t, err) 522 opts := NewDefaultActorOptions(NewReader(invoker.New(rc, nil)), acc) 523 rc.invRes = &result.Invoke{ 524 State: "HALT", 525 GasConsumed: 3, 526 Script: opts.FbScript, 527 Stack: []stackitem.Item{stackitem.Make(42)}, 528 } 529 tx := transaction.New(opts.FbScript, 1) 530 require.Error(t, opts.MainCheckerModifier(&result.Invoke{State: "FAULT"}, tx)) 531 rc.invRes.State = "FAULT" 532 require.Error(t, opts.MainCheckerModifier(&result.Invoke{State: "HALT"}, tx)) 533 rc.invRes.State = "HALT" 534 require.NoError(t, opts.MainCheckerModifier(&result.Invoke{State: "HALT"}, tx)) 535 require.Equal(t, uint32(42), tx.ValidUntilBlock) 536 } 537 538 func TestWait(t *testing.T) { 539 rc := &RPCClient{version: &result.Version{Protocol: result.Protocol{MillisecondsPerBlock: 1}}} 540 541 key0, err := keys.NewPrivateKey() 542 require.NoError(t, err) 543 key1, err := keys.NewPrivateKey() 544 require.NoError(t, err) 545 546 acc0 := wallet.NewAccountFromPrivateKey(key0) 547 facc1 := FakeSimpleAccount(key1.PublicKey()) 548 549 act, err := NewActor(rc, []actor.SignerAccount{{ 550 Signer: transaction.Signer{ 551 Account: acc0.Contract.ScriptHash(), 552 Scopes: transaction.None, 553 }, 554 Account: acc0, 555 }, { 556 Signer: transaction.Signer{ 557 Account: facc1.Contract.ScriptHash(), 558 Scopes: transaction.CalledByEntry, 559 }, 560 Account: facc1, 561 }}, acc0) 562 require.NoError(t, err) 563 564 someErr := errors.New("someErr") 565 _, err = act.Wait(util.Uint256{}, util.Uint256{}, 0, someErr) 566 require.ErrorIs(t, err, someErr) 567 568 cont := util.Uint256{1, 2, 3} 569 ex := state.Execution{ 570 Trigger: trigger.Application, 571 VMState: vmstate.Halt, 572 GasConsumed: 123, 573 Stack: []stackitem.Item{stackitem.Null{}}, 574 } 575 applog := &result.ApplicationLog{ 576 Container: cont, 577 IsTransaction: true, 578 Executions: []state.Execution{ex}, 579 } 580 rc.applog = applog 581 res, err := act.Wait(util.Uint256{}, util.Uint256{}, 0, nil) 582 require.NoError(t, err) 583 require.Equal(t, &state.AppExecResult{ 584 Container: cont, 585 Execution: ex, 586 }, res) 587 }