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.
|
||||
|
||||
This layer builds on top of the basic RPC client and 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.
|
||||
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.
|
||||
*/
|
||||
package actor
|
||||
|
||||
|
@ -44,6 +45,14 @@ type SignerAccount struct {
|
|||
// 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.
|
||||
type Actor struct {
|
||||
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