actor

package
v0.105.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 12, 2024 License: MIT Imports: 10 Imported by: 9

Documentation

Overview

Package actor provides a way to change chain state via RPC client.

This layer builds on top of the basic RPC client and invoker package, it simplifies creating, signing and sending transactions to the network (since that's the only way chain state is changed). It's generic enough to be used for any contract that you may want to invoke and contract-specific functions can build on top of it.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func DefaultCheckerModifier added in v0.99.3

func DefaultCheckerModifier(r *result.Invoke, t *transaction.Transaction) error

DefaultCheckerModifier is the default TransactionCheckerModifier, it checks for HALT state in the invocation result given to it and does nothing else.

func DefaultModifier added in v0.99.3

func DefaultModifier(t *transaction.Transaction) error

DefaultModifier is the default modifier, it does nothing.

Types

type Actor

type Actor struct {
	invoker.Invoker
	waiter.Waiter
	// contains filtered or unexported fields
}

Actor keeps a connection to the RPC endpoint and allows to perform state-changing actions (via transactions that can also be created without sending them to the network) on behalf of a set of signers. It also provides an Invoker interface to perform test calls with the same set of signers.

Actor-specific APIs follow the naming scheme set by Invoker in method suffixes. *Call methods operate with function calls and require a contract hash, a method and parameters if any. *Run methods operate with scripts and require a NeoVM script that will be used directly. Prefixes denote the action to be performed, "Make" prefix is used for methods that create transactions in various ways, while "Send" prefix is used by methods that directly transmit created transactions to the RPC server.

Actor also provides a waiter.Waiter interface to wait until transaction will be accepted to the chain. Depending on the underlying RPCActor functionality, transaction awaiting can be performed via web-socket using RPC notifications subsystem with waiter.EventBased, via regular RPC requests using a poll-based algorithm with waiter.PollingBased or can not be performed if RPCActor doesn't implement none of waiter.RPCEventBased and waiter.RPCPollingBased interfaces with waiter.Null. waiter.ErrAwaitingNotSupported will be returned on attempt to await the transaction in the latter case. waiter.Waiter uses context of the underlying RPCActor and interrupts transaction awaiting process if the context is done. waiter.ErrContextDone wrapped with the context's error will be returned in this case. Otherwise, transaction awaiting process is ended with ValidUntilBlock acceptance and waiter.ErrTxNotAccepted is returned if transaction wasn't accepted by this moment.

Example
package main

import (
	"context"
	"encoding/json"
	"os"

	"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/rpcclient"
	"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
	"github.com/nspcc-dev/neo-go/pkg/rpcclient/neo"
	"github.com/nspcc-dev/neo-go/pkg/rpcclient/policy"

	sccontext "github.com/nspcc-dev/neo-go/pkg/smartcontract/context"
	"github.com/nspcc-dev/neo-go/pkg/util"
	"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
	"github.com/nspcc-dev/neo-go/pkg/wallet"
)

func main() {
	// No error checking done at all, intentionally.
	w, _ := wallet.NewWalletFromFile("somewhere")
	defer w.Close()

	c, _ := rpcclient.New(context.Background(), "url", rpcclient.Options{})

	// Create a simple CalledByEntry-scoped actor (assuming there are accounts
	// inside the wallet).
	a, _ := actor.NewSimple(c, w.Accounts[0])

	customContract := util.Uint160{9, 8, 7}
	// Actor has an Invoker inside, so we can perform test invocations, it will
	// have a signer with the first wallet account and CalledByEntry scope.
	res, _ := a.Call(customContract, "method", 1, 2, 3)
	if res.State != vmstate.Halt.String() {
		panic("failed")
	}
	// All of the side-effects in res can be analyzed.

	// Now we want to send the same invocation in a transaction, but we already
	// have the script and a proper system fee for it, therefore SendUncheckedRun
	// can be used.
	txid, vub, _ := a.SendUncheckedRun(res.Script, res.GasConsumed, nil, nil)
	_ = txid
	_ = vub
	// You need to wait for it to persist and then check the on-chain result of it.

	// Now we want to send some transaction, but give it a priority by increasing
	// its network fee, this can be done with Tuned APIs.
	txid, vub, _ = a.SendTunedCall(customContract, "method", nil, func(r *result.Invoke, t *transaction.Transaction) error {
		// This code is run after the test-invocation done by *Call methods.
		// Reuse the default function to check for HALT execution state.
		err := actor.DefaultCheckerModifier(r, t)
		if err != nil {
			return err
		}
		// Some additional checks can be performed right here, but we only
		// want to raise the network fee by ~20%.
		t.NetworkFee += (t.NetworkFee / 5)
		return nil
	}, 1, 2, 3)
	_ = txid
	_ = vub

	// Actor can be used for higher-level wrappers as well, if we want to interact with
	// NEO then [neo] package can accept our Actor and allow to easily use NEO methods.
	neoContract := neo.New(a)
	balance, _ := neoContract.BalanceOf(a.Sender())
	_ = balance

	// Now suppose the second wallet account is a committee account. We want to
	// create and sign transactions for committee, but use the first account as
	// a sender (because committee account has no GAS). We at the same time want
	// to make all transactions using this actor high-priority ones, because
	// committee can use this attribute.

	// Get the default options to have CheckerModifier/Modifier set up correctly.
	opts := actor.NewDefaultOptions()
	// And override attributes.
	opts.Attributes = []transaction.Attribute{{Type: transaction.HighPriority}}

	// Create an Actor.
	a, _ = actor.NewTuned(c, []actor.SignerAccount{{
		// Sender, regular account with None scope.
		Signer: transaction.Signer{
			Account: w.Accounts[0].ScriptHash(),
			Scopes:  transaction.None,
		},
		Account: w.Accounts[0],
	}, {
		// Commmitee.
		Signer: transaction.Signer{
			Account: w.Accounts[1].ScriptHash(),
			Scopes:  transaction.CalledByEntry,
		},
		Account: w.Accounts[1],
	}}, opts)

	// Use policy contract wrapper to simplify things. All changes in the
	// Policy contract are made by the committee.
	policyContract := policy.New(a)

	// Create a transaction to set storage price, it'll be high-priority and have two
	// signers from above. Committee is a multisignature account, so we can't sign/send
	// it right away, w.Accounts[1] has only one public key. Therefore, we need to
	// create a partially signed transaction and save it, then collect other signatures
	// and send.
	tx, _ := policyContract.SetStoragePriceUnsigned(10)

	net := a.GetNetwork()
	scCtx := sccontext.NewParameterContext(sccontext.TransactionType, net, tx)
	sign := w.Accounts[0].SignHashable(net, tx)
	_ = scCtx.AddSignature(w.Accounts[0].ScriptHash(), w.Accounts[0].Contract, w.Accounts[0].PublicKey(), sign)

	sign = w.Accounts[1].SignHashable(net, tx)
	_ = scCtx.AddSignature(w.Accounts[1].ScriptHash(), w.Accounts[1].Contract, w.Accounts[1].PublicKey(), sign)

	data, _ := json.Marshal(scCtx)
	_ = os.WriteFile("tx.json", data, 0644)

	// Signature collection is out of scope, usually it's manual for cases like this.
}
Output:

func New

func New(ra RPCActor, signers []SignerAccount) (*Actor, error)

New creates an Actor instance using the specified RPC interface and the set of signers with corresponding accounts. Every transaction created by this Actor will have this set of signers and all communication will be performed via this RPC. Upon Actor instance creation a GetVersion call is made and the result of it is cached forever (and used for internal purposes). The actor will use default Options (which can be overridden using NewTuned).

func NewSimple

func NewSimple(ra RPCActor, acc *wallet.Account) (*Actor, error)

NewSimple makes it easier to create an Actor for the most widespread case when transactions have only one signer that uses CalledByEntry scope. When other scopes or multiple signers are needed use New.

func NewTuned added in v0.99.3

func NewTuned(ra RPCActor, signers []SignerAccount, opts Options) (*Actor, error)

NewTuned creates an Actor that will use the specified Options as defaults when creating new transactions. If checker/modifier callbacks are not provided (nil), then default ones (from NewDefaultOptions) are used.

func (*Actor) CalculateNetworkFee

func (a *Actor) CalculateNetworkFee(tx *transaction.Transaction) (int64, error)

CalculateNetworkFee wraps RPCActor's CalculateNetworkFee, making it available to Actor users directly. It returns network fee value for the given transaction.

func (*Actor) CalculateValidUntilBlock

func (a *Actor) CalculateValidUntilBlock() (uint32, error)

CalculateValidUntilBlock returns correct ValidUntilBlock value for a new transaction relative to the current blockchain height. It uses "height + number of validators + 1" formula suggesting shorter transaction lifetime than the usual "height + MaxValidUntilBlockIncrement" approach. Shorter lifetime can be useful to control transaction acceptance wait time because it can't be added into a block after ValidUntilBlock.

func (*Actor) GetBlockCount

func (a *Actor) GetBlockCount() (uint32, error)

GetBlockCount wraps RPCActor's GetBlockCount, making it available to Actor users directly. It returns current number of blocks in the chain.

func (*Actor) GetNetwork

func (a *Actor) GetNetwork() netmode.Magic

GetNetwork is a convenience method that returns the network's magic number.

func (*Actor) GetVersion

func (a *Actor) GetVersion() result.Version

GetVersion returns version data from the RPC endpoint.

func (*Actor) MakeCall

func (a *Actor) MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)

