cli: working txs and account fetching
This commit is contained in:
parent
665b39e330
commit
8262d0cc71
|
@ -0,0 +1,69 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
|
||||||
|
"github.com/tendermint/basecoin/types"
|
||||||
|
cmn "github.com/tendermint/go-common"
|
||||||
|
client "github.com/tendermint/go-rpc/client"
|
||||||
|
"github.com/tendermint/go-wire"
|
||||||
|
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func cmdAccount(c *cli.Context) error {
|
||||||
|
if len(c.Args()) != 1 {
|
||||||
|
return errors.New("account command requires an argument ([address])")
|
||||||
|
}
|
||||||
|
addrHex := c.Args()[0]
|
||||||
|
|
||||||
|
// convert destination address to bytes
|
||||||
|
addr, err := hex.DecodeString(addrHex)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("Account address is invalid hex: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
acc, err := getAcc(c, addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(string(wire.JSONBytes(acc)))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch the account by querying the app
|
||||||
|
func getAcc(c *cli.Context, address []byte) (*types.Account, error) {
|
||||||
|
tmAddr := c.String("tendermint")
|
||||||
|
clientURI := client.NewClientURI(tmAddr)
|
||||||
|
tmResult := new(ctypes.TMResult)
|
||||||
|
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"path": "/key",
|
||||||
|
"data": append([]byte("base/a/"), address...),
|
||||||
|
"prove": false,
|
||||||
|
}
|
||||||
|
_, err := clientURI.Call("abci_query", params, tmResult)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New(cmn.Fmt("Error calling /abci_query: %v", err))
|
||||||
|
}
|
||||||
|
res := (*tmResult).(*ctypes.ResultABCIQuery)
|
||||||
|
if !res.Response.Code.IsOK() {
|
||||||
|
return nil, errors.New(cmn.Fmt("Query got non-zero exit code: %v. %s", res.Response.Code, res.Response.Log))
|
||||||
|
}
|
||||||
|
accountBytes := res.Response.Value
|
||||||
|
|
||||||
|
if len(accountBytes) == 0 {
|
||||||
|
return nil, errors.New(cmn.Fmt("Account bytes are empty from query for address %X", address))
|
||||||
|
}
|
||||||
|
var acc *types.Account
|
||||||
|
err = wire.ReadBinaryBytes(accountBytes, &acc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New(cmn.Fmt("Error reading account %X error: %v",
|
||||||
|
accountBytes, err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc, nil
|
||||||
|
}
|
|
@ -44,6 +44,12 @@ var (
|
||||||
// tx flags
|
// tx flags
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
tmAddrFlag = cli.StringFlag{
|
||||||
|
Name: "tendermint",
|
||||||
|
Value: "tcp://localhost:46657",
|
||||||
|
Usage: "Tendermint RPC address",
|
||||||
|
}
|
||||||
|
|
||||||
toFlag = cli.StringFlag{
|
toFlag = cli.StringFlag{
|
||||||
Name: "to",
|
Name: "to",
|
||||||
Value: "",
|
Value: "",
|
||||||
|
@ -97,6 +103,12 @@ var (
|
||||||
Value: "",
|
Value: "",
|
||||||
Usage: "Plugin to send the transaction to",
|
Usage: "Plugin to send the transaction to",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chainIDFlag = cli.StringFlag{
|
||||||
|
Name: "chain_id",
|
||||||
|
Value: "test_chain_id",
|
||||||
|
Usage: "ID of the chain for replay protection",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -118,6 +130,7 @@ func main() {
|
||||||
eyesDBFlag,
|
eyesDBFlag,
|
||||||
genesisFlag,
|
genesisFlag,
|
||||||
inProcTMFlag,
|
inProcTMFlag,
|
||||||
|
chainIDFlag,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -129,12 +142,15 @@ func main() {
|
||||||
return cmdSendTx(c)
|
return cmdSendTx(c)
|
||||||
},
|
},
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
|
tmAddrFlag,
|
||||||
toFlag,
|
toFlag,
|
||||||
fromFlag,
|
fromFlag,
|
||||||
amountFlag,
|
amountFlag,
|
||||||
coinFlag,
|
coinFlag,
|
||||||
gasFlag,
|
gasFlag,
|
||||||
feeFlag,
|
feeFlag,
|
||||||
|
chainIDFlag,
|
||||||
|
seqFlag,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -146,6 +162,7 @@ func main() {
|
||||||
return cmdAppTx(c)
|
return cmdAppTx(c)
|
||||||
},
|
},
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
|
tmAddrFlag,
|
||||||
nameFlag,
|
nameFlag,
|
||||||
fromFlag,
|
fromFlag,
|
||||||
amountFlag,
|
amountFlag,
|
||||||
|
@ -153,6 +170,19 @@ func main() {
|
||||||
gasFlag,
|
gasFlag,
|
||||||
feeFlag,
|
feeFlag,
|
||||||
dataFlag,
|
dataFlag,
|
||||||
|
seqFlag,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
Name: "account",
|
||||||
|
Usage: "Get details of an account",
|
||||||
|
ArgsUsage: "",
|
||||||
|
Action: func(c *cli.Context) error {
|
||||||
|
return cmdAccount(c)
|
||||||
|
},
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
tmAddrFlag,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,21 @@ import (
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
|
||||||
"github.com/tendermint/abci/server"
|
"github.com/tendermint/abci/server"
|
||||||
"github.com/tendermint/basecoin/app"
|
|
||||||
cmn "github.com/tendermint/go-common"
|
cmn "github.com/tendermint/go-common"
|
||||||
|
cfg "github.com/tendermint/go-config"
|
||||||
|
//logger "github.com/tendermint/go-logger"
|
||||||
eyes "github.com/tendermint/merkleeyes/client"
|
eyes "github.com/tendermint/merkleeyes/client"
|
||||||
|
|
||||||
|
tmcfg "github.com/tendermint/tendermint/config/tendermint"
|
||||||
|
"github.com/tendermint/tendermint/node"
|
||||||
|
"github.com/tendermint/tendermint/proxy"
|
||||||
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
|
|
||||||
|
"github.com/tendermint/basecoin/app"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var config cfg.Config
|
||||||
|
|
||||||
const EyesCacheSize = 10000
|
const EyesCacheSize = 10000
|
||||||
|
|
||||||
func cmdStart(c *cli.Context) error {
|
func cmdStart(c *cli.Context) error {
|
||||||
|
@ -28,27 +38,57 @@ func cmdStart(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create Basecoin app
|
// Create Basecoin app
|
||||||
app := app.NewBasecoin(eyesCli)
|
basecoinApp := app.NewBasecoin(eyesCli)
|
||||||
|
|
||||||
// If genesis file was specified, set key-value options
|
// If genesis file was specified, set key-value options
|
||||||
if c.String("genesis") != "" {
|
if c.String("genesis") != "" {
|
||||||
err := app.LoadGenesis(c.String("genesis"))
|
err := basecoinApp.LoadGenesis(c.String("genesis"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(cmn.Fmt("%+v", err))
|
return errors.New(cmn.Fmt("%+v", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the listener
|
if c.Bool("in-proc") {
|
||||||
svr, err := server.NewServer(c.String("address"), "socket", app)
|
startTendermint(c, basecoinApp)
|
||||||
|
} else {
|
||||||
|
startBasecoinABCI(c, basecoinApp)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func startBasecoinABCI(c *cli.Context, basecoinApp *app.Basecoin) error {
|
||||||
|
// Start the ABCI listener
|
||||||
|
svr, err := server.NewServer(c.String("address"), "socket", basecoinApp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("create listener: " + err.Error())
|
return errors.New("create listener: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait forever
|
// Wait forever
|
||||||
cmn.TrapSignal(func() {
|
cmn.TrapSignal(func() {
|
||||||
// Cleanup
|
// Cleanup
|
||||||
svr.Stop()
|
svr.Stop()
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func startTendermint(c *cli.Context, basecoinApp *app.Basecoin) {
|
||||||
|
// Get configuration
|
||||||
|
config = tmcfg.GetConfig("")
|
||||||
|
// logger.SetLogLevel("notice") //config.GetString("log_level"))
|
||||||
|
|
||||||
|
// parseFlags(config, args[1:]) // Command line overrides
|
||||||
|
|
||||||
|
// Create & start tendermint node
|
||||||
|
privValidatorFile := config.GetString("priv_validator_file")
|
||||||
|
privValidator := tmtypes.LoadOrGenPrivValidator(privValidatorFile)
|
||||||
|
n := node.NewNode(config, privValidator, proxy.NewLocalClientCreator(basecoinApp))
|
||||||
|
|
||||||
|
n.Start()
|
||||||
|
|
||||||
|
// Wait forever
|
||||||
|
cmn.TrapSignal(func() {
|
||||||
|
// Cleanup
|
||||||
|
n.Stop()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,9 @@ import (
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
cmn "github.com/tendermint/go-common"
|
cmn "github.com/tendermint/go-common"
|
||||||
|
client "github.com/tendermint/go-rpc/client"
|
||||||
"github.com/tendermint/go-wire"
|
"github.com/tendermint/go-wire"
|
||||||
|
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -21,28 +23,44 @@ func cmdSendTx(c *cli.Context) error {
|
||||||
gas, fee := c.Int("gas"), int64(c.Int("fee"))
|
gas, fee := c.Int("gas"), int64(c.Int("fee"))
|
||||||
chainID := c.String("chain_id")
|
chainID := c.String("chain_id")
|
||||||
|
|
||||||
|
// convert destination address to bytes
|
||||||
to, err := hex.DecodeString(toHex)
|
to, err := hex.DecodeString(toHex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("To address is invalid hex: " + err.Error())
|
return errors.New("To address is invalid hex: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// load the priv validator
|
||||||
|
// XXX: this is overkill for now, we need a keys solution
|
||||||
privVal := tmtypes.LoadPrivValidator(fromFile)
|
privVal := tmtypes.LoadPrivValidator(fromFile)
|
||||||
|
|
||||||
sequence := getSeq(c)
|
// get the sequence number for the tx
|
||||||
|
sequence, err := getSeq(c, privVal.Address)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// craft the tx
|
||||||
input := types.NewTxInput(privVal.PubKey, types.Coins{types.Coin{coin, amount}}, sequence)
|
input := types.NewTxInput(privVal.PubKey, types.Coins{types.Coin{coin, amount}}, sequence)
|
||||||
output := newOutput(to, coin, amount)
|
output := newOutput(to, coin, amount)
|
||||||
|
tx := &types.SendTx{
|
||||||
tx := types.SendTx{
|
|
||||||
Gas: int64(gas),
|
Gas: int64(gas),
|
||||||
Fee: types.Coin{coin, fee},
|
Fee: types.Coin{coin, fee},
|
||||||
Inputs: []types.TxInput{input},
|
Inputs: []types.TxInput{input},
|
||||||
Outputs: []types.TxOutput{output},
|
Outputs: []types.TxOutput{output},
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.Inputs[0].Signature = privVal.Sign(tx.SignBytes(chainID))
|
// sign that puppy
|
||||||
|
signBytes := tx.SignBytes(chainID)
|
||||||
|
tx.Inputs[0].Signature = privVal.Sign(signBytes)
|
||||||
|
|
||||||
|
fmt.Println("Signed SendTx:")
|
||||||
fmt.Println(string(wire.JSONBytes(tx)))
|
fmt.Println(string(wire.JSONBytes(tx)))
|
||||||
|
|
||||||
|
// broadcast the transaction to tendermint
|
||||||
|
if err := broadcastTx(c, tx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +73,7 @@ func cmdAppTx(c *cli.Context) error {
|
||||||
chainID := c.String("chain_id")
|
chainID := c.String("chain_id")
|
||||||
dataString := c.String("data")
|
dataString := c.String("data")
|
||||||
|
|
||||||
|
// convert data to bytes
|
||||||
data := []byte(dataString)
|
data := []byte(dataString)
|
||||||
if cmn.IsHex(dataString) {
|
if cmn.IsHex(dataString) {
|
||||||
data, _ = hex.DecodeString(dataString)
|
data, _ = hex.DecodeString(dataString)
|
||||||
|
@ -62,11 +81,13 @@ func cmdAppTx(c *cli.Context) error {
|
||||||
|
|
||||||
privVal := tmtypes.LoadPrivValidator(fromFile)
|
privVal := tmtypes.LoadPrivValidator(fromFile)
|
||||||
|
|
||||||
sequence := getSeq(c)
|
sequence, err := getSeq(c, privVal.Address)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
input := types.NewTxInput(privVal.PubKey, types.Coins{types.Coin{coin, amount}}, sequence)
|
input := types.NewTxInput(privVal.PubKey, types.Coins{types.Coin{coin, amount}}, sequence)
|
||||||
|
tx := &types.AppTx{
|
||||||
tx := types.AppTx{
|
|
||||||
Gas: int64(gas),
|
Gas: int64(gas),
|
||||||
Fee: types.Coin{coin, fee},
|
Fee: types.Coin{coin, fee},
|
||||||
Name: name,
|
Name: name,
|
||||||
|
@ -75,16 +96,74 @@ func cmdAppTx(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.Input.Signature = privVal.Sign(tx.SignBytes(chainID))
|
tx.Input.Signature = privVal.Sign(tx.SignBytes(chainID))
|
||||||
|
|
||||||
|
fmt.Println("Signed AppTx:")
|
||||||
fmt.Println(string(wire.JSONBytes(tx)))
|
fmt.Println(string(wire.JSONBytes(tx)))
|
||||||
|
|
||||||
|
if err := broadcastTx(c, tx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSeq(c *cli.Context) int {
|
// broadcast the transaction to tendermint
|
||||||
if c.IsSet("sequence") {
|
func broadcastTx(c *cli.Context, tx types.Tx) error {
|
||||||
return c.Int("sequence")
|
tmResult := new(ctypes.TMResult)
|
||||||
|
tmAddr := c.String("tendermint")
|
||||||
|
clientURI := client.NewClientURI(tmAddr)
|
||||||
|
|
||||||
|
/*txBytes := []byte(wire.JSONBytes(struct {
|
||||||
|
types.Tx `json:"unwrap"`
|
||||||
|
}{tx}))*/
|
||||||
|
txBytes := wire.BinaryBytes(tx)
|
||||||
|
_, err := clientURI.Call("broadcast_tx_sync", map[string]interface{}{"tx": txBytes}, tmResult)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(cmn.Fmt("Error on broadcast tx: %v", err))
|
||||||
}
|
}
|
||||||
// TODO: get from query
|
res := (*tmResult).(*ctypes.ResultBroadcastTx)
|
||||||
return 0
|
if !res.Code.IsOK() {
|
||||||
|
return errors.New(cmn.Fmt("BroadcastTxSync got non-zero exit code: %v. %X; %s", res.Code, res.Data, res.Log))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the sequence flag is set, return it;
|
||||||
|
// else, fetch the account by querying the app and return the sequence number
|
||||||
|
func getSeq(c *cli.Context, address []byte) (int, error) {
|
||||||
|
if c.IsSet("sequence") {
|
||||||
|
return c.Int("sequence"), nil
|
||||||
|
}
|
||||||
|
tmAddr := c.String("tendermint")
|
||||||
|
clientURI := client.NewClientURI(tmAddr)
|
||||||
|
tmResult := new(ctypes.TMResult)
|
||||||
|
|
||||||
|
params := map[string]interface{}{
|
||||||
|
"path": "/key",
|
||||||
|
"data": append([]byte("base/a/"), address...),
|
||||||
|
"prove": false,
|
||||||
|
}
|
||||||
|
_, err := clientURI.Call("abci_query", params, tmResult)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.New(cmn.Fmt("Error calling /abci_query: %v", err))
|
||||||
|
}
|
||||||
|
res := (*tmResult).(*ctypes.ResultABCIQuery)
|
||||||
|
if !res.Response.Code.IsOK() {
|
||||||
|
return 0, errors.New(cmn.Fmt("Query got non-zero exit code: %v. %s", res.Response.Code, res.Response.Log))
|
||||||
|
}
|
||||||
|
accountBytes := res.Response.Value
|
||||||
|
|
||||||
|
if len(accountBytes) == 0 {
|
||||||
|
return 0, errors.New(cmn.Fmt("Account bytes are empty from query for address %X", address))
|
||||||
|
}
|
||||||
|
var acc *types.Account
|
||||||
|
err = wire.ReadBinaryBytes(accountBytes, &acc)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.New(cmn.Fmt("Error reading account %X error: %v",
|
||||||
|
accountBytes, err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc.Sequence + 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newOutput(to []byte, coin string, amount int64) types.TxOutput {
|
func newOutput(to []byte, coin string, amount int64) types.TxOutput {
|
||||||
|
|
Loading…
Reference in New Issue