Port sendtx to new format

This commit is contained in:
Ethan Frey 2017-06-15 13:11:09 +02:00
parent 65837cf952
commit 66ec2f266c
7 changed files with 151 additions and 108 deletions

View File

@ -2,126 +2,104 @@ package commands
import (
"encoding/hex"
"encoding/json"
"github.com/pkg/errors"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
"github.com/spf13/viper"
crypto "github.com/tendermint/go-crypto"
wire "github.com/tendermint/go-wire"
lightclient "github.com/tendermint/light-client"
"github.com/tendermint/light-client/commands"
"github.com/tendermint/light-client/proofs"
txcmd "github.com/tendermint/light-client/commands/txs"
btypes "github.com/tendermint/basecoin/types"
)
type BaseTxPresenter struct {
proofs.RawPresenter // this handles MakeKey as hex bytes
/*** Here is the sendtx command ***/
var SendTxCmd = &cobra.Command{
Use: "send",
Short: "send tokens from one account to another",
RunE: doSendTx,
}
func (_ BaseTxPresenter) ParseData(raw []byte) (interface{}, error) {
var tx btypes.TxS
err := wire.ReadBinaryBytes(raw, &tx)
return tx, err
const (
ToFlag = "to"
AmountFlag = "amount"
FeeFlag = "fee"
GasFlag = "gas"
SequenceFlag = "sequence"
)
func init() {
flags := SendTxCmd.Flags()
flags.String(ToFlag, "", "Destination address for the bits")
flags.String(AmountFlag, "", "Coins to send in the format <amt><coin>,<amt><coin>...")
flags.String(FeeFlag, "0mycoin", "Coins for the transaction fee of the format <amt><coin>")
flags.Int64(GasFlag, 0, "Amount of gas for this transaction")
flags.Int(SequenceFlag, -1, "Sequence number for this transaction")
}
/******** SendTx *********/
// runDemo is an example of how to make a tx
func doSendTx(cmd *cobra.Command, args []string) error {
tx := new(btypes.SendTx)
type SendTxMaker struct{}
func (m SendTxMaker) MakeReader() (lightclient.TxReader, error) {
chainID := viper.GetString(commands.ChainFlag)
return SendTxReader{ChainID: chainID}, nil
}
type SendFlags struct {
To string
Amount string
Fee string
Gas int64
Sequence int
}
func (m SendTxMaker) Flags() (*flag.FlagSet, interface{}) {
fs := flag.NewFlagSet("", flag.ContinueOnError)
fs.String("to", "", "Destination address for the bits")
fs.String("amount", "", "Coins to send in the format <amt><coin>,<amt><coin>...")
fs.String("fee", "0mycoin", "Coins for the transaction fee of the format <amt><coin>")
fs.Int64("gas", 0, "Amount of gas for this transaction")
fs.Int("sequence", -1, "Sequence number for this transaction")
return fs, &SendFlags{}
}
// SendTXReader allows us to create SendTx
type SendTxReader struct {
ChainID string
}
func (t SendTxReader) ReadTxJSON(data []byte, pk crypto.PubKey) (interface{}, error) {
// TODO: use pk info to help construct data
var tx btypes.SendTx
err := json.Unmarshal(data, &tx)
send := SendTx{
chainID: t.ChainID,
Tx: &tx,
// load data from json or flags
found, err := txcmd.LoadJSON(tx)
if !found {
err = readSendTxFlags(tx)
}
return &send, errors.Wrap(err, "parse sendtx")
if err != nil {
return err
}
send := &SendTx{
chainID: viper.GetString(commands.ChainFlag),
Tx: tx,
}
send.AddSigner(txcmd.GetSigner())
// Sign if needed and post. This it the work-horse
bres, err := txcmd.SignAndPostTx(send)
if err != nil {
return err
}
// output result
return txcmd.OutputTx(bres)
}
func (t SendTxReader) ReadTxFlags(flags interface{}, pk crypto.PubKey) (interface{}, error) {
data := flags.(*SendFlags)
// parse to and from addresses
to, err := hex.DecodeString(StripHex(data.To))
func readSendTxFlags(tx *btypes.SendTx) error {
// parse to address
to, err := hex.DecodeString(StripHex(viper.GetString(ToFlag)))
if err != nil {
return nil, errors.Errorf("To address is invalid hex: %v\n", err)
return errors.Errorf("To address is invalid hex: %v\n", err)
}
//parse the fee and amounts into coin types
feeCoin, err := btypes.ParseCoin(data.Fee)
tx.Fee, err = btypes.ParseCoin(viper.GetString(FeeFlag))
if err != nil {
return nil, err
return err
}
amountCoins, err := btypes.ParseCoins(data.Amount)
amountCoins, err := btypes.ParseCoins(viper.GetString(AmountFlag))
if err != nil {
return nil, err
return err
}
// get addr if available
var addr []byte
if !pk.Empty() {
addr = pk.Address()
}
// set the gas
tx.Gas = viper.GetInt64(GasFlag)
// craft the tx
input := btypes.TxInput{
Address: addr,
// craft the inputs and outputs
tx.Inputs = []btypes.TxInput{{
Coins: amountCoins,
Sequence: data.Sequence,
}
if data.Sequence == 1 {
input.PubKey = pk
}
output := btypes.TxOutput{
Sequence: viper.GetInt(SequenceFlag),
}}
tx.Outputs = []btypes.TxOutput{{
Address: to,
Coins: amountCoins,
}
tx := btypes.SendTx{
Gas: data.Gas,
Fee: feeCoin,
Inputs: []btypes.TxInput{input},
Outputs: []btypes.TxOutput{output},
}
}}
// wrap it in the proper signer thing...
send := SendTx{
chainID: t.ChainID,
Tx: &tx,
}
return &send, nil
return nil
}
/******** AppTx *********/

View File

@ -41,3 +41,15 @@ func doAccountQuery(cmd *cobra.Command, args []string) error {
return proofcmd.OutputProof(acc, proof.BlockHeight())
}
/*** this decodes the basecoin tx ***/
type BaseTxPresenter struct {
proofs.RawPresenter // this handles MakeKey as hex bytes
}
func (_ BaseTxPresenter) ParseData(raw []byte) (interface{}, error) {
var tx btypes.TxS
err := wire.ReadBinaryBytes(raw, &tx)
return tx, err
}

View File

@ -58,3 +58,55 @@ func (s *SendTx) TxBytes() ([]byte, error) {
}{s.Tx})
return txBytes, nil
}
// AddSigner sets address and pubkey info on the tx based on the key that
// will be used for signing
func (s *SendTx) AddSigner(pk crypto.PubKey) {
// get addr if available
var addr []byte
if !pk.Empty() {
addr = pk.Address()
}
// set the send address, and pubkey if needed
in := s.Tx.Inputs
in[0].Address = addr
if in[0].Sequence == 1 {
in[0].PubKey = pk
}
}
// TODO: this should really be in the basecoin.types SendTx,
// but that code is too ugly now, needs refactor..
func (s *SendTx) ValidateBasic() error {
if s.chainID == "" {
return errors.New("No chainId specified")
}
for _, in := range s.Tx.Inputs {
if len(in.Address) != 20 {
return errors.Errorf("Invalid input address length: %d", len(in.Address))
}
if !in.Coins.IsValid() {
return errors.Errorf("Invalid input coins %v", in.Coins)
}
if in.Coins.IsZero() {
return errors.New("Input coins cannot be zero")
}
if in.Sequence <= 0 {
return errors.New("Sequence must be greater than 0")
}
}
for _, out := range s.Tx.Outputs {
if len(out.Address) != 20 {
return errors.Errorf("Invalid output address length: %d", len(out.Address))
}
if !out.Coins.IsValid() {
return errors.Errorf("Invalid output coins %v", out.Coins)
}
if out.Coins.IsZero() {
return errors.New("Output coins cannot be zero")
}
}
return nil
}

View File

@ -15,19 +15,6 @@ import (
btypes "github.com/tendermint/basecoin/types"
)
type CounterPresenter struct{}
func (_ CounterPresenter) MakeKey(str string) ([]byte, error) {
key := counter.New().StateKey()
return key, nil
}
func (_ CounterPresenter) ParseData(raw []byte) (interface{}, error) {
var cp counter.CounterPluginState
err := wire.ReadBinaryBytes(raw, &cp)
return cp, err
}
/**** build out the tx ****/
var (

View File

@ -36,3 +36,18 @@ func doCounterQuery(cmd *cobra.Command, args []string) error {
return proofcmd.OutputProof(cp, proof.BlockHeight())
}
/*** doesn't seem to be needed anymore??? ***/
// type CounterPresenter struct{}
// func (_ CounterPresenter) MakeKey(str string) ([]byte, error) {
// key := counter.New().StateKey()
// return key, nil
// }
// func (_ CounterPresenter) ParseData(raw []byte) (interface{}, error) {
// var cp counter.CounterPluginState
// err := wire.ReadBinaryBytes(raw, &cp)
// return cp, err
// }

View File

@ -14,7 +14,6 @@ import (
"github.com/tendermint/tmlibs/cli"
bcmd "github.com/tendermint/basecoin/cmd/basecli/commands"
bcount "github.com/tendermint/basecoin/cmd/basecli/counter"
)
// BaseCli represents the base command when called without any subcommands
@ -38,18 +37,18 @@ func main() {
pr.AddCommand(proofs.TxCmd)
pr.AddCommand(proofs.KeyCmd)
pr.AddCommand(bcmd.AccountQueryCmd)
pr.AddCommand(bcount.CounterQueryCmd)
// pr.AddCommand(bcount.CounterQueryCmd)
// here is how you would add the custom txs... but don't really add demo in your app
proofs.TxPresenters.Register("base", bcmd.BaseTxPresenter{})
tr := txs.RootCmd
// tr.AddCommand(txs.DemoCmd)
tr.AddCommand(bcmd.SendTxCmd)
// tr.AddCommand(bcmd.AppTxCmd)
// TODO
proofs.TxPresenters.Register("base", bcmd.BaseTxPresenter{})
//proofs.StatePresenters.Register("counter", bcount.CounterPresenter{})
txs.Register("send", bcmd.SendTxMaker{})
txs.Register("counter", bcount.CounterTxMaker{})
// txs.Register("send", bcmd.SendTxMaker{})
// txs.Register("counter", bcount.CounterTxMaker{})
// set up the various commands to use
BaseCli.AddCommand(

2
glide.lock generated
View File

@ -172,7 +172,7 @@ imports:
- types
- version
- name: github.com/tendermint/tmlibs
version: 0ecb38c6da95a1e8f60117b2bd4a6f76c7a0f944
version: 59a77e7bef092eef0e1f9b44c983dc9e35eed0d6
subpackages:
- autofile
- cli