MakeCall creates a transaction that calls the given method of the given contract with the given parameters. Test call is performed and filtered through Actor-configured TransactionCheckerModifier. The resulting transaction has Actor-configured attributes added as well. If you need to override attributes and/or TransactionCheckerModifier use MakeTunedCall.

func (*Actor) MakeRun

func (a *Actor) MakeRun(script []byte) (*transaction.Transaction, error)

MakeRun creates a transaction with the given executable script. Test invocation of this script is performed and filtered through Actor's TransactionCheckerModifier. The resulting transaction has attributes that are configured for current Actor. If you need to override them or use a different TransactionCheckerModifier use MakeTunedRun.

func (*Actor) MakeTunedCall

func (a *Actor) MakeTunedCall(contract util.Uint160, method string, attrs []transaction.Attribute, txHook TransactionCheckerModifier, params ...any) (*transaction.Transaction, error)

MakeTunedCall creates a transaction with the given attributes (or Actor default ones if nil) that calls the given method of the given contract with the given parameters. It's filtered through the provided callback (or Actor default one's if nil, see TransactionCheckerModifier documentation also), so the process can be aborted and transaction can be modified before signing.

func (*Actor) MakeTunedRun

func (a *Actor) MakeTunedRun(script []byte, attrs []transaction.Attribute, txHook TransactionCheckerModifier) (*transaction.Transaction, error)

