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"
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"
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"
"github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/core/storage/dbconfig"
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-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"
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"
boolType = "bool"
boolFalse = "false"
boolTrue = "true"
intType = "int"
stringType = "string"
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 (
verboseFlagFullName = "verbose"
)
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-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" ,
Usage : "Load a NEF-consistent script into the VM" ,
UsageText : ` loadnef <file> <manifest> ` ,
Description : ` loadnef < file > < manifest >
2020-12-21 11:27:07 +00:00
both parameters are mandatory , example :
> 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" ,
Usage : "Load a base64-encoded script string into the VM" ,
UsageText : ` loadbase64 <string> ` ,
Description : ` loadbase64 < string >
2020-06-25 14:25:05 +00:00
< string > is mandatory parameter , example :
> 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" ,
Usage : "Load a hex-encoded script string into the VM" ,
UsageText : ` loadhex <string> ` ,
Description : ` loadhex < string >
2019-09-10 16:11:48 +00:00
< string > is mandatory parameter , 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" ,
Usage : "Compile and load a Go file with the manifest into the VM" ,
UsageText : ` loadgo <file> ` ,
Description : ` loadgo < file >
2019-09-10 16:11:48 +00:00
< file > is mandatory parameter , 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-02-16 15:43:12 +00:00
{
Name : "reset" ,
Usage : "Unload compiled script from the VM" ,
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
2019-09-10 16:30:32 +00:00
as < type > : < value > , where type can be :
2019-09-10 16:41:11 +00:00
' ` + boolType + ` ' : supports ' ` + boolFalse + ` ' and ' ` + boolTrue + ` ' values
' ` + intType + ` ' : supports integers as values
' ` + stringType + ` ' : supports strings as values ( that are pushed as a byte array
values to the stack )
or can be just < value > , for which the type will be detected automatically
following these rules : ' ` + boolTrue + ` ' and ' ` + boolFalse + ` ' are treated as respective
boolean values , everything that can be converted to integer is treated as
integer and everything else is treated like a string .
2019-09-10 16:30:32 +00:00
2019-09-10 16:11:48 +00:00
Example :
2019-09-10 16:41:11 +00:00
> run put ` + stringType + ` : "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 ,
} ,
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" )
)
2019-10-14 15:38:05 +00:00
// VMCLI object for interacting with the VM.
2018-03-30 16:15:06 +00:00
type VMCLI 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-03 12:05:48 +00:00
// NewWithConfig returns new VMCLI instance using provided config and (optionally)
// provided node config for state-backed VM.
func NewWithConfig ( printLogotype bool , onExit func ( int ) , c * readline . Config , cfg config . Config ) ( * VMCLI , 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 ( )
}
exitF := func ( i int ) {
_ = store . Close ( )
onExit ( i )
}
log := zap . NewNop ( )
chain , err := core . NewBlockchain ( store , cfg . ProtocolConfiguration , log )
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
2019-09-10 16:11:48 +00:00
vmcli := VMCLI {
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-02-15 12:55:25 +00:00
v := getVMFromContext ( c . App )
args := c . Args ( )
if len ( args ) != 1 {
return 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-02-15 12:55:25 +00:00
return fmt . Errorf ( "%w: %s" , ErrInvalidParameter , err )
2018-03-30 16:15:06 +00:00
}
2019-09-10 16:11:48 +00:00
v . AddBreakPoint ( n )
2022-02-15 12:55:25 +00:00
fmt . Fprintf ( c . App . Writer , "breakpoint added at instruction %d\n" , n )
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 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-02-15 12:55:25 +00:00
func handleLoadNEF ( c * cli . Context ) error {
2022-10-06 10:24:57 +00:00
err := resetState ( c . App )
if err != nil {
return err
}
2022-02-15 12:55:25 +00:00
v := getVMFromContext ( c . App )
args := c . Args ( )
if len ( args ) < 2 {
return fmt . Errorf ( "%w: <file> <manifest>" , ErrMissingParameter )
2020-06-25 14:32:58 +00:00
}
2022-02-15 12:55:25 +00:00
if err := v . LoadFileWithFlags ( args [ 0 ] , callflag . All ) ; err != nil {
return fmt . Errorf ( "failed to read nef: %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-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 {
2022-10-06 10:24:57 +00:00
err := resetState ( c . App )
if err != nil {
return err
}
2022-02-15 12:55:25 +00:00
v := getVMFromContext ( c . App )
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
}
2021-05-28 09:07:03 +00:00
v . LoadWithFlags ( b , callflag . All )
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-02-15 12:55:25 +00:00
func handleLoadHex ( c * cli . Context ) error {
2022-10-06 10:24:57 +00:00
err := resetState ( c . App )
if err != nil {
return err
}
2022-02-15 12:55:25 +00:00
v := getVMFromContext ( c . App )
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
}
2021-05-28 09:07:03 +00:00
v . LoadWithFlags ( b , callflag . All )
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 {
2022-10-06 10:24:57 +00:00
err := resetState ( c . App )
if err != nil {
return err
}
2022-02-15 12:55:25 +00:00
v := getVMFromContext ( c . App )
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-02-15 12:55:25 +00:00
setManifestInContext ( c . App , m )
2020-12-21 11:27:07 +00:00
2021-12-08 19:33:03 +00:00
v . LoadWithFlags ( b . Script , callflag . All )
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
}
func handleReset ( c * cli . Context ) error {
2022-10-06 10:24:57 +00:00
err := resetState ( c . App )
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
// it with the newly created one.
2022-10-06 10:24:57 +00:00
func resetInteropContext ( app * cli . App ) error {
2022-10-03 12:05:48 +00:00
finalizeInteropContext ( app )
bc := getChainFromContext ( app )
2022-10-06 10:24:57 +00:00
newIc , err := bc . GetTestVM ( trigger . Application , nil , nil )
if err != nil {
return fmt . Errorf ( "failed to create test VM: %w" , err )
}
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-06 10:24:57 +00:00
func resetState ( app * cli . App ) error {
err := resetInteropContext ( app )
if err != nil {
return fmt . Errorf ( "failed to reset interop context state: %w" , err )
}
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-02-15 12:55:25 +00:00
params , err = parseArgs ( args [ 1 : ] )
2021-01-26 14:37:34 +00:00
if err != nil {
2022-02-15 12:55:25 +00:00
return 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 )
message := fmt . Sprintf ( "Chain height: %d\nNetwork magic: %d\nDB type: %s\n" , bc . BlockHeight ( ) , bc . GetConfig ( ) . Magic , cfg . ApplicationConfiguration . DBConfiguration . Type )
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 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.
func ( c * VMCLI ) 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-06-03 12:55:06 +00:00
func parseArgs ( args [ ] string ) ( [ ] stackitem . Item , error ) {
items := make ( [ ] stackitem . Item , len ( args ) )
2018-04-10 09:45:31 +00:00
for i , arg := range args {
2019-09-10 16:41:11 +00:00
var typ , value string
2018-04-10 09:45:31 +00:00
typeAndVal := strings . Split ( arg , ":" )
if len ( typeAndVal ) < 2 {
2019-09-10 16:41:11 +00:00
if typeAndVal [ 0 ] == boolFalse || typeAndVal [ 0 ] == boolTrue {
typ = boolType
} else if _ , err := strconv . Atoi ( typeAndVal [ 0 ] ) ; err == nil {
typ = intType
} else {
typ = stringType
}
value = typeAndVal [ 0 ]
} else {
typ = typeAndVal [ 0 ]
value = typeAndVal [ 1 ]
2018-04-10 09:45:31 +00:00
}
switch typ {
2019-09-10 16:41:11 +00:00
case boolType :
if value == boolFalse {
2020-06-03 12:55:06 +00:00
items [ i ] = stackitem . NewBool ( false )
2019-09-10 16:41:11 +00:00
} else if value == boolTrue {
2020-06-03 12:55:06 +00:00
items [ i ] = stackitem . NewBool ( true )
2019-09-10 16:30:32 +00:00
} else {
2020-12-01 14:15:01 +00:00
return nil , fmt . Errorf ( "%w: invalid bool value" , ErrInvalidParameter )
2019-09-10 16:30:32 +00:00
}
2019-09-10 16:41:11 +00:00
case intType :
2020-03-24 08:06:26 +00:00
val , err := strconv . ParseInt ( value , 10 , 64 )
2018-04-10 09:45:31 +00:00
if err != nil {
2020-12-01 14:15:01 +00:00
return nil , fmt . Errorf ( "%w: invalid integer value" , ErrInvalidParameter )
2018-04-10 09:45:31 +00:00
}
2020-06-03 12:55:06 +00:00
items [ i ] = stackitem . NewBigInteger ( big . NewInt ( val ) )
2019-09-10 16:41:11 +00:00
case stringType :
2020-06-03 12:55:06 +00:00
items [ i ] = stackitem . NewByteArray ( [ ] byte ( value ) )
2018-04-10 09:45:31 +00:00
}
}
return items , nil
}
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
}