github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/neotest/client.go (about) 1 package neotest 2 3 import ( 4 "testing" 5 6 "github.com/nspcc-dev/neo-go/pkg/core/transaction" 7 "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" 8 "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" 9 "github.com/nspcc-dev/neo-go/pkg/util" 10 "github.com/nspcc-dev/neo-go/pkg/vm" 11 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 12 "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" 13 "github.com/stretchr/testify/require" 14 ) 15 16 // ContractInvoker is a client for a specific contract. 17 type ContractInvoker struct { 18 *Executor 19 Hash util.Uint160 20 Signers []Signer 21 } 22 23 // NewInvoker creates a new ContractInvoker for the contract with hash h and the specified signers. 24 func (e *Executor) NewInvoker(h util.Uint160, signers ...Signer) *ContractInvoker { 25 return &ContractInvoker{ 26 Executor: e, 27 Hash: h, 28 Signers: signers, 29 } 30 } 31 32 // CommitteeInvoker creates a new ContractInvoker for the contract with hash h and a committee multisignature signer. 33 func (e *Executor) CommitteeInvoker(h util.Uint160) *ContractInvoker { 34 return &ContractInvoker{ 35 Executor: e, 36 Hash: h, 37 Signers: []Signer{e.Committee}, 38 } 39 } 40 41 // ValidatorInvoker creates a new ContractInvoker for the contract with hash h and a validators multisignature signer. 42 func (e *Executor) ValidatorInvoker(h util.Uint160) *ContractInvoker { 43 return &ContractInvoker{ 44 Executor: e, 45 Hash: h, 46 Signers: []Signer{e.Validator}, 47 } 48 } 49 50 // TestInvokeScript creates test VM and invokes the script with the args and signers. 51 func (c *ContractInvoker) TestInvokeScript(t testing.TB, script []byte, signers []Signer, validUntilBlock ...uint32) (*vm.Stack, error) { 52 tx := c.PrepareInvocationNoSign(t, script, validUntilBlock...) 53 for _, acc := range signers { 54 tx.Signers = append(tx.Signers, transaction.Signer{ 55 Account: acc.ScriptHash(), 56 Scopes: transaction.Global, 57 }) 58 } 59 b := c.NewUnsignedBlock(t, tx) 60 ic, err := c.Chain.GetTestVM(trigger.Application, tx, b) 61 if err != nil { 62 return nil, err 63 } 64 t.Cleanup(ic.Finalize) 65 66 ic.VM.LoadWithFlags(tx.Script, callflag.All) 67 err = ic.VM.Run() 68 return ic.VM.Estack(), err 69 } 70 71 // TestInvoke creates test VM and invokes the method with the args. 72 func (c *ContractInvoker) TestInvoke(t testing.TB, method string, args ...any) (*vm.Stack, error) { 73 tx := c.PrepareInvokeNoSign(t, method, args...) 74 b := c.NewUnsignedBlock(t, tx) 75 ic, err := c.Chain.GetTestVM(trigger.Application, tx, b) 76 if err != nil { 77 return nil, err 78 } 79 t.Cleanup(ic.Finalize) 80 81 ic.VM.LoadWithFlags(tx.Script, callflag.All) 82 err = ic.VM.Run() 83 return ic.VM.Estack(), err 84 } 85 86 // WithSigners creates a new client with the provided signer. 87 func (c *ContractInvoker) WithSigners(signers ...Signer) *ContractInvoker { 88 newC := *c 89 newC.Signers = signers 90 return &newC 91 } 92 93 // PrepareInvoke creates a new invocation transaction. 94 func (c *ContractInvoker) PrepareInvoke(t testing.TB, method string, args ...any) *transaction.Transaction { 95 return c.Executor.NewTx(t, c.Signers, c.Hash, method, args...) 96 } 97 98 // PrepareInvokeNoSign creates a new unsigned invocation transaction. 99 func (c *ContractInvoker) PrepareInvokeNoSign(t testing.TB, method string, args ...any) *transaction.Transaction { 100 return c.Executor.NewUnsignedTx(t, c.Hash, method, args...) 101 } 102 103 // Invoke invokes the method with the args, persists the transaction and checks the result. 104 // Returns transaction hash. 105 func (c *ContractInvoker) Invoke(t testing.TB, result any, method string, args ...any) util.Uint256 { 106 tx := c.PrepareInvoke(t, method, args...) 107 c.AddNewBlock(t, tx) 108 c.CheckHalt(t, tx.Hash(), stackitem.Make(result)) 109 return tx.Hash() 110 } 111 112 // InvokeAndCheck invokes the method with the args, persists the transaction and checks the result 113 // using the provided function. It returns the transaction hash. 114 func (c *ContractInvoker) InvokeAndCheck(t testing.TB, checkResult func(t testing.TB, stack []stackitem.Item), method string, args ...any) util.Uint256 { 115 tx := c.PrepareInvoke(t, method, args...) 116 c.AddNewBlock(t, tx) 117 aer, err := c.Chain.GetAppExecResults(tx.Hash(), trigger.Application) 118 require.NoError(t, err) 119 require.Equal(t, vmstate.Halt, aer[0].VMState, aer[0].FaultException) 120 if checkResult != nil { 121 checkResult(t, aer[0].Stack) 122 } 123 return tx.Hash() 124 } 125 126 // InvokeWithFeeFail is like InvokeFail but sets the custom system fee for the transaction. 127 func (c *ContractInvoker) InvokeWithFeeFail(t testing.TB, message string, sysFee int64, method string, args ...any) util.Uint256 { 128 tx := c.PrepareInvokeNoSign(t, method, args...) 129 c.Executor.SignTx(t, tx, sysFee, c.Signers...) 130 c.AddNewBlock(t, tx) 131 c.CheckFault(t, tx.Hash(), message) 132 return tx.Hash() 133 } 134 135 // InvokeFail invokes the method with the args, persists the transaction and checks the error message. 136 // It returns the transaction hash. 137 func (c *ContractInvoker) InvokeFail(t testing.TB, message string, method string, args ...any) util.Uint256 { 138 tx := c.PrepareInvoke(t, method, args...) 139 c.AddNewBlock(t, tx) 140 c.CheckFault(t, tx.Hash(), message) 141 return tx.Hash() 142 }