Merge pull request #2180 from nspcc-dev/cli-show-fee

cli/wallet: show tx fee before relaying
This commit is contained in:
Roman Khimov 2021-09-22 13:33:40 +03:00 committed by GitHub
commit d915f085a7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 104 additions and 5 deletions

View file

@ -22,6 +22,7 @@ func TestRegisterCandidate(t *testing.T) {
"--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--wallet", validatorWallet,
"--from", validatorAddr, "--from", validatorAddr,
"--force",
"NEO:"+validatorPriv.Address()+":10", "NEO:"+validatorPriv.Address()+":10",
"GAS:"+validatorPriv.Address()+":10000") "GAS:"+validatorPriv.Address()+":10000")
e.checkTxPersisted(t) e.checkTxPersisted(t)

View file

@ -175,6 +175,7 @@ func TestContractDeployWithData(t *testing.T) {
"--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--address", validatorAddr, "--wallet", validatorWallet, "--address", validatorAddr,
"--in", nefName, "--manifest", manifestName, "--in", nefName, "--manifest", manifestName,
"--force",
"[", "key1", "12", "key2", "take_me_to_church", "]") "[", "key1", "12", "key2", "take_me_to_church", "]")
e.checkTxPersisted(t, "Sent invocation transaction ") e.checkTxPersisted(t, "Sent invocation transaction ")
@ -242,6 +243,7 @@ func TestContractManifestGroups(t *testing.T) {
e.Run(t, "neo-go", "contract", "deploy", e.Run(t, "neo-go", "contract", "deploy",
"--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--in", nefName, "--manifest", manifestName, "--in", nefName, "--manifest", manifestName,
"--force",
"--wallet", validatorWallet, "--address", validatorAddr) "--wallet", validatorWallet, "--address", validatorAddr)
} }
@ -261,6 +263,7 @@ func deployContract(t *testing.T, e *executor, inPath, configPath, wallet, addre
e.Run(t, "neo-go", "contract", "deploy", e.Run(t, "neo-go", "contract", "deploy",
"--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", wallet, "--address", address, "--wallet", wallet, "--address", address,
"--force",
"--in", nefName, "--manifest", manifestName) "--in", nefName, "--manifest", manifestName)
e.checkTxPersisted(t, "Sent invocation transaction ") e.checkTxPersisted(t, "Sent invocation transaction ")
line, err := e.Out.ReadString('\n') line, err := e.Out.ReadString('\n')
@ -297,7 +300,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, "neo-go", "contract", "deploy", e.Run(t, "neo-go", "contract", "deploy",
"--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr, "--force",
"--wallet", validatorWallet, "--address", validatorAddr, "--wallet", validatorWallet, "--address", validatorAddr,
"--in", nefName, "--manifest", manifestName) "--in", nefName, "--manifest", manifestName)
@ -371,11 +374,20 @@ func TestComlileAndInvokeFunction(t *testing.T) {
}) })
cmd = append(cmd, "--wallet", validatorWallet, "--address", validatorAddr) cmd = append(cmd, "--wallet", validatorWallet, "--address", validatorAddr)
t.Run("cancelled", func(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.In.WriteString("n\r")
e.RunWithError(t, append(cmd, h.StringLE(), "getValue")...)
})
t.Run("confirmed", func(t *testing.T) {
e.In.WriteString("one\r")
e.In.WriteString("y\r")
e.Run(t, append(cmd, h.StringLE(), "getValue")...) e.Run(t, append(cmd, h.StringLE(), "getValue")...)
})
t.Run("failind method", func(t *testing.T) { t.Run("failind method", func(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.In.WriteString("y\r")
e.RunWithError(t, append(cmd, h.StringLE(), "fail")...) e.RunWithError(t, append(cmd, h.StringLE(), "fail")...)
e.In.WriteString("one\r") e.In.WriteString("one\r")
@ -384,6 +396,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
t.Run("cosigner is deployed contract", func(t *testing.T) { t.Run("cosigner is deployed contract", func(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.In.WriteString("y\r")
e.Run(t, append(cmd, h.StringLE(), "getValue", e.Run(t, append(cmd, h.StringLE(), "getValue",
"--", validatorAddr, hVerify.StringLE())...) "--", validatorAddr, hVerify.StringLE())...)
}) })
@ -499,6 +512,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
e.Run(t, "neo-go", "contract", "invokefunction", e.Run(t, "neo-go", "contract", "invokefunction",
"--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--address", validatorAddr, "--wallet", validatorWallet, "--address", validatorAddr,
"--force",
h.StringLE(), "update", h.StringLE(), "update",
"bytes:"+hex.EncodeToString(rawNef), "bytes:"+hex.EncodeToString(rawNef),
"bytes:"+hex.EncodeToString(rawManifest), "bytes:"+hex.EncodeToString(rawManifest),

View file

@ -1,10 +1,13 @@
package input package input
import ( import (
"errors"
"fmt"
"io" "io"
"os" "os"
"syscall" "syscall"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"golang.org/x/term" "golang.org/x/term"
) )
@ -55,3 +58,18 @@ func ReadPassword(prompt string) (string, error) {
} }
return trm.ReadPassword(prompt) return trm.ReadPassword(prompt)
} }
// ConfirmTx asks for a confirmation to send tx.
func ConfirmTx(w io.Writer, tx *transaction.Transaction) error {
fmt.Fprintf(w, "Network fee: %d\n", tx.NetworkFee)
fmt.Fprintf(w, "System fee: %d\n", tx.SystemFee)
fmt.Fprintf(w, "Total fee: %d\n", tx.NetworkFee+tx.SystemFee)
ln, err := ReadLine("Relay transaction (y|N)> ")
if err != nil {
return err
}
if 0 < len(ln) && ln[0] == 'y' || ln[0] == 'Y' {
return nil
}
return errors.New("cancelled")
}

View file

@ -51,6 +51,7 @@ func TestSignMultisigTx(t *testing.T) {
"--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--wallet", validatorWallet,
"--from", validatorAddr, "--from", validatorAddr,
"--force",
"NEO:"+multisigAddr+":4", "NEO:"+multisigAddr+":4",
"GAS:"+multisigAddr+":1") "GAS:"+multisigAddr+":1")
e.checkTxPersisted(t) e.checkTxPersisted(t)

View file

@ -107,6 +107,7 @@ func TestNEP11_OwnerOf_BalanceOf_Transfer(t *testing.T) {
"--to", nftOwnerAddr, "--to", nftOwnerAddr,
"--token", "GAS", "--token", "GAS",
"--amount", "10000", "--amount", "10000",
"--force",
"--from", validatorAddr) "--from", validatorAddr)
e.checkTxPersisted(t) e.checkTxPersisted(t)
@ -122,6 +123,7 @@ func TestNEP11_OwnerOf_BalanceOf_Transfer(t *testing.T) {
"--to", h.StringLE(), "--to", h.StringLE(),
"--token", "GAS", "--token", "GAS",
"--amount", "10", "--amount", "10",
"--force",
"--from", nftOwnerAddr) "--from", nftOwnerAddr)
txMint, _ := e.checkTxPersisted(t) txMint, _ := e.checkTxPersisted(t)
@ -259,6 +261,7 @@ func TestNEP11_OwnerOf_BalanceOf_Transfer(t *testing.T) {
"--wallet", wall, "--wallet", wall,
"--to", validatorAddr, "--to", validatorAddr,
"--from", nftOwnerAddr, "--from", nftOwnerAddr,
"--force",
} }
// transfer: unimported token with symbol id specified // transfer: unimported token with symbol id specified
@ -290,6 +293,7 @@ func TestNEP11_OwnerOf_BalanceOf_Transfer(t *testing.T) {
"--from", nftOwnerAddr, "--from", nftOwnerAddr,
"--token", h.StringLE(), "--token", h.StringLE(),
"--id", string(tokenID1), "--id", string(tokenID1),
"--force",
"string:some_data", "string:some_data",
} }
e.In.WriteString(nftOwnerPass + "\r") e.In.WriteString(nftOwnerPass + "\r")

