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  }