Add support for --prepare to store tx for multisig
This commit is contained in:
parent
29273e5656
commit
e7da4c2d3a
@ -4,6 +4,8 @@ import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
@ -15,7 +17,8 @@ import (
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
keycmd "github.com/tendermint/go-crypto/cmd"
|
||||
"github.com/tendermint/go-crypto/keys"
|
||||
lc "github.com/tendermint/light-client"
|
||||
wire "github.com/tendermint/go-wire"
|
||||
"github.com/tendermint/go-wire/data"
|
||||
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
|
||||
@ -49,48 +52,85 @@ func GetSignerAct() (res basecoin.Actor) {
|
||||
return res
|
||||
}
|
||||
|
||||
// Sign if it is Signable, otherwise, just convert it to bytes
|
||||
func Sign(tx interface{}) (packet []byte, err error) {
|
||||
name := viper.GetString(FlagName)
|
||||
manager := keycmd.GetKeyManager()
|
||||
|
||||
if sign, ok := tx.(keys.Signable); ok {
|
||||
if name == "" {
|
||||
return nil, errors.New("--name is required to sign tx")
|
||||
}
|
||||
packet, err = signTx(manager, sign, name)
|
||||
} else if val, ok := tx.(lc.Value); ok {
|
||||
packet = val.Bytes()
|
||||
} else {
|
||||
err = errors.Errorf("Reader returned invalid tx type: %#v\n", tx)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SignAndPostTx does all work once we construct a proper struct
|
||||
// it validates the data, signs if needed, transforms to bytes,
|
||||
// and posts to the node.
|
||||
func SignAndPostTx(tx Validatable) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||
// SignTx will validate the tx, and signs it if it is wrapping a Signable.
|
||||
// Modifies tx in place, and returns an error if it should sign but couldn't
|
||||
func SignTx(tx basecoin.Tx) error {
|
||||
// validate tx client-side
|
||||
err := tx.ValidateBasic()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
// sign the tx if needed
|
||||
packet, err := Sign(tx)
|
||||
name := viper.GetString(FlagName)
|
||||
manager := keycmd.GetKeyManager()
|
||||
|
||||
if sign, ok := tx.Unwrap().(keys.Signable); ok {
|
||||
// TODO: allow us not to sign? if so then what use?
|
||||
if name == "" {
|
||||
return errors.New("--name is required to sign tx")
|
||||
}
|
||||
err = signTx(manager, sign, name)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// PrepareOrPostTx checks the flags to decide to prepare the tx for future
|
||||
// multisig, or to post it to the node. Returns error on any failure.
|
||||
// If no error and the result is nil, it means it already wrote to file,
|
||||
// no post, no need to do more.
|
||||
func PrepareOrPostTx(tx basecoin.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||
wrote, err := PrepareTx(tx)
|
||||
// error in prep
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// successfully wrote the tx!
|
||||
if wrote {
|
||||
return nil, nil
|
||||
}
|
||||
// or try to post it
|
||||
return PostTx(tx)
|
||||
}
|
||||
|
||||
// PrepareTx checks for FlagPrepare and if set, write the tx as json
|
||||
// to the specified location for later multi-sig. Returns true if it
|
||||
// handled the tx (no futher work required), false if it did nothing
|
||||
// (and we should post the tx)
|
||||
func PrepareTx(tx basecoin.Tx) (bool, error) {
|
||||
prep := viper.GetString(FlagPrepare)
|
||||
if prep == "" {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
js, err := data.ToJSON(tx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
err = writeOutput(prep, js)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// PostTx does all work once we construct a proper struct
|
||||
// it validates the data, signs if needed, transforms to bytes,
|
||||
// and posts to the node.
|
||||
func PostTx(tx basecoin.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
|
||||
packet := wire.BinaryBytes(tx)
|
||||
// post the bytes
|
||||
node := commands.GetNode()
|
||||
return node.BroadcastTxCommit(packet)
|
||||
}
|
||||
|
||||
// OutputTx prints the tx result to stdout
|
||||
// TODO: something other than raw json?
|
||||
// OutputTx validates if success and prints the tx result to stdout
|
||||
func OutputTx(res *ctypes.ResultBroadcastTxCommit) error {
|
||||
if res.CheckTx.IsErr() {
|
||||
return errors.Errorf("CheckTx: (%d): %s", res.CheckTx.Code, res.CheckTx.Log)
|
||||
}
|
||||
if res.DeliverTx.IsErr() {
|
||||
return errors.Errorf("DeliverTx: (%d): %s", res.DeliverTx.Code, res.DeliverTx.Log)
|
||||
}
|
||||
js, err := json.MarshalIndent(res, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
@ -99,17 +139,13 @@ func OutputTx(res *ctypes.ResultBroadcastTxCommit) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func signTx(manager keys.Manager, tx keys.Signable, name string) ([]byte, error) {
|
||||
func signTx(manager keys.Manager, tx keys.Signable, name string) error {
|
||||
prompt := fmt.Sprintf("Please enter passphrase for %s: ", name)
|
||||
pass, err := getPassword(prompt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
err = manager.Sign(name, pass, tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tx.TxBytes()
|
||||
return manager.Sign(name, pass, tx)
|
||||
}
|
||||
|
||||
// if we read from non-tty, we just need to init the buffer reader once,
|
||||
@ -139,3 +175,40 @@ func getPassword(prompt string) (pass string, err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func writeOutput(file string, d []byte) error {
|
||||
var writer io.Writer
|
||||
if file == "-" {
|
||||
writer = os.Stdout
|
||||
} else {
|
||||
f, err := os.Create(file)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
defer f.Close()
|
||||
writer = f
|
||||
}
|
||||
|
||||
_, err := writer.Write(d)
|
||||
// this returns nil if err == nil
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
func readInput(file string) ([]byte, error) {
|
||||
var reader io.Reader
|
||||
// get the input stream
|
||||
if file == "" || file == "-" {
|
||||
reader = os.Stdin
|
||||
} else {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
defer f.Close()
|
||||
reader = f
|
||||
}
|
||||
|
||||
// and read it all!
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
return data, errors.WithStack(err)
|
||||
}
|
||||
|
||||
@ -1,11 +1,8 @@
|
||||
package txs
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
wire "github.com/tendermint/go-wire"
|
||||
"github.com/tendermint/light-client/proofs"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
|
||||
"github.com/tendermint/basecoin"
|
||||
)
|
||||
@ -21,15 +18,3 @@ func (BaseTxPresenter) ParseData(raw []byte) (interface{}, error) {
|
||||
err := wire.ReadBinaryBytes(raw, &tx)
|
||||
return tx, err
|
||||
}
|
||||
|
||||
// ValidateResult returns an appropriate error if the server rejected the
|
||||
// tx in CheckTx or DeliverTx
|
||||
func ValidateResult(res *ctypes.ResultBroadcastTxCommit) error {
|
||||
if res.CheckTx.IsErr() {
|
||||
return errors.Errorf("CheckTx: (%d): %s", res.CheckTx.Code, res.CheckTx.Log)
|
||||
}
|
||||
if res.DeliverTx.IsErr() {
|
||||
return errors.Errorf("DeliverTx: (%d): %s", res.DeliverTx.Code, res.DeliverTx.Log)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -2,9 +2,6 @@ package txs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
@ -47,34 +44,19 @@ func doRawTx(cmd *cobra.Command, args []string) error {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
// Sign if needed and post. This it the work-horse
|
||||
bres, err := SignAndPostTx(tx.Unwrap())
|
||||
// sign it
|
||||
err = SignTx(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ValidateResult(bres); err != nil {
|
||||
|
||||
// otherwise, post it and display response
|
||||
bres, err := PrepareOrPostTx(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Output result
|
||||
return OutputTx(bres)
|
||||
}
|
||||
|
||||
func readInput(file string) ([]byte, error) {
|
||||
var reader io.Reader
|
||||
// get the input stream
|
||||
if file == "" || file == "-" {
|
||||
reader = os.Stdin
|
||||
} else {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
defer f.Close()
|
||||
reader = f
|
||||
if bres == nil {
|
||||
return nil // successful prep, nothing left to do
|
||||
}
|
||||
|
||||
// and read it all!
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
return data, errors.WithStack(err)
|
||||
return OutputTx(bres) // print response of the post
|
||||
}
|
||||
|
||||
@ -46,17 +46,19 @@ func counterTx(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Sign if needed and post. This it the work-horse
|
||||
bres, err := txcmd.SignAndPostTx(tx.Unwrap())
|
||||
err = txcmd.SignTx(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = txcmd.ValidateResult(bres); err != nil {
|
||||
|
||||
bres, err := txcmd.PrepareOrPostTx(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Output result
|
||||
return txcmd.OutputTx(bres)
|
||||
if bres == nil {
|
||||
return nil // successful prep, nothing left to do
|
||||
}
|
||||
return txcmd.OutputTx(bres) // print response of the post
|
||||
}
|
||||
|
||||
func readCounterTxFlags() (tx basecoin.Tx, err error) {
|
||||
|
||||
@ -33,13 +33,6 @@ func init() {
|
||||
|
||||
// sendTxCmd is an example of how to make a tx
|
||||
func sendTxCmd(cmd *cobra.Command, args []string) error {
|
||||
// load data from json or flags
|
||||
// var tx basecoin.Tx
|
||||
// found, err := txcmd.LoadJSON(&tx)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
tx, err := readSendTxFlags()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -50,17 +43,20 @@ func sendTxCmd(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Sign if needed and post. This it the work-horse
|
||||
bres, err := txcmd.SignAndPostTx(tx.Unwrap())
|
||||
err = txcmd.SignTx(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = txcmd.ValidateResult(bres); err != nil {
|
||||
|
||||
// otherwise, post it and display response
|
||||
bres, err := txcmd.PrepareOrPostTx(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Output result
|
||||
return txcmd.OutputTx(bres)
|
||||
if bres == nil {
|
||||
return nil // successful prep, nothing left to do
|
||||
}
|
||||
return txcmd.OutputTx(bres) // print response of the post
|
||||
}
|
||||
|
||||
func readSendTxFlags() (tx basecoin.Tx, err error) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user