MakeTunedRun creates a transaction with the given attributes (or Actor default ones if nil) that executes the given script. It's filtered through the provided callback (if not nil, otherwise Actor default one is used, see TransactionCheckerModifier documentation also), so the process can be aborted and transaction can be modified before signing.

func (*Actor) MakeUncheckedRun

func (a *Actor) MakeUncheckedRun(script []byte, sysfee int64, attrs []transaction.Attribute, txHook TransactionModifier) (*transaction.Transaction, error)

MakeUncheckedRun creates a transaction with the given attributes (or Actor default ones if nil) that executes the given script and is expected to use up to sysfee GAS for its execution. The transaction is filtered through the provided callback (or Actor default one, see TransactionModifier documentation also), so the process can be aborted and transaction can be modified before signing. This method is mostly useful when test invocation is already performed and the script and required system fee values are already known.

func (*Actor) MakeUnsignedCall

func (a *Actor) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)

MakeUnsignedCall creates an unsigned transaction with the given attributes that calls the given method of the given contract with the given parameters. Test-invocation is performed and is expected to end up in HALT state, the transaction returned has correct SystemFee and NetworkFee values. TransactionModifier is not applied to the result of this method, but default attributes are used if attrs is nil.

func (*Actor) MakeUnsignedRun

func (a *Actor) MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)

MakeUnsignedRun creates an unsigned transaction with the given attributes that executes the given script. Test-invocation is performed and is expected to end up in HALT state, the transaction returned has correct SystemFee and NetworkFee values. TransactionModifier is not applied to the result of this method, but default attributes are used if attrs is nil.

func (*Actor) MakeUnsignedUncheckedRun

func (a *Actor) MakeUnsignedUncheckedRun(script []byte, sysFee int64, attrs []transaction.Attribute) (*transaction.Transaction, error)

MakeUnsignedUncheckedRun creates an unsigned transaction containing the given script with the system fee value and attributes. It's expected to be used when test invocation is already done and the script and system fee value are already known to be good, so it doesn't do test invocation internally. But it fills Signers with Actor's signers, calculates proper ValidUntilBlock and NetworkFee values. The resulting transaction can be changed in its Nonce, SystemFee, NetworkFee and ValidUntilBlock values and then be signed and sent or exchanged via context.ParameterContext. TransactionModifier is not applied to the result of this method, but default attributes are used if attrs is nil.

func (*Actor) Send

Send allows to send arbitrary prepared transaction to the network. It returns transaction hash and ValidUntilBlock value.

func (*Actor) SendCall

func (a *Actor) SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)

SendCall creates a transaction that calls the given method of the given contract with the given parameters (see also MakeCall) and sends it to the network.

func (*Actor) SendRun

func (a *Actor) SendRun(script []byte) (util.Uint256, uint32, error)

SendRun creates a transaction with the given executable script (see also MakeRun) and sends it to the network.

func (*Actor) SendTunedCall

func (a *Actor) SendTunedCall(contract util.Uint160, method string, attrs []transaction.Attribute, txHook TransactionCheckerModifier, params ...any) (util.Uint256, uint32, error)

