cli/wallet: allow to testinvoke transaction before signing
Signed-off-by: Evgeniy Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
parent
e2a93f3e41
commit
bf2ca35453
5 changed files with 124 additions and 4 deletions
|
@ -2,6 +2,8 @@ package main
|
|||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
|
@ -10,7 +12,9 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||
"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/rpc/response/result"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
@ -92,6 +96,27 @@ func TestSignMultisigTx(t *testing.T) {
|
|||
"--wallet", wallet2Path,
|
||||
"--in", txPath, "--out", txPath)
|
||||
|
||||
t.Run("test invoke", func(t *testing.T) {
|
||||
t.Run("missing file", func(t *testing.T) {
|
||||
e.RunWithError(t, "neo-go", "util", "txdump")
|
||||
fmt.Println(e.Out.String())
|
||||
})
|
||||
|
||||
t.Run("no invoke", func(t *testing.T) {
|
||||
e.Run(t, "neo-go", "util", "txdump", txPath)
|
||||
e.checkTxTestInvokeOutput(t, 11)
|
||||
e.checkEOF(t)
|
||||
})
|
||||
|
||||
e.Run(t, "neo-go", "util", "txdump",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
txPath)
|
||||
e.checkTxTestInvokeOutput(t, 11)
|
||||
res := new(result.Invoke)
|
||||
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
||||
require.Equal(t, vm.HaltState.String(), res.State, res.FaultException)
|
||||
})
|
||||
|
||||
e.In.WriteString("pass\r")
|
||||
e.Run(t, "neo-go", "wallet", "sign",
|
||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||
|
@ -153,3 +178,21 @@ func TestSignMultisigTx(t *testing.T) {
|
|||
require.Equal(t, big.NewInt(2), b)
|
||||
})
|
||||
}
|
||||
|
||||
func (e *executor) checkTxTestInvokeOutput(t *testing.T, scriptSize int) {
|
||||
e.checkNextLine(t, `Hash:\s+`)
|
||||
e.checkNextLine(t, `OnChain:\s+false`)
|
||||
e.checkNextLine(t, `ValidUntil:\s+\d+`)
|
||||
e.checkNextLine(t, `Signer:\s+\w+`)
|
||||
e.checkNextLine(t, `SystemFee:\s+(\d|\.)+`)
|
||||
e.checkNextLine(t, `NetworkFee:\s+(\d|\.)+`)
|
||||
e.checkNextLine(t, `Script:\s+\w+`)
|
||||
e.checkScriptDump(t, scriptSize)
|
||||
}
|
||||
|
||||
func (e *executor) checkScriptDump(t *testing.T, scriptSize int) {
|
||||
e.checkNextLine(t, `INDEX\s+`)
|
||||
for i := 0; i < scriptSize; i++ {
|
||||
e.checkNextLine(t, `\d+\s+\w+`)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/cli/options"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||
"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/encoding/fixedn"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
||||
|
@ -102,12 +103,16 @@ func queryTx(ctx *cli.Context) error {
|
|||
}
|
||||
}
|
||||
|
||||
dumpApplicationLog(ctx, res, txOut)
|
||||
DumpApplicationLog(ctx, res, &txOut.Transaction, &txOut.TransactionMetadata, ctx.Bool("verbose"))
|
||||
return nil
|
||||
}
|
||||
|
||||
func dumpApplicationLog(ctx *cli.Context, res *result.ApplicationLog, tx *result.TransactionOutputRaw) {
|
||||
verbose := ctx.Bool("verbose")
|
||||
func DumpApplicationLog(
|
||||
ctx *cli.Context,
|
||||
res *result.ApplicationLog,
|
||||
tx *transaction.Transaction,
|
||||
txMeta *result.TransactionMetadata,
|
||||
verbose bool) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
||||
// Ignore the errors below because `Write` to buffer doesn't return error.
|
||||
|
@ -117,7 +122,9 @@ func dumpApplicationLog(ctx *cli.Context, res *result.ApplicationLog, tx *result
|
|||
if res == nil {
|
||||
_, _ = tw.Write([]byte("ValidUntil:\t" + strconv.FormatUint(uint64(tx.ValidUntilBlock), 10) + "\n"))
|
||||
} else {
|
||||
_, _ = tw.Write([]byte("BlockHash:\t" + tx.Blockhash.StringLE() + "\n"))
|
||||
if txMeta != nil {
|
||||
_, _ = tw.Write([]byte("BlockHash:\t" + txMeta.Blockhash.StringLE() + "\n"))
|
||||
}
|
||||
if len(res.Executions) != 1 {
|
||||
_, _ = tw.Write([]byte("Success:\tunknown (no execution data)\n"))
|
||||
} else {
|
||||
|
@ -133,6 +140,9 @@ func dumpApplicationLog(ctx *cli.Context, res *result.ApplicationLog, tx *result
|
|||
_, _ = tw.Write([]byte("SystemFee:\t" + fixedn.Fixed8(tx.SystemFee).String() + " GAS\n"))
|
||||
_, _ = tw.Write([]byte("NetworkFee:\t" + fixedn.Fixed8(tx.NetworkFee).String() + " GAS\n"))
|
||||
_, _ = tw.Write([]byte("Script:\t" + base64.StdEncoding.EncodeToString(tx.Script) + "\n"))
|
||||
v := vm.New()
|
||||
v.Load(tx.Script)
|
||||
v.PrintOps(tw)
|
||||
if res != nil {
|
||||
for _, e := range res.Executions {
|
||||
if e.VMState != vm.HaltState {
|
||||
|
|
|
@ -125,6 +125,13 @@ func (e *executor) compareQueryTxVerbose(t *testing.T, tx *transaction.Transacti
|
|||
e.checkNextLine(t, `SystemFee:\s+`+fixedn.Fixed8(tx.SystemFee).String()+" GAS$")
|
||||
e.checkNextLine(t, `NetworkFee:\s+`+fixedn.Fixed8(tx.NetworkFee).String()+" GAS$")
|
||||
e.checkNextLine(t, `Script:\s+`+regexp.QuoteMeta(base64.StdEncoding.EncodeToString(tx.Script)))
|
||||
c := vm.NewContext(tx.Script)
|
||||
n := 0
|
||||
for ; c.NextIP() < c.LenInstr(); _, _, err = c.Next() {
|
||||
require.NoError(t, err)
|
||||
n++
|
||||
}
|
||||
e.checkScriptDump(t, n)
|
||||
|
||||
if res[0].Execution.VMState != vm.HaltState {
|
||||
e.checkNextLine(t, `Exception:\s+`+regexp.QuoteMeta(res[0].Execution.FaultException))
|
||||
|
|
|
@ -3,12 +3,14 @@ package util
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/cli/options"
|
||||
vmcli "github.com/nspcc-dev/neo-go/pkg/vm/cli"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// NewCommands returns util commands for neo-go CLI.
|
||||
func NewCommands() []cli.Command {
|
||||
txDumpFlags := append([]cli.Flag{}, options.RPC...)
|
||||
return []cli.Command{
|
||||
{
|
||||
Name: "util",
|
||||
|
@ -23,6 +25,13 @@ func NewCommands() []cli.Command {
|
|||
and converted to other formats. Strings are escaped and output in quotes.`,
|
||||
Action: handleParse,
|
||||
},
|
||||
{
|
||||
Name: "txdump",
|
||||
Usage: "Dump transaction stored in file",
|
||||
UsageText: "txdump [-r <endpoint>] <file.in>",
|
||||
Action: txDump,
|
||||
Flags: txDumpFlags,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
51
cli/util/dump.go
Normal file
51
cli/util/dump.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/cli/options"
|
||||
"github.com/nspcc-dev/neo-go/cli/paramcontext"
|
||||
"github.com/nspcc-dev/neo-go/cli/query"
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func txDump(ctx *cli.Context) error {
|
||||
if len(ctx.Args()) == 0 {
|
||||
return cli.NewExitError("missing input file", 1)
|
||||
}
|
||||
|
||||
c, err := paramcontext.Read(ctx.Args()[0])
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
|
||||
tx, ok := c.Verifiable.(*transaction.Transaction)
|
||||
if !ok {
|
||||
return cli.NewExitError("verifiable item is not a transaction", 1)
|
||||
}
|
||||
|
||||
query.DumpApplicationLog(ctx, nil, tx, nil, true)
|
||||
|
||||
if ctx.String(options.RPCEndpointFlag) != "" {
|
||||
gctx, cancel := options.GetTimeoutContext(ctx)
|
||||
defer cancel()
|
||||
|
||||
var err error
|
||||
cl, err := options.GetRPCClient(gctx, ctx)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
res, err := cl.InvokeScript(tx.Script, tx.Signers)
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
resS, err := json.MarshalIndent(res, "", " ")
|
||||
if err != nil {
|
||||
return cli.NewExitError(err, 1)
|
||||
}
|
||||
fmt.Fprintln(ctx.App.Writer, string(resS))
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Reference in a new issue