Merge pull request #2683 from nspcc-dev/historic-cli

Historic calls from CLI
This commit is contained in:
Roman Khimov 2022-09-09 15:08:22 +03:00 committed by GitHub
commit a1ca28446d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 210 additions and 80 deletions

View file

@ -602,6 +602,39 @@ func TestContract_TestInvokeScript(t *testing.T) {
"--rpc-endpoint", "http://123456789",
"--in", goodNef)
})
t.Run("good", func(t *testing.T) {
e.Run(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--in", goodNef)
})
t.Run("good with hashed signer", func(t *testing.T) {
e.Run(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--in", goodNef, "--", util.Uint160{1, 2, 3}.StringLE())
})
t.Run("good with addressed signer", func(t *testing.T) {
e.Run(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--in", goodNef, "--", address.Uint160ToString(util.Uint160{1, 2, 3}))
})
t.Run("historic, invalid", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--historic", "bad",
"--in", goodNef)
})
t.Run("historic, index", func(t *testing.T) {
e.Run(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--historic", "0",
"--in", goodNef)
})
t.Run("historic, hash", func(t *testing.T) {
e.Run(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--historic", e.Chain.GetHeaderHash(0).StringLE(),
"--in", goodNef)
})
}
func TestComlileAndInvokeFunction(t *testing.T) {
@ -618,16 +651,6 @@ func TestComlileAndInvokeFunction(t *testing.T) {
"--config", "testdata/deploy/neo-go.yml",
"--out", nefName, "--manifest", manifestName)
// Check that it is possible to invoke before deploy.
// This doesn't make much sense, because every method has an offset
// which is contained in the manifest. This should be either removed or refactored.
e.Run(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--in", nefName, "--", util.Uint160{1, 2, 3}.StringLE())
e.Run(t, "neo-go", "contract", "testinvokescript",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--in", nefName, "--", address.Uint160ToString(util.Uint160{1, 2, 3}))
tmp := t.TempDir()
configPath := filepath.Join(tmp, "config.yaml")
cfg := config.Wallet{
@ -689,11 +712,14 @@ func TestComlileAndInvokeFunction(t *testing.T) {
e.Run(t, cmd...)
res := new(result.Invoke)
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException)
require.Len(t, res.Stack, 1)
require.Equal(t, []byte("on create|sub create"), res.Stack[0].Value())
checkGetValueOut := func(str string) {
res := new(result.Invoke)
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException)
require.Len(t, res.Stack, 1)
require.Equal(t, []byte(str), res.Stack[0].Value())
}
checkGetValueOut("on create|sub create")
// deploy verification contract
hVerify := deployVerifyContract(t, e)
@ -866,6 +892,12 @@ func TestComlileAndInvokeFunction(t *testing.T) {
})
})
var (
hashBeforeUpdate util.Uint256
indexBeforeUpdate uint32
indexAfterUpdate uint32
stateBeforeUpdate util.Uint256
)
t.Run("Update", func(t *testing.T) {
nefName := filepath.Join(tmpDir, "updated.nef")
manifestName := filepath.Join(tmpDir, "updated.manifest.json")
@ -884,6 +916,11 @@ func TestComlileAndInvokeFunction(t *testing.T) {
rawManifest, err := os.ReadFile(manifestName)
require.NoError(t, err)
indexBeforeUpdate = e.Chain.BlockHeight()
hashBeforeUpdate = e.Chain.CurrentHeaderHash()
mptBeforeUpdate, err := e.Chain.GetStateRoot(indexBeforeUpdate)
require.NoError(t, err)
stateBeforeUpdate = mptBeforeUpdate.Root
e.In.WriteString("one\r")
e.Run(t, "neo-go", "contract", "invokefunction",
"--rpc-endpoint", "http://"+e.RPC.Addr,
@ -895,16 +932,40 @@ func TestComlileAndInvokeFunction(t *testing.T) {
)
e.checkTxPersisted(t, "Sent invocation transaction ")
indexAfterUpdate = e.Chain.BlockHeight()
e.In.WriteString("one\r")
e.Run(t, "neo-go", "contract", "testinvokefunction",
"--rpc-endpoint", "http://"+e.RPC.Addr,
h.StringLE(), "getValue")
res := new(result.Invoke)
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
require.Equal(t, vmstate.Halt.String(), res.State)
require.Len(t, res.Stack, 1)
require.Equal(t, []byte("on update|sub update"), res.Stack[0].Value())
checkGetValueOut("on update|sub update")
})
t.Run("historic", func(t *testing.T) {
t.Run("bad ref", func(t *testing.T) {
e.RunWithError(t, "neo-go", "contract", "testinvokefunction",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--historic", "bad",
h.StringLE(), "getValue")
})
for name, ref := range map[string]string{
"by index": strconv.FormatUint(uint64(indexBeforeUpdate), 10),
"by block hash": hashBeforeUpdate.StringLE(),
"by state hash": stateBeforeUpdate.StringLE(),
} {
t.Run(name, func(t *testing.T) {
e.Run(t, "neo-go", "contract", "testinvokefunction",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--historic", ref,
h.StringLE(), "getValue")
})
checkGetValueOut("on create|sub create")
}
t.Run("updated historic", func(t *testing.T) {
e.Run(t, "neo-go", "contract", "testinvokefunction",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--historic", strconv.FormatUint(uint64(indexAfterUpdate), 10),
h.StringLE(), "getValue")
checkGetValueOut("on update|sub update")
})
})
}

View file

@ -161,6 +161,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
}
tokenID := mint(t)
var hashBeforeTransfer = e.Chain.CurrentHeaderHash()
// check the balance
cmdCheckBalance := []string{"neo-go", "wallet", "nep11", "balance",
@ -358,6 +359,24 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// check balance after transfer
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, nftOwnerAddr)
// historic calls still remember the good old days.
cmdOwnerOf = append(cmdOwnerOf, "--historic", hashBeforeTransfer.StringLE())
e.Run(t, cmdOwnerOf...)
e.checkNextLine(t, nftOwnerAddr)
cmdTokensOf = append(cmdTokensOf, "--historic", hashBeforeTransfer.StringLE())
e.Run(t, cmdTokensOf...)
require.Equal(t, hex.EncodeToString(tokenID), e.getNextLine(t))
cmdTokens = append(cmdTokens, "--historic", hashBeforeTransfer.StringLE())
e.Run(t, cmdTokens...)
require.Equal(t, hex.EncodeToString(tokenID), e.getNextLine(t))
// this one is not affected by transfer, but anyway
cmdProperties = append(cmdProperties, "--historic", hashBeforeTransfer.StringLE())
e.Run(t, cmdProperties...)
require.Equal(t, fmt.Sprintf(`{"name":"HASHY %s"}`, base64.StdEncoding.EncodeToString(tokenID)), e.getNextLine(t))
}
func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) {

View file

@ -6,10 +6,14 @@ package options
import (
"context"
"errors"
"strconv"
"time"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/urfave/cli"
)
@ -42,7 +46,14 @@ var RPC = []cli.Flag{
},
}
// Historic is a flag for commands that can perform historic invocations.
var Historic = cli.StringFlag{
Name: "historic",
Usage: "Use historic state (height, block hash or state root hash)",
}
var errNoEndpoint = errors.New("no RPC endpoint specified, use option '--" + RPCEndpointFlag + "' or '-r'")
var errInvalidHistoric = errors.New("invalid 'historic' parameter, neither a block number, nor a block/state hash")
// GetNetwork examines Context's flags and returns the appropriate network. It
// defaults to PrivNet if no flags are given.
@ -85,3 +96,35 @@ func GetRPCClient(gctx context.Context, ctx *cli.Context) (*rpcclient.Client, cl
}
return c, nil
}
// GetInvoker returns an invoker using the given RPC client, context and signers.
// It parses "--historic" parameter to adjust it.
func GetInvoker(c *rpcclient.Client, ctx *cli.Context, signers []transaction.Signer) (*invoker.Invoker, cli.ExitCoder) {
historic := ctx.String("historic")
if historic == "" {
return invoker.New(c, signers), nil
}
if index, err := strconv.ParseUint(historic, 10, 32); err == nil {
return invoker.NewHistoricAtHeight(uint32(index), c, signers), nil
}
if u256, err := util.Uint256DecodeStringLE(historic); err == nil {
// Might as well be a block hash, but it makes no practical difference.
return invoker.NewHistoricWithState(u256, c, signers), nil
}
return nil, cli.NewExitError(errInvalidHistoric, 1)
}
// GetRPCWithInvoker combines GetRPCClient with GetInvoker for cases where it's
// appropriate to do so.
func GetRPCWithInvoker(gctx context.Context, ctx *cli.Context, signers []transaction.Signer) (*rpcclient.Client, *invoker.Invoker, cli.ExitCoder) {
c, err := GetRPCClient(gctx, ctx)
if err != nil {
return nil, nil, err
}
inv, err := GetInvoker(c, ctx, signers)
if err != nil {
c.Close()
return nil, nil, err
}
return c, inv, err
}

