forked from TrueCloudLab/neoneo-go
invoker: update documentation, add example
This commit is contained in:
parent
ea92f3d716
commit
00a9376311
2 changed files with 113 additions and 1 deletions
99
pkg/rpcclient/invoker/doc_test.go
Normal file
99
pkg/rpcclient/invoker/doc_test.go
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
package invoker_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/neo"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleInvoker() {
|
||||||
|
// No error checking done at all, intentionally.
|
||||||
|
c, _ := rpcclient.New(context.Background(), "url", rpcclient.Options{})
|
||||||
|
|
||||||
|
// A simple invoker with no signers, perfectly fine for reads from safe methods.
|
||||||
|
inv := invoker.New(c, nil)
|
||||||
|
|
||||||
|
// Get the NEO token supply (notice that unwrap is used to get the result).
|
||||||
|
supply, _ := unwrap.BigInt(inv.Call(neo.Hash, "totalSupply"))
|
||||||
|
_ = supply
|
||||||
|
|
||||||
|
acc, _ := address.StringToUint160("NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq")
|
||||||
|
// Get the NEO balance for account NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq.
|
||||||
|
balance, _ := unwrap.BigInt(inv.Call(neo.Hash, "balanceOf", acc))
|
||||||
|
_ = balance
|
||||||
|
|
||||||
|
// Test-invoke transfer call.
|
||||||
|
res, _ := inv.Call(neo.Hash, "transfer", acc, util.Uint160{1, 2, 3}, 1, nil)
|
||||||
|
if res.State == vmstate.Halt.String() {
|
||||||
|
// NEO is broken! inv has no signers and transfer requires a witness to be performed.
|
||||||
|
} else {
|
||||||
|
// OK, this actually should fail.
|
||||||
|
}
|
||||||
|
|
||||||
|
// A historic invoker with no signers at block 1000000.
|
||||||
|
inv = invoker.NewHistoricAtHeight(1000000, c, nil)
|
||||||
|
|
||||||
|
// It's the same call as above, but the data is for a state at block 1000000.
|
||||||
|
balance, _ = unwrap.BigInt(inv.Call(neo.Hash, "balanceOf", acc))
|
||||||
|
_ = balance
|
||||||
|
|
||||||
|
// This invoker has a signer for NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq account with
|
||||||
|
// CalledByEntry scope, which is sufficient for most operation. It uses current
|
||||||
|
// state which is exactly what you need if you want to then create a transaction
|
||||||
|
// with the same action.
|
||||||
|
inv = invoker.New(c, []transaction.Signer{{Account: acc, Scopes: transaction.CalledByEntry}})
|
||||||
|
|
||||||
|
// Now test invocation should be fine (if NVTiAjNgagDkTr5HTzDmQP9kPwPHN5BgVq has 1 NEO of course).
|
||||||
|
res, _ = inv.Call(neo.Hash, "transfer", acc, util.Uint160{1, 2, 3}, 1, nil)
|
||||||
|
if res.State == vmstate.Halt.String() {
|
||||||
|
// transfer actually returns a value, so check it too.
|
||||||
|
ok, _ := unwrap.Bool(res, nil)
|
||||||
|
if ok {
|
||||||
|
// OK, as expected. res.Script contains the corresponding
|
||||||
|
// script and res.GasConsumed has an appropriate system fee
|
||||||
|
// required for a transaction.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now let's try working with iterators.
|
||||||
|
nep11Contract := util.Uint160{1, 2, 3}
|
||||||
|
|
||||||
|
var tokens [][]byte
|
||||||
|
|
||||||
|
// Try doing it the right way, by traversing the iterator.
|
||||||
|
sess, iter, err := unwrap.SessionIterator(inv.Call(nep11Contract, "tokensOf", acc))
|
||||||
|
|
||||||
|
// The server doesn't support sessions and doesn't perform iterator expansion,
|
||||||
|
// iterators can't be used.
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, unwrap.ErrNoSessionID) {
|
||||||
|
// But if we expect some low number of elements, CallAndExpandIterator
|
||||||
|
// can help us in this case. If the account has more than 10 elements,
|
||||||
|
// some of them will be missing from the response.
|
||||||
|
tokens, _ = unwrap.ArrayOfBytes(inv.CallAndExpandIterator(nep11Contract, "tokensOf", 10, acc))
|
||||||
|
} else {
|
||||||
|
panic("some error")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
items, err := inv.TraverseIterator(sess, &iter, 100)
|
||||||
|
// Keep going until there are no more elements
|
||||||
|
for err == nil && len(items) != 0 {
|
||||||
|
for _, itm := range items {
|
||||||
|
tokenID, _ := itm.TryBytes()
|
||||||
|
tokens = append(tokens, tokenID)
|
||||||
|
}
|
||||||
|
items, err = inv.TraverseIterator(sess, &iter, 100)
|
||||||
|
}
|
||||||
|
// Let the server release the session.
|
||||||
|
_ = inv.TerminateSession(sess)
|
||||||
|
}
|
||||||
|
_ = tokens
|
||||||
|
}
|
|
@ -1,3 +1,12 @@
|
||||||
|
/*
|
||||||
|
Package invoker provides a convenient wrapper to perform test calls via RPC client.
|
||||||
|
|
||||||
|
This layer builds on top of the basic RPC client and simplifies performing
|
||||||
|
test function invocations and script runs. It also makes historic calls (NeoGo
|
||||||
|
extension) transparent, allowing to use the same API as for regular calls.
|
||||||
|
Results of these calls can be interpreted by upper layer packages like actor
|
||||||
|
(to create transactions) or unwrap (to retrieve data from return values).
|
||||||
|
*/
|
||||||
package invoker
|
package invoker
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -70,6 +79,9 @@ type historicConverter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates an Invoker to test-execute things at the current blockchain height.
|
// New creates an Invoker to test-execute things at the current blockchain height.
|
||||||
|
// If you only want to read data from the contract using its safe methods normally
|
||||||
|
// (but contract-specific in general case) it's OK to pass nil for signers (that
|
||||||
|
// is, use no signers).
|
||||||
func New(client RPCInvoke, signers []transaction.Signer) *Invoker {
|
func New(client RPCInvoke, signers []transaction.Signer) *Invoker {
|
||||||
return &Invoker{client, signers}
|
return &Invoker{client, signers}
|
||||||
}
|
}
|
||||||
|
@ -187,7 +199,8 @@ func (v *Invoker) Run(script []byte) (*result.Invoke, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TerminateSession closes the given session, returning an error if anything
|
// TerminateSession closes the given session, returning an error if anything
|
||||||
// goes wrong.
|
// goes wrong. It's not strictly required to close the session (it'll expire on
|
||||||
|
// the server anyway), but it helps to release server resources earlier.
|
||||||
func (v *Invoker) TerminateSession(sessionID uuid.UUID) error {
|
func (v *Invoker) TerminateSession(sessionID uuid.UUID) error {
|
||||||
return termSession(v.client, sessionID)
|
return termSession(v.client, sessionID)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue