From d09a0c18a74c42509b0345c3882ce56768bd86ab Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Tue, 11 Oct 2022 14:59:51 +0300 Subject: [PATCH] cli: unify signers parsing Share signers parsing code between 'contract invokefunction' and 'vm load*' commands, quite a useful thing when it comes to witness checks. --- cli/cmdargs/parser.go | 65 +++++++++-- cli/smartcontract/smart_contract.go | 37 +------ cli/vm/cli.go | 165 ++++++++++++++++++++-------- cli/vm/cli_test.go | 141 ++++++++++++++++++++++-- 4 files changed, 311 insertions(+), 97 deletions(-) diff --git a/cli/cmdargs/parser.go b/cli/cmdargs/parser.go index 13fccaf8a..b90fa60d7 100644 --- a/cli/cmdargs/parser.go +++ b/cli/cmdargs/parser.go @@ -96,25 +96,76 @@ const ( * '[ a b [ c d ] e ]' is an array with 4 values: string 'a', string 'b', array of two strings 'c' and 'd', string 'e' * '[ ]' is an empty array` + + // SignersParsingDoc is a documentation for signers parsing. + SignersParsingDoc = ` Signers represent a set of Uint160 hashes with witness scopes and are used + to verify hashes in System.Runtime.CheckWitness syscall. First signer is treated + as a sender. To specify signers use signer[:scope] syntax where + * 'signer' is a signer's address (as Neo address or hex-encoded 160 bit (20 byte) + LE value with or without '0x' prefix). + * 'scope' is a comma-separated set of cosigner's scopes, which could be: + - 'None' - default witness scope which may be used for the sender + to only pay fee for the transaction. + - 'Global' - allows this witness in all contexts. This cannot be combined + with other flags. + - 'CalledByEntry' - means that this condition must hold: EntryScriptHash + == CallingScriptHash. The witness/permission/signature + given on first invocation will automatically expire if + entering deeper internal invokes. This can be default + safe choice for native NEO/GAS. + - 'CustomContracts' - define valid custom contract hashes for witness check. + Hashes are be provided as hex-encoded LE value string. + At lest one hash must be provided. Multiple hashes + are separated by ':'. + - 'CustomGroups' - define custom public keys for group members. Public keys are + provided as short-form (1-byte prefix + 32 bytes) hex-encoded + values. At least one key must be provided. Multiple keys + are separated by ':'. + + If no scopes were specified, 'CalledByEntry' used as default. If no signers were + specified, no array is passed. Note that scopes are properly handled by + neo-go RPC server only. C# implementation does not support scopes capability. + + Examples: + * 'NNQk4QXsxvsrr3GSozoWBUxEmfag7B6hz5' + * 'NVquyZHoPirw6zAEPvY1ZezxM493zMWQqs:Global' + * '0x0000000009070e030d0f0e020d0c06050e030c02' + * '0000000009070e030d0f0e020d0c06050e030c02:CalledByEntry,` + + `CustomGroups:0206d7495ceb34c197093b5fc1cccf1996ada05e69ef67e765462a7f5d88ee14d0' + * '0000000009070e030d0f0e020d0c06050e030c02:CalledByEntry,` + + `CustomContracts:1011120009070e030d0f0e020d0c06050e030c02:0x1211100009070e030d0f0e020d0c06050e030c02'` ) // GetSignersFromContext returns signers parsed from context args starting // from the specified offset. func GetSignersFromContext(ctx *cli.Context, offset int) ([]transaction.Signer, *cli.ExitError) { args := ctx.Args() - var signers []transaction.Signer + var ( + signers []transaction.Signer + err error + ) if args.Present() && len(args) > offset { - for i, c := range args[offset:] { - cosigner, err := parseCosigner(c) - if err != nil { - return nil, cli.NewExitError(fmt.Errorf("failed to parse signer #%d: %w", i, err), 1) - } - signers = append(signers, cosigner) + signers, err = ParseSigners(args[offset:]) + if err != nil { + return nil, cli.NewExitError(err, 1) } } return signers, nil } +// ParseSigners returns array of signers parsed from their string representation. +func ParseSigners(args []string) ([]transaction.Signer, error) { + var signers []transaction.Signer + for i, c := range args { + cosigner, err := parseCosigner(c) + if err != nil { + return nil, fmt.Errorf("failed to parse signer #%d: %w", i, err) + } + signers = append(signers, cosigner) + } + return signers, nil +} + func parseCosigner(c string) (transaction.Signer, error) { var ( err error diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index 51cc2d9e6..8a468ed1a 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -211,42 +211,7 @@ func NewCommands() []cli.Command { ` + cmdargs.ParamsParsingDoc + ` - Signers represent a set of Uint160 hashes with witness scopes and are used - to verify hashes in System.Runtime.CheckWitness syscall. First signer is treated - as a sender. To specify signers use signer[:scope] syntax where - * 'signer' is a signer's address (as Neo address or hex-encoded 160 bit (20 byte) - LE value with or without '0x' prefix). - * 'scope' is a comma-separated set of cosigner's scopes, which could be: - - 'None' - default witness scope which may be used for the sender - to only pay fee for the transaction. - - 'Global' - allows this witness in all contexts. This cannot be combined - with other flags. - - 'CalledByEntry' - means that this condition must hold: EntryScriptHash - == CallingScriptHash. The witness/permission/signature - given on first invocation will automatically expire if - entering deeper internal invokes. This can be default - safe choice for native NEO/GAS. - - 'CustomContracts' - define valid custom contract hashes for witness check. - Hashes are be provided as hex-encoded LE value string. - At lest one hash must be provided. Multiple hashes - are separated by ':'. - - 'CustomGroups' - define custom public keys for group members. Public keys are - provided as short-form (1-byte prefix + 32 bytes) hex-encoded - values. At least one key must be provided. Multiple keys - are separated by ':'. - - If no scopes were specified, 'CalledByEntry' used as default. If no signers were - specified, no array is passed. Note that scopes are properly handled by - neo-go RPC server only. C# implementation does not support scopes capability. - - Examples: - * 'NNQk4QXsxvsrr3GSozoWBUxEmfag7B6hz5' - * 'NVquyZHoPirw6zAEPvY1ZezxM493zMWQqs:Global' - * '0x0000000009070e030d0f0e020d0c06050e030c02' - * '0000000009070e030d0f0e020d0c06050e030c02:CalledByEntry,` + - `CustomGroups:0206d7495ceb34c197093b5fc1cccf1996ada05e69ef67e765462a7f5d88ee14d0' - * '0000000009070e030d0f0e020d0c06050e030c02:CalledByEntry,` + - `CustomContracts:1011120009070e030d0f0e020d0c06050e030c02:0x1211100009070e030d0f0e020d0c06050e030c02' +` + cmdargs.SignersParsingDoc + ` `, Action: testInvokeFunction, Flags: testInvokeFunctionFlags, diff --git a/cli/vm/cli.go b/cli/vm/cli.go index e947c3ef4..4aaeab3cf 100644 --- a/cli/vm/cli.go +++ b/cli/vm/cli.go @@ -35,6 +35,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util/slice" @@ -123,44 +124,57 @@ var commands = []cli.Command{ }, { Name: "loadnef", - Usage: "Load a NEF-consistent script into the VM", - UsageText: `loadnef `, + Usage: "Load a NEF-consistent script into the VM optionally attaching to it provided signers with scopes", + UsageText: `loadnef [, ...]`, Flags: []cli.Flag{historicFlag}, - Description: `loadnef [--historic ] -both parameters are mandatory, example: + Description: `loadnef [--historic ] [, ...] + + and parameters are mandatory. +` + cmdargs.SignersParsingDoc + ` + +Example: > loadnef /path/to/script.nef /path/to/manifest.json`, Action: handleLoadNEF, }, { Name: "loadbase64", - Usage: "Load a base64-encoded script string into the VM", - UsageText: `loadbase64 [--historic ] `, + Usage: "Load a base64-encoded script string into the VM optionally attaching to it provided signers with scopes", + UsageText: `loadbase64 [--historic ] [, ...]`, Flags: []cli.Flag{historicFlag}, - Description: `loadbase64 [--historic ] + Description: `loadbase64 [--historic ] [, ...] - is mandatory parameter, example: + is mandatory parameter. +` + cmdargs.SignersParsingDoc + ` + +Example: > loadbase64 AwAQpdToAAAADBQV9ehtQR1OrVZVhtHtoUHRfoE+agwUzmFvf3Rhfg/EuAVYOvJgKiON9j8TwAwIdHJhbnNmZXIMFDt9NxHG8Mz5sdypA9G/odiW8SOMQWJ9W1I4`, Action: handleLoadBase64, }, { Name: "loadhex", - Usage: "Load a hex-encoded script string into the VM", - UsageText: `loadhex [--historic ] `, + Usage: "Load a hex-encoded script string into the VM optionally attaching to it provided signers with scopes", + UsageText: `loadhex [--historic ] [, ...]`, Flags: []cli.Flag{historicFlag}, - Description: `loadhex [--historic ] + Description: `loadhex [--historic ] [, ...] - is mandatory parameter, example: + is mandatory parameter. +` + cmdargs.SignersParsingDoc + ` + +Example: > loadhex 0c0c48656c6c6f20776f726c6421`, Action: handleLoadHex, }, { Name: "loadgo", - Usage: "Compile and load a Go file with the manifest into the VM", - UsageText: `loadgo [--historic ] `, + Usage: "Compile and load a Go file with the manifest into the VM optionally attaching to it provided signers with scopes", + UsageText: `loadgo [--historic ] [, ...]`, Flags: []cli.Flag{historicFlag}, - Description: `loadgo [--historic ] + Description: `loadgo [--historic ] [, ...] - is mandatory parameter, example: + is mandatory parameter. +` + cmdargs.SignersParsingDoc + ` + +Example: > loadgo /path/to/file.go`, Action: handleLoadGo, }, @@ -181,14 +195,18 @@ The transaction script will be loaded into VM; the resulting execution context w }, { Name: "loaddeployed", - Usage: "Load deployed contract into the VM from chain. If '--historic' flag specified, then the historic contract state (historic script and manifest) will be loaded.", - UsageText: `loaddeployed [--historic ] `, + Usage: "Load deployed contract into the VM from chain optionally attaching to it provided signers with scopes. If '--historic' flag specified, then the historic contract state (historic script and manifest) will be loaded.", + UsageText: `loaddeployed [--historic ] [, ...]`, Flags: []cli.Flag{historicFlag}, - Description: `loaddeployed [--historic ] + Description: `loaddeployed [--historic ] [, ...] -Load deployed contract into the VM from chain. If '--historic' flag specified, then the historic contract state (historic script and manifest) will be loaded. +Load deployed contract into the VM from chain optionally attaching to it provided signers with scopes. +If '--historic' flag specified, then the historic contract state (historic script and manifest) will be loaded. - is mandatory parameter, example: + is mandatory parameter. +` + cmdargs.SignersParsingDoc + ` + +Example: > loaddeployed 0x0000000009070e030d0f0e020d0c06050e030c02`, Action: handleLoadDeployed, }, @@ -604,22 +622,35 @@ func prepareVM(c *cli.Context, tx *transaction.Transaction) error { } func handleLoadNEF(c *cli.Context) error { - err := prepareVM(c, nil) - if err != nil { - return err - } - v := getVMFromContext(c.App) args := c.Args() if len(args) < 2 { return fmt.Errorf("%w: ", ErrMissingParameter) } - if err := v.LoadFileWithFlags(args[0], callflag.All); err != nil { - return fmt.Errorf("failed to read nef: %w", err) + b, err := os.ReadFile(args[0]) + if err != nil { + return err + } + nef, err := nef.FileFromBytes(b) + if err != nil { + return fmt.Errorf("failed to decode NEF file: %w", err) } m, err := getManifestFromFile(args[1]) if err != nil { return fmt.Errorf("failed to read manifest: %w", err) } + var signers []transaction.Signer + if len(args) > 2 { + signers, err = cmdargs.ParseSigners(c.Args()[2:]) + if err != nil { + return fmt.Errorf("%w: %v", ErrInvalidParameter, err) + } + } + err = prepareVM(c, createFakeTransaction(nef.Script, signers)) + if err != nil { + return err + } + v := getVMFromContext(c.App) + v.LoadWithFlags(nef.Script, callflag.All) fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", v.Context().LenInstr()) setManifestInContext(c.App, m) changePrompt(c.App) @@ -627,11 +658,6 @@ func handleLoadNEF(c *cli.Context) error { } func handleLoadBase64(c *cli.Context) error { - err := prepareVM(c, nil) - if err != nil { - return err - } - v := getVMFromContext(c.App) args := c.Args() if len(args) < 1 { return fmt.Errorf("%w: ", ErrMissingParameter) @@ -640,18 +666,33 @@ func handleLoadBase64(c *cli.Context) error { if err != nil { return fmt.Errorf("%w: %s", ErrInvalidParameter, err) } + var signers []transaction.Signer + if len(args) > 1 { + signers, err = cmdargs.ParseSigners(args[1:]) + if err != nil { + return fmt.Errorf("%w: %v", ErrInvalidParameter, err) + } + } + err = prepareVM(c, createFakeTransaction(b, signers)) + if err != nil { + return err + } + v := getVMFromContext(c.App) v.LoadWithFlags(b, callflag.All) fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", v.Context().LenInstr()) changePrompt(c.App) return nil } -func handleLoadHex(c *cli.Context) error { - err := prepareVM(c, nil) - if err != nil { - return err +// 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, } - v := getVMFromContext(c.App) +} + +func handleLoadHex(c *cli.Context) error { args := c.Args() if len(args) < 1 { return fmt.Errorf("%w: ", ErrMissingParameter) @@ -660,6 +701,18 @@ func handleLoadHex(c *cli.Context) error { if err != nil { return fmt.Errorf("%w: %s", ErrInvalidParameter, err) } + var signers []transaction.Signer + if len(args) > 1 { + signers, err = cmdargs.ParseSigners(args[1:]) + if err != nil { + return fmt.Errorf("%w: %v", ErrInvalidParameter, err) + } + } + err = prepareVM(c, createFakeTransaction(b, signers)) + if err != nil { + return err + } + v := getVMFromContext(c.App) v.LoadWithFlags(b, callflag.All) fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", v.Context().LenInstr()) changePrompt(c.App) @@ -667,11 +720,6 @@ func handleLoadHex(c *cli.Context) error { } func handleLoadGo(c *cli.Context) error { - err := prepareVM(c, nil) - if err != nil { - return err - } - v := getVMFromContext(c.App) args := c.Args() if len(args) < 1 { return fmt.Errorf("%w: ", ErrMissingParameter) @@ -688,8 +736,20 @@ func handleLoadGo(c *cli.Context) error { if err != nil { return fmt.Errorf("can't create manifest: %w", err) } - setManifestInContext(c.App, m) + var signers []transaction.Signer + if len(args) > 1 { + signers, err = cmdargs.ParseSigners(args[1:]) + if err != nil { + return fmt.Errorf("%w: %v", ErrInvalidParameter, err) + } + } + err = prepareVM(c, createFakeTransaction(b.Script, signers)) + if err != nil { + return err + } + v := getVMFromContext(c.App) + setManifestInContext(c.App, m) v.LoadWithFlags(b.Script, callflag.All) fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", v.Context().LenInstr()) changePrompt(c.App) @@ -763,6 +823,17 @@ func handleLoadDeployed(c *cli.Context) error { return fmt.Errorf("contract %s not found: %w", h.StringLE(), err) } + var signers []transaction.Signer + if len(c.Args()) > 1 { + signers, err = cmdargs.ParseSigners(c.Args()[1:]) + if err != nil { + return fmt.Errorf("%w: %v", ErrInvalidParameter, err) + } + } + err = prepareVM(c, createFakeTransaction(cs.NEF.Script, signers)) // prepare VM one more time for proper IC initialization. + if err != nil { + return err + } v := getVMFromContext(c.App) v.LoadScriptWithHash(cs.NEF.Script, h, callflag.All) fmt.Fprintf(c.App.Writer, "READY: loaded %d instructions\n", v.Context().LenInstr()) @@ -796,11 +867,17 @@ func resetInteropContext(app *cli.App, tx *transaction.Transaction, height ...ui err error ) if len(height) != 0 { + if tx != nil { + tx.ValidUntilBlock = height[0] + 1 + } newIc, err = bc.GetTestHistoricVM(trigger.Application, tx, height[0]+1) if err != nil { return fmt.Errorf("failed to create historic VM for height %d: %w", height[0], err) } } else { + if tx != nil { + tx.ValidUntilBlock = bc.BlockHeight() + 1 + } newIc, err = bc.GetTestVM(trigger.Application, tx, nil) if err != nil { return fmt.Errorf("failed to create VM: %w", err) diff --git a/cli/vm/cli_test.go b/cli/vm/cli_test.go index de7cf7a04..af1e3efcd 100644 --- a/cli/vm/cli_test.go +++ b/cli/vm/cli_test.go @@ -281,27 +281,79 @@ func (e *executor) checkSlot(t *testing.T, items ...interface{}) { func TestLoad(t *testing.T) { script := []byte{byte(opcode.PUSH3), byte(opcode.PUSH4), byte(opcode.ADD)} + + ownerAddress := "NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB" + ownerAcc, err := address.StringToUint160(ownerAddress) + require.NoError(t, err) + sideAcc := util.Uint160{1, 2, 3} + buff := io.NewBufBinWriter() + emit.Bytes(buff.BinWriter, ownerAcc.BytesBE()) + emit.Syscall(buff.BinWriter, interopnames.SystemRuntimeCheckWitness) + checkWitnessScript := buff.Bytes() + t.Run("loadhex", func(t *testing.T) { e := newTestVMCLI(t) e.runProg(t, "loadhex", "loadhex notahex", - "loadhex "+hex.EncodeToString(script)) + "loadhex "+hex.EncodeToString(script), + "loadhex "+hex.EncodeToString(checkWitnessScript)+" "+ownerAddress, // owner:DefaultScope => true + "run", + "loadhex "+hex.EncodeToString(checkWitnessScript)+" "+ownerAddress+":None", // owner:None => false + "run", + "loadhex "+hex.EncodeToString(checkWitnessScript)+" "+ownerAcc.StringLE(), // ownerLE:DefaultScope => true + "run", + "loadhex "+hex.EncodeToString(checkWitnessScript)+" 0x"+ownerAcc.StringLE(), // owner0xLE:DefaultScope => true + "run", + "loadhex "+hex.EncodeToString(checkWitnessScript)+" "+sideAcc.StringLE(), // sideLE:DefaultScope => false + "run", + ) e.checkError(t, ErrMissingParameter) e.checkError(t, ErrInvalidParameter) e.checkNextLine(t, "READY: loaded 3 instructions") + e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkStack(t, true) + e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkStack(t, false) + e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkStack(t, true) + e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkStack(t, true) + e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkStack(t, false) }) t.Run("loadbase64", func(t *testing.T) { e := newTestVMCLI(t) e.runProg(t, "loadbase64", "loadbase64 not_a_base64", - "loadbase64 "+base64.StdEncoding.EncodeToString(script)) + "loadbase64 "+base64.StdEncoding.EncodeToString(script), + "loadbase64 "+base64.StdEncoding.EncodeToString(checkWitnessScript)+" "+ownerAddress, // owner:DefaultScope => true + "run", + "loadbase64 "+base64.StdEncoding.EncodeToString(checkWitnessScript)+" "+ownerAddress+":None", // owner:None => false + "run", + "loadbase64 "+base64.StdEncoding.EncodeToString(checkWitnessScript)+" "+ownerAcc.StringLE(), // ownerLE:DefaultScope => true + "run", + "loadbase64 "+base64.StdEncoding.EncodeToString(checkWitnessScript)+" 0x"+ownerAcc.StringLE(), // owner0xLE:DefaultScope => true + "run", + "loadbase64 "+base64.StdEncoding.EncodeToString(checkWitnessScript)+" "+sideAcc.StringLE(), // sideLE:DefaultScope => false + "run", + ) e.checkError(t, ErrMissingParameter) e.checkError(t, ErrInvalidParameter) e.checkNextLine(t, "READY: loaded 3 instructions") + e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkStack(t, true) + e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkStack(t, false) + e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkStack(t, true) + e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkStack(t, true) + e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkStack(t, false) }) src := `package kek @@ -343,14 +395,7 @@ go 1.17`) checkLoadgo(t, "simple", "vmtestcontract.go", "vmtestcontract_err.go") checkLoadgo(t, "utf-8 with spaces", "тестовый контракт.go", "тестовый контракт с ошибкой.go") - t.Run("loadgo, check calling flags", func(t *testing.T) { - srcAllowNotify := `package kek - import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" - func Main() int { - runtime.Log("Hello, world!") - return 1 - } -` + prepareLoadgoSrc := func(t *testing.T, srcAllowNotify string) string { filename := filepath.Join(tmpDir, "vmtestcontract.go") require.NoError(t, os.WriteFile(filename, []byte(srcAllowNotify), os.ModePerm)) filename = "'" + filename + "'" @@ -363,6 +408,17 @@ require ( replace github.com/nspcc-dev/neo-go/pkg/interop => ` + filepath.Join(wd, "../../pkg/interop") + ` go 1.17`) require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "go.mod"), goMod, os.ModePerm)) + return filename + } + t.Run("loadgo, check calling flags", func(t *testing.T) { + srcAllowNotify := `package kek + import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + func Main() int { + runtime.Log("Hello, world!") + return 1 + } +` + filename := prepareLoadgoSrc(t, srcAllowNotify) e := newTestVMCLI(t) e.runProg(t, @@ -371,6 +427,41 @@ go 1.17`) e.checkNextLine(t, "READY: loaded \\d* instructions") e.checkStack(t, 1) }) + t.Run("loadgo, check signers", func(t *testing.T) { + srcCheckWitness := `package kek + import ( + "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + "github.com/nspcc-dev/neo-go/pkg/interop/util" + ) + func Main() bool { + var owner = util.FromAddress("` + ownerAddress + `") + return runtime.CheckWitness(owner) + } +` + filename := prepareLoadgoSrc(t, srcCheckWitness) + e := newTestVMCLI(t) + e.runProg(t, + "loadgo "+filename+" "+ownerAddress, // owner:DefaultScope => true + "run main", + "loadgo "+filename+" "+ownerAddress+":None", // owner:None => false + "run main", + "loadgo "+filename+" "+ownerAcc.StringLE(), // ownerLE:DefaultScope => true + "run main", + "loadgo "+filename+" 0x"+ownerAcc.StringLE(), // owner0xLE:DefaultScope => true + "run main", + "loadgo "+filename+" "+sideAcc.StringLE(), // sideLE:DefaultScope => false + "run main") + e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkStack(t, true) + e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkStack(t, false) + e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkStack(t, true) + e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkStack(t, true) + e.checkNextLine(t, "READY: loaded \\d+ instructions") + e.checkStack(t, false) + }) t.Run("loadnef", func(t *testing.T) { config.Version = "0.92.0-test" @@ -1037,6 +1128,10 @@ func TestLoaddeployed(t *testing.T) { h, err := e.cli.chain.GetContractScriptHash(1) // examples/storage/storage.go require.NoError(t, err) + ownerAddress := "NbrUYaZgyhSkNoRo9ugRyEMdUZxrhkNaWB" // owner of examples/runtime/runtime.go (taken from deployed contract with ID=2) + ownerAcc, err := address.StringToUint160(ownerAddress) + require.NoError(t, err) + sideAcc := util.Uint160{1, 2, 3} e.runProg(t, "loaddeployed "+h.StringLE(), // hash LE @@ -1047,6 +1142,19 @@ func TestLoaddeployed(t *testing.T) { "run get 1", "loaddeployed --historic 2 1", // historic state, check that hash is properly set "run get 1", + // Check signers parsing: + "loaddeployed 2 "+ownerAddress, // check witness (owner:DefautScope => true) + "run checkWitness", + "loaddeployed 2 "+ownerAddress+":None", // check witness (owner:None => false) + "run checkWitness", + "loaddeployed 2 "+ownerAddress+":CalledByEntry", // check witness (owner:CalledByEntry => true) + "run checkWitness", + "loaddeployed 2 "+ownerAcc.StringLE()+":CalledByEntry", // check witness (ownerLE:CalledByEntry => true) + "run checkWitness", + "loaddeployed 2 0x"+ownerAcc.StringLE()+":CalledByEntry", // check witness (owner0xLE:CalledByEntry => true) + "run checkWitness", + "loaddeployed 2 "+sideAcc.StringLE()+":Global", // check witness (sideLE:Global => false) + "run checkWitness", "loaddeployed", // missing argument "exit", ) @@ -1058,5 +1166,18 @@ func TestLoaddeployed(t *testing.T) { e.checkStack(t, []byte{2}) e.checkNextLine(t, "READY: loaded \\d+ instructions") e.checkStack(t, []byte{1}) + // Check signers parsing: + e.checkNextLine(t, "READY: loaded \\d+ instructions") // check witness of owner:DefaultScope + e.checkStack(t, true) + e.checkNextLine(t, "READY: loaded \\d+ instructions") // check witness of owner:None + e.checkStack(t, false) + e.checkNextLine(t, "READY: loaded \\d+ instructions") // check witness of owner:CalledByEntry + e.checkStack(t, true) + e.checkNextLine(t, "READY: loaded \\d+ instructions") // check witness of ownerLE:CalledByEntry + e.checkStack(t, true) + e.checkNextLine(t, "READY: loaded \\d+ instructions") // check witness of owner0xLE:CalledByEntry + e.checkStack(t, true) + e.checkNextLine(t, "READY: loaded \\d+ instructions") // check witness of owner0xLE:CalledByEntry + e.checkStack(t, false) e.checkError(t, errors.New("contract hash, address or ID is mandatory argument")) }