2022-10-05 09:30:54 +00:00
package vm
2018-03-30 16:15:06 +00:00
import (
2018-04-04 19:41:19 +00:00
"bytes"
2021-08-12 13:39:46 +00:00
"crypto/elliptic"
2020-06-25 14:25:05 +00:00
"encoding/base64"
2022-10-05 12:06:20 +00:00
"encoding/binary"
2018-04-04 19:41:19 +00:00
"encoding/hex"
2020-12-21 11:27:07 +00:00
"encoding/json"
2018-04-10 09:45:31 +00:00
"errors"
2018-03-30 16:15:06 +00:00
"fmt"
2022-02-15 12:55:25 +00:00
"io"
2020-04-16 12:28:34 +00:00
"math/big"
2018-03-30 16:15:06 +00:00
"os"
"strconv"
"strings"
2020-07-23 11:09:21 +00:00
"text/tabwriter"
2018-03-30 16:15:06 +00:00
2022-02-15 12:55:25 +00:00
"github.com/chzyer/readline"
"github.com/kballard/go-shellquote"
2022-10-10 11:00:26 +00:00
"github.com/nspcc-dev/neo-go/cli/cmdargs"
2022-10-04 12:38:42 +00:00
"github.com/nspcc-dev/neo-go/cli/flags"
2022-10-07 12:27:24 +00:00
"github.com/nspcc-dev/neo-go/cli/options"
2022-10-10 10:59:37 +00:00
"github.com/nspcc-dev/neo-go/cli/paramcontext"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/compiler"
2022-02-15 12:55:25 +00:00
"github.com/nspcc-dev/neo-go/pkg/config"
2022-10-03 12:05:48 +00:00
"github.com/nspcc-dev/neo-go/pkg/core"
"github.com/nspcc-dev/neo-go/pkg/core/interop"
2022-10-07 12:27:24 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
2022-10-03 12:05:48 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
2022-10-10 10:59:37 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
2021-08-12 13:39:46 +00:00
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
2020-07-23 11:09:21 +00:00
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
2021-05-28 09:07:03 +00:00
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
2020-12-21 11:27:07 +00:00
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
2022-10-11 11:59:51 +00:00
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
2022-10-03 12:05:48 +00:00
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
2020-07-23 11:09:21 +00:00
"github.com/nspcc-dev/neo-go/pkg/util"
2021-07-18 12:55:37 +00:00
"github.com/nspcc-dev/neo-go/pkg/util/slice"
2020-03-03 14:21:42 +00:00
"github.com/nspcc-dev/neo-go/pkg/vm"
2020-06-03 12:55:06 +00:00
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
2022-02-15 12:55:25 +00:00
"github.com/urfave/cli"
2022-10-03 12:05:48 +00:00
"go.uber.org/zap"
2022-10-07 12:27:24 +00:00
"go.uber.org/zap/zapcore"
2018-03-30 16:15:06 +00:00
)
2019-09-10 16:41:11 +00:00
const (
2022-10-03 12:05:48 +00:00
chainKey = "chain"
2022-10-04 11:19:42 +00:00
chainCfgKey = "chainCfg"
2022-10-03 12:05:48 +00:00
icKey = "ic"
2022-02-15 12:55:25 +00:00
manifestKey = "manifest"
exitFuncKey = "exitFunc"
readlineInstanceKey = "readlineKey"
printLogoKey = "printLogoKey"
2019-09-10 16:41:11 +00:00
)
2019-09-10 16:11:48 +00:00
2022-10-04 11:19:42 +00:00
// Various flag names.
const (
2022-10-04 12:38:42 +00:00
verboseFlagFullName = "verbose"
historicFlagFullName = "historic"
backwardsFlagFullName = "backwards"
2022-10-05 10:56:26 +00:00
diffFlagFullName = "diff"
2022-10-04 11:19:42 +00:00
)
2022-10-04 11:53:31 +00:00
var historicFlag = cli . IntFlag {
Name : historicFlagFullName ,
Usage : "Height for historic script invocation (for MPT-enabled blockchain configuration with KeepOnlyLatestState setting disabled). " +
"Assuming that block N-th is specified as an argument, the historic invocation is based on the storage state of height N and fake currently-accepting block with index N+1." ,
}
2022-02-15 12:55:25 +00:00
var commands = [ ] cli . Command {
2019-09-10 16:11:48 +00:00
{
2022-02-15 12:55:25 +00:00
Name : "exit" ,
Usage : "Exit the VM prompt" ,
Description : "Exit the VM prompt" ,
Action : handleExit ,
2019-09-10 16:11:48 +00:00
} ,
{
2022-02-15 12:55:25 +00:00
Name : "ip" ,
Usage : "Show current instruction" ,
Description : "Show current instruction" ,
Action : handleIP ,
2019-09-10 16:11:48 +00:00
} ,
{
2022-02-15 12:55:25 +00:00
Name : "break" ,
Usage : "Place a breakpoint" ,
UsageText : ` break <ip> ` ,
Description : ` break < ip >
2019-09-10 16:11:48 +00:00
< ip > is mandatory parameter , example :
> break 12 ` ,
2022-02-15 12:55:25 +00:00
Action : handleBreak ,
2019-09-10 16:11:48 +00:00
} ,
2022-10-12 10:16:19 +00:00
{
Name : "jump" ,
Usage : "Jump to the specified instruction (absolute IP value)" ,
UsageText : ` jump <ip> ` ,
Description : ` jump < ip >
< ip > is mandatory parameter , example :
> jump 12 ` ,
Action : handleJump ,
} ,
2019-09-10 16:11:48 +00:00
{
2022-02-15 12:55:25 +00:00
Name : "estack" ,
Usage : "Show evaluation stack contents" ,
Description : "Show evaluation stack contents" ,
Action : handleXStack ,
2019-09-10 16:11:48 +00:00
} ,
{
2022-02-15 12:55:25 +00:00
Name : "istack" ,
Usage : "Show invocation stack contents" ,
Description : "Show invocation stack contents" ,
Action : handleXStack ,
2019-09-10 16:11:48 +00:00
} ,
2021-09-08 14:27:11 +00:00
{
2022-02-15 12:55:25 +00:00
Name : "sslot" ,
Usage : "Show static slot contents" ,
Description : "Show static slot contents" ,
Action : handleSlots ,
2021-09-08 14:27:11 +00:00
} ,
{
2022-02-15 12:55:25 +00:00
Name : "lslot" ,
Usage : "Show local slot contents" ,
Description : "Show local slot contents" ,
Action : handleSlots ,
2021-09-08 14:27:11 +00:00
} ,
{
2022-02-15 12:55:25 +00:00
Name : "aslot" ,
Usage : "Show arguments slot contents" ,
Description : "Show arguments slot contents" ,
Action : handleSlots ,
2021-09-08 14:27:11 +00:00
} ,
2019-09-10 16:11:48 +00:00
{
2022-02-15 12:55:25 +00:00
Name : "loadnef" ,
2022-10-11 11:59:51 +00:00
Usage : "Load a NEF-consistent script into the VM optionally attaching to it provided signers with scopes" ,
UsageText : ` loadnef <file> <manifest> [<signer-with-scope>, ...] ` ,
2022-10-04 11:53:31 +00:00
Flags : [ ] cli . Flag { historicFlag } ,
2022-10-11 11:59:51 +00:00
Description : ` loadnef [ -- historic < height > ] < file > < manifest > [ < signer - with - scope > , ... ]
< file > and < manifest > parameters are mandatory .
` + cmdargs.SignersParsingDoc + `
Example :
2020-12-21 11:27:07 +00:00
> loadnef / path / to / script . nef / path / to / manifest . json ` ,
2022-02-15 12:55:25 +00:00
Action : handleLoadNEF ,
2019-09-10 16:11:48 +00:00
} ,
2020-06-25 14:25:05 +00:00
{
2022-02-15 12:55:25 +00:00
Name : "loadbase64" ,
2022-10-11 11:59:51 +00:00
Usage : "Load a base64-encoded script string into the VM optionally attaching to it provided signers with scopes" ,
UsageText : ` loadbase64 [--historic <height>] <string> [<signer-with-scope>, ...] ` ,
2022-10-04 11:53:31 +00:00
Flags : [ ] cli . Flag { historicFlag } ,
2022-10-11 11:59:51 +00:00
Description : ` loadbase64 [ -- historic < height > ] < string > [ < signer - with - scope > , ... ]
2022-02-15 12:55:25 +00:00
2022-10-11 11:59:51 +00:00
< string > is mandatory parameter .
` + cmdargs.SignersParsingDoc + `
Example :
2020-06-25 14:25:05 +00:00
> loadbase64 AwAQpdToAAAADBQV9ehtQR1OrVZVhtHtoUHRfoE + agwUzmFvf3Rhfg / EuAVYOvJgKiON9j8TwAwIdHJhbnNmZXIMFDt9NxHG8Mz5sdypA9G / odiW8SOMQWJ9W1I4 ` ,
2022-02-15 12:55:25 +00:00
Action : handleLoadBase64 ,
2020-06-25 14:25:05 +00:00
} ,
2019-09-10 16:11:48 +00:00
{
2022-02-15 12:55:25 +00:00
Name : "loadhex" ,
2022-10-11 11:59:51 +00:00
Usage : "Load a hex-encoded script string into the VM optionally attaching to it provided signers with scopes" ,
UsageText : ` loadhex [--historic <height>] <string> [<signer-with-scope>, ...] ` ,
2022-10-04 11:53:31 +00:00
Flags : [ ] cli . Flag { historicFlag } ,
2022-10-11 11:59:51 +00:00
Description : ` loadhex [ -- historic < height > ] < string > [ < signer - with - scope > , ... ]
2022-02-15 12:55:25 +00:00
2022-10-11 11:59:51 +00:00
< string > is mandatory parameter .
` + cmdargs.SignersParsingDoc + `
Example :
2020-06-25 07:38:53 +00:00
> loadhex 0 c0c48656c6c6f20776f726c6421 ` ,
2022-02-15 12:55:25 +00:00
Action : handleLoadHex ,
2019-09-10 16:11:48 +00:00
} ,
{
2022-02-15 12:55:25 +00:00
Name : "loadgo" ,
2022-10-11 11:59:51 +00:00
Usage : "Compile and load a Go file with the manifest into the VM optionally attaching to it provided signers with scopes" ,
UsageText : ` loadgo [--historic <height>] <file> [<signer-with-scope>, ...] ` ,
2022-10-04 11:53:31 +00:00
Flags : [ ] cli . Flag { historicFlag } ,
2022-10-11 11:59:51 +00:00
Description : ` loadgo [ -- historic < height > ] < file > [ < signer - with - scope > , ... ]
< file > is mandatory parameter .
` + cmdargs.SignersParsingDoc + `
2022-02-15 12:55:25 +00:00
2022-10-11 11:59:51 +00:00
Example :
2020-06-25 07:38:53 +00:00
> loadgo / path / to / file . go ` ,
2022-02-15 12:55:25 +00:00
Action : handleLoadGo ,
2019-09-10 16:11:48 +00:00
} ,
2022-10-10 10:59:37 +00:00
{
2022-10-12 10:21:37 +00:00
Name : "loadtx" ,
Usage : "Load transaction into the VM from chain or from parameter context file" ,
2022-10-10 10:59:37 +00:00
UsageText : ` loadtx [--historic <height>] <file-or-hash> ` ,
Flags : [ ] cli . Flag { historicFlag } ,
Description : ` loadtx [ -- historic < height > ] < file - or - hash >
Load transaction into the VM from chain or from parameter context file .
The transaction script will be loaded into VM ; the resulting execution context will use the provided transaction as script container including its signers , hash and nonce .
< file - or - hash > is mandatory parameter , example :
> loadtx / path / to / file ` ,
Action : handleLoadTx ,
} ,
2022-10-11 09:22:07 +00:00
{
Name : "loaddeployed" ,
2022-10-12 10:21:37 +00:00
Usage : "Load deployed contract into the VM from chain optionally attaching to it provided signers with scopes" ,
2022-10-11 11:59:51 +00:00
UsageText : ` loaddeployed [--historic <height>] <hash-or-address-or-id> [<signer-with-scope>, ...] ` ,
2022-10-11 09:22:07 +00:00
Flags : [ ] cli . Flag { historicFlag } ,
2022-10-11 11:59:51 +00:00
Description : ` loaddeployed [ -- historic < height > ] < hash - or - address - or - id > [ < signer - with - scope > , ... ]
Load deployed contract into the VM from chain optionally attaching to it provided signers with scopes .
If ' -- historic ' flag specified , then the historic contract state ( historic script and manifest ) will be loaded .
2022-10-11 09:22:07 +00:00
2022-10-11 11:59:51 +00:00
< hash - or - address - or - id > is mandatory parameter .
` + cmdargs.SignersParsingDoc + `
2022-10-11 09:22:07 +00:00
2022-10-11 11:59:51 +00:00
Example :
2022-10-11 09:22:07 +00:00
> loaddeployed 0x0000000009070e030d0f0e020d0c06050e030c02 ` ,
Action : handleLoadDeployed ,
} ,
2022-02-16 15:43:12 +00:00
{
Name : "reset" ,
2022-10-04 11:53:31 +00:00
Usage : "Unload compiled script from the VM and reset context to proper (possibly, historic) state" ,
Flags : [ ] cli . Flag { historicFlag } ,
2022-02-16 15:43:12 +00:00
Action : handleReset ,
} ,
2020-07-23 11:09:21 +00:00
{
2022-02-15 12:55:25 +00:00
Name : "parse" ,
Usage : "Parse provided argument and convert it into other possible formats" ,
UsageText : ` parse <arg> ` ,
Description : ` parse < arg >
2020-07-23 11:09:21 +00:00
< arg > is an argument which is tried to be interpreted as an item of different types
2022-02-15 12:55:25 +00:00
and converted to other formats . Strings are escaped and output in quotes . ` ,
Action : handleParse ,
2020-07-23 11:09:21 +00:00
} ,
2019-09-10 16:11:48 +00:00
{
2022-02-15 12:55:25 +00:00
Name : "run" ,
Usage : "Execute the current loaded script" ,
UsageText : ` run [<method> [<parameter>...]] ` ,
Description : ` run [ < method > [ < parameter > ... ] ]
2019-09-10 16:11:48 +00:00
2022-02-17 12:54:46 +00:00
< method > is a contract method , specified in manifest . It can be '_' which will push
parameters onto the stack and execute from the current offset .
2019-09-10 16:41:11 +00:00
< parameter > is a parameter ( can be repeated multiple times ) that can be specified
2022-10-10 11:00:26 +00:00
using the same rules as for ' contract testinvokefunction ' command :
` + cmdargs.ParamsParsingDoc + `
2019-09-10 16:30:32 +00:00
2019-09-10 16:11:48 +00:00
Example :
2022-10-10 11:00:26 +00:00
> run put string : "Something to put" ` ,
2022-02-15 12:55:25 +00:00
Action : handleRun ,
2019-09-10 16:11:48 +00:00
} ,
{
2022-02-15 12:55:25 +00:00
Name : "cont" ,
Usage : "Continue execution of the current loaded script" ,
Description : "Continue execution of the current loaded script" ,
Action : handleCont ,
2019-09-10 16:11:48 +00:00
} ,
{
2022-02-15 12:55:25 +00:00
Name : "step" ,
Usage : "Step (n) instruction in the program" ,
UsageText : ` step [<n>] ` ,
Description : ` step [ < n > ]
2019-09-10 16:11:48 +00:00
< n > is optional parameter to specify number of instructions to run , example :
> step 10 ` ,
2022-02-15 12:55:25 +00:00
Action : handleStep ,
2019-09-10 16:11:48 +00:00
} ,
2019-10-14 15:37:11 +00:00
{
2022-02-15 12:55:25 +00:00
Name : "stepinto" ,
Usage : "Stepinto instruction to take in the debugger" ,
Description : ` Usage : stepInto
2019-10-14 15:37:11 +00:00
example :
> stepinto ` ,
2022-02-15 12:55:25 +00:00
Action : handleStepInto ,
2019-10-14 15:37:11 +00:00
} ,
{
2022-02-15 12:55:25 +00:00
Name : "stepout" ,
Usage : "Stepout instruction to take in the debugger" ,
Description : ` stepOut
2019-10-14 15:37:11 +00:00
example :
> stepout ` ,
2022-02-15 12:55:25 +00:00
Action : handleStepOut ,
2019-10-14 15:37:11 +00:00
} ,
{
2022-02-15 12:55:25 +00:00
Name : "stepover" ,
Usage : "Stepover instruction to take in the debugger" ,
Description : ` stepOver
2019-10-14 15:37:11 +00:00
example :
> stepover ` ,
2022-02-15 12:55:25 +00:00
Action : handleStepOver ,
2019-10-14 15:37:11 +00:00
} ,
2019-09-10 16:11:48 +00:00
{
2022-02-15 12:55:25 +00:00
Name : "ops" ,
Usage : "Dump opcodes of the current loaded program" ,
Description : "Dump opcodes of the current loaded program" ,
Action : handleOps ,
2019-09-10 16:11:48 +00:00
} ,
2022-10-04 10:05:51 +00:00
{
Name : "events" ,
Usage : "Dump events emitted by the current loaded program" ,
Description : "Dump events emitted by the current loaded program" ,
Action : handleEvents ,
} ,
2022-10-04 11:19:42 +00:00
{
Name : "env" ,
Usage : "Dump state of the chain that is used for VM CLI invocations (use -v for verbose node configuration)" ,
UsageText : ` env [-v] ` ,
Flags : [ ] cli . Flag {
cli . BoolFlag {
Name : verboseFlagFullName + ",v" ,
Usage : "Print the whole blockchain node configuration." ,
} ,
} ,
Description : ` env [ - v ]
Dump state of the chain that is used for VM CLI invocations ( use - v for verbose node configuration ) .
Example :
> env - v ` ,
Action : handleEnv ,
} ,
2022-10-04 12:38:42 +00:00
{
2022-10-12 10:21:37 +00:00
Name : "storage" ,
Usage : "Dump storage of the contract with the specified hash, address or ID as is at the current stage of script invocation" ,
2022-10-05 10:56:26 +00:00
UsageText : ` storage <hash-or-address-or-id> [<prefix>] [--backwards] [--diff] ` ,
2022-10-04 12:38:42 +00:00
Flags : [ ] cli . Flag {
cli . BoolFlag {
Name : backwardsFlagFullName + ",b" ,
Usage : "Backwards traversal direction" ,
} ,
2022-10-05 10:56:26 +00:00
cli . BoolFlag {
Name : diffFlagFullName + ",d" ,
2022-10-05 12:06:20 +00:00
Usage : "Dump only those storage items that were added or changed during the current script invocation. Note that this call won't show removed storage items, use 'changes' command for that." ,
2022-10-05 10:56:26 +00:00
} ,
2022-10-04 12:38:42 +00:00
} ,
2022-10-05 10:56:26 +00:00
Description : ` storage < hash - or - address - or - id > < prefix > [ -- backwards ] [ -- diff ]
2022-10-04 12:38:42 +00:00
Dump storage of the contract with the specified hash , address or ID as is at the current stage of script invocation .
Can be used if no script is loaded .
Hex - encoded storage items prefix may be specified ( empty by default to return the whole set of storage items ) .
If seek prefix is not empty , then it ' s trimmed from the resulting keys .
Items are sorted . Backwards seek direction may be specified ( false by default , which means forwards storage seek direction ) .
2022-10-05 10:56:26 +00:00
It is possible to dump only those storage items that were added or changed during current script invocation ( use -- diff flag for it ) .
2022-10-05 12:06:20 +00:00
To dump the whole set of storage changes including removed items use ' changes ' command .
2022-10-04 12:38:42 +00:00
Example :
2022-10-05 10:56:26 +00:00
> storage 0x0000000009070e030d0f0e020d0c06050e030c02 030 e -- backwards -- diff ` ,
2022-10-04 12:38:42 +00:00
Action : handleStorage ,
} ,
2022-10-05 12:06:20 +00:00
{
2022-10-12 10:21:37 +00:00
Name : "changes" ,
Usage : "Dump storage changes as is at the current stage of loaded script invocation" ,
2022-10-05 12:06:20 +00:00
UsageText : ` changes [<hash-or-address-or-id> [<prefix>]] ` ,
Description : ` changes [ < hash - or - address - or - id > [ < prefix > ] ]
Dump storage changes as is at the current stage of loaded script invocation .
If no script is loaded or executed , then no changes are present .
The contract hash , address or ID may be specified as the first parameter to dump the specified contract storage changes .
Hex - encoded search prefix ( without contract ID ) may be specified to dump matching storage changes .
Resulting values are not sorted .
Example :
> changes 0x0000000009070e030d0f0e020d0c06050e030c02 030 e ` ,
Action : handleChanges ,
} ,
2018-03-30 16:15:06 +00:00
}
2022-02-17 10:01:40 +00:00
var completer * readline . PrefixCompleter
func init ( ) {
var pcItems [ ] readline . PrefixCompleterInterface
for _ , c := range commands {
if ! c . Hidden {
var flagsItems [ ] readline . PrefixCompleterInterface
for _ , f := range c . Flags {
names := strings . SplitN ( f . GetName ( ) , ", " , 2 ) // only long name will be offered
flagsItems = append ( flagsItems , readline . PcItem ( "--" + names [ 0 ] ) )
}
pcItems = append ( pcItems , readline . PcItem ( c . Name , flagsItems ... ) )
}
}
completer = readline . NewPrefixCompleter ( pcItems ... )
}
2020-12-01 14:15:01 +00:00
// Various errors.
var (
ErrMissingParameter = errors . New ( "missing argument" )
ErrInvalidParameter = errors . New ( "can't parse argument" )
)
2022-10-07 12:47:21 +00:00
// CLI object for interacting with the VM.
type CLI struct {
2022-10-03 12:05:48 +00:00
chain * core . Blockchain
2022-02-15 12:55:25 +00:00
shell * cli . App
2018-03-30 16:15:06 +00:00
}
2022-10-07 12:47:21 +00:00
// NewWithConfig returns new CLI instance using provided config and (optionally)
2022-10-03 12:05:48 +00:00
// provided node config for state-backed VM.
2022-10-07 12:47:21 +00:00
func NewWithConfig ( printLogotype bool , onExit func ( int ) , c * readline . Config , cfg config . Config ) ( * CLI , error ) {
2022-02-17 10:01:40 +00:00
if c . AutoComplete == nil {
// Autocomplete commands/flags on TAB.
c . AutoComplete = completer
}
2022-02-15 12:55:25 +00:00
l , err := readline . NewEx ( c )
if err != nil {
2022-10-03 12:05:48 +00:00
return nil , fmt . Errorf ( "failed to create readline instance: %w" , err )
2022-02-15 12:55:25 +00:00
}
ctl := cli . NewApp ( )
ctl . Name = "VM CLI"
// Note: need to set empty `ctl.HelpName` and `ctl.UsageText`, otherwise
// `filepath.Base(os.Args[0])` will be used which is `neo-go`.
ctl . HelpName = ""
ctl . UsageText = ""
ctl . Writer = l . Stdout ( )
ctl . ErrWriter = l . Stderr ( )
ctl . Version = config . Version
ctl . Usage = "Official VM CLI for Neo-Go"
// Override default error handler in order not to exit on error.
ctl . ExitErrHandler = func ( context * cli . Context , err error ) { }
ctl . Commands = commands
2022-10-03 12:05:48 +00:00
store , err := storage . NewStore ( cfg . ApplicationConfiguration . DBConfiguration )
if err != nil {
writeErr ( ctl . ErrWriter , fmt . Errorf ( "failed to open DB, clean in-memory storage will be used: %w" , err ) )
cfg . ApplicationConfiguration . DBConfiguration . Type = dbconfig . InMemoryDB
store = storage . NewMemoryStore ( )
}
2022-10-07 12:27:24 +00:00
log , logCloser , err := options . HandleLoggingParams ( false , cfg . ApplicationConfiguration )
if err != nil {
return nil , cli . NewExitError ( fmt . Errorf ( "failed to init logger: %w" , err ) , 1 )
}
filter := zap . WrapCore ( func ( z zapcore . Core ) zapcore . Core {
return options . NewFilteringCore ( z , func ( entry zapcore . Entry ) bool {
// Log only Runtime.Notify messages.
return entry . Level == zapcore . InfoLevel && entry . Message == runtime . SystemRuntimeLogMessage
} )
} )
fLog := log . WithOptions ( filter )
2022-10-03 12:05:48 +00:00
exitF := func ( i int ) {
_ = store . Close ( )
2022-10-07 12:27:24 +00:00
if logCloser != nil {
_ = logCloser ( )
}
2022-10-03 12:05:48 +00:00
onExit ( i )
}
2022-10-07 12:27:24 +00:00
chain , err := core . NewBlockchain ( store , cfg . ProtocolConfiguration , fLog )
2022-10-03 12:05:48 +00:00
if err != nil {
return nil , cli . NewExitError ( fmt . Errorf ( "could not initialize blockchain: %w" , err ) , 1 )
}
// Do not run chain, we need only state-related functionality from it.
2022-10-06 10:24:57 +00:00
ic , err := chain . GetTestVM ( trigger . Application , nil , nil )
if err != nil {
return nil , cli . NewExitError ( fmt . Errorf ( "failed to create test VM: %w" , err ) , 1 )
}
2022-10-03 12:05:48 +00:00
2022-10-07 12:47:21 +00:00
vmcli := CLI {
2022-10-03 12:05:48 +00:00
chain : chain ,
2022-02-15 12:55:25 +00:00
shell : ctl ,
2019-09-10 16:11:48 +00:00
}
2022-02-15 12:55:25 +00:00
vmcli . shell . Metadata = map [ string ] interface { } {
2022-10-03 12:05:48 +00:00
chainKey : chain ,
2022-10-04 11:19:42 +00:00
chainCfgKey : cfg ,
2022-10-03 12:05:48 +00:00
icKey : ic ,
2022-02-15 12:55:25 +00:00
manifestKey : new ( manifest . Manifest ) ,
2022-10-03 12:05:48 +00:00
exitFuncKey : exitF ,
2022-02-15 12:55:25 +00:00
readlineInstanceKey : l ,
printLogoKey : printLogotype ,
2019-09-10 16:11:48 +00:00
}
2022-02-15 12:55:25 +00:00
changePrompt ( vmcli . shell )
2022-10-03 12:05:48 +00:00
return & vmcli , nil
2019-09-10 16:11:48 +00:00
}
2022-02-15 12:55:25 +00:00
func getExitFuncFromContext ( app * cli . App ) func ( int ) {
return app . Metadata [ exitFuncKey ] . ( func ( int ) )
}
func getReadlineInstanceFromContext ( app * cli . App ) * readline . Instance {
return app . Metadata [ readlineInstanceKey ] . ( * readline . Instance )
}
func getVMFromContext ( app * cli . App ) * vm . VM {
2022-10-03 12:05:48 +00:00
return getInteropContextFromContext ( app ) . VM
}
func getChainFromContext ( app * cli . App ) * core . Blockchain {
return app . Metadata [ chainKey ] . ( * core . Blockchain )
2019-09-10 16:11:48 +00:00
}
2022-10-04 11:19:42 +00:00
func getChainConfigFromContext ( app * cli . App ) config . Config {
return app . Metadata [ chainCfgKey ] . ( config . Config )
}
2022-10-03 12:05:48 +00:00
func getInteropContextFromContext ( app * cli . App ) * interop . Context {
return app . Metadata [ icKey ] . ( * interop . Context )
2022-02-16 15:43:12 +00:00
}
2022-02-15 12:55:25 +00:00
func getManifestFromContext ( app * cli . App ) * manifest . Manifest {
return app . Metadata [ manifestKey ] . ( * manifest . Manifest )
2020-12-21 11:27:07 +00:00
}
2022-02-15 12:55:25 +00:00
func getPrintLogoFromContext ( app * cli . App ) bool {
return app . Metadata [ printLogoKey ] . ( bool )
}
2022-10-03 12:05:48 +00:00
func setInteropContextInContext ( app * cli . App , ic * interop . Context ) {
app . Metadata [ icKey ] = ic
}
2022-02-15 12:55:25 +00:00
func setManifestInContext ( app * cli . App , m * manifest . Manifest ) {
2022-10-04 10:19:18 +00:00
app . Metadata [ manifestKey ] = m
2020-12-21 11:27:07 +00:00
}
2022-02-15 12:55:25 +00:00
func checkVMIsReady ( app * cli . App ) bool {
v := getVMFromContext ( app )
2019-09-10 16:11:48 +00:00
if v == nil || ! v . Ready ( ) {
2022-02-15 12:55:25 +00:00
writeErr ( app . Writer , errors . New ( "VM is not ready: no program loaded" ) )
2019-09-10 16:11:48 +00:00
return false
2018-03-30 16:15:06 +00:00
}
2019-09-10 16:11:48 +00:00
return true
}
2022-02-15 12:55:25 +00:00
func handleExit ( c * cli . Context ) error {
2022-10-03 12:05:48 +00:00
finalizeInteropContext ( c . App )
2022-02-15 12:55:25 +00:00
l := getReadlineInstanceFromContext ( c . App )
_ = l . Close ( )
exit := getExitFuncFromContext ( c . App )
fmt . Fprintln ( c . App . Writer , "Bye!" )
exit ( 0 )
return nil
2018-03-30 16:15:06 +00:00
}
2022-02-15 12:55:25 +00:00
func handleIP ( c * cli . Context ) error {
if ! checkVMIsReady ( c . App ) {
return nil
2018-03-30 16:15:06 +00:00
}
2022-02-15 12:55:25 +00:00
v := getVMFromContext ( c . App )
2020-12-01 13:53:38 +00:00
ctx := v . Context ( )
if ctx . NextIP ( ) < ctx . LenInstr ( ) {
ip , opcode := v . Context ( ) . NextInstr ( )
2022-02-15 12:55:25 +00:00
fmt . Fprintf ( c . App . Writer , "instruction pointer at %d (%s)\n" , ip , opcode )
2020-12-01 13:53:38 +00:00
} else {
2022-02-15 12:55:25 +00:00
fmt . Fprintln ( c . App . Writer , "execution has finished" )
2020-12-01 13:53:38 +00:00
}
2022-02-15 12:55:25 +00:00
return nil
2019-09-10 16:11:48 +00:00
}
2022-02-15 12:55:25 +00:00
func handleBreak ( c * cli . Context ) error {
if ! checkVMIsReady ( c . App ) {
return nil
2018-03-30 16:15:06 +00:00
}
2022-10-12 10:16:19 +00:00
n , err := getInstructionParameter ( c )
if err != nil {
return err
}
2022-02-15 12:55:25 +00:00
v := getVMFromContext ( c . App )
2022-10-12 10:16:19 +00:00
v . AddBreakPoint ( n )
fmt . Fprintf ( c . App . Writer , "breakpoint added at instruction %d\n" , n )
return nil
}
func handleJump ( c * cli . Context ) error {
if ! checkVMIsReady ( c . App ) {
return nil
}
n , err := getInstructionParameter ( c )
if err != nil {
return err
}
v := getVMFromContext ( c . App )
v . Context ( ) . Jump ( n )
fmt . Fprintf ( c . App . Writer , "jumped to instruction %d\n" , n )
return nil
}
func getInstructionParameter ( c * cli . Context ) ( int , error ) {
2022-02-15 12:55:25 +00:00
args := c . Args ( )
if len ( args ) != 1 {
2022-10-12 10:16:19 +00:00
return 0 , fmt . Errorf ( "%w: <ip>" , ErrMissingParameter )
2019-09-10 16:11:48 +00:00
}
2022-02-15 12:55:25 +00:00
n , err := strconv . Atoi ( args [ 0 ] )
2019-09-10 16:11:48 +00:00
if err != nil {
2022-10-12 10:16:19 +00:00
return 0 , fmt . Errorf ( "%w: %s" , ErrInvalidParameter , err )
2018-03-30 16:15:06 +00:00
}
2022-10-12 10:16:19 +00:00
return n , nil
2019-09-10 16:11:48 +00:00
}
2018-03-30 16:15:06 +00:00
2022-02-15 12:55:25 +00:00
func handleXStack ( c * cli . Context ) error {
v := getVMFromContext ( c . App )
2021-09-08 15:51:34 +00:00
var stackDump string
2022-02-15 12:55:25 +00:00
switch c . Command . Name {
2021-09-08 15:51:34 +00:00
case "estack" :
stackDump = v . DumpEStack ( )
case "istack" :
stackDump = v . DumpIStack ( )
default :
2022-02-15 12:55:25 +00:00
return errors . New ( "unknown stack" )
2021-09-08 15:51:34 +00:00
}
2022-02-15 12:55:25 +00:00
fmt . Fprintln ( c . App . Writer , stackDump )
return nil
2019-09-10 16:11:48 +00:00
}
2018-03-30 16:15:06 +00:00
2022-02-15 12:55:25 +00:00
func handleSlots ( c * cli . Context ) error {
v := getVMFromContext ( c . App )
2021-09-08 14:27:11 +00:00
vmCtx := v . Context ( )
if vmCtx == nil {
2022-02-15 12:55:25 +00:00
return errors . New ( "no program loaded" )
2021-09-08 14:27:11 +00:00
}
var rawSlot string
2022-02-15 12:55:25 +00:00
switch c . Command . Name {
2021-09-08 14:27:11 +00:00
case "sslot" :
rawSlot = vmCtx . DumpStaticSlot ( )
case "lslot" :
rawSlot = vmCtx . DumpLocalSlot ( )
case "aslot" :
rawSlot = vmCtx . DumpArgumentsSlot ( )
default :
2022-02-15 12:55:25 +00:00
return errors . New ( "unknown slot" )
2021-09-08 14:27:11 +00:00
}
2022-02-15 12:55:25 +00:00
fmt . Fprintln ( c . App . Writer , rawSlot )
return nil
2021-09-08 14:27:11 +00:00
}
2022-10-04 11:53:31 +00:00
// prepareVM retrieves --historic flag from context (if set) and resets app state
// (to the specified historic height if given).
2022-10-10 10:59:37 +00:00
func prepareVM ( c * cli . Context , tx * transaction . Transaction ) error {
2022-10-04 11:53:31 +00:00
if c . IsSet ( historicFlagFullName ) {
height := c . Int ( historicFlagFullName )
2022-10-10 10:59:37 +00:00
return resetState ( c . App , tx , uint32 ( height ) )
2022-10-04 11:53:31 +00:00
}
2022-10-10 10:59:37 +00:00
return resetState ( c . App , tx )
2022-10-04 11:53:31 +00:00
}
2022-02-15 12:55:25 +00:00
func handleLoadNEF ( c * cli . Context ) error {
args := c . Args ( )
if len ( args ) < 2 {
return fmt . Errorf ( "%w: <file> <manifest>" , ErrMissingParameter )
2020-06-25 14:32:58 +00:00
}
2022-10-11 11:59:51 +00:00
b , err := os . ReadFile ( args [ 0 ] )
if err != nil {
return err
}
nef , err := nef . FileFromBytes ( b )
if err != nil {
return fmt . Errorf ( "failed to decode NEF file: %w" , err )
2019-09-10 16:11:48 +00:00
}
2022-02-15 12:55:25 +00:00
m , err := getManifestFromFile ( args [ 1 ] )
2020-12-21 11:27:07 +00:00
if err != nil {
2022-02-15 12:55:25 +00:00
return fmt . Errorf ( "failed to read manifest: %w" , err )
2020-12-21 11:27:07 +00:00
}
2022-10-11 11:59:51 +00:00
var signers [ ] transaction . Signer
if len ( args ) > 2 {
signers , err = cmdargs . ParseSigners ( c . Args ( ) [ 2 : ] )
if err != nil {
return fmt . Errorf ( "%w: %v" , ErrInvalidParameter , err )
}
}
err = prepareVM ( c , createFakeTransaction ( nef . Script , signers ) )
if err != nil {
return err
}
v := getVMFromContext ( c . App )
2022-02-15 12:55:25 +00:00
fmt . Fprintf ( c . App . Writer , "READY: loaded %d instructions\n" , v . Context ( ) . LenInstr ( ) )
setManifestInContext ( c . App , m )
changePrompt ( c . App )
return nil
2019-09-10 16:11:48 +00:00
}
2018-03-30 16:15:06 +00:00
2022-02-15 12:55:25 +00:00
func handleLoadBase64 ( c * cli . Context ) error {
args := c . Args ( )
if len ( args ) < 1 {
return fmt . Errorf ( "%w: <string>" , ErrMissingParameter )
2020-06-25 14:32:58 +00:00
}
2022-02-15 12:55:25 +00:00
b , err := base64 . StdEncoding . DecodeString ( args [ 0 ] )
2020-06-25 14:25:05 +00:00
if err != nil {
2022-02-15 12:55:25 +00:00
return fmt . Errorf ( "%w: %s" , ErrInvalidParameter , err )
2020-06-25 14:25:05 +00:00
}
2022-10-11 11:59:51 +00:00
var signers [ ] transaction . Signer
if len ( args ) > 1 {
signers , err = cmdargs . ParseSigners ( args [ 1 : ] )
if err != nil {
return fmt . Errorf ( "%w: %v" , ErrInvalidParameter , err )
}
}
err = prepareVM ( c , createFakeTransaction ( b , signers ) )
if err != nil {
return err
}
v := getVMFromContext ( c . App )
2022-02-15 12:55:25 +00:00
fmt . Fprintf ( c . App . Writer , "READY: loaded %d instructions\n" , v . Context ( ) . LenInstr ( ) )
changePrompt ( c . App )
return nil
2020-06-25 14:25:05 +00:00
}
2022-10-11 11:59:51 +00:00
// createFakeTransaction creates fake transaction with prefilled script, VUB and signers.
func createFakeTransaction ( script [ ] byte , signers [ ] transaction . Signer ) * transaction . Transaction {
return & transaction . Transaction {
Script : script ,
Signers : signers ,
2022-10-06 10:24:57 +00:00
}
2022-10-11 11:59:51 +00:00
}
func handleLoadHex ( c * cli . Context ) error {
2022-02-15 12:55:25 +00:00
args := c . Args ( )
if len ( args ) < 1 {
return fmt . Errorf ( "%w: <string>" , ErrMissingParameter )
2020-06-25 14:32:58 +00:00
}
2022-02-15 12:55:25 +00:00
b , err := hex . DecodeString ( args [ 0 ] )
2019-09-10 16:11:48 +00:00
if err != nil {
2022-02-15 12:55:25 +00:00
return fmt . Errorf ( "%w: %s" , ErrInvalidParameter , err )
2019-09-10 16:11:48 +00:00
}
2022-10-11 11:59:51 +00:00
var signers [ ] transaction . Signer
if len ( args ) > 1 {
signers , err = cmdargs . ParseSigners ( args [ 1 : ] )
if err != nil {
return fmt . Errorf ( "%w: %v" , ErrInvalidParameter , err )
}
}
err = prepareVM ( c , createFakeTransaction ( b , signers ) )
if err != nil {
return err
}
v := getVMFromContext ( c . App )
2022-02-15 12:55:25 +00:00
fmt . Fprintf ( c . App . Writer , "READY: loaded %d instructions\n" , v . Context ( ) . LenInstr ( ) )
changePrompt ( c . App )
return nil
2019-09-10 16:11:48 +00:00
}
2018-03-30 16:15:06 +00:00
2022-02-15 12:55:25 +00:00
func handleLoadGo ( c * cli . Context ) error {
args := c . Args ( )
if len ( args ) < 1 {
return fmt . Errorf ( "%w: <file>" , ErrMissingParameter )
2020-06-25 14:32:58 +00:00
}
2021-12-02 13:36:29 +00:00
2022-02-15 12:55:25 +00:00
name := strings . TrimSuffix ( args [ 0 ] , ".go" )
b , di , err := compiler . CompileWithOptions ( args [ 0 ] , nil , & compiler . Options { Name : name } )
2019-09-10 16:11:48 +00:00
if err != nil {
2022-02-15 12:55:25 +00:00
return fmt . Errorf ( "failed to compile: %w" , err )
2019-09-10 16:11:48 +00:00
}
2018-03-30 16:15:06 +00:00
2020-12-21 11:27:07 +00:00
// Don't perform checks, just load.
m , err := di . ConvertToManifest ( & compiler . Options { } )
if err != nil {
2022-02-15 12:55:25 +00:00
return fmt . Errorf ( "can't create manifest: %w" , err )
2020-12-21 11:27:07 +00:00
}
2022-10-11 11:59:51 +00:00
var signers [ ] transaction . Signer
if len ( args ) > 1 {
signers , err = cmdargs . ParseSigners ( args [ 1 : ] )
if err != nil {
return fmt . Errorf ( "%w: %v" , ErrInvalidParameter , err )
}
}
2020-12-21 11:27:07 +00:00
2022-10-11 11:59:51 +00:00
err = prepareVM ( c , createFakeTransaction ( b . Script , signers ) )
if err != nil {
return err
}
v := getVMFromContext ( c . App )
setManifestInContext ( c . App , m )
2022-02-15 12:55:25 +00:00
fmt . Fprintf ( c . App . Writer , "READY: loaded %d instructions\n" , v . Context ( ) . LenInstr ( ) )
changePrompt ( c . App )
2022-02-16 15:43:12 +00:00
return nil
}
2022-10-10 10:59:37 +00:00
func handleLoadTx ( c * cli . Context ) error {
args := c . Args ( )
if len ( args ) < 1 {
return fmt . Errorf ( "%w: <file-or-hash>" , ErrMissingParameter )
}
var (
tx * transaction . Transaction
err error
)
h , err := util . Uint256DecodeStringLE ( strings . TrimPrefix ( args [ 0 ] , "0x" ) )
if err != nil {
pc , err := paramcontext . Read ( args [ 0 ] )
if err != nil {
return fmt . Errorf ( "invalid tx hash or path to parameter context: %w" , err )
}
var ok bool
tx , ok = pc . Verifiable . ( * transaction . Transaction )
if ! ok {
return errors . New ( "failed to retrieve transaction from parameter context: verifiable item is not a transaction" )
}
} else {
bc := getChainFromContext ( c . App )
tx , _ , err = bc . GetTransaction ( h )
if err != nil {
return fmt . Errorf ( "failed to get transaction from chain: %w" , err )
}
}
err = prepareVM ( c , tx )
if err != nil {
return err
}
v := getVMFromContext ( c . App )
fmt . Fprintf ( c . App . Writer , "READY: loaded %d instructions\n" , v . Context ( ) . LenInstr ( ) )
changePrompt ( c . App )
return nil
}
2022-10-11 09:22:07 +00:00
func handleLoadDeployed ( c * cli . Context ) error {
err := prepareVM ( c , nil ) // prepare historic IC if needed (for further historic contract state retrieving).
if err != nil {
return err
}
if ! c . Args ( ) . Present ( ) {
return errors . New ( "contract hash, address or ID is mandatory argument" )
}
hashOrID := c . Args ( ) . Get ( 0 )
ic := getInteropContextFromContext ( c . App )
h , err := flags . ParseAddress ( hashOrID )
if err != nil {
i , err := strconv . ParseInt ( hashOrID , 10 , 32 )
if err != nil {
return fmt . Errorf ( "failed to parse contract hash, address or ID: %w" , err )
}
bc := getChainFromContext ( c . App )
h , err = bc . GetContractScriptHash ( int32 ( i ) ) // @fixme: can be improved after #2702 to retrieve historic state of destroyed contract by ID.
if err != nil {
return fmt . Errorf ( "failed to retrieve contract hash by ID: %w" , err )
}
}
cs , err := ic . GetContract ( h ) // will return historic contract state.
if err != nil {
return fmt . Errorf ( "contract %s not found: %w" , h . StringLE ( ) , err )
}
2022-10-11 11:59:51 +00:00
var signers [ ] transaction . Signer
if len ( c . Args ( ) ) > 1 {
signers , err = cmdargs . ParseSigners ( c . Args ( ) [ 1 : ] )
if err != nil {
return fmt . Errorf ( "%w: %v" , ErrInvalidParameter , err )
}
}
err = prepareVM ( c , createFakeTransaction ( cs . NEF . Script , signers ) ) // prepare VM one more time for proper IC initialization.
if err != nil {
return err
}
2022-10-12 07:19:47 +00:00
ic = getInteropContextFromContext ( c . App ) // fetch newly-created IC.
ic . ReuseVM ( ic . VM ) // clear previously loaded program and context.
ic . VM . LoadScriptWithHash ( cs . NEF . Script , cs . Hash , callflag . All )
fmt . Fprintf ( c . App . Writer , "READY: loaded %d instructions\n" , ic . VM . Context ( ) . LenInstr ( ) )
2022-10-11 09:22:07 +00:00
setManifestInContext ( c . App , & cs . Manifest )
changePrompt ( c . App )
return nil
}
2022-02-16 15:43:12 +00:00
func handleReset ( c * cli . Context ) error {
2022-10-10 10:59:37 +00:00
err := prepareVM ( c , nil )
2022-10-06 10:24:57 +00:00
if err != nil {
return err
}
2022-02-16 15:43:12 +00:00
changePrompt ( c . App )
2022-02-15 12:55:25 +00:00
return nil
2019-09-10 16:11:48 +00:00
}
2018-03-30 16:15:06 +00:00
2022-10-03 12:05:48 +00:00
// finalizeInteropContext calls finalizer for the current interop context.
func finalizeInteropContext ( app * cli . App ) {
ic := getInteropContextFromContext ( app )
ic . Finalize ( )
}
// resetInteropContext calls finalizer for current interop context and replaces
2022-10-12 07:19:47 +00:00
// it with the newly created one. If transaction is provided, then its script is
// loaded into bound VM.
2022-10-10 10:59:37 +00:00
func resetInteropContext ( app * cli . App , tx * transaction . Transaction , height ... uint32 ) error {
2022-10-03 12:05:48 +00:00
finalizeInteropContext ( app )
bc := getChainFromContext ( app )
2022-10-04 11:53:31 +00:00
var (
newIc * interop . Context
err error
)
if len ( height ) != 0 {
2022-10-11 11:59:51 +00:00
if tx != nil {
tx . ValidUntilBlock = height [ 0 ] + 1
}
2022-10-10 10:59:37 +00:00
newIc , err = bc . GetTestHistoricVM ( trigger . Application , tx , height [ 0 ] + 1 )
2022-10-04 11:53:31 +00:00
if err != nil {
return fmt . Errorf ( "failed to create historic VM for height %d: %w" , height [ 0 ] , err )
}
} else {
2022-10-11 11:59:51 +00:00
if tx != nil {
tx . ValidUntilBlock = bc . BlockHeight ( ) + 1
}
2022-10-10 10:59:37 +00:00
newIc , err = bc . GetTestVM ( trigger . Application , tx , nil )
2022-10-04 11:53:31 +00:00
if err != nil {
return fmt . Errorf ( "failed to create VM: %w" , err )
}
2022-10-06 10:24:57 +00:00
}
2022-10-12 07:19:47 +00:00
if tx != nil {
newIc . VM . LoadWithFlags ( tx . Script , callflag . All )
}
2022-10-04 11:53:31 +00:00
2022-10-03 12:05:48 +00:00
setInteropContextInContext ( app , newIc )
2022-10-06 10:24:57 +00:00
return nil
2022-10-03 12:05:48 +00:00
}
2022-10-04 10:19:18 +00:00
// resetManifest removes manifest from app context.
func resetManifest ( app * cli . App ) {
setManifestInContext ( app , nil )
}
// resetState resets state of the app (clear interop context and manifest) so that it's ready
// to load new program.
2022-10-10 10:59:37 +00:00
func resetState ( app * cli . App , tx * transaction . Transaction , height ... uint32 ) error {
err := resetInteropContext ( app , tx , height ... )
2022-10-06 10:24:57 +00:00
if err != nil {
2022-10-04 11:53:31 +00:00
return err
2022-10-06 10:24:57 +00:00
}
2022-10-04 10:19:18 +00:00
resetManifest ( app )
2022-10-06 10:24:57 +00:00
return nil
2022-10-04 10:19:18 +00:00
}
2020-12-21 11:27:07 +00:00
func getManifestFromFile ( name string ) ( * manifest . Manifest , error ) {
2022-02-22 16:27:32 +00:00
bs , err := os . ReadFile ( name )
2020-12-21 11:27:07 +00:00
if err != nil {
return nil , fmt . Errorf ( "%w: can't read manifest" , ErrInvalidParameter )
}
var m manifest . Manifest
if err := json . Unmarshal ( bs , & m ) ; err != nil {
return nil , fmt . Errorf ( "%w: can't unmarshal manifest" , ErrInvalidParameter )
}
return & m , nil
}
2022-02-15 12:55:25 +00:00
func handleRun ( c * cli . Context ) error {
v := getVMFromContext ( c . App )
m := getManifestFromContext ( c . App )
args := c . Args ( )
if len ( args ) != 0 {
2019-09-10 16:11:48 +00:00
var (
2020-12-21 11:27:07 +00:00
params [ ] stackitem . Item
offset int
err error
2022-02-15 12:55:25 +00:00
runCurrent = args [ 0 ] != "_"
2019-09-10 16:11:48 +00:00
)
2020-12-21 11:27:07 +00:00
2022-10-10 11:00:26 +00:00
_ , scParams , err := cmdargs . ParseParams ( args [ 1 : ] , true )
2021-01-26 14:37:34 +00:00
if err != nil {
2022-10-10 11:00:26 +00:00
return fmt . Errorf ( "%w: %v" , ErrInvalidParameter , err )
}
params = make ( [ ] stackitem . Item , len ( scParams ) )
for i := range scParams {
params [ i ] , err = scParams [ i ] . ToStackItem ( )
if err != nil {
return fmt . Errorf ( "failed to convert parameter #%d to stackitem: %w" , i , err )
}
2021-01-26 14:37:34 +00:00
}
2020-12-21 11:27:07 +00:00
if runCurrent {
2022-10-04 10:19:18 +00:00
if m == nil {
return fmt . Errorf ( "manifest is not loaded; either use 'run' command to run loaded script from the start or use 'loadgo' and 'loadnef' commands to provide manifest" )
}
2022-02-15 12:55:25 +00:00
md := m . ABI . GetMethod ( args [ 0 ] , len ( params ) )
2020-12-21 11:27:07 +00:00
if md == nil {
2022-02-15 12:55:25 +00:00
return fmt . Errorf ( "%w: method not found" , ErrInvalidParameter )
2020-12-21 11:27:07 +00:00
}
offset = md . Offset
}
for i := len ( params ) - 1 ; i >= 0 ; i -- {
v . Estack ( ) . PushVal ( params [ i ] )
}
if runCurrent {
vm CLI: check whether VM is ready before jumping to the instruction
It allows to avoid panic:
```
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0xdab469]
goroutine 1 [running]:
github.com/nspcc-dev/neo-go/pkg/vm.(*VM).Jump(...)
github.com/nspcc-dev/neo-go/pkg/vm/vm.go:1506
github.com/nspcc-dev/neo-go/pkg/vm/cli.handleRun(0xc0005988f0)
github.com/nspcc-dev/neo-go/pkg/vm/cli/cli.go:413 +0x2e9
github.com/abiosoft/ishell/v2.(*Shell).handleCommand(0xc0004320f0, {0xc00032c7c0, 0xc0002a3920, 0x0})
github.com/abiosoft/ishell/v2@v2.0.2/ishell.go:279 +0x143
github.com/abiosoft/ishell/v2.handleInput(0xc0004320f0, {0xc00032c7c0, 0x3, 0x4})
github.com/abiosoft/ishell/v2@v2.0.2/ishell.go:233 +0x31
github.com/abiosoft/ishell/v2.(*Shell).run(0xc0004320f0)
github.com/abiosoft/ishell/v2@v2.0.2/ishell.go:212 +0x30f
github.com/abiosoft/ishell/v2.(*Shell).Run(0xc0004320f0)
github.com/abiosoft/ishell/v2@v2.0.2/ishell.go:112 +0x28
github.com/nspcc-dev/neo-go/pkg/vm/cli.(*VMCLI).Run(0xc000224030)
github.com/nspcc-dev/neo-go/pkg/vm/cli/cli.go:538 +0x39
github.com/nspcc-dev/neo-go/cli/vm.startVMPrompt(0xc0001f46e0)
github.com/nspcc-dev/neo-go/cli/vm/vm.go:28 +0xb4
github.com/urfave/cli.HandleAction({0xe65fa0, 0x1161c68}, 0x2)
github.com/urfave/cli@v1.22.5/app.go:524 +0xa8
github.com/urfave/cli.Command.Run({{0xfed435, 0x2}, {0x0, 0x0}, {0x0, 0x0, 0x0}, {0x100576d, 0x19}, {0x0, ...}, ...}, ...)
github.com/urfave/cli@v1.22.5/command.go:173 +0x652
github.com/urfave/cli.(*App).Run(0xc0001016c0, {0xc0000c6000, 0x2, 0x2})
github.com/urfave/cli@v1.22.5/app.go:277 +0x705
main.main()
./main.go:19 +0x33
```
2021-09-08 09:38:53 +00:00
if ! v . Ready ( ) {
2022-02-15 12:55:25 +00:00
return errors . New ( "no program loaded" )
vm CLI: check whether VM is ready before jumping to the instruction
It allows to avoid panic:
```
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0xdab469]
goroutine 1 [running]:
github.com/nspcc-dev/neo-go/pkg/vm.(*VM).Jump(...)
github.com/nspcc-dev/neo-go/pkg/vm/vm.go:1506
github.com/nspcc-dev/neo-go/pkg/vm/cli.handleRun(0xc0005988f0)
github.com/nspcc-dev/neo-go/pkg/vm/cli/cli.go:413 +0x2e9
github.com/abiosoft/ishell/v2.(*Shell).handleCommand(0xc0004320f0, {0xc00032c7c0, 0xc0002a3920, 0x0})
github.com/abiosoft/ishell/v2@v2.0.2/ishell.go:279 +0x143
github.com/abiosoft/ishell/v2.handleInput(0xc0004320f0, {0xc00032c7c0, 0x3, 0x4})
github.com/abiosoft/ishell/v2@v2.0.2/ishell.go:233 +0x31
github.com/abiosoft/ishell/v2.(*Shell).run(0xc0004320f0)
github.com/abiosoft/ishell/v2@v2.0.2/ishell.go:212 +0x30f
github.com/abiosoft/ishell/v2.(*Shell).Run(0xc0004320f0)
github.com/abiosoft/ishell/v2@v2.0.2/ishell.go:112 +0x28
github.com/nspcc-dev/neo-go/pkg/vm/cli.(*VMCLI).Run(0xc000224030)
github.com/nspcc-dev/neo-go/pkg/vm/cli/cli.go:538 +0x39
github.com/nspcc-dev/neo-go/cli/vm.startVMPrompt(0xc0001f46e0)
github.com/nspcc-dev/neo-go/cli/vm/vm.go:28 +0xb4
github.com/urfave/cli.HandleAction({0xe65fa0, 0x1161c68}, 0x2)
github.com/urfave/cli@v1.22.5/app.go:524 +0xa8
github.com/urfave/cli.Command.Run({{0xfed435, 0x2}, {0x0, 0x0}, {0x0, 0x0, 0x0}, {0x100576d, 0x19}, {0x0, ...}, ...}, ...)
github.com/urfave/cli@v1.22.5/command.go:173 +0x652
github.com/urfave/cli.(*App).Run(0xc0001016c0, {0xc0000c6000, 0x2, 0x2})
github.com/urfave/cli@v1.22.5/app.go:277 +0x705
main.main()
./main.go:19 +0x33
```
2021-09-08 09:38:53 +00:00
}
2021-11-19 13:46:29 +00:00
v . Context ( ) . Jump ( offset )
2021-01-26 14:37:34 +00:00
if initMD := m . ABI . GetMethod ( manifest . MethodInit , 0 ) ; initMD != nil {
2021-11-19 14:15:30 +00:00
v . Call ( initMD . Offset )
2020-12-21 11:27:07 +00:00
}
}
2019-09-10 16:11:48 +00:00
}
2022-02-15 12:55:25 +00:00
runVMWithHandling ( c )
changePrompt ( c . App )
return nil
2019-09-10 16:11:48 +00:00
}
2018-04-04 19:41:19 +00:00
2019-10-22 10:44:14 +00:00
// runVMWithHandling runs VM with handling errors and additional state messages.
2022-02-15 12:55:25 +00:00
func runVMWithHandling ( c * cli . Context ) {
v := getVMFromContext ( c . App )
2019-10-22 10:44:14 +00:00
err := v . Run ( )
if err != nil {
2022-02-15 12:55:25 +00:00
writeErr ( c . App . ErrWriter , err )
2019-10-22 10:44:14 +00:00
}
2022-10-04 10:05:51 +00:00
var (
message string
dumpNtf bool
)
2019-10-22 10:44:14 +00:00
switch {
case v . HasFailed ( ) :
2020-12-01 13:53:38 +00:00
message = "" // the error will be printed on return
2022-10-04 10:05:51 +00:00
dumpNtf = true
2019-10-22 10:44:14 +00:00
case v . HasHalted ( ) :
2021-09-08 15:51:34 +00:00
message = v . DumpEStack ( )
2022-10-04 10:05:51 +00:00
dumpNtf = true
2019-10-22 10:44:14 +00:00
case v . AtBreakpoint ( ) :
ctx := v . Context ( )
2020-12-01 13:53:38 +00:00
if ctx . NextIP ( ) < ctx . LenInstr ( ) {
i , op := ctx . NextInstr ( )
message = fmt . Sprintf ( "at breakpoint %d (%s)" , i , op )
} else {
message = "execution has finished"
}
2019-10-22 10:44:14 +00:00
}
2022-10-04 10:05:51 +00:00
if dumpNtf {
var e string
e , err = dumpEvents ( c . App )
if err == nil && len ( e ) != 0 {
if message != "" {
message += "\n"
}
message += "Events:\n" + e
}
}
2019-10-22 10:44:14 +00:00
if message != "" {
2022-02-15 12:55:25 +00:00
fmt . Fprintln ( c . App . Writer , message )
2019-10-22 10:44:14 +00:00
}
}
2022-02-15 12:55:25 +00:00
func handleCont ( c * cli . Context ) error {
if ! checkVMIsReady ( c . App ) {
return nil
2019-09-10 16:11:48 +00:00
}
2022-02-15 12:55:25 +00:00
runVMWithHandling ( c )
changePrompt ( c . App )
return nil
2019-09-10 16:11:48 +00:00
}
2022-02-15 12:55:25 +00:00
func handleStep ( c * cli . Context ) error {
2019-09-10 16:11:48 +00:00
var (
n = 1
err error
)
2022-02-15 12:55:25 +00:00
if ! checkVMIsReady ( c . App ) {
return nil
2019-09-10 16:11:48 +00:00
}
2022-02-15 12:55:25 +00:00
v := getVMFromContext ( c . App )
args := c . Args ( )
if len ( args ) > 0 {
n , err = strconv . Atoi ( args [ 0 ] )
2018-04-04 19:41:19 +00:00
if err != nil {
2022-02-15 12:55:25 +00:00
return fmt . Errorf ( "%w: %s" , ErrInvalidParameter , err )
2018-04-04 19:41:19 +00:00
}
2019-09-10 16:11:48 +00:00
}
v . AddBreakPointRel ( n )
2022-02-15 12:55:25 +00:00
runVMWithHandling ( c )
changePrompt ( c . App )
return nil
2019-09-10 16:11:48 +00:00
}
2018-03-30 16:15:06 +00:00
2022-02-15 12:55:25 +00:00
func handleStepInto ( c * cli . Context ) error {
return handleStepType ( c , "into" )
2019-10-14 15:37:11 +00:00
}
2022-02-15 12:55:25 +00:00
func handleStepOut ( c * cli . Context ) error {
return handleStepType ( c , "out" )
2019-10-14 15:37:11 +00:00
}
2022-02-15 12:55:25 +00:00
func handleStepOver ( c * cli . Context ) error {
return handleStepType ( c , "over" )
2019-10-14 15:37:11 +00:00
}
2022-02-15 12:55:25 +00:00
func handleStepType ( c * cli . Context , stepType string ) error {
if ! checkVMIsReady ( c . App ) {
return nil
2019-10-14 15:37:11 +00:00
}
2022-02-15 12:55:25 +00:00
v := getVMFromContext ( c . App )
2019-10-22 10:44:14 +00:00
var err error
2019-10-14 15:37:11 +00:00
switch stepType {
case "into" :
2019-10-22 10:44:14 +00:00
err = v . StepInto ( )
2019-10-14 15:37:11 +00:00
case "out" :
2019-10-22 10:44:14 +00:00
err = v . StepOut ( )
2019-10-14 15:37:11 +00:00
case "over" :
2019-10-22 10:44:14 +00:00
err = v . StepOver ( )
}
if err != nil {
2022-02-15 12:55:25 +00:00
return err
2019-10-14 15:37:11 +00:00
}
2022-02-15 12:55:25 +00:00
_ = handleIP ( c )
changePrompt ( c . App )
return nil
2019-10-14 15:37:11 +00:00
}
2022-02-15 12:55:25 +00:00
func handleOps ( c * cli . Context ) error {
if ! checkVMIsReady ( c . App ) {
return nil
2019-09-10 16:11:48 +00:00
}
2022-02-15 12:55:25 +00:00
v := getVMFromContext ( c . App )
2020-12-01 13:52:23 +00:00
out := bytes . NewBuffer ( nil )
v . PrintOps ( out )
2022-02-15 12:55:25 +00:00
fmt . Fprintln ( c . App . Writer , out . String ( ) )
return nil
2019-09-10 16:11:48 +00:00
}
2018-03-30 16:15:06 +00:00
2022-02-15 12:55:25 +00:00
func changePrompt ( app * cli . App ) {
v := getVMFromContext ( app )
l := getReadlineInstanceFromContext ( app )
2020-12-01 13:53:38 +00:00
if v . Ready ( ) && v . Context ( ) . NextIP ( ) >= 0 && v . Context ( ) . NextIP ( ) < v . Context ( ) . LenInstr ( ) {
2022-02-15 12:55:25 +00:00
l . SetPrompt ( fmt . Sprintf ( "\033[32mNEO-GO-VM %d >\033[0m " , v . Context ( ) . NextIP ( ) ) )
2019-09-10 16:11:48 +00:00
} else {
2022-02-15 12:55:25 +00:00
l . SetPrompt ( "\033[32mNEO-GO-VM >\033[0m " )
2018-03-30 16:15:06 +00:00
}
}
2022-10-04 10:05:51 +00:00
func handleEvents ( c * cli . Context ) error {
e , err := dumpEvents ( c . App )
if err != nil {
writeErr ( c . App . ErrWriter , err )
return nil
}
fmt . Fprintln ( c . App . Writer , e )
return nil
}
2022-10-04 11:19:42 +00:00
func handleEnv ( c * cli . Context ) error {
bc := getChainFromContext ( c . App )
cfg := getChainConfigFromContext ( c . App )
2022-10-04 11:53:31 +00:00
ic := getInteropContextFromContext ( c . App )
message := fmt . Sprintf ( "Chain height: %d\nVM height (may differ from chain height in case of historic call): %d\nNetwork magic: %d\nDB type: %s\n" ,
bc . BlockHeight ( ) , ic . BlockHeight ( ) , bc . GetConfig ( ) . Magic , cfg . ApplicationConfiguration . DBConfiguration . Type )
2022-10-04 11:19:42 +00:00
if c . Bool ( verboseFlagFullName ) {
cfgBytes , err := json . MarshalIndent ( cfg , "" , "\t" )
if err != nil {
return fmt . Errorf ( "failed to marshal node configuration: %w" , err )
}
message += "Node config:\n" + string ( cfgBytes ) + "\n"
}
fmt . Fprint ( c . App . Writer , message )
return nil
}
2022-10-04 12:38:42 +00:00
func handleStorage ( c * cli . Context ) error {
2022-10-05 12:06:20 +00:00
id , prefix , err := getDumpArgs ( c )
if err != nil {
return err
2022-10-04 12:38:42 +00:00
}
var (
backwards bool
2022-10-05 10:56:26 +00:00
seekDepth int
2022-10-05 12:06:20 +00:00
ic = getInteropContextFromContext ( c . App )
)
if c . Bool ( backwardsFlagFullName ) {
backwards = true
}
if c . Bool ( diffFlagFullName ) {
seekDepth = 1 // take only upper DAO layer which stores only added or updated items.
}
ic . DAO . Seek ( id , storage . SeekRange {
Prefix : prefix ,
Backwards : backwards ,
SearchDepth : seekDepth ,
} , func ( k , v [ ] byte ) bool {
fmt . Fprintf ( c . App . Writer , "%s: %v\n" , hex . EncodeToString ( k ) , hex . EncodeToString ( v ) )
return true
} )
return nil
}
func handleChanges ( c * cli . Context ) error {
var (
expectedID int32
prefix [ ] byte
err error
hasAgs = c . Args ( ) . Present ( )
)
if hasAgs {
expectedID , prefix , err = getDumpArgs ( c )
if err != nil {
return err
}
}
ic := getInteropContextFromContext ( c . App )
b := ic . DAO . GetBatch ( )
if b == nil {
return nil
}
ops := storage . BatchToOperations ( b )
var notFirst bool
for _ , op := range ops {
id := int32 ( binary . LittleEndian . Uint32 ( op . Key ) )
if hasAgs && ( expectedID != id || ( len ( prefix ) != 0 && ! bytes . HasPrefix ( op . Key [ 4 : ] , prefix ) ) ) {
continue
}
var message string
if notFirst {
message += "\n"
}
message += fmt . Sprintf ( "Contract ID: %d\nState: %s\nKey: %s\n" , id , op . State , hex . EncodeToString ( op . Key [ 4 : ] ) )
if op . Value != nil {
message += fmt . Sprintf ( "Value: %s\n" , hex . EncodeToString ( op . Value ) )
}
fmt . Fprint ( c . App . Writer , message )
notFirst = true
}
return nil
}
// getDumpArgs is a helper function that retrieves contract ID and search prefix (if given).
func getDumpArgs ( c * cli . Context ) ( int32 , [ ] byte , error ) {
2022-10-11 09:22:07 +00:00
id , err := getContractID ( c )
if err != nil {
return 0 , nil , err
}
var prefix [ ] byte
if c . NArg ( ) > 1 {
prefix , err = hex . DecodeString ( c . Args ( ) . Get ( 1 ) )
if err != nil {
return 0 , nil , fmt . Errorf ( "failed to decode prefix from hex: %w" , err )
}
}
return id , prefix , nil
}
// getContractID returns contract ID parsed from the first argument which can be ID,
// hash or address.
func getContractID ( c * cli . Context ) ( int32 , error ) {
2022-10-05 12:06:20 +00:00
if ! c . Args ( ) . Present ( ) {
2022-10-11 09:22:07 +00:00
return 0 , errors . New ( "contract hash, address or ID is mandatory argument" )
2022-10-05 12:06:20 +00:00
}
hashOrID := c . Args ( ) . Get ( 0 )
2022-10-11 09:22:07 +00:00
var ic = getInteropContextFromContext ( c . App )
2022-10-04 12:38:42 +00:00
h , err := flags . ParseAddress ( hashOrID )
if err != nil {
2022-10-07 14:10:04 +00:00
i , err := strconv . ParseInt ( hashOrID , 10 , 32 )
2022-10-04 12:38:42 +00:00
if err != nil {
2022-10-11 09:22:07 +00:00
return 0 , fmt . Errorf ( "failed to parse contract hash, address or ID: %w" , err )
2022-10-04 12:38:42 +00:00
}
2022-10-11 09:22:07 +00:00
return int32 ( i ) , nil
2022-10-04 12:38:42 +00:00
}
2022-10-11 09:22:07 +00:00
cs , err := ic . GetContract ( h )
if err != nil {
return 0 , fmt . Errorf ( "contract %s not found: %w" , h . StringLE ( ) , err )
2022-10-04 12:38:42 +00:00
}
2022-10-11 09:22:07 +00:00
return cs . ID , nil
2022-10-04 12:38:42 +00:00
}
2022-10-04 10:05:51 +00:00
func dumpEvents ( app * cli . App ) ( string , error ) {
ic := getInteropContextFromContext ( app )
if len ( ic . Notifications ) == 0 {
return "" , nil
}
b , err := json . MarshalIndent ( ic . Notifications , "" , "\t" )
if err != nil {
return "" , fmt . Errorf ( "failed to marshal notifications: %w" , err )
}
return string ( b ) , nil
}
2018-03-30 16:15:06 +00:00
// Run waits for user input from Stdin and executes the passed command.
2022-10-07 12:47:21 +00:00
func ( c * CLI ) Run ( ) error {
2022-02-15 12:55:25 +00:00
if getPrintLogoFromContext ( c . shell ) {
printLogo ( c . shell . Writer )
}
l := getReadlineInstanceFromContext ( c . shell )
for {
line , err := l . Readline ( )
2022-09-02 11:29:47 +00:00
if errors . Is ( err , io . EOF ) || errors . Is ( err , readline . ErrInterrupt ) {
2022-02-15 12:55:25 +00:00
return nil // OK, stop execution.
}
if err != nil {
return fmt . Errorf ( "failed to read input: %w" , err ) // Critical error, stop execution.
}
args , err := shellquote . Split ( line )
if err != nil {
writeErr ( c . shell . ErrWriter , fmt . Errorf ( "failed to parse arguments: %w" , err ) )
continue // Not a critical error, continue execution.
}
err = c . shell . Run ( append ( [ ] string { "vm" } , args ... ) )
if err != nil {
writeErr ( c . shell . ErrWriter , err ) // Various command/flags parsing errors and execution errors.
}
2020-12-01 15:27:38 +00:00
}
2018-03-30 16:15:06 +00:00
}
2022-02-15 12:55:25 +00:00
func handleParse ( c * cli . Context ) error {
res , err := Parse ( c . Args ( ) )
2020-08-04 06:40:06 +00:00
if err != nil {
2022-02-15 12:55:25 +00:00
return err
2020-07-23 11:09:21 +00:00
}
2022-02-15 12:55:25 +00:00
fmt . Fprintln ( c . App . Writer , res )
return nil
2020-08-04 06:40:06 +00:00
}
// Parse converts it's argument to other formats.
func Parse ( args [ ] string ) ( string , error ) {
if len ( args ) < 1 {
2020-12-01 14:15:01 +00:00
return "" , ErrMissingParameter
2020-08-04 06:40:06 +00:00
}
arg := args [ 0 ]
2020-07-23 11:09:21 +00:00
buf := bytes . NewBuffer ( nil )
if val , err := strconv . ParseInt ( arg , 10 , 64 ) ; err == nil {
bs := bigint . ToBytes ( big . NewInt ( val ) )
buf . WriteString ( fmt . Sprintf ( "Integer to Hex\t%s\n" , hex . EncodeToString ( bs ) ) )
buf . WriteString ( fmt . Sprintf ( "Integer to Base64\t%s\n" , base64 . StdEncoding . EncodeToString ( bs ) ) )
}
noX := strings . TrimPrefix ( arg , "0x" )
if rawStr , err := hex . DecodeString ( noX ) ; err == nil {
if val , err := util . Uint160DecodeBytesBE ( rawStr ) ; err == nil {
buf . WriteString ( fmt . Sprintf ( "BE ScriptHash to Address\t%s\n" , address . Uint160ToString ( val ) ) )
buf . WriteString ( fmt . Sprintf ( "LE ScriptHash to Address\t%s\n" , address . Uint160ToString ( val . Reverse ( ) ) ) )
}
2021-08-12 13:39:46 +00:00
if pub , err := keys . NewPublicKeyFromBytes ( rawStr , elliptic . P256 ( ) ) ; err == nil {
sh := pub . GetScriptHash ( )
buf . WriteString ( fmt . Sprintf ( "Public key to BE ScriptHash\t%s\n" , sh ) )
buf . WriteString ( fmt . Sprintf ( "Public key to LE ScriptHash\t%s\n" , sh . Reverse ( ) ) )
buf . WriteString ( fmt . Sprintf ( "Public key to Address\t%s\n" , address . Uint160ToString ( sh ) ) )
}
2020-07-23 11:09:21 +00:00
buf . WriteString ( fmt . Sprintf ( "Hex to String\t%s\n" , fmt . Sprintf ( "%q" , string ( rawStr ) ) ) )
buf . WriteString ( fmt . Sprintf ( "Hex to Integer\t%s\n" , bigint . FromBytes ( rawStr ) ) )
2021-07-18 12:55:37 +00:00
buf . WriteString ( fmt . Sprintf ( "Swap Endianness\t%s\n" , hex . EncodeToString ( slice . CopyReverse ( rawStr ) ) ) )
2020-07-23 11:09:21 +00:00
}
if addr , err := address . StringToUint160 ( arg ) ; err == nil {
buf . WriteString ( fmt . Sprintf ( "Address to BE ScriptHash\t%s\n" , addr ) )
buf . WriteString ( fmt . Sprintf ( "Address to LE ScriptHash\t%s\n" , addr . Reverse ( ) ) )
buf . WriteString ( fmt . Sprintf ( "Address to Base64 (BE)\t%s\n" , base64 . StdEncoding . EncodeToString ( addr . BytesBE ( ) ) ) )
buf . WriteString ( fmt . Sprintf ( "Address to Base64 (LE)\t%s\n" , base64 . StdEncoding . EncodeToString ( addr . BytesLE ( ) ) ) )
}
if rawStr , err := base64 . StdEncoding . DecodeString ( arg ) ; err == nil {
buf . WriteString ( fmt . Sprintf ( "Base64 to String\t%s\n" , fmt . Sprintf ( "%q" , string ( rawStr ) ) ) )
buf . WriteString ( fmt . Sprintf ( "Base64 to BigInteger\t%s\n" , bigint . FromBytes ( rawStr ) ) )
2021-10-29 11:55:08 +00:00
if u , err := util . Uint160DecodeBytesBE ( rawStr ) ; err == nil {
buf . WriteString ( fmt . Sprintf ( "Base64 to BE ScriptHash\t%s\n" , u . StringBE ( ) ) )
buf . WriteString ( fmt . Sprintf ( "Base64 to LE ScriptHash\t%s\n" , u . StringLE ( ) ) )
buf . WriteString ( fmt . Sprintf ( "Base64 to Address (BE)\t%s\n" , address . Uint160ToString ( u ) ) )
buf . WriteString ( fmt . Sprintf ( "Base64 to Address (LE)\t%s\n" , address . Uint160ToString ( u . Reverse ( ) ) ) )
}
2020-07-23 11:09:21 +00:00
}
buf . WriteString ( fmt . Sprintf ( "String to Hex\t%s\n" , hex . EncodeToString ( [ ] byte ( arg ) ) ) )
buf . WriteString ( fmt . Sprintf ( "String to Base64\t%s\n" , base64 . StdEncoding . EncodeToString ( [ ] byte ( arg ) ) ) )
out := buf . Bytes ( )
buf = bytes . NewBuffer ( nil )
2020-08-04 06:40:27 +00:00
w := tabwriter . NewWriter ( buf , 0 , 4 , 4 , '\t' , 0 )
2020-07-23 11:09:21 +00:00
if _ , err := w . Write ( out ) ; err != nil {
2020-08-04 06:40:06 +00:00
return "" , err
2020-07-23 11:09:21 +00:00
}
if err := w . Flush ( ) ; err != nil {
2020-08-04 06:40:06 +00:00
return "" , err
2020-07-23 11:09:21 +00:00
}
2021-05-12 15:42:50 +00:00
return buf . String ( ) , nil
2020-07-23 11:09:21 +00:00
}
2020-12-01 15:27:38 +00:00
const logo = `
2018-03-30 16:15:06 +00:00
_ ____________ __________ _ ____ ___
/ | / / ____ / __ \ / ____ / __ \ | | / / | / /
/ | / / __ / / / / / _____ / / __ / / / / ____ | | / / / | _ / /
/ / | / / ___ / / _ / / _____ / / _ / / / _ / / _____ / | / / / / /
/ _ / | _ / _____ / \ ____ / \ ____ / \ ____ / | ___ / _ / / _ /
`
2020-12-01 15:27:38 +00:00
2022-02-15 12:55:25 +00:00
func printLogo ( w io . Writer ) {
2022-02-17 13:01:03 +00:00
fmt . Fprint ( w , logo )
fmt . Fprintln ( w )
2022-02-15 12:55:25 +00:00
fmt . Fprintln ( w )
fmt . Fprintln ( w )
}
func writeErr ( w io . Writer , err error ) {
fmt . Fprintf ( w , "Error: %s\n" , err )
2018-03-30 16:15:06 +00:00
}