forked from TrueCloudLab/neoneo-go
actor: extend documentation, add example
This commit is contained in:
parent
e1fe76137e
commit
cb1a1f8532
2 changed files with 135 additions and 4 deletions
|
@ -1,10 +1,11 @@
|
||||||
/*
|
/*
|
||||||
Package actor provides a way to change chain state via RPC client.
|
Package actor provides a way to change chain state via RPC client.
|
||||||
|
|
||||||
This layer builds on top of the basic RPC client and simplifies creating,
|
This layer builds on top of the basic RPC client and [invoker] package, it
|
||||||
signing and sending transactions to the network (since that's the only way chain
|
simplifies creating, signing and sending transactions to the network (since
|
||||||
state is changed). It's generic enough to be used for any contract that you may
|
that's the only way chain state is changed). It's generic enough to be used for
|
||||||
want to invoke and contract-specific functions can build on top of it.
|
any contract that you may want to invoke and contract-specific functions can
|
||||||
|
build on top of it.
|
||||||
*/
|
*/
|
||||||
package actor
|
package actor
|
||||||
|
|
||||||
|
@ -44,6 +45,14 @@ type SignerAccount struct {
|
||||||
// state-changing actions (via transactions that can also be created without
|
// 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
|
// 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.
|
// 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.
|
||||||
type Actor struct {
|
type Actor struct {
|
||||||
invoker.Invoker
|
invoker.Invoker
|
||||||
|
|
||||||
|
|
122
pkg/rpcclient/actor/doc_test.go
Normal file
122
pkg/rpcclient/actor/doc_test.go
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
package actor_test
|
||||||
|
|
||||||
|
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 ExampleActor() {
|
||||||
|
// 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() {
|
||||||
|
// The call 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("Neo.Network.P2P.Payloads.Transaction", 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.
|
||||||
|
}
|
Loading…
Reference in a new issue