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-09-19 19:56:33 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/native"
2023-05-17 16:01:24 +00:00
"github.com/nspcc-dev/neo-go/pkg/core/state"
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"
2023-05-17 16:01:24 +00:00
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
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"
2023-05-17 16:01:24 +00:00
contractStateKey = "contractState"
2022-02-15 12:55:25 +00:00
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"
2022-11-19 20:08:06 +00:00
gasFlagFullName = "gas"
2022-10-04 12:38:42 +00:00
backwardsFlagFullName = "backwards"
2022-10-05 10:56:26 +00:00
diffFlagFullName = "diff"
2023-05-17 16:01:24 +00:00
hashFlagFullName = "hash"
2022-10-04 11:19:42 +00:00
)
2022-11-19 20:08:06 +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." ,
}
gasFlag = cli . Int64Flag {
Name : gasFlagFullName ,
Usage : "GAS limit for this execution (integer number, satoshi)." ,
}
2023-05-17 16:01:24 +00:00
hashFlag = cli . StringFlag {
Name : hashFlagFullName ,
Usage : "Smart-contract hash in LE form or address" ,
}
2022-11-19 20:08:06 +00:00
)
2022-10-04 11:53:31 +00:00
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" ,
2022-10-12 10:40:36 +00:00
UsageText : "exit" ,
Description : "Exit the VM prompt." ,
2022-02-15 12:55:25 +00:00
Action : handleExit ,
2019-09-10 16:11:48 +00:00
} ,
{
2022-02-15 12:55:25 +00:00
Name : "ip" ,
Usage : "Show current instruction" ,
2022-10-12 10:40:36 +00:00
UsageText : "ip" ,
Description : "Show current instruction." ,
2022-02-15 12:55:25 +00:00
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> ` ,
2022-10-12 10:40:36 +00:00
Description : ` < ip > is mandatory parameter .
Example :
2019-09-10 16:11:48 +00:00
> 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> ` ,
2022-10-12 10:40:36 +00:00
Description : ` < ip > is mandatory parameter ( absolute IP value ) .
Example :
2022-10-12 10:16:19 +00:00
> 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" ,
2022-10-12 10:40:36 +00:00
UsageText : "estack" ,
Description : "Show evaluation stack contents." ,
2022-02-15 12:55:25 +00:00
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" ,
2022-10-12 10:40:36 +00:00
UsageText : "istack" ,
Description : "Show invocation stack contents." ,
2022-02-15 12:55:25 +00:00
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" ,
2022-10-12 10:40:36 +00:00
UsageText : "sslot" ,
Description : "Show static slot contents." ,
2022-02-15 12:55:25 +00:00
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" ,
2022-10-12 10:40:36 +00:00
UsageText : "lslot" ,
2022-02-15 12:55:25 +00:00
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" ,
2022-10-12 10:40:36 +00:00
UsageText : "aslot" ,
Description : "Show arguments slot contents." ,
2022-02-15 12:55:25 +00:00
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" ,
2023-05-17 16:01:24 +00:00
Usage : "Load a NEF (possibly with a contract hash) into the VM optionally using provided scoped signers in the context" ,
UsageText : ` loadnef [--historic <height>] [--gas <int>] [--hash <hash-or-address>] <file> [<manifest>] [-- <signer-with-scope>, ...] ` ,
Flags : [ ] cli . Flag { historicFlag , gasFlag , hashFlag } ,
2023-05-16 14:57:13 +00:00
Description : ` < file > parameter is mandatory , < manifest > parameter ( if omitted ) will
be guessed from the < file > parameter by replacing ' . nef ' suffix with ' . manifest . json '
suffix .
2022-10-11 11:59:51 +00:00
` + 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" ,
2023-05-16 14:54:41 +00:00
UsageText : ` loadbase64 [--historic <height>] [--gas <int>] <string> [-- <signer-with-scope>, ...] ` ,
2022-11-19 20:08:06 +00:00
Flags : [ ] cli . Flag { historicFlag , gasFlag } ,
2022-10-12 10:40:36 +00:00
Description : ` < string > is mandatory parameter .
2022-02-15 12:55:25 +00:00
2022-10-11 11:59:51 +00:00
` + 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" ,
2023-05-16 14:54:41 +00:00
UsageText : ` loadhex [--historic <height>] [--gas <int>] <string> [-- <signer-with-scope>, ...] ` ,
2022-11-19 20:08:06 +00:00
Flags : [ ] cli . Flag { historicFlag , gasFlag } ,
2022-10-12 10:40:36 +00:00
Description : ` < string > is mandatory parameter .
2022-02-15 12:55:25 +00:00
2022-10-11 11:59:51 +00:00
` + 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" ,
2023-05-17 16:01:24 +00:00
Usage : "Compile and load a Go file with the manifest into the VM optionally attaching to it provided signers with scopes and setting provided hash" ,
UsageText : ` loadgo [--historic <height>] [--gas <int>] [--hash <hash-or-address>] <file> [-- <signer-with-scope>, ...] ` ,
Flags : [ ] cli . Flag { historicFlag , gasFlag , hashFlag } ,
2022-10-12 10:40:36 +00:00
Description : ` < file > is mandatory parameter .
2022-10-11 11:59:51 +00:00
` + 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-11-19 20:08:06 +00:00
UsageText : ` loadtx [--historic <height>] [--gas <int>] <file-or-hash> ` ,
Flags : [ ] cli . Flag { historicFlag , gasFlag } ,
2022-10-12 10:40:36 +00:00
Description : ` Load transaction into the VM from chain or from parameter context file .
2022-11-20 13:02:24 +00:00
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 . It ' ll also use transaction ' s system fee value as GAS limit if
-- gas option is not used .
2022-10-10 10:59:37 +00:00
2022-10-12 10:40:36 +00:00
< file - or - hash > is mandatory parameter .
Example :
2022-10-10 10:59:37 +00:00
> 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" ,
2023-05-16 14:54:41 +00:00
UsageText : ` loaddeployed [--historic <height>] [--gas <int>] <hash-or-address-or-id> [-- <signer-with-scope>, ...] ` ,
2022-11-19 20:08:06 +00:00
Flags : [ ] cli . Flag { historicFlag , gasFlag } ,
2022-10-12 10:40:36 +00:00
Description : ` Load deployed contract into the VM from chain optionally attaching to it provided signers with scopes .
2022-10-11 11:59:51 +00:00
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 .
2022-10-12 10:40:36 +00:00
2022-10-11 11:59:51 +00:00
` + 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
{
2022-10-12 10:40:36 +00:00
Name : "reset" ,
Usage : "Unload compiled script from the VM and reset context to proper (possibly, historic) state" ,
UsageText : "reset" ,
Flags : [ ] cli . Flag { historicFlag } ,
Description : "Unload compiled script from the VM and reset context to proper (possibly, historic) state." ,
Action : handleReset ,
2022-02-16 15:43:12 +00:00
} ,
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> ` ,
2022-10-12 10:40:36 +00:00
Description : ` < 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" ,
2022-10-12 10:40:36 +00:00
Usage : "Usage Execute the current loaded script" ,
2022-02-15 12:55:25 +00:00
UsageText : ` run [<method> [<parameter>...]] ` ,
2022-10-12 10:40:36 +00:00
Description : ` < method > is a contract method , specified in manifest . It can be '_' which will push
2022-02-17 12:54:46 +00:00
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-12 10:40:36 +00:00
> run put int : 5 string : some_string_value ` ,
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" ,
2022-10-12 10:40:36 +00:00
UsageText : "cont" ,
Description : "Continue execution of the current loaded script." ,
2022-02-15 12:55:25 +00:00
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>] ` ,
2022-10-12 10:40:36 +00:00
Description : ` < n > is optional parameter to specify number of instructions to run .
Example :
2019-09-10 16:11:48 +00:00
> 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-10-12 10:40:36 +00:00
Name : "stepinto" ,
Usage : "Stepinto instruction to take in the debugger" ,
UsageText : "stepinto" ,
Description : ` Stepinto instruction to take in the debugger .
Example :
2019-10-14 15:37:11 +00:00
> stepinto ` ,
2022-02-15 12:55:25 +00:00
Action : handleStepInto ,
2019-10-14 15:37:11 +00:00
} ,
{
2022-10-12 10:40:36 +00:00
Name : "stepout" ,
Usage : "Stepout instruction to take in the debugger" ,
UsageText : "stepout" ,
Description : ` Stepout instruction to take in the debugger .
Example :
2019-10-14 15:37:11 +00:00
> stepout ` ,
2022-02-15 12:55:25 +00:00
Action : handleStepOut ,
2019-10-14 15:37:11 +00:00
} ,
{
2022-10-12 10:40:36 +00:00
Name : "stepover" ,
Usage : "Stepover instruction to take in the debugger" ,
UsageText : "stepover" ,
Description : ` Stepover instruction to take in the debugger .
Example :
2019-10-14 15:37:11 +00:00
> 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" ,
2022-10-12 10:40:36 +00:00
UsageText : "ops" ,
2022-02-15 12:55:25 +00:00
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" ,
2022-10-12 10:40:36 +00:00
UsageText : "events" ,
2022-10-04 10:05:51 +00:00
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." ,
} ,
} ,
2022-10-12 10:40:36 +00:00
Description : ` Dump state of the chain that is used for VM CLI invocations ( use - v for verbose node configuration ) .
2022-10-04 11:19:42 +00:00
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-12 10:40:36 +00:00
Description : ` Dump storage of the contract with the specified hash , address or ID as is at the current stage of script invocation .
2022-10-04 12:38:42 +00:00
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>]] ` ,
2022-10-12 10:40:36 +00:00
Description : ` Dump storage changes as is at the current stage of loaded script invocation .
2022-10-05 12:06:20 +00:00
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
2022-12-07 13:51:03 +00:00
ctl . Usage = "Official VM CLI for NeoGo"
2022-02-15 12:55:25 +00:00
// 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-12-05 12:43:55 +00:00
log , _ , logCloser , err := options . HandleLoggingParams ( false , cfg . ApplicationConfiguration )
2022-10-07 12:27:24 +00:00
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-12-06 13:34:38 +00:00
chain , err := core . NewBlockchain ( store , cfg . Blockchain ( ) , 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
2023-04-03 10:34:24 +00:00
vmcli . shell . Metadata = map [ string ] any {
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 ,
2023-05-17 16:01:24 +00:00
contractStateKey : new ( state . ContractBase ) ,
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
}
2023-05-17 16:01:24 +00:00
func getContractStateFromContext ( app * cli . App ) * state . ContractBase {
return app . Metadata [ contractStateKey ] . ( * state . ContractBase )
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
}
2023-05-17 16:01:24 +00:00
func setContractStateInContext ( app * cli . App , cs * state . ContractBase ) {
app . Metadata [ contractStateKey ] = cs
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 {
2024-03-04 14:59:04 +00:00
return 0 , fmt . Errorf ( "%w: %w" , 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-11-19 20:08:06 +00:00
var err error
2022-10-04 11:53:31 +00:00
if c . IsSet ( historicFlagFullName ) {
height := c . Int ( historicFlagFullName )
2022-11-19 20:08:06 +00:00
err = resetState ( c . App , tx , uint32 ( height ) )
} else {
err = resetState ( c . App , tx )
2022-10-04 11:53:31 +00:00
}
2022-11-19 20:08:06 +00:00
if err != nil {
return err
}
if c . IsSet ( gasFlagFullName ) {
gas := c . Int64 ( gasFlagFullName )
v := getVMFromContext ( c . App )
v . GasLimit = gas
}
return nil
2022-10-04 11:53:31 +00:00
}
2023-05-17 16:01:24 +00:00
func getHashFlag ( c * cli . Context ) ( util . Uint160 , error ) {
if ! c . IsSet ( hashFlagFullName ) {
return util . Uint160 { } , nil
}
h , err := flags . ParseAddress ( c . String ( hashFlagFullName ) )
if err != nil {
return util . Uint160 { } , fmt . Errorf ( "failed to parse contract hash: %w" , err )
}
return h , nil
}
2022-02-15 12:55:25 +00:00
func handleLoadNEF ( c * cli . Context ) error {
args := c . Args ( )
2023-05-16 14:57:13 +00:00
if len ( args ) < 1 {
return fmt . Errorf ( "%w: <nef> is required" , ErrMissingParameter )
}
nefFile := args [ 0 ]
var (
manifestFile string
signersStartOffset int
)
if len ( args ) == 2 {
manifestFile = args [ 1 ]
} else if len ( args ) == 3 {
if args [ 1 ] != cmdargs . CosignersSeparator {
return fmt . Errorf ( "%w: `%s` was expected as the second parameter, got %s" , ErrInvalidParameter , cmdargs . CosignersSeparator , args [ 1 ] )
}
signersStartOffset = 2
} else if len ( args ) > 3 {
if args [ 1 ] == cmdargs . CosignersSeparator {
signersStartOffset = 2
} else {
manifestFile = args [ 1 ]
if args [ 2 ] != cmdargs . CosignersSeparator {
return fmt . Errorf ( "%w: `%s` was expected as the third parameter, got %s" , ErrInvalidParameter , cmdargs . CosignersSeparator , args [ 2 ] )
}
signersStartOffset = 3
}
}
if len ( manifestFile ) == 0 {
manifestFile = strings . TrimSuffix ( nefFile , ".nef" ) + ".manifest.json"
2020-06-25 14:32:58 +00:00
}
2023-05-16 14:57:13 +00:00
b , err := os . ReadFile ( nefFile )
2022-10-11 11:59:51 +00:00
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
}
2023-05-16 14:57:13 +00:00
m , err := getManifestFromFile ( manifestFile )
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
2023-05-16 14:57:13 +00:00
if signersStartOffset != 0 && len ( args ) > signersStartOffset {
signers , err = cmdargs . ParseSigners ( c . Args ( ) [ signersStartOffset : ] )
2022-10-11 11:59:51 +00:00
if err != nil {
2024-03-04 14:59:04 +00:00
return fmt . Errorf ( "%w: failed to parse signers: %w" , ErrInvalidParameter , err )
2022-10-11 11:59:51 +00:00
}
}
err = prepareVM ( c , createFakeTransaction ( nef . Script , signers ) )
if err != nil {
return err
}
2023-05-17 16:01:24 +00:00
h , err := getHashFlag ( c )
if err != nil {
return err
}
cs := & state . ContractBase {
Hash : h ,
NEF : nef ,
Manifest : * m ,
}
setContractStateInContext ( c . App , cs )
2022-10-11 11:59:51 +00:00
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 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 {
2024-03-04 14:59:04 +00:00
return fmt . Errorf ( "%w: %w" , 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 {
2023-05-16 14:54:41 +00:00
if args [ 1 ] != cmdargs . CosignersSeparator {
return fmt . Errorf ( "%w: `%s` was expected as the second parameter, got %s" , ErrInvalidParameter , cmdargs . CosignersSeparator , args [ 1 ] )
}
if len ( args ) < 3 {
return fmt . Errorf ( "%w: signers expected after `%s`, got none" , ErrInvalidParameter , cmdargs . CosignersSeparator )
}
signers , err = cmdargs . ParseSigners ( args [ 2 : ] )
2022-10-11 11:59:51 +00:00
if err != nil {
2024-03-04 14:59:04 +00:00
return fmt . Errorf ( "%w: %w" , ErrInvalidParameter , err )
2022-10-11 11:59:51 +00:00
}
}
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 {
2024-03-04 14:59:04 +00:00
return fmt . Errorf ( "%w: %w" , 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 {
2023-05-16 14:54:41 +00:00
if args [ 1 ] != cmdargs . CosignersSeparator {
return fmt . Errorf ( "%w: `%s` was expected as the second parameter, got %s" , ErrInvalidParameter , cmdargs . CosignersSeparator , args [ 1 ] )
}
if len ( args ) < 3 {
return fmt . Errorf ( "%w: signers expected after `%s`, got none" , ErrInvalidParameter , cmdargs . CosignersSeparator )
}
signers , err = cmdargs . ParseSigners ( args [ 2 : ] )
2022-10-11 11:59:51 +00:00
if err != nil {
2024-03-04 14:59:04 +00:00
return fmt . Errorf ( "%w: %w" , ErrInvalidParameter , err )
2022-10-11 11:59:51 +00:00
}
}
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" )
2023-05-17 16:01:24 +00:00
ne , 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 {
2023-05-16 14:54:41 +00:00
if args [ 1 ] != cmdargs . CosignersSeparator {
return fmt . Errorf ( "%w: `%s` was expected as the second parameter, got %s" , ErrInvalidParameter , cmdargs . CosignersSeparator , args [ 1 ] )
}
if len ( args ) < 3 {
return fmt . Errorf ( "%w: signers expected after `%s`, got none" , ErrInvalidParameter , cmdargs . CosignersSeparator )
}
signers , err = cmdargs . ParseSigners ( args [ 2 : ] )
2022-10-11 11:59:51 +00:00
if err != nil {
2024-03-04 14:59:04 +00:00
return fmt . Errorf ( "%w: %w" , ErrInvalidParameter , err )
2022-10-11 11:59:51 +00:00
}
}
2020-12-21 11:27:07 +00:00
2023-05-17 16:01:24 +00:00
err = prepareVM ( c , createFakeTransaction ( ne . Script , signers ) )
2022-10-11 11:59:51 +00:00
if err != nil {
return err
}
2023-05-17 16:01:24 +00:00
h , err := getHashFlag ( c )
if err != nil {
return err
}
cs := & state . ContractBase {
Hash : h ,
NEF : * ne ,
Manifest : * m ,
}
setContractStateInContext ( c . App , cs )
2022-10-11 11:59:51 +00:00
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 )
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 )
2022-11-20 13:02:24 +00:00
if v . GasLimit == - 1 {
v . GasLimit = tx . SystemFee
}
2022-10-10 10:59:37 +00:00
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" )
}
2023-05-16 14:54:41 +00:00
args := c . Args ( )
hashOrID := args [ 0 ]
2022-10-11 09:22:07 +00:00
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 )
}
2022-09-19 19:56:33 +00:00
h , err = native . GetContractScriptHash ( ic . DAO , int32 ( i ) )
2022-10-11 09:22:07 +00:00
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
2023-05-16 14:54:41 +00:00
if len ( args ) > 1 {
if args [ 1 ] != cmdargs . CosignersSeparator {
return fmt . Errorf ( "%w: %s was expected as the second parameter, got %s" , ErrInvalidParameter , cmdargs . CosignersSeparator , args [ 1 ] )
}
if len ( args ) < 3 {
return fmt . Errorf ( "%w: signers expected after `%s`, got none" , ErrInvalidParameter , cmdargs . CosignersSeparator )
}
signers , err = cmdargs . ParseSigners ( args [ 2 : ] )
2022-10-11 11:59:51 +00:00
if err != nil {
2024-03-04 14:59:04 +00:00
return fmt . Errorf ( "%w: %w" , ErrInvalidParameter , err )
2022-10-11 11:59:51 +00:00
}
}
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.
2022-11-19 20:08:06 +00:00
gasLimit := ic . VM . GasLimit
ic . ReuseVM ( ic . VM ) // clear previously loaded program and context.
ic . VM . GasLimit = gasLimit
2022-10-12 07:19:47 +00:00
ic . VM . LoadScriptWithHash ( cs . NEF . Script , cs . Hash , callflag . All )
fmt . Fprintf ( c . App . Writer , "READY: loaded %d instructions\n" , ic . VM . Context ( ) . LenInstr ( ) )
2023-05-17 16:01:24 +00:00
setContractStateInContext ( c . App , & cs . ContractBase )
2022-10-11 09:22:07 +00:00
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
}
2023-05-17 16:01:24 +00:00
// resetContractState removes loaded contract state from app context.
func resetContractState ( app * cli . App ) {
setContractStateInContext ( app , nil )
2022-10-04 10:19:18 +00:00
}
// 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
}
2023-05-17 16:01:24 +00:00
resetContractState ( 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 )
2023-05-17 16:01:24 +00:00
cs := getContractStateFromContext ( c . App )
2022-02-15 12:55:25 +00:00
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 ] != "_"
2023-05-17 16:01:24 +00:00
hasRet bool
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 {
2024-03-04 14:59:04 +00:00
return fmt . Errorf ( "%w: %w" , ErrInvalidParameter , err )
2022-10-10 11:00:26 +00:00
}
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 {
2023-05-17 16:01:24 +00:00
if cs == nil {
2023-05-17 16:03:02 +00:00
return fmt . Errorf ( "manifest is not loaded; either use 'run' command to run loaded script from the start or use 'loadgo', 'loadnef' or 'loaddeployed' commands to provide manifest" )
2022-10-04 10:19:18 +00:00
}
2023-05-17 16:01:24 +00:00
md := cs . Manifest . 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
}
2023-05-17 16:01:24 +00:00
hasRet = md . ReturnType != smartcontract . VoidType
2020-12-21 11:27:07 +00:00
offset = md . Offset
2023-05-17 16:01:24 +00:00
var initOff = - 1
if initMD := cs . Manifest . ABI . GetMethod ( manifest . MethodInit , 0 ) ; initMD != nil {
initOff = initMD . Offset
}
// Clear context loaded by 'loadgo', 'loadnef' or 'loaddeployed' to properly handle LoadNEFMethod.
// At the same time, preserve previously set gas limit and the set of breakpoints.
ic := getInteropContextFromContext ( c . App )
gasLimit := v . GasLimit
breaks := v . Context ( ) . BreakPoints ( ) // We ensure that there's a context loaded.
ic . ReuseVM ( v )
v . GasLimit = gasLimit
v . LoadNEFMethod ( & cs . NEF , util . Uint160 { } , cs . Hash , callflag . All , hasRet , offset , initOff , nil )
for _ , bp := range breaks {
v . AddBreakPoint ( bp )
}
2020-12-21 11:27:07 +00:00
}
for i := len ( params ) - 1 ; i >= 0 ; i -- {
v . Estack ( ) . PushVal ( params [ i ] )
}
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 {
2024-03-04 14:59:04 +00:00
return fmt . Errorf ( "%w: %w" , 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 ]
2023-10-12 15:07:54 +00:00
var buf [ ] byte
2020-07-23 11:09:21 +00:00
if val , err := strconv . ParseInt ( arg , 10 , 64 ) ; err == nil {
bs := bigint . ToBytes ( big . NewInt ( val ) )
2023-10-12 15:07:54 +00:00
buf = fmt . Appendf ( buf , "Integer to Hex\t%s\n" , hex . EncodeToString ( bs ) )
buf = fmt . Appendf ( buf , "Integer to Base64\t%s\n" , base64 . StdEncoding . EncodeToString ( bs ) )
2020-07-23 11:09:21 +00:00
}
noX := strings . TrimPrefix ( arg , "0x" )
if rawStr , err := hex . DecodeString ( noX ) ; err == nil {
if val , err := util . Uint160DecodeBytesBE ( rawStr ) ; err == nil {
2023-10-12 15:07:54 +00:00
buf = fmt . Appendf ( buf , "BE ScriptHash to Address\t%s\n" , address . Uint160ToString ( val ) )
buf = fmt . Appendf ( buf , "LE ScriptHash to Address\t%s\n" , address . Uint160ToString ( val . Reverse ( ) ) )
2020-07-23 11:09:21 +00:00
}
2021-08-12 13:39:46 +00:00
if pub , err := keys . NewPublicKeyFromBytes ( rawStr , elliptic . P256 ( ) ) ; err == nil {
sh := pub . GetScriptHash ( )
2023-10-12 15:07:54 +00:00
buf = fmt . Appendf ( buf , "Public key to BE ScriptHash\t%s\n" , sh )
buf = fmt . Appendf ( buf , "Public key to LE ScriptHash\t%s\n" , sh . Reverse ( ) )
buf = fmt . Appendf ( buf , "Public key to Address\t%s\n" , address . Uint160ToString ( sh ) )
2021-08-12 13:39:46 +00:00
}
2023-10-12 15:07:54 +00:00
buf = fmt . Appendf ( buf , "Hex to String\t%s\n" , fmt . Sprintf ( "%q" , string ( rawStr ) ) )
buf = fmt . Appendf ( buf , "Hex to Integer\t%s\n" , bigint . FromBytes ( rawStr ) )
buf = fmt . Appendf ( buf , "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 {
2023-10-12 15:07:54 +00:00
buf = fmt . Appendf ( buf , "Address to BE ScriptHash\t%s\n" , addr )
buf = fmt . Appendf ( buf , "Address to LE ScriptHash\t%s\n" , addr . Reverse ( ) )
buf = fmt . Appendf ( buf , "Address to Base64 (BE)\t%s\n" , base64 . StdEncoding . EncodeToString ( addr . BytesBE ( ) ) )
buf = fmt . Appendf ( buf , "Address to Base64 (LE)\t%s\n" , base64 . StdEncoding . EncodeToString ( addr . BytesLE ( ) ) )
2020-07-23 11:09:21 +00:00
}
if rawStr , err := base64 . StdEncoding . DecodeString ( arg ) ; err == nil {
2023-10-12 15:07:54 +00:00
buf = fmt . Appendf ( buf , "Base64 to String\t%s\n" , fmt . Sprintf ( "%q" , string ( rawStr ) ) )
buf = fmt . Appendf ( buf , "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 {
2023-10-12 15:07:54 +00:00
buf = fmt . Appendf ( buf , "Base64 to BE ScriptHash\t%s\n" , u . StringBE ( ) )
buf = fmt . Appendf ( buf , "Base64 to LE ScriptHash\t%s\n" , u . StringLE ( ) )
buf = fmt . Appendf ( buf , "Base64 to Address (BE)\t%s\n" , address . Uint160ToString ( u ) )
buf = fmt . Appendf ( buf , "Base64 to Address (LE)\t%s\n" , address . Uint160ToString ( u . Reverse ( ) ) )
2021-10-29 11:55:08 +00:00
}
2020-07-23 11:09:21 +00:00
}
2023-10-12 15:07:54 +00:00
buf = fmt . Appendf ( buf , "String to Hex\t%s\n" , hex . EncodeToString ( [ ] byte ( arg ) ) )
buf = fmt . Appendf ( buf , "String to Base64\t%s\n" , base64 . StdEncoding . EncodeToString ( [ ] byte ( arg ) ) )
2020-07-23 11:09:21 +00:00
2023-10-12 15:07:54 +00:00
res := bytes . NewBuffer ( nil )
w := tabwriter . NewWriter ( res , 0 , 4 , 4 , '\t' , 0 )
if _ , err := w . Write ( buf ) ; 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
}
2023-10-12 15:07:54 +00:00
return res . 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
}