View file

@ -23,6 +23,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/management"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
@ -110,8 +111,11 @@ func NewCommands() []cli.Command {
Name: "in, i",
Usage: "Input location of the .nef file that needs to be invoked",
},
options.Historic,
}
testInvokeScriptFlags = append(testInvokeScriptFlags, options.RPC...)
testInvokeFunctionFlags := []cli.Flag{options.Historic}
testInvokeFunctionFlags = append(testInvokeFunctionFlags, options.RPC...)
invokeFunctionFlags := []cli.Flag{
walletFlag,
walletConfigFlag,
@ -213,7 +217,7 @@ func NewCommands() []cli.Command {
{
Name: "testinvokefunction",
Usage: "invoke deployed contract on the blockchain (test mode)",
UsageText: "neo-go contract testinvokefunction -r endpoint scripthash [method] [arguments...] [--] [signers...]",
UsageText: "neo-go contract testinvokefunction -r endpoint [--historic index/hash] scripthash [method] [arguments...] [--] [signers...]",
Description: `Executes given (as a script hash) deployed script with the given method,
arguments and signers (sender is not included by default). If no method is given
"" is passed to the script, if no arguments are given, an empty array is
@ -328,12 +332,12 @@ func NewCommands() []cli.Command {
`CustomContracts:1011120009070e030d0f0e020d0c06050e030c02:0x1211100009070e030d0f0e020d0c06050e030c02'
`,
Action: testInvokeFunction,
Flags: options.RPC,
Flags: testInvokeFunctionFlags,
},
{
Name: "testinvokescript",
Usage: "Invoke compiled AVM code in NEF format on the blockchain (test mode, not creating a transaction for it)",
UsageText: "neo-go contract testinvokescript -r endpoint -i input.nef [signers...]",
UsageText: "neo-go contract testinvokescript -r endpoint -i input.nef [--historic index/hash] [signers...]",
Description: `Executes given compiled AVM instructions in NEF format with the given set of
signers not included sender by default. See testinvokefunction documentation
for the details about parameters.
@ -608,8 +612,9 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
err error
exitErr *cli.ExitError
operation string
params = make([]smartcontract.Parameter, 0)
params []interface{}
paramsStart = 1
scParams []smartcontract.Parameter
cosigners []transaction.Signer
cosignersOffset = 0
)
@ -629,10 +634,14 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
paramsStart++
if len(args) > paramsStart {
cosignersOffset, params, err = cmdargs.ParseParams(args[paramsStart:], true)
cosignersOffset, scParams, err = cmdargs.ParseParams(args[paramsStart:], true)
if err != nil {
return cli.NewExitError(err, 1)
}
params = make([]interface{}, len(scParams))
for i := range scParams {
params[i] = scParams[i]
}
}
cosignersStart := paramsStart + cosignersOffset
@ -653,18 +662,17 @@ func invokeInternal(ctx *cli.Context, signAndPush bool) error {
defer w.Close()
}
_, err = invokeWithArgs(ctx, acc, w, script, operation, params, cosigners)
return err
return invokeWithArgs(ctx, acc, w, script, operation, params, cosigners)
}
func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet, script util.Uint160, operation string, params []smartcontract.Parameter, cosigners []transaction.Signer) (util.Uint160, error) {
func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet, script util.Uint160, operation string, params []interface{}, cosigners []transaction.Signer) error {
var (
err error
gas, sysgas fixedn.Fixed8
signersAccounts []actor.SignerAccount
resp *result.Invoke
sender util.Uint160
signAndPush = acc != nil
inv *invoker.Invoker
act *actor.Actor
)
if signAndPush {
@ -672,35 +680,37 @@ func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet,
sysgas = flags.Fixed8FromContext(ctx, "sysgas")
signersAccounts, err = cmdargs.GetSignersAccounts(acc, wall, cosigners, transaction.None)
if err != nil {
return sender, cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1)
return cli.NewExitError(fmt.Errorf("invalid signers: %w", err), 1)
}
sender = signersAccounts[0].Signer.Account
}
gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()
c, err := options.GetRPCClient(gctx, ctx)
if err != nil {
return sender, err
return err
}
if signAndPush {
act, err = actor.New(c, signersAccounts)
if err != nil {
return sender, cli.NewExitError(fmt.Errorf("failed to create RPC actor: %w", err), 1)
return cli.NewExitError(fmt.Errorf("failed to create RPC actor: %w", err), 1)
}
inv = &act.Invoker
} else {
inv, err = options.GetInvoker(c, ctx, cosigners)
if err != nil {
return err
}
}
out := ctx.String("out")
// It's a bit easier to keep this as is (not using invoker.Invoker)
// during transition period. Mostly because of the need to convert params
// to []interface{}.
resp, err = c.InvokeFunction(script, operation, params, cosigners)
resp, err = inv.Call(script, operation, params...)
if err != nil {
return sender, cli.NewExitError(err, 1)
return cli.NewExitError(err, 1)
}
if resp.State != "HALT" {
errText := fmt.Sprintf("Warning: %s VM state returned from the RPC node: %s", resp.State, resp.FaultException)
if !signAndPush {
return sender, cli.NewExitError(errText, 1)
return cli.NewExitError(errText, 1)
}
action := "send"
@ -710,25 +720,25 @@ func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet,
process = "Saving"
}
if !ctx.Bool("force") {
return sender, cli.NewExitError(errText+".\nUse --force flag to "+action+" the transaction anyway.", 1)
return cli.NewExitError(errText+".\nUse --force flag to "+action+" the transaction anyway.", 1)
}
fmt.Fprintln(ctx.App.Writer, errText+".\n"+process+" transaction...")
}
if !signAndPush {
b, err := json.MarshalIndent(resp, "", " ")
if err != nil {
return sender, cli.NewExitError(err, 1)
return cli.NewExitError(err, 1)
}
fmt.Fprintln(ctx.App.Writer, string(b))
} else {
if len(resp.Script) == 0 {
return sender, cli.NewExitError(errors.New("no script returned from the RPC node"), 1)
return cli.NewExitError(errors.New("no script returned from the RPC node"), 1)
}
ver := act.GetVersion()
tx, err := act.MakeUnsignedUncheckedRun(resp.Script, resp.GasConsumed+int64(sysgas), nil)
if err != nil {
return sender, cli.NewExitError(fmt.Errorf("failed to create tx: %w", err), 1)
return cli.NewExitError(fmt.Errorf("failed to create tx: %w", err), 1)
}
tx.NetworkFee += int64(gas)
if out != "" {
@ -736,7 +746,7 @@ func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet,
tx.ValidUntilBlock += (ver.Protocol.MaxValidUntilBlockIncrement - uint32(ver.Protocol.ValidatorsCount)) - 2
m := act.GetNetwork()
if err := paramcontext.InitAndSave(m, tx, acc, out); err != nil {
return sender, cli.NewExitError(err, 1)
return cli.NewExitError(err, 1)
}
fmt.Fprintln(ctx.App.Writer, tx.Hash().StringLE())
} else {
@ -744,7 +754,7 @@ func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet,
promptTime := time.Now()
err := input.ConfirmTx(ctx.App.Writer, tx)
if err != nil {
return sender, cli.NewExitError(err, 1)
return cli.NewExitError(err, 1)
}
waitTime := time.Since(promptTime)
// Compensate for confirmation waiting.
@ -752,13 +762,13 @@ func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet,
}
txHash, _, err := act.SignAndSend(tx)
if err != nil {
return sender, cli.NewExitError(fmt.Errorf("failed to push invocation tx: %w", err), 1)
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %w", err), 1)
}
fmt.Fprintf(ctx.App.Writer, "Sent invocation transaction %s\n", txHash.StringLE())
}
}
return sender, nil
return nil
}
func testInvokeScript(ctx *cli.Context) error {
@ -784,12 +794,12 @@ func testInvokeScript(ctx *cli.Context) error {
gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()
c, err := options.GetRPCClient(gctx, ctx)
_, inv, err := options.GetRPCWithInvoker(gctx, ctx, signers)
if err != nil {
return err
}
resp, err := c.InvokeScript(nefFile.Script, signers)
resp, err := inv.Run(nefFile.Script)
if err != nil {
return cli.NewExitError(err, 1)
}
@ -925,16 +935,8 @@ func contractDeploy(ctx *cli.Context) error {
return cli.NewExitError(fmt.Errorf("failed to read manifest file: %w", err), 1)
}
appCallParams := []smartcontract.Parameter{
{
Type: smartcontract.ByteArrayType,
Value: f,
},
{
Type: smartcontract.ByteArrayType,
Value: manifestBytes,
},
}
var appCallParams = []interface{}{f, manifestBytes}
signOffset, data, err := cmdargs.ParseParams(ctx.Args(), true)
if err != nil {
return cli.NewExitError(fmt.Errorf("unable to parse 'data' parameter: %w", err), 1)
@ -951,6 +953,7 @@ func contractDeploy(ctx *cli.Context) error {
return cli.NewExitError(fmt.Errorf("can't get sender address: %w", err), 1)
}
defer w.Close()
sender := acc.ScriptHash()
cosigners, sgnErr := cmdargs.GetSignersFromContext(ctx, signOffset)
if sgnErr != nil {
@ -962,7 +965,7 @@ func contractDeploy(ctx *cli.Context) error {
}}
}
sender, extErr := invokeWithArgs(ctx, acc, w, management.Hash, "deploy", appCallParams, cosigners)
extErr := invokeWithArgs(ctx, acc, w, management.Hash, "deploy", appCallParams, cosigners)
if extErr != nil {
return extErr
}

View file

@ -13,7 +13,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"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/invoker"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util"
@ -116,50 +115,55 @@ func newNEP11Commands() []cli.Command {
{
Name: "properties",
Usage: "print properties of NEP-11 token",
UsageText: "properties --rpc-endpoint <node> --timeout <time> --token <hash> --id <token-id>",
UsageText: "properties --rpc-endpoint <node> [--timeout <time>] --token <hash> --id <token-id> [--historic <block/hash>]",
Action: printNEP11Properties,
Flags: append([]cli.Flag{
tokenAddressFlag,
tokenID,
options.Historic,
}, options.RPC...),
},
{
Name: "ownerOf",
Usage: "print owner of non-divisible NEP-11 token with the specified ID",
UsageText: "ownerOf --rpc-endpoint <node> --timeout <time> --token <hash> --id <token-id>",
UsageText: "ownerOf --rpc-endpoint <node> [--timeout <time>] --token <hash> --id <token-id> [--historic <block/hash>]",
Action: printNEP11NDOwner,
Flags: append([]cli.Flag{
tokenAddressFlag,
tokenID,
options.Historic,
}, options.RPC...),
},
{
Name: "ownerOfD",
Usage: "print set of owners of divisible NEP-11 token with the specified ID (" + maxIters + " will be printed at max)",
UsageText: "ownerOfD --rpc-endpoint <node> --timeout <time> --token <hash> --id <token-id>",
UsageText: "ownerOfD --rpc-endpoint <node> [--timeout <time>] --token <hash> --id <token-id> [--historic <block/hash>]",
Action: printNEP11DOwner,
Flags: append([]cli.Flag{
tokenAddressFlag,
tokenID,
options.Historic,
}, options.RPC...),
},
{
Name: "tokensOf",
Usage: "print list of tokens IDs for the specified NFT owner (" + maxIters + " will be printed at max)",
UsageText: "tokensOf --rpc-endpoint <node> --timeout <time> --token <hash> --address <addr>",
UsageText: "tokensOf --rpc-endpoint <node> [--timeout <time>] --token <hash> --address <addr> [--historic <block/hash>]",
Action: printNEP11TokensOf,
Flags: append([]cli.Flag{
tokenAddressFlag,
ownerAddressFlag,
options.Historic,
}, options.RPC...),
},
{
Name: "tokens",
Usage: "print list of tokens IDs minted by the specified NFT (optional method; " + maxIters + " will be printed at max)",
UsageText: "tokens --rpc-endpoint <node> --timeout <time> --token <hash>",
UsageText: "tokens --rpc-endpoint <node> [--timeout <time>] --token <hash> [--historic <block/hash>]",
Action: printNEP11Tokens,
Flags: append([]cli.Flag{
tokenAddressFlag,
options.Historic,
}, options.RPC...),
},
}
@ -256,13 +260,13 @@ func printNEP11Owner(ctx *cli.Context, divisible bool) error {
gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()
c, err := options.GetRPCClient(gctx, ctx)
_, inv, err := options.GetRPCWithInvoker(gctx, ctx, nil)
if err != nil {
return cli.NewExitError(err, 1)
return err
}
if divisible {
n11 := nep11.NewDivisibleReader(invoker.New(c, nil), tokenHash.Uint160())
n11 := nep11.NewDivisibleReader(inv, tokenHash.Uint160())
result, err := n11.OwnerOfExpanded(tokenIDBytes, config.DefaultMaxIteratorResultItems)
if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to call NEP-11 divisible `ownerOf` method: %s", err.Error()), 1)
@ -271,7 +275,7 @@ func printNEP11Owner(ctx *cli.Context, divisible bool) error {
fmt.Fprintln(ctx.App.Writer, address.Uint160ToString(h))
}
} else {
n11 := nep11.NewNonDivisibleReader(invoker.New(c, nil), tokenHash.Uint160())
n11 := nep11.NewNonDivisibleReader(inv, tokenHash.Uint160())
result, err := n11.OwnerOf(tokenIDBytes)
if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to call NEP-11 non-divisible `ownerOf` method: %s", err.Error()), 1)
@ -297,12 +301,12 @@ func printNEP11TokensOf(ctx *cli.Context) error {
gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()
c, err := options.GetRPCClient(gctx, ctx)
_, inv, err := options.GetRPCWithInvoker(gctx, ctx, nil)
if err != nil {
return cli.NewExitError(err, 1)
return err
}
n11 := nep11.NewBaseReader(invoker.New(c, nil), tokenHash.Uint160())
n11 := nep11.NewBaseReader(inv, tokenHash.Uint160())
result, err := n11.TokensOfExpanded(acc.Uint160(), config.DefaultMaxIteratorResultItems)
if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to call NEP-11 `tokensOf` method: %s", err.Error()), 1)
@ -327,12 +331,12 @@ func printNEP11Tokens(ctx *cli.Context) error {
gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()
c, err := options.GetRPCClient(gctx, ctx)
_, inv, err := options.GetRPCWithInvoker(gctx, ctx, nil)
if err != nil {
return cli.NewExitError(err, 1)
return err
}
n11 := nep11.NewBaseReader(invoker.New(c, nil), tokenHash.Uint160())
n11 := nep11.NewBaseReader(inv, tokenHash.Uint160())
result, err := n11.TokensExpanded(config.DefaultMaxIteratorResultItems)
if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to call optional NEP-11 `tokens` method: %s", err.Error()), 1)
@ -366,12 +370,12 @@ func printNEP11Properties(ctx *cli.Context) error {
gctx, cancel := options.GetTimeoutContext(ctx)
defer cancel()
c, err := options.GetRPCClient(gctx, ctx)
_, inv, err := options.GetRPCWithInvoker(gctx, ctx, nil)
if err != nil {
return cli.NewExitError(err, 1)
return err
}
n11 := nep11.NewBaseReader(invoker.New(c, nil), tokenHash.Uint160())
n11 := nep11.NewBaseReader(inv, tokenHash.Uint160())
result, err := n11.Properties(tokenIDBytes)
if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to call NEP-11 `properties` method: %s", err.Error()), 1)