Merge pull request #1807 from nspcc-dev/feature/walletsign
cli: allow to sign dumped transaction by several non-multisig signers
This commit is contained in:
commit
19a23a36e4
8 changed files with 167 additions and 65 deletions
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -75,8 +76,23 @@ func TestSignMultisigTx(t *testing.T) {
|
|||
"--to", priv.Address(), "--token", "NEO", "--amount", "1",
|
||||
"--out", txPath)
|
||||
|
||||
simplePriv, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
{ // Simple signer, not in signers.
|
||||
e.In.WriteString("acc\rpass\rpass\r")
|
||||
e.Run(t, "neo-go", "wallet", "import",
|
||||
"--wallet", wallet1Path,
|
||||
"--wif", simplePriv.WIF())
|
||||
t.Run("sign with missing signer", func(t *testing.T) {
|
||||
e.In.WriteString("pass\r")
|
||||
e.RunWithError(t, "neo-go", "wallet", "sign",
|
||||
"--wallet", wallet1Path, "--address", simplePriv.Address(),
|
||||
"--in", txPath, "--out", txPath)
|
||||
})
|
||||
}
|
||||
|
||||
e.In.WriteString("pass\r")
|
||||
e.Run(t, "neo-go", "wallet", "multisig", "sign",
|
||||
e.Run(t, "neo-go", "wallet", "sign",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--wallet", wallet2Path, "--address", multisigAddr,
|
||||
"--in", txPath, "--out", txPath)
|
||||
|
@ -88,6 +104,26 @@ func TestSignMultisigTx(t *testing.T) {
|
|||
require.Equal(t, big.NewInt(3), b)
|
||||
|
||||
t.Run("via invokefunction", func(t *testing.T) {
|
||||
e.In.WriteString("one\r")
|
||||
e.Run(t, "neo-go", "contract", "deploy",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--wallet", validatorWallet, "--address", validatorAddr,
|
||||
"--in", "testdata/verify.nef", "--manifest", "testdata/verify.manifest.json")
|
||||
|
||||
line, err := e.Out.ReadString('\n')
|
||||
require.NoError(t, err)
|
||||
line = strings.TrimSpace(strings.TrimPrefix(line, "Contract: "))
|
||||
h, err := util.Uint160DecodeStringLE(line)
|
||||
require.NoError(t, err)
|
||||
|
||||
e.checkTxPersisted(t)
|
||||
|
||||
e.In.WriteString("acc\rpass\rpass\r")
|
||||
e.Run(t, "neo-go", "wallet", "import-deployed",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--wallet", wallet1Path, "--wif", simplePriv.WIF(),
|
||||
"--contract", h.StringLE())
|
||||
|
||||
e.In.WriteString("pass\r")
|
||||
e.Run(t, "neo-go", "contract", "invokefunction",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
|
@ -97,14 +133,30 @@ func TestSignMultisigTx(t *testing.T) {
|
|||
"bytes:"+multisigHash.StringBE(),
|
||||
"bytes:"+priv.GetScriptHash().StringBE(),
|
||||
"int:1", "bytes:",
|
||||
"--", strings.Join([]string{multisigHash.StringLE(), ":", "Global"}, ""))
|
||||
"--", multisigHash.StringLE()+":"+"Global",
|
||||
h.StringLE(),
|
||||
simplePriv.GetScriptHash().StringLE(),
|
||||
)
|
||||
|
||||
e.In.WriteString("pass\r")
|
||||
e.Run(t, "neo-go", "wallet", "multisig", "sign",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
e.Run(t, "neo-go", "wallet", "sign",
|
||||
"--wallet", wallet2Path, "--address", multisigAddr,
|
||||
"--in", txPath, "--out", txPath)
|
||||
e.checkTxPersisted(t)
|
||||
|
||||
// Simple signer, not in signers.
|
||||
e.In.WriteString("pass\r")
|
||||
e.Run(t, "neo-go", "wallet", "sign",
|
||||
"--wallet", wallet1Path, "--address", simplePriv.Address(),
|
||||
"--in", txPath, "--out", txPath)
|
||||
|
||||
// Contract.
|
||||
e.In.WriteString("pass\r")
|
||||
e.Run(t, "neo-go", "wallet", "sign",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
"--wallet", wallet1Path, "--address", address.Uint160ToString(h),
|
||||
"--in", txPath, "--out", txPath)
|
||||
tx, _ := e.checkTxPersisted(t)
|
||||
require.Equal(t, 3, len(tx.Signers))
|
||||
|
||||
b, _ := e.Chain.GetGoverningTokenBalance(priv.GetScriptHash())
|
||||
require.Equal(t, big.NewInt(2), b)
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"io/ioutil"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/context"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
)
|
||||
|
@ -22,7 +23,11 @@ func InitAndSave(tx *transaction.Transaction, acc *wallet.Account, filename stri
|
|||
pub := priv.PublicKey()
|
||||
sign := priv.Sign(tx.GetSignedPart())
|
||||
scCtx := context.NewParameterContext("Neo.Core.ContractTransaction", tx)
|
||||
if err := scCtx.AddSignature(acc.Contract, pub, sign); err != nil {
|
||||
h, err := address.StringToUint160(acc.Address)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid address: %s", acc.Address)
|
||||
}
|
||||
if err := scCtx.AddSignature(h, acc.Contract, pub, sign); err != nil {
|
||||
return fmt.Errorf("can't add signature: %w", err)
|
||||
}
|
||||
return Save(scCtx, filename)
|
||||
|
|
|
@ -10,29 +10,7 @@ import (
|
|||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func newMultisigCommands() []cli.Command {
|
||||
signFlags := []cli.Flag{
|
||||
walletPathFlag,
|
||||
outFlag,
|
||||
inFlag,
|
||||
cli.StringFlag{
|
||||
Name: "address, a",
|
||||
Usage: "Address to use",
|
||||
},
|
||||
}
|
||||
signFlags = append(signFlags, options.RPC...)
|
||||
return []cli.Command{
|
||||
{
|
||||
Name: "sign",
|
||||
Usage: "sign a transaction",
|
||||
UsageText: "multisig sign --wallet <path> --address <address> --in <file.in> --out <file.out>",
|
||||
Action: signMultisig,
|
||||
Flags: signFlags,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func signMultisig(ctx *cli.Context) error {
|
||||
func signStoredTransaction(ctx *cli.Context) error {
|
||||
wall, err := openWallet(ctx.String("wallet"))
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
|
@ -58,9 +36,24 @@ func signMultisig(ctx *cli.Context) error {
|
|||
return cli.NewExitError("verifiable item is not a transaction", 1)
|
||||
}
|
||||
|
||||
ch, err := address.StringToUint160(acc.Address)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("wallet contains invalid account: %s", acc.Address), 1)
|
||||
}
|
||||
signerFound := false
|
||||
for i := range tx.Signers {
|
||||
if tx.Signers[i].Account == ch {
|
||||
signerFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !signerFound {
|
||||
return cli.NewExitError("tx signers don't contain provided account", 1)
|
||||
}
|
||||
|
||||
priv := acc.PrivateKey()
|
||||
sign := priv.Sign(tx.GetSignedPart())
|
||||
if err := c.AddSignature(acc.Contract, priv.PublicKey(), sign); err != nil {
|
||||
if err := c.AddSignature(ch, acc.Contract, priv.PublicKey(), sign); err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("can't add signature: %w", err), 1)
|
||||
}
|
||||
if out := ctx.String("out"); out != "" {
|
||||
|
@ -69,15 +62,18 @@ func signMultisig(ctx *cli.Context) error {
|
|||
}
|
||||
}
|
||||
if len(ctx.String(options.RPCEndpointFlag)) != 0 {
|
||||
w, err := c.GetWitness(acc.Contract)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
for i := range tx.Signers {
|
||||
w, err := c.GetWitness(tx.Signers[i].Account)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
tx.Scripts = append(tx.Scripts, *w)
|
||||
}
|
||||
tx.Scripts = append(tx.Scripts, *w)
|
||||
|
||||
gctx, cancel := options.GetTimeoutContext(ctx)
|
||||
defer cancel()
|
||||
|
||||
var err error // `GetRPCClient` returns specialized type.
|
||||
c, err := options.GetRPCClient(gctx, ctx)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
|
|
|
@ -70,6 +70,16 @@ func NewCommands() []cli.Command {
|
|||
},
|
||||
}
|
||||
claimFlags = append(claimFlags, options.RPC...)
|
||||
signFlags := []cli.Flag{
|
||||
walletPathFlag,
|
||||
outFlag,
|
||||
inFlag,
|
||||
cli.StringFlag{
|
||||
Name: "address, a",
|
||||
Usage: "Address to use",
|
||||
},
|
||||
}
|
||||
signFlags = append(signFlags, options.RPC...)
|
||||
return []cli.Command{{
|
||||
Name: "wallet",
|
||||
Usage: "create, open and manage a NEO wallet",
|
||||
|
@ -204,9 +214,11 @@ func NewCommands() []cli.Command {
|
|||
},
|
||||
},
|
||||
{
|
||||
Name: "multisig",
|
||||
Usage: "work with multisig address",
|
||||
Subcommands: newMultisigCommands(),
|
||||
Name: "sign",
|
||||
Usage: "cosign transaction with multisig/contract/additional account",
|
||||
UsageText: "sign --wallet <path> --address <address> --in <file.in> --out <file.out>",
|
||||
Action: signStoredTransaction,
|
||||
Flags: signFlags,
|
||||
},
|
||||
{
|
||||
Name: "nep17",
|
||||
|
|
|
@ -51,8 +51,11 @@ func NewParameterContext(typ string, verif crypto.VerifiableDecodable) *Paramete
|
|||
}
|
||||
|
||||
// GetWitness returns invocation and verification scripts for the specified contract.
|
||||
func (c *ParameterContext) GetWitness(ctr *wallet.Contract) (*transaction.Witness, error) {
|
||||
item := c.getItemForContract(ctr)
|
||||
func (c *ParameterContext) GetWitness(h util.Uint160) (*transaction.Witness, error) {
|
||||
item, ok := c.Items[h]
|
||||
if !ok {
|
||||
return nil, errors.New("witness not found")
|
||||
}
|
||||
bw := io.NewBufBinWriter()
|
||||
for i := range item.Parameters {
|
||||
if item.Parameters[i].Type != smartcontract.SignatureType {
|
||||
|
@ -64,13 +67,13 @@ func (c *ParameterContext) GetWitness(ctr *wallet.Contract) (*transaction.Witnes
|
|||
}
|
||||
return &transaction.Witness{
|
||||
InvocationScript: bw.Bytes(),
|
||||
VerificationScript: ctr.Script,
|
||||
VerificationScript: item.Script,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AddSignature adds a signature for the specified contract and public key.
|
||||
func (c *ParameterContext) AddSignature(ctr *wallet.Contract, pub *keys.PublicKey, sig []byte) error {
|
||||
item := c.getItemForContract(ctr)
|
||||
func (c *ParameterContext) AddSignature(h util.Uint160, ctr *wallet.Contract, pub *keys.PublicKey, sig []byte) error {
|
||||
item := c.getItemForContract(h, ctr)
|
||||
if _, pubs, ok := vm.ParseMultiSigContract(ctr.Script); ok {
|
||||
if item.GetSignature(pub) != nil {
|
||||
return errors.New("signature is already added")
|
||||
|
@ -118,24 +121,29 @@ func (c *ParameterContext) AddSignature(ctr *wallet.Contract, pub *keys.PublicKe
|
|||
index = i
|
||||
}
|
||||
}
|
||||
if index == -1 {
|
||||
if index != -1 {
|
||||
item.Parameters[index].Value = sig
|
||||
} else if !ctr.Deployed {
|
||||
return errors.New("missing signature parameter")
|
||||
}
|
||||
item.Parameters[index].Value = sig
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ParameterContext) getItemForContract(ctr *wallet.Contract) *Item {
|
||||
h := ctr.ScriptHash()
|
||||
if item, ok := c.Items[h]; ok {
|
||||
func (c *ParameterContext) getItemForContract(h util.Uint160, ctr *wallet.Contract) *Item {
|
||||
item, ok := c.Items[ctr.ScriptHash()]
|
||||
if ok {
|
||||
return item
|
||||
}
|
||||
params := make([]smartcontract.Parameter, len(ctr.Parameters))
|
||||
for i := range params {
|
||||
params[i].Type = ctr.Parameters[i].Type
|
||||
}
|
||||
item := &Item{
|
||||
Script: h,
|
||||
script := ctr.Script
|
||||
if ctr.Deployed {
|
||||
script = nil
|
||||
}
|
||||
item = &Item{
|
||||
Script: script,
|
||||
Parameters: params,
|
||||
Signatures: make(map[string][]byte),
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package context
|
|||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||
|
@ -34,13 +35,13 @@ func TestParameterContext_AddSignatureSimpleContract(t *testing.T) {
|
|||
newParam(smartcontract.SignatureType, "parameter1"),
|
||||
},
|
||||
}
|
||||
require.Error(t, c.AddSignature(ctr, pub, sig))
|
||||
require.Error(t, c.AddSignature(ctr.ScriptHash(), ctr, pub, sig))
|
||||
if item := c.Items[ctr.ScriptHash()]; item != nil {
|
||||
require.Nil(t, item.Parameters[0].Value)
|
||||
}
|
||||
|
||||
ctr.Parameters = ctr.Parameters[:0]
|
||||
require.Error(t, c.AddSignature(ctr, pub, sig))
|
||||
require.Error(t, c.AddSignature(ctr.ScriptHash(), ctr, pub, sig))
|
||||
if item := c.Items[ctr.ScriptHash()]; item != nil {
|
||||
require.Nil(t, item.Parameters[0].Value)
|
||||
}
|
||||
|
@ -51,19 +52,27 @@ func TestParameterContext_AddSignatureSimpleContract(t *testing.T) {
|
|||
Script: pub.GetVerificationScript(),
|
||||
Parameters: []wallet.ContractParam{newParam(smartcontract.SignatureType, "parameter0")},
|
||||
}
|
||||
require.NoError(t, c.AddSignature(ctr, pub, sig))
|
||||
require.NoError(t, c.AddSignature(ctr.ScriptHash(), ctr, pub, sig))
|
||||
item := c.Items[ctr.ScriptHash()]
|
||||
require.NotNil(t, item)
|
||||
require.Equal(t, sig, item.Parameters[0].Value)
|
||||
|
||||
t.Run("GetWitness", func(t *testing.T) {
|
||||
w, err := c.GetWitness(ctr)
|
||||
w, err := c.GetWitness(ctr.ScriptHash())
|
||||
require.NoError(t, err)
|
||||
v := newTestVM(w, tx)
|
||||
require.NoError(t, v.Run())
|
||||
require.Equal(t, 1, v.Estack().Len())
|
||||
require.Equal(t, true, v.Estack().Pop().Value())
|
||||
})
|
||||
t.Run("not found", func(t *testing.T) {
|
||||
ctr := &wallet.Contract{
|
||||
Script: []byte{byte(opcode.DROP), byte(opcode.PUSHT)},
|
||||
Parameters: []wallet.ContractParam{newParam(smartcontract.SignatureType, "parameter0")},
|
||||
}
|
||||
_, err := c.GetWitness(ctr.ScriptHash())
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestParameterContext_AddSignatureMultisig(t *testing.T) {
|
||||
|
@ -86,13 +95,13 @@ func TestParameterContext_AddSignatureMultisig(t *testing.T) {
|
|||
priv, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
sig := priv.Sign(data)
|
||||
require.Error(t, c.AddSignature(ctr, priv.PublicKey(), sig))
|
||||
require.Error(t, c.AddSignature(ctr.ScriptHash(), ctr, priv.PublicKey(), sig))
|
||||
|
||||
indices := []int{2, 3, 0} // random order
|
||||
for _, i := range indices {
|
||||
sig := privs[i].Sign(data)
|
||||
require.NoError(t, c.AddSignature(ctr, pubs[i], sig))
|
||||
require.Error(t, c.AddSignature(ctr, pubs[i], sig))
|
||||
require.NoError(t, c.AddSignature(ctr.ScriptHash(), ctr, pubs[i], sig))
|
||||
require.Error(t, c.AddSignature(ctr.ScriptHash(), ctr, pubs[i], sig))
|
||||
|
||||
item := c.Items[ctr.ScriptHash()]
|
||||
require.NotNil(t, item)
|
||||
|
@ -100,7 +109,7 @@ func TestParameterContext_AddSignatureMultisig(t *testing.T) {
|
|||
}
|
||||
|
||||
t.Run("GetWitness", func(t *testing.T) {
|
||||
w, err := c.GetWitness(ctr)
|
||||
w, err := c.GetWitness(ctr.ScriptHash())
|
||||
require.NoError(t, err)
|
||||
v := newTestVM(w, tx)
|
||||
require.NoError(t, v.Run())
|
||||
|
@ -131,7 +140,7 @@ func TestParameterContext_MarshalJSON(t *testing.T) {
|
|||
Verifiable: tx,
|
||||
Items: map[util.Uint160]*Item{
|
||||
priv.GetScriptHash(): {
|
||||
Script: priv.GetScriptHash(),
|
||||
Script: priv.PublicKey().GetVerificationScript(),
|
||||
Parameters: []smartcontract.Parameter{{
|
||||
Type: smartcontract.SignatureType,
|
||||
Value: sign,
|
||||
|
@ -144,6 +153,22 @@ func TestParameterContext_MarshalJSON(t *testing.T) {
|
|||
}
|
||||
|
||||
testserdes.MarshalUnmarshalJSON(t, expected, new(ParameterContext))
|
||||
|
||||
t.Run("invalid script", func(t *testing.T) {
|
||||
js := `{
|
||||
"script": "AQID",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "Signature",
|
||||
"value": "QfOZLLqjMyPWMzRxMAKw7fcd8leLcpwiiTV2pUyC0pth/y7Iw7o7WzNpxeAJm5bmExmlF7g5pMhXz1xVT6KK3g=="
|
||||
}
|
||||
],
|
||||
"signatures": {
|
||||
"025c210bde738e0e646929ee04ec2ccb42a700356083f55386b5347b9b725c10b9": "a6c6d8a2334791888df559419f07209ee39e2f20688af8cc38010854b98abf77194e37f173bbc86b77dce4afa8ce3ae5170dd346b5265bcb9b723d83299a6f0f",
|
||||
"035d4da640b3a39f19ed88855aeddd97725422b4230ccae56bd5544419d0056ea9": "058e577f23395f382194eebb83f66bb8903c8f3c5b6afd759c20f2518466124dcd9cbccfc029a42e9a7d5a3a060b091edc73dcac949fd894d7a9d10678296ac6"
|
||||
}`
|
||||
require.Error(t, json.Unmarshal([]byte(js), new(ParameterContext)))
|
||||
})
|
||||
}
|
||||
|
||||
func getPrivateKeys(t *testing.T, n int) ([]*keys.PrivateKey, []*keys.PublicKey) {
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
package context
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
)
|
||||
|
||||
// Item represents a transaction context item.
|
||||
type Item struct {
|
||||
Script util.Uint160
|
||||
Script []byte
|
||||
Parameters []smartcontract.Parameter
|
||||
Signatures map[string][]byte
|
||||
}
|
||||
|
||||
type itemAux struct {
|
||||
Script util.Uint160 `json:"script"`
|
||||
Script string `json:"script"`
|
||||
Parameters []smartcontract.Parameter `json:"parameters"`
|
||||
Signatures map[string]string `json:"signatures"`
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ func (it *Item) AddSignature(pub *keys.PublicKey, sig []byte) {
|
|||
// MarshalJSON implements json.Marshaler interface.
|
||||
func (it Item) MarshalJSON() ([]byte, error) {
|
||||
ci := itemAux{
|
||||
Script: it.Script,
|
||||
Script: base64.StdEncoding.EncodeToString(it.Script),
|
||||
Parameters: it.Parameters,
|
||||
Signatures: make(map[string]string, len(it.Signatures)),
|
||||
}
|
||||
|
@ -55,6 +55,11 @@ func (it *Item) UnmarshalJSON(data []byte) error {
|
|||
return err
|
||||
}
|
||||
|
||||
script, err := base64.StdEncoding.DecodeString(ci.Script)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sigs := make(map[string][]byte, len(ci.Signatures))
|
||||
for keyHex, sigHex := range ci.Signatures {
|
||||
_, err := keys.NewPublicKeyFromString(keyHex)
|
||||
|
@ -69,7 +74,7 @@ func (it *Item) UnmarshalJSON(data []byte) error {
|
|||
}
|
||||
|
||||
it.Signatures = sigs
|
||||
it.Script = ci.Script
|
||||
it.Script = script
|
||||
it.Parameters = ci.Parameters
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -41,7 +40,7 @@ func TestContextItem_MarshalJSON(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
expected := &Item{
|
||||
Script: util.Uint160{1, 2, 3},
|
||||
Script: []byte{1, 2, 3},
|
||||
Parameters: []smartcontract.Parameter{{
|
||||
Type: smartcontract.SignatureType,
|
||||
Value: random.Bytes(64),
|
||||
|
|
Loading…
Reference in a new issue