2022-07-29 19:02:25 +00:00
|
|
|
package invoker
|
|
|
|
|
|
|
|
import (
|
2022-08-01 12:27:36 +00:00
|
|
|
"fmt"
|
|
|
|
|
2022-07-29 19:02:25 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
|
|
)
|
|
|
|
|
|
|
|
// RPCInvoke is a set of RPC methods needed to execute things at the current
|
|
|
|
// blockchain height.
|
|
|
|
type RPCInvoke interface {
|
|
|
|
InvokeContractVerify(contract util.Uint160, params []smartcontract.Parameter, signers []transaction.Signer, witnesses ...transaction.Witness) (*result.Invoke, error)
|
|
|
|
InvokeFunction(contract util.Uint160, operation string, params []smartcontract.Parameter, signers []transaction.Signer) (*result.Invoke, error)
|
|
|
|
InvokeScript(script []byte, signers []transaction.Signer) (*result.Invoke, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// RPCInvokeHistoric is a set of RPC methods needed to execute things at some
|
|
|
|
// fixed point in blockchain's life.
|
|
|
|
type RPCInvokeHistoric interface {
|
|
|
|
InvokeContractVerifyAtBlock(blockHash util.Uint256, contract util.Uint160, params []smartcontract.Parameter, signers []transaction.Signer, witnesses ...transaction.Witness) (*result.Invoke, error)
|
|
|
|
InvokeContractVerifyAtHeight(height uint32, contract util.Uint160, params []smartcontract.Parameter, signers []transaction.Signer, witnesses ...transaction.Witness) (*result.Invoke, error)
|
|
|
|
InvokeContractVerifyWithState(stateroot util.Uint256, contract util.Uint160, params []smartcontract.Parameter, signers []transaction.Signer, witnesses ...transaction.Witness) (*result.Invoke, error)
|
|
|
|
InvokeFunctionAtBlock(blockHash util.Uint256, contract util.Uint160, operation string, params []smartcontract.Parameter, signers []transaction.Signer) (*result.Invoke, error)
|
|
|
|
InvokeFunctionAtHeight(height uint32, contract util.Uint160, operation string, params []smartcontract.Parameter, signers []transaction.Signer) (*result.Invoke, error)
|
|
|
|
InvokeFunctionWithState(stateroot util.Uint256, contract util.Uint160, operation string, params []smartcontract.Parameter, signers []transaction.Signer) (*result.Invoke, error)
|
|
|
|
InvokeScriptAtBlock(blockHash util.Uint256, script []byte, signers []transaction.Signer) (*result.Invoke, error)
|
|
|
|
InvokeScriptAtHeight(height uint32, script []byte, signers []transaction.Signer) (*result.Invoke, error)
|
|
|
|
InvokeScriptWithState(stateroot util.Uint256, script []byte, signers []transaction.Signer) (*result.Invoke, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Invoker allows to test-execute things using RPC client. Its API simplifies
|
|
|
|
// reusing the same signers list for a series of invocations and at the
|
|
|
|
// same time uses regular Go types for call parameters. It doesn't do anything with
|
|
|
|
// the result of invocation, that's left for upper (contract) layer to deal with.
|
|
|
|
// Invoker does not produce any transactions and does not change the state of the
|
|
|
|
// chain.
|
|
|
|
type Invoker struct {
|
|
|
|
client RPCInvoke
|
|
|
|
signers []transaction.Signer
|
|
|
|
}
|
|
|
|
|
|
|
|
type historicConverter struct {
|
|
|
|
client RPCInvokeHistoric
|
|
|
|
block *util.Uint256
|
|
|
|
height *uint32
|
|
|
|
root *util.Uint256
|
|
|
|
}
|
|
|
|
|
|
|
|
// New creates an Invoker to test-execute things at the current blockchain height.
|
|
|
|
func New(client RPCInvoke, signers []transaction.Signer) *Invoker {
|
|
|
|
return &Invoker{client, signers}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewHistoricAtBlock creates an Invoker to test-execute things at some given block.
|
|
|
|
func NewHistoricAtBlock(block util.Uint256, client RPCInvokeHistoric, signers []transaction.Signer) *Invoker {
|
|
|
|
return New(&historicConverter{
|
|
|
|
client: client,
|
|
|
|
block: &block,
|
|
|
|
}, signers)
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewHistoricAtHeight creates an Invoker to test-execute things at some given height.
|
|
|
|
func NewHistoricAtHeight(height uint32, client RPCInvokeHistoric, signers []transaction.Signer) *Invoker {
|
|
|
|
return New(&historicConverter{
|
|
|
|
client: client,
|
|
|
|
height: &height,
|
|
|
|
}, signers)
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewHistoricWithState creates an Invoker to test-execute things with some given state.
|
|
|
|
func NewHistoricWithState(root util.Uint256, client RPCInvokeHistoric, signers []transaction.Signer) *Invoker {
|
|
|
|
return New(&historicConverter{
|
|
|
|
client: client,
|
|
|
|
root: &root,
|
|
|
|
}, signers)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *historicConverter) InvokeScript(script []byte, signers []transaction.Signer) (*result.Invoke, error) {
|
|
|
|
if h.block != nil {
|
|
|
|
return h.client.InvokeScriptAtBlock(*h.block, script, signers)
|
|
|
|
}
|
|
|
|
if h.height != nil {
|
|
|
|
return h.client.InvokeScriptAtHeight(*h.height, script, signers)
|
|
|
|
}
|
|
|
|
if h.root != nil {
|
|
|
|
return h.client.InvokeScriptWithState(*h.root, script, signers)
|
|
|
|
}
|
|
|
|
panic("uninitialized historicConverter")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *historicConverter) InvokeFunction(contract util.Uint160, operation string, params []smartcontract.Parameter, signers []transaction.Signer) (*result.Invoke, error) {
|
|
|
|
if h.block != nil {
|
|
|
|
return h.client.InvokeFunctionAtBlock(*h.block, contract, operation, params, signers)
|
|
|
|
}
|
|
|
|
if h.height != nil {
|
|
|
|
return h.client.InvokeFunctionAtHeight(*h.height, contract, operation, params, signers)
|
|
|
|
}
|
|
|
|
if h.root != nil {
|
|
|
|
return h.client.InvokeFunctionWithState(*h.root, contract, operation, params, signers)
|
|
|
|
}
|
|
|
|
panic("uninitialized historicConverter")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *historicConverter) InvokeContractVerify(contract util.Uint160, params []smartcontract.Parameter, signers []transaction.Signer, witnesses ...transaction.Witness) (*result.Invoke, error) {
|
|
|
|
if h.block != nil {
|
|
|
|
return h.client.InvokeContractVerifyAtBlock(*h.block, contract, params, signers, witnesses...)
|
|
|
|
}
|
|
|
|
if h.height != nil {
|
|
|
|
return h.client.InvokeContractVerifyAtHeight(*h.height, contract, params, signers, witnesses...)
|
|
|
|
}
|
|
|
|
if h.root != nil {
|
|
|
|
return h.client.InvokeContractVerifyWithState(*h.root, contract, params, signers, witnesses...)
|
|
|
|
}
|
|
|
|
panic("uninitialized historicConverter")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call invokes a method of the contract with the given parameters (and
|
|
|
|
// Invoker-specific list of signers) and returns the result as is.
|
|
|
|
func (v *Invoker) Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error) {
|
|
|
|
ps, err := smartcontract.NewParametersFromValues(params...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return v.client.InvokeFunction(contract, operation, ps, v.signers)
|
|
|
|
}
|
|
|
|
|
2022-08-01 12:27:36 +00:00
|
|
|
// CallAndExpandIterator creates a script containing a call of the specified method
|
|
|
|
// of a contract with given parameters (similar to how Call operates). But then this
|
|
|
|
// script contains additional code that expects that the result of the first call is
|
|
|
|
// an iterator. This iterator is traversed extracting values from it and adding them
|
|
|
|
// into an array until maxItems is reached or iterator has no more elements. The
|
|
|
|
// result of the whole script is an array containing up to maxResultItems elements
|
|
|
|
// from the iterator returned from the contract's method call. This script is executed
|
|
|
|
// using regular JSON-API (according to the way Iterator is set up).
|
|
|
|
func (v *Invoker) CallAndExpandIterator(contract util.Uint160, method string, maxItems int, params ...interface{}) (*result.Invoke, error) {
|
|
|
|
bytes, err := smartcontract.CreateCallAndUnwrapIteratorScript(contract, method, maxItems, params...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("iterator unwrapper script: %w", err)
|
|
|
|
}
|
|
|
|
return v.Run(bytes)
|
|
|
|
}
|
|
|
|
|
2022-07-29 19:02:25 +00:00
|
|
|
// Verify invokes contract's verify method in the verification context with
|
|
|
|
// Invoker-specific signers and given witnesses and parameters.
|
|
|
|
func (v *Invoker) Verify(contract util.Uint160, witnesses []transaction.Witness, params ...interface{}) (*result.Invoke, error) {
|
|
|
|
ps, err := smartcontract.NewParametersFromValues(params...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return v.client.InvokeContractVerify(contract, ps, v.signers, witnesses...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run executes given bytecode with Invoker-specific list of signers.
|
|
|
|
func (v *Invoker) Run(script []byte) (*result.Invoke, error) {
|
|
|
|
return v.client.InvokeScript(script, v.signers)
|
|
|
|
}
|