View file

@ -122,8 +122,23 @@ func TestNEP17Transfer(t *testing.T) {
e.In.Reset() e.In.Reset()
}) })
t.Run("no confirmation", func(t *testing.T) {
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.RunWithError(t, args...)
e.In.Reset()
})
t.Run("cancel after prompt", func(t *testing.T) {
e.In.WriteString("one\r")
e.RunWithError(t, args...)
e.In.Reset()
})
e.In.WriteString("one\r")
e.In.WriteString("Y\r")
e.Run(t, args...) e.Run(t, args...)
e.checkNextLine(t, `^Network fee:\s*(\d|\.)+`)
e.checkNextLine(t, `^System fee:\s*(\d|\.)+`)
e.checkNextLine(t, `^Total fee:\s*(\d|\.)+`)
e.checkTxPersisted(t) e.checkTxPersisted(t)
sh, err := address.StringToUint160(w.Accounts[0].Address) sh, err := address.StringToUint160(w.Accounts[0].Address)
@ -131,6 +146,17 @@ func TestNEP17Transfer(t *testing.T) {
b, _ := e.Chain.GetGoverningTokenBalance(sh) b, _ := e.Chain.GetGoverningTokenBalance(sh)
require.Equal(t, big.NewInt(1), b) require.Equal(t, big.NewInt(1), b)
t.Run("with force", func(t *testing.T) {
e.In.WriteString("one\r")
e.Run(t, append(args, "--force")...)
e.checkTxPersisted(t)
sh, err := address.StringToUint160(w.Accounts[0].Address)
require.NoError(t, err)
b, _ := e.Chain.GetGoverningTokenBalance(sh)
require.Equal(t, big.NewInt(2), b)
})
hVerify := deployVerifyContract(t, e) hVerify := deployVerifyContract(t, e)
const validatorDefault = "Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn" const validatorDefault = "Nhfg3TbpwogLvDGVvAvqyThbsHgoSUKwtn"
@ -140,11 +166,13 @@ func TestNEP17Transfer(t *testing.T) {
"--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--wallet", validatorWallet,
"--from", validatorAddr, "--from", validatorAddr,
"--force",
"NEO:"+validatorDefault+":42", "NEO:"+validatorDefault+":42",
"GAS:"+validatorDefault+":7") "GAS:"+validatorDefault+":7")
e.checkTxPersisted(t) e.checkTxPersisted(t)
args := args[:len(args)-2] // cut '--from' argument args := args[:len(args)-2] // cut '--from' argument
args = append(args, "--force")
e.In.WriteString("one\r") e.In.WriteString("one\r")
e.Run(t, args...) e.Run(t, args...)
e.checkTxPersisted(t) e.checkTxPersisted(t)
@ -152,7 +180,7 @@ func TestNEP17Transfer(t *testing.T) {
sh, err := address.StringToUint160(w.Accounts[0].Address) sh, err := address.StringToUint160(w.Accounts[0].Address)
require.NoError(t, err) require.NoError(t, err)
b, _ := e.Chain.GetGoverningTokenBalance(sh) b, _ := e.Chain.GetGoverningTokenBalance(sh)
require.Equal(t, big.NewInt(2), b) require.Equal(t, big.NewInt(3), b)
sh, err = address.StringToUint160(validatorDefault) sh, err = address.StringToUint160(validatorDefault)
require.NoError(t, err) require.NoError(t, err)
@ -166,6 +194,7 @@ func TestNEP17Transfer(t *testing.T) {
"--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--wallet", validatorWallet,
"--from", validatorAddr, "--from", validatorAddr,
"--force",
"NEO:"+validatorDefault+":42", "NEO:"+validatorDefault+":42",
"GAS:"+validatorDefault+":7", "GAS:"+validatorDefault+":7",
"--", validatorAddr+":Global") "--", validatorAddr+":Global")
@ -181,6 +210,7 @@ func TestNEP17Transfer(t *testing.T) {
"--token", "GAS", "--token", "GAS",
"--amount", "1", "--amount", "1",
"--from", validatorAddr, "--from", validatorAddr,
"--force",
"[", validatorAddr, strconv.Itoa(int(validTil)), "]"} "[", validatorAddr, strconv.Itoa(int(validTil)), "]"}
t.Run("with data", func(t *testing.T) { t.Run("with data", func(t *testing.T) {
@ -218,6 +248,7 @@ func TestNEP17MultiTransfer(t *testing.T) {
"--rpc-endpoint", "http://" + e.RPC.Addr, "--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", validatorWallet, "--wallet", validatorWallet,
"--from", validatorAddr, "--from", validatorAddr,
"--force",
"NEO:" + privs[0].Address() + ":42", "NEO:" + privs[0].Address() + ":42",
"GAS:" + privs[1].Address() + ":7", "GAS:" + privs[1].Address() + ":7",
neoContractHash.StringLE() + ":" + privs[2].Address() + ":13", neoContractHash.StringLE() + ":" + privs[2].Address() + ":13",

View file

@ -34,6 +34,7 @@ func TestQueryTx(t *testing.T) {
"--to", w.Accounts[0].Address, "--to", w.Accounts[0].Address,
"--token", "NEO", "--token", "NEO",
"--from", validatorAddr, "--from", validatorAddr,
"--force",
} }
e.In.WriteString("one\r") e.In.WriteString("one\r")

View file

@ -660,7 +660,17 @@ func invokeWithArgs(ctx *cli.Context, acc *wallet.Account, wall *wallet.Wallet,
if len(resp.Script) == 0 { if len(resp.Script) == 0 {
return sender, cli.NewExitError(errors.New("no script returned from the RPC node"), 1) return sender, cli.NewExitError(errors.New("no script returned from the RPC node"), 1)
} }
txHash, err := c.SignAndPushInvocationTx(resp.Script, acc, resp.GasConsumed+int64(sysgas), gas, cosignersAccounts) tx, err := c.CreateTxFromScript(resp.Script, acc, resp.GasConsumed+int64(sysgas), int64(gas), cosignersAccounts)
if err != nil {
return sender, cli.NewExitError(fmt.Errorf("failed to create tx: %w", err), 1)
}
if !ctx.Bool("force") {
err := input.ConfirmTx(ctx.App.Writer, tx)
if err != nil {
return sender, cli.NewExitError(err, 1)
}
}
txHash, err := c.SignAndPushTx(tx, acc, cosignersAccounts)
if err != nil { if err != nil {
return sender, cli.NewExitError(fmt.Errorf("failed to push invocation tx: %w", err), 1) return sender, cli.NewExitError(fmt.Errorf("failed to push invocation tx: %w", err), 1)
} }

View file

@ -6,6 +6,7 @@ import (
"math/big" "math/big"
"github.com/nspcc-dev/neo-go/cli/flags" "github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/cli/input"
"github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/cli/paramcontext" "github.com/nspcc-dev/neo-go/cli/paramcontext"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
@ -262,6 +263,12 @@ func signAndSendNEP11Transfer(ctx *cli.Context, c *client.Client, acc *wallet.Ac
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
} else { } else {
if !ctx.Bool("force") {
err := input.ConfirmTx(ctx.App.Writer, tx)
if err != nil {
return cli.NewExitError(err, 1)
}
}
_, err := c.SignAndPushTx(tx, acc, cosigners) _, err := c.SignAndPushTx(tx, acc, cosigners)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)

View file

@ -8,6 +8,7 @@ import (
"github.com/nspcc-dev/neo-go/cli/cmdargs" "github.com/nspcc-dev/neo-go/cli/cmdargs"
"github.com/nspcc-dev/neo-go/cli/flags" "github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/cli/input"
"github.com/nspcc-dev/neo-go/cli/options" "github.com/nspcc-dev/neo-go/cli/options"
"github.com/nspcc-dev/neo-go/cli/paramcontext" "github.com/nspcc-dev/neo-go/cli/paramcontext"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
@ -55,6 +56,7 @@ var (
tokenFlag, tokenFlag,
gasFlag, gasFlag,
sysGasFlag, sysGasFlag,
forceFlag,
cli.StringFlag{ cli.StringFlag{
Name: "amount", Name: "amount",
Usage: "Amount of asset to send", Usage: "Amount of asset to send",
@ -66,6 +68,7 @@ var (
fromAddrFlag, fromAddrFlag,
gasFlag, gasFlag,
sysGasFlag, sysGasFlag,
forceFlag,
}, options.RPC...) }, options.RPC...)
) )
@ -580,6 +583,12 @@ func signAndSendNEP17Transfer(ctx *cli.Context, c *client.Client, acc *wallet.Ac
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
} else { } else {
if !ctx.Bool("force") {
err := input.ConfirmTx(ctx.App.Writer, tx)
if err != nil {
return cli.NewExitError(err, 1)
}
}
_, err := c.SignAndPushTx(tx, acc, cosigners) _, err := c.SignAndPushTx(tx, acc, cosigners)
if err != nil { if err != nil {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)

View file

@ -222,6 +222,7 @@ func TestClaimGas(t *testing.T) {
"--rpc-endpoint", "http://" + e.RPC.Addr, "--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", validatorWallet, "--wallet", validatorWallet,
"--from", validatorAddr, "--from", validatorAddr,
"--force",
"NEO:" + testWalletAccount + ":1000", "NEO:" + testWalletAccount + ":1000",
"GAS:" + testWalletAccount + ":1000", // for tx send "GAS:" + testWalletAccount + ":1000", // for tx send
} }
@ -293,6 +294,7 @@ func TestImportDeployed(t *testing.T) {
e.Run(t, "neo-go", "wallet", "nep17", "multitransfer", e.Run(t, "neo-go", "wallet", "nep17", "multitransfer",
"--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", validatorWallet, "--from", validatorAddr, "--wallet", validatorWallet, "--from", validatorAddr,
"--force",
"NEO:"+contractAddr+":10", "NEO:"+contractAddr+":10",
"GAS:"+contractAddr+":10") "GAS:"+contractAddr+":10")
e.checkTxPersisted(t) e.checkTxPersisted(t)
@ -304,6 +306,7 @@ func TestImportDeployed(t *testing.T) {
e.Run(t, "neo-go", "wallet", "nep17", "transfer", e.Run(t, "neo-go", "wallet", "nep17", "transfer",
"--rpc-endpoint", "http://"+e.RPC.Addr, "--rpc-endpoint", "http://"+e.RPC.Addr,
"--wallet", walletPath, "--from", contractAddr, "--wallet", walletPath, "--from", contractAddr,
"--force",
"--to", privTo.Address(), "--token", "NEO", "--amount", "1") "--to", privTo.Address(), "--token", "NEO", "--amount", "1")
e.checkTxPersisted(t) e.checkTxPersisted(t)