SendTunedCall creates a transaction that calls the given method of the given contract with the given parameters (see also MakeTunedCall) and attributes, allowing to check for execution results of this call and modify transaction before it's signed; this transaction is then sent to the network.

func (*Actor) SendTunedRun

func (a *Actor) SendTunedRun(script []byte, attrs []transaction.Attribute, txHook TransactionCheckerModifier) (util.Uint256, uint32, error)

SendTunedRun creates a transaction with the given executable script and attributes, allowing to check for execution results of this script and modify transaction before it's signed (see also MakeTunedRun). This transaction is then sent to the network.

func (*Actor) SendUncheckedRun

func (a *Actor) SendUncheckedRun(script []byte, sysfee int64, attrs []transaction.Attribute, txHook TransactionModifier) (util.Uint256, uint32, error)

SendUncheckedRun creates a transaction with the given executable script and attributes that can use up to sysfee GAS for its execution, allowing to modify this transaction before it's signed (see also MakeUncheckedRun). This transaction is then sent to the network.

func (*Actor) Sender added in v0.99.3

func (a *Actor) Sender() util.Uint160

Sender return the sender address that will be used in transactions created by Actor.

func (*Actor) Sign

func (a *Actor) Sign(tx *transaction.Transaction) error

Sign adds signatures to arbitrary transaction using Actor signers wallets. Most of the time it shouldn't be used directly since it'll be successful only if the transaction is made using the same set of accounts as the one used for Actor creation.

func (*Actor) SignAndSend

func (a *Actor) SignAndSend(tx *transaction.Transaction) (util.Uint256, uint32, error)

SignAndSend signs arbitrary transaction (see also Sign) and sends it to the network.

type Options added in v0.99.3

type Options struct {
	// Attributes are set as is into every transaction created by Actor,
	// unless they're explicitly set in a method call that accepts
	// attributes (like MakeTuned* or MakeUnsigned*).
	Attributes []transaction.Attribute
	// CheckerModifier is used by any method that creates and signs a
	// transaction inside (some of them provide ways to override this
	// default, some don't).
	CheckerModifier TransactionCheckerModifier
	// Modifier is used only by MakeUncheckedRun to modify transaction
	// before it's signed (other methods that perform test invocations
	// use CheckerModifier). MakeUnsigned* methods do not run it.
	Modifier TransactionModifier
}

Options are used to create Actor with non-standard transaction checkers or additional attributes to be applied for all transactions.

func NewDefaultOptions added in v0.99.3

func NewDefaultOptions() Options

NewDefaultOptions returns Options that have no attributes and use the default TransactionCheckerModifier function (that checks for the invocation result to be in HALT state) and TransactionModifier (that does nothing).

type RPCActor

type RPCActor interface {
	invoker.RPCInvoke

	// CalculateNetworkFee calculates network fee for the given transaction.
	//
	// CalculateNetworkFee MUST NOT call state-changing methods (like Hash or Size)
	// of the transaction through the passed pointer: make a copy if necessary.
	CalculateNetworkFee(tx *transaction.Transaction) (int64, error)
	GetBlockCount() (uint32, error)
	GetVersion() (*result.Version, error)
	SendRawTransaction(tx *transaction.Transaction) (util.Uint256, error)
}

RPCActor is an interface required from the RPC client to successfully create and send transactions.

type SignerAccount

type SignerAccount struct {
	Signer  transaction.Signer
	Account *wallet.Account
}

SignerAccount represents combination of the transaction.Signer and the corresponding wallet.Account. It's used to create and sign transactions, each transaction has a set of signers that must witness the transaction with their signatures.

type TransactionCheckerModifier

type TransactionCheckerModifier func(r *result.Invoke, t *transaction.Transaction) error

TransactionCheckerModifier is a callback that receives the result of test-invocation and the transaction that can perform the same invocation on chain. This callback is accepted by methods that create transactions, it can examine both arguments and return an error if there is anything wrong there which will abort the creation process. Notice that when used this callback is completely responsible for invocation result checking, including checking for HALT execution state (so if you don't check for it in a callback you can send a transaction that is known to end up in FAULT state). It can also modify the transaction (see TransactionModifier).

type TransactionModifier

type TransactionModifier func(t *transaction.Transaction) error

TransactionModifier is a callback that receives the transaction before it's signed from a method that creates signed transactions. It can check fees and other fields of the transaction and return an error if there is anything wrong there which will abort the creation process. It also can modify Nonce, SystemFee, NetworkFee and ValidUntilBlock values taking full responsibility on the effects of these modifications (smaller fee values, too low or too high ValidUntilBlock or bad Nonce can render transaction invalid). Modifying other fields is not supported. Mostly it's useful for increasing fee values since by default they're just enough for transaction to be successfully accepted and executed.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL