2021-10-23 12:36:26 +00:00
|
|
|
package neotest
|
|
|
|
|
|
|
|
import (
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
|
|
|
"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"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
2022-07-08 14:28:29 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
2021-12-03 15:45:55 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2021-10-23 12:36:26 +00:00
|
|
|
)
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// ContractInvoker is a client for a specific contract.
|
2021-10-23 12:36:26 +00:00
|
|
|
type ContractInvoker struct {
|
|
|
|
*Executor
|
2021-11-03 10:44:46 +00:00
|
|
|
Hash util.Uint160
|
|
|
|
Signers []Signer
|
2021-10-23 12:36:26 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// NewInvoker creates a new ContractInvoker for the contract with hash h and the specified signers.
|
2022-03-10 14:12:04 +00:00
|
|
|
func (e *Executor) NewInvoker(h util.Uint160, signers ...Signer) *ContractInvoker {
|
|
|
|
return &ContractInvoker{
|
|
|
|
Executor: e,
|
|
|
|
Hash: h,
|
|
|
|
Signers: signers,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// CommitteeInvoker creates a new ContractInvoker for the contract with hash h and a committee multisignature signer.
|
2021-10-23 12:36:26 +00:00
|
|
|
func (e *Executor) CommitteeInvoker(h util.Uint160) *ContractInvoker {
|
|
|
|
return &ContractInvoker{
|
|
|
|
Executor: e,
|
|
|
|
Hash: h,
|
2021-11-03 10:44:46 +00:00
|
|
|
Signers: []Signer{e.Committee},
|
2021-10-23 12:36:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// ValidatorInvoker creates a new ContractInvoker for the contract with hash h and a validators multisignature signer.
|
2021-12-09 14:58:20 +00:00
|
|
|
func (e *Executor) ValidatorInvoker(h util.Uint160) *ContractInvoker {
|
|
|
|
return &ContractInvoker{
|
|
|
|
Executor: e,
|
|
|
|
Hash: h,
|
|
|
|
Signers: []Signer{e.Validator},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-17 15:38:38 +00:00
|
|
|
// TestInvokeScript creates test VM and invokes the script with the args and signers.
|
|
|
|
func (c *ContractInvoker) TestInvokeScript(t testing.TB, script []byte, signers []Signer, validUntilBlock ...uint32) (*vm.Stack, error) {
|
|
|
|
tx := c.PrepareInvocationNoSign(t, script, validUntilBlock...)
|
|
|
|
for _, acc := range signers {
|
|
|
|
tx.Signers = append(tx.Signers, transaction.Signer{
|
|
|
|
Account: acc.ScriptHash(),
|
|
|
|
Scopes: transaction.Global,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
b := c.NewUnsignedBlock(t, tx)
|
|
|
|
ic, err := c.Chain.GetTestVM(trigger.Application, tx, b)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
t.Cleanup(ic.Finalize)
|
|
|
|
|
|
|
|
ic.VM.LoadWithFlags(tx.Script, callflag.All)
|
|
|
|
err = ic.VM.Run()
|
|
|
|
return ic.VM.Estack(), err
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestInvoke creates test VM and invokes the method with the args.
|
2023-04-03 10:34:24 +00:00
|
|
|
func (c *ContractInvoker) TestInvoke(t testing.TB, method string, args ...any) (*vm.Stack, error) {
|
2021-10-23 12:36:26 +00:00
|
|
|
tx := c.PrepareInvokeNoSign(t, method, args...)
|
|
|
|
b := c.NewUnsignedBlock(t, tx)
|
2022-10-06 10:24:57 +00:00
|
|
|
ic, err := c.Chain.GetTestVM(trigger.Application, tx, b)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-01-12 22:20:08 +00:00
|
|
|
t.Cleanup(ic.Finalize)
|
2021-10-23 12:36:26 +00:00
|
|
|
|
2022-01-12 22:20:08 +00:00
|
|
|
ic.VM.LoadWithFlags(tx.Script, callflag.All)
|
2022-10-06 10:24:57 +00:00
|
|
|
err = ic.VM.Run()
|
2022-01-12 22:20:08 +00:00
|
|
|
return ic.VM.Estack(), err
|
2021-10-23 12:36:26 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// WithSigners creates a new client with the provided signer.
|
2021-11-03 10:44:46 +00:00
|
|
|
func (c *ContractInvoker) WithSigners(signers ...Signer) *ContractInvoker {
|
2021-10-23 12:36:26 +00:00
|
|
|
newC := *c
|
2021-11-03 10:44:46 +00:00
|
|
|
newC.Signers = signers
|
2021-10-23 12:36:26 +00:00
|
|
|
return &newC
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// PrepareInvoke creates a new invocation transaction.
|
2023-04-03 10:34:24 +00:00
|
|
|
func (c *ContractInvoker) PrepareInvoke(t testing.TB, method string, args ...any) *transaction.Transaction {
|
2021-11-03 10:44:46 +00:00
|
|
|
return c.Executor.NewTx(t, c.Signers, c.Hash, method, args...)
|
2021-10-23 12:36:26 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// PrepareInvokeNoSign creates a new unsigned invocation transaction.
|
2023-04-03 10:34:24 +00:00
|
|
|
func (c *ContractInvoker) PrepareInvokeNoSign(t testing.TB, method string, args ...any) *transaction.Transaction {
|
2021-10-23 12:36:26 +00:00
|
|
|
return c.Executor.NewUnsignedTx(t, c.Hash, method, args...)
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// Invoke invokes the method with the args, persists the transaction and checks the result.
|
2021-10-23 12:36:26 +00:00
|
|
|
// Returns transaction hash.
|
2023-04-03 10:34:24 +00:00
|
|
|
func (c *ContractInvoker) Invoke(t testing.TB, result any, method string, args ...any) util.Uint256 {
|
2021-10-23 12:36:26 +00:00
|
|
|
tx := c.PrepareInvoke(t, method, args...)
|
|
|
|
c.AddNewBlock(t, tx)
|
|
|
|
c.CheckHalt(t, tx.Hash(), stackitem.Make(result))
|
|
|
|
return tx.Hash()
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// InvokeAndCheck invokes the method with the args, persists the transaction and checks the result
|
|
|
|
// using the provided function. It returns the transaction hash.
|
2023-04-03 10:34:24 +00:00
|
|
|
func (c *ContractInvoker) InvokeAndCheck(t testing.TB, checkResult func(t testing.TB, stack []stackitem.Item), method string, args ...any) util.Uint256 {
|
2021-12-03 15:45:55 +00:00
|
|
|
tx := c.PrepareInvoke(t, method, args...)
|
|
|
|
c.AddNewBlock(t, tx)
|
|
|
|
aer, err := c.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
|
|
|
|
require.NoError(t, err)
|
2022-07-08 14:28:29 +00:00
|
|
|
require.Equal(t, vmstate.Halt, aer[0].VMState, aer[0].FaultException)
|
2021-12-03 15:45:55 +00:00
|
|
|
if checkResult != nil {
|
|
|
|
checkResult(t, aer[0].Stack)
|
|
|
|
}
|
|
|
|
return tx.Hash()
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// InvokeWithFeeFail is like InvokeFail but sets the custom system fee for the transaction.
|
2023-04-03 10:34:24 +00:00
|
|
|
func (c *ContractInvoker) InvokeWithFeeFail(t testing.TB, message string, sysFee int64, method string, args ...any) util.Uint256 {
|
2021-10-23 12:36:26 +00:00
|
|
|
tx := c.PrepareInvokeNoSign(t, method, args...)
|
2021-11-03 10:44:46 +00:00
|
|
|
c.Executor.SignTx(t, tx, sysFee, c.Signers...)
|
2021-10-23 12:36:26 +00:00
|
|
|
c.AddNewBlock(t, tx)
|
|
|
|
c.CheckFault(t, tx.Hash(), message)
|
|
|
|
return tx.Hash()
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// InvokeFail invokes the method with the args, persists the transaction and checks the error message.
|
|
|
|
// It returns the transaction hash.
|
2023-04-03 10:34:24 +00:00
|
|
|
func (c *ContractInvoker) InvokeFail(t testing.TB, message string, method string, args ...any) util.Uint256 {
|
2021-10-23 12:36:26 +00:00
|
|
|
tx := c.PrepareInvoke(t, method, args...)
|
|
|
|
c.AddNewBlock(t, tx)
|
|
|
|
c.CheckFault(t, tx.Hash(), message)
|
2022-05-19 10:55:32 +00:00
|
|
|
return tx.Hash()
|
2021-10-23 12:36:26 +00:00
|
|
|
}
|