From 106c27782e5f12cf34aed9ed500a54c9c5c471d0 Mon Sep 17 00:00:00 2001 From: Evgeniy Stratonikov Date: Thu, 4 Mar 2021 14:14:25 +0300 Subject: [PATCH] cli/wallet: allow to cosign with a contract Contracts have empty verification script and their hash is calculated differently. --- cli/paramcontext/context.go | 7 ++++++- cli/wallet/multisig.go | 8 ++++++-- pkg/smartcontract/context/context.go | 15 ++++++++++----- pkg/smartcontract/context/context_test.go | 12 ++++++------ 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/cli/paramcontext/context.go b/cli/paramcontext/context.go index f1bc55e9c..5b3da2daa 100644 --- a/cli/paramcontext/context.go +++ b/cli/paramcontext/context.go @@ -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) diff --git a/cli/wallet/multisig.go b/cli/wallet/multisig.go index b2b545c5c..2a7955d28 100644 --- a/cli/wallet/multisig.go +++ b/cli/wallet/multisig.go @@ -36,9 +36,13 @@ func signStoredTransaction(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 == acc.Contract.ScriptHash() { + if tx.Signers[i].Account == ch { signerFound = true break } @@ -49,7 +53,7 @@ func signStoredTransaction(ctx *cli.Context) error { 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 != "" { diff --git a/pkg/smartcontract/context/context.go b/pkg/smartcontract/context/context.go index 9fc7b88e3..842273a15 100644 --- a/pkg/smartcontract/context/context.go +++ b/pkg/smartcontract/context/context.go @@ -72,8 +72,8 @@ func (c *ParameterContext) GetWitness(h util.Uint160) (*transaction.Witness, err } // 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.ScriptHash(), 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") @@ -121,10 +121,11 @@ 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 } @@ -137,8 +138,12 @@ func (c *ParameterContext) getItemForContract(h util.Uint160, ctr *wallet.Contra for i := range params { params[i].Type = ctr.Parameters[i].Type } + script := ctr.Script + if ctr.Deployed { + script = nil + } item = &Item{ - Script: ctr.Script, + Script: script, Parameters: params, Signatures: make(map[string][]byte), } diff --git a/pkg/smartcontract/context/context_test.go b/pkg/smartcontract/context/context_test.go index 98de00b2d..1a47e05fa 100644 --- a/pkg/smartcontract/context/context_test.go +++ b/pkg/smartcontract/context/context_test.go @@ -35,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) } @@ -52,7 +52,7 @@ 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) @@ -95,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)