cli/wallet: process non-out non-rpc calls to sign

And document the behavior better. Fixes #2664.
This commit is contained in:
Roman Khimov 2022-08-31 15:43:30 +03:00
parent c316107c9f
commit c2c10c111c
3 changed files with 70 additions and 19 deletions

View file

@ -14,6 +14,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/context"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
"github.com/stretchr/testify/require"
@ -162,13 +163,41 @@ func TestSignMultisigTx(t *testing.T) {
require.Equal(t, vmstate.Halt.String(), res.State, res.FaultException)
})
t.Run("console output", func(t *testing.T) {
oldIn, err := os.ReadFile(txPath)
require.NoError(t, err)
e.In.WriteString("pass\r")
e.Run(t, "neo-go", "wallet", "sign",
"--wallet", wallet2Path, "--address", multisigAddr,
"--in", txPath)
newIn, err := os.ReadFile(txPath)
require.NoError(t, err)
require.Equal(t, oldIn, newIn)
pcOld := new(context.ParameterContext)
require.NoError(t, json.Unmarshal(oldIn, pcOld))
jOut := e.Out.Bytes()
pcNew := new(context.ParameterContext)
require.NoError(t, json.Unmarshal(jOut, pcNew))
require.Equal(t, pcOld.Type, pcNew.Type)
require.Equal(t, pcOld.Network, pcNew.Network)
require.Equal(t, pcOld.Verifiable, pcNew.Verifiable)
require.Equal(t, pcOld.Items[multisigHash].Script, pcNew.Items[multisigHash].Script)
// It's completely signed after this, so parameters have signatures now as well.
require.NotEqual(t, pcOld.Items[multisigHash].Parameters, pcNew.Items[multisigHash].Parameters)
require.NotEqual(t, pcOld.Items[multisigHash].Signatures, pcNew.Items[multisigHash].Signatures)
})
t.Run("sign, save and send", func(t *testing.T) {
e.In.WriteString("pass\r")
e.Run(t, "neo-go", "wallet", "sign",
"--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wallet2Path, "--address", multisigAddr,
"--in", txPath, "--out", txPath)
e.checkTxPersisted(t)
})
t.Run("double-sign", func(t *testing.T) {
e.In.WriteString("pass\r")
e.RunWithError(t, "neo-go", "wallet", "sign",

View file

@ -1,6 +1,7 @@
package wallet
import (
"encoding/json"
"fmt"
"github.com/nspcc-dev/neo-go/cli/cmdargs"
@ -12,6 +13,11 @@ import (
)
func signStoredTransaction(ctx *cli.Context) error {
var (
out = ctx.String("out")
rpcNode = ctx.String(options.RPCEndpointFlag)
addrFlag = ctx.Generic("address").(*flags.Address)
)
if err := cmdargs.EnsureNone(ctx); err != nil {
return err
}
@ -20,11 +26,11 @@ func signStoredTransaction(ctx *cli.Context) error {
return cli.NewExitError(err, 1)
}
c, err := paramcontext.Read(ctx.String("in"))
pc, err := paramcontext.Read(ctx.String("in"))
if err != nil {
return cli.NewExitError(err, 1)
}
addrFlag := ctx.Generic("address").(*flags.Address)
if !addrFlag.IsSet {
return cli.NewExitError("address was not provided", 1)
}
@ -35,7 +41,7 @@ func signStoredTransaction(ctx *cli.Context) error {
return cli.NewExitError(err, 1)
}
tx, ok := c.Verifiable.(*transaction.Transaction)
tx, ok := pc.Verifiable.(*transaction.Transaction)
if !ok {
return cli.NewExitError("verifiable item is not a transaction", 1)
}
@ -52,18 +58,27 @@ func signStoredTransaction(ctx *cli.Context) error {
}
priv := acc.PrivateKey()
sign := priv.SignHashable(uint32(c.Network), tx)
if err := c.AddSignature(ch, acc.Contract, priv.PublicKey(), sign); err != nil {
sign := priv.SignHashable(uint32(pc.Network), tx)
if err := pc.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 != "" {
if err := paramcontext.Save(c, out); err != nil {
return cli.NewExitError(fmt.Errorf("failed to dump resulting transaction: %w", err), 1)
// Not saving and not sending, print.
if out == "" && rpcNode == "" {
txt, err := json.MarshalIndent(pc, " ", " ")
if err != nil {
return cli.NewExitError(fmt.Errorf("can't display resulting context: %w", err), 1)
}
fmt.Fprintln(ctx.App.Writer, string(txt))
return nil
}
if out != "" {
if err := paramcontext.Save(pc, out); err != nil {
return cli.NewExitError(fmt.Errorf("can't save resulting context: %w", err), 1)
}
}
if len(ctx.String(options.RPCEndpointFlag)) != 0 {
if rpcNode != "" {
for i := range tx.Signers {
w, err := c.GetWitness(tx.Signers[i].Account)
w, err := pc.GetWitness(tx.Signers[i].Account)
if err != nil {
return cli.NewExitError(fmt.Errorf("failed to construct witness for signer #%d: %w", i, err), 1)
}

View file

@ -281,7 +281,14 @@ func NewCommands() []cli.Command {
{
Name: "sign",
Usage: "cosign transaction with multisig/contract/additional account",
UsageText: "sign -w wallet [--wallet-config path] --address <address> --in <file.in> --out <file.out> [-r <endpoint>]",
UsageText: "sign -w wallet [--wallet-config path] --address <address> --in <file.in> [--out <file.out>] [-r <endpoint>]",
Description: `Signs the given (in file.in) context (which must be a transaction
signing context) for the given address using the given wallet. This command can
output the resulting JSON (with additional signature added) right to the console
(if no file.out and no RPC endpoint specified) or into a file (which can be the
same as input one). If an RPC endpoint is given it'll also try to construct a
complete transaction and send it via RPC (printing its hash if everything is OK).
`,
Action: signStoredTransaction,
Flags: signFlags,
},