cli: refactor ibc and counter into own binaries

This commit is contained in:
Ethan Buchman
2017-02-07 16:12:50 -05:00
parent f77d43040b
commit abac65bacc
10 changed files with 212 additions and 183 deletions
-56
View File
@@ -1,56 +0,0 @@
package commands
import (
"fmt"
"github.com/tendermint/basecoin/plugins/counter"
"github.com/tendermint/basecoin/types"
wire "github.com/tendermint/go-wire"
"github.com/urfave/cli"
)
var (
CounterTxCmd = cli.Command{
Name: "counter",
Usage: "Craft a transaction to the counter plugin",
Action: func(c *cli.Context) error {
return cmdCounterTx(c)
},
Flags: []cli.Flag{
ValidFlag,
},
}
CounterPluginFlag = cli.BoolFlag{
Name: "counter-plugin",
Usage: "Enable the counter plugin",
}
)
func init() {
RegisterTxPlugin(CounterTxCmd)
RegisterStartPlugin(CounterPluginFlag,
func() types.Plugin { return counter.New("counter") })
}
func cmdCounterTx(c *cli.Context) error {
valid := c.Bool("valid")
parent := c.Parent()
counterTx := counter.CounterTx{
Valid: valid,
Fee: types.Coins{
{
Denom: parent.String("coin"),
Amount: int64(parent.Int("fee")),
},
},
}
fmt.Println("CounterTx:", string(wire.JSONBytes(counterTx)))
data := wire.BinaryBytes(counterTx)
name := "counter"
return AppTx(parent, name, data)
}
-216
View File
@@ -1,216 +0,0 @@
package commands
import (
"github.com/urfave/cli"
)
// start flags
var (
AddrFlag = cli.StringFlag{
Name: "address",
Value: "tcp://0.0.0.0:46658",
Usage: "Listen address",
}
EyesFlag = cli.StringFlag{
Name: "eyes",
Value: "local",
Usage: "MerkleEyes address, or 'local' for embedded",
}
// TODO: move to config file
// eyesCacheSizePtr := flag.Int("eyes-cache-size", 10000, "MerkleEyes db cache size, for embedded")
DirFlag = cli.StringFlag{
Name: "dir",
Value: ".",
Usage: "Root directory",
}
InProcTMFlag = cli.BoolFlag{
Name: "in-proc",
Usage: "Run Tendermint in-process with the App",
}
IbcPluginFlag = cli.BoolFlag{
Name: "ibc-plugin",
Usage: "Enable the ibc plugin",
}
)
// tx flags
var (
NodeFlag = cli.StringFlag{
Name: "node",
Value: "tcp://localhost:46657",
Usage: "Tendermint RPC address",
}
ToFlag = cli.StringFlag{
Name: "to",
Value: "",
Usage: "Destination address for the transaction",
}
AmountFlag = cli.IntFlag{
Name: "amount",
Value: 0,
Usage: "Amount of coins to send in the transaction",
}
FromFlag = cli.StringFlag{
Name: "from",
Value: "priv_validator.json",
Usage: "Path to a private key to sign the transaction",
}
SeqFlag = cli.IntFlag{
Name: "sequence",
Value: 0,
Usage: "Sequence number for the account",
}
CoinFlag = cli.StringFlag{
Name: "coin",
Value: "blank",
Usage: "Specify a coin denomination",
}
GasFlag = cli.IntFlag{
Name: "gas",
Value: 0,
Usage: "The amount of gas for the transaction",
}
FeeFlag = cli.IntFlag{
Name: "fee",
Value: 0,
Usage: "The transaction fee",
}
DataFlag = cli.StringFlag{
Name: "data",
Value: "",
Usage: "Data to send with the transaction",
}
NameFlag = cli.StringFlag{
Name: "name",
Value: "",
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",
}
ValidFlag = cli.BoolFlag{
Name: "valid",
Usage: "Set valid field in CounterTx",
}
)
// ibc flags
var (
IbcChainIDFlag = cli.StringFlag{
Name: "chain_id",
Usage: "ChainID for the new blockchain",
Value: "",
}
IbcGenesisFlag = cli.StringFlag{
Name: "genesis",
Usage: "Genesis file for the new blockchain",
Value: "",
}
IbcHeaderFlag = cli.StringFlag{
Name: "header",
Usage: "Block header for an ibc update",
Value: "",
}
IbcCommitFlag = cli.StringFlag{
Name: "commit",
Usage: "Block commit for an ibc update",
Value: "",
}
IbcFromFlag = cli.StringFlag{
Name: "from",
Usage: "Source ChainID",
Value: "",
}
IbcToFlag = cli.StringFlag{
Name: "to",
Usage: "Destination ChainID",
Value: "",
}
IbcTypeFlag = cli.StringFlag{
Name: "type",
Usage: "IBC packet type (eg. coin)",
Value: "",
}
IbcPayloadFlag = cli.StringFlag{
Name: "payload",
Usage: "IBC packet payload",
Value: "",
}
IbcPacketFlag = cli.StringFlag{
Name: "packet",
Usage: "hex-encoded IBC packet",
Value: "",
}
IbcProofFlag = cli.StringFlag{
Name: "proof",
Usage: "hex-encoded proof of IBC packet from source chain",
Value: "",
}
IbcSequenceFlag = cli.IntFlag{
Name: "sequence",
Usage: "sequence number for IBC packet",
Value: 0,
}
IbcHeightFlag = cli.IntFlag{
Name: "height",
Usage: "Height the packet became egress in source chain",
Value: 0,
}
)
// proof flags
var (
ProofFlag = cli.StringFlag{
Name: "proof",
Usage: "hex-encoded IAVL proof",
Value: "",
}
KeyFlag = cli.StringFlag{
Name: "key",
Usage: "key to the IAVL tree",
Value: "",
}
ValueFlag = cli.StringFlag{
Name: "value",
Usage: "value in the IAVL tree",
Value: "",
}
RootFlag = cli.StringFlag{
Name: "root",
Usage: "root hash of the IAVL tree",
Value: "",
}
)
-251
View File
@@ -1,251 +0,0 @@
package commands
import (
"encoding/hex"
"errors"
"fmt"
"io/ioutil"
"github.com/urfave/cli"
"github.com/tendermint/basecoin/plugins/ibc"
cmn "github.com/tendermint/go-common"
"github.com/tendermint/go-merkle"
"github.com/tendermint/go-wire"
tmtypes "github.com/tendermint/tendermint/types"
)
var (
IbcCmd = cli.Command{
Name: "ibc",
Usage: "Send a transaction to the interblockchain (ibc) plugin",
Flags: []cli.Flag{
NodeFlag,
ChainIDFlag,
FromFlag,
AmountFlag,
CoinFlag,
GasFlag,
FeeFlag,
SeqFlag,
NameFlag,
DataFlag,
},
Subcommands: []cli.Command{
IbcRegisterTxCmd,
IbcUpdateTxCmd,
IbcPacketTxCmd,
},
}
IbcRegisterTxCmd = cli.Command{
Name: "register",
Usage: "Register a blockchain via IBC",
Action: func(c *cli.Context) error {
return cmdIBCRegisterTx(c)
},
Flags: []cli.Flag{
IbcChainIDFlag,
IbcGenesisFlag,
},
}
IbcUpdateTxCmd = cli.Command{
Name: "update",
Usage: "Update the latest state of a blockchain via IBC",
Action: func(c *cli.Context) error {
return cmdIBCUpdateTx(c)
},
Flags: []cli.Flag{
IbcHeaderFlag,
IbcCommitFlag,
},
}
IbcPacketTxCmd = cli.Command{
Name: "packet",
Usage: "Send a new packet via IBC",
Flags: []cli.Flag{
//
},
Subcommands: []cli.Command{
IbcPacketCreateTx,
IbcPacketPostTx,
},
}
IbcPacketCreateTx = cli.Command{
Name: "create",
Usage: "Create an egress IBC packet",
Action: func(c *cli.Context) error {
return cmdIBCPacketCreateTx(c)
},
Flags: []cli.Flag{
IbcFromFlag,
IbcToFlag,
IbcTypeFlag,
IbcPayloadFlag,
IbcSequenceFlag,
},
}
IbcPacketPostTx = cli.Command{
Name: "post",
Usage: "Deliver an IBC packet to another chain",
Action: func(c *cli.Context) error {
return cmdIBCPacketPostTx(c)
},
Flags: []cli.Flag{
IbcFromFlag,
IbcHeightFlag,
IbcPacketFlag,
IbcProofFlag,
},
}
)
func cmdIBCRegisterTx(c *cli.Context) error {
chainID := c.String("chain_id")
genesisFile := c.String("genesis")
parent := c.Parent()
genesisBytes, err := ioutil.ReadFile(genesisFile)
if err != nil {
return errors.New(cmn.Fmt("Error reading genesis file %v: %v", genesisFile, err))
}
ibcTx := ibc.IBCRegisterChainTx{
ibc.BlockchainGenesis{
ChainID: chainID,
Genesis: string(genesisBytes),
},
}
fmt.Println("IBCTx:", string(wire.JSONBytes(ibcTx)))
data := []byte(wire.BinaryBytes(struct {
ibc.IBCTx `json:"unwrap"`
}{ibcTx}))
name := "IBC"
return AppTx(parent, name, data)
}
func cmdIBCUpdateTx(c *cli.Context) error {
headerBytes, err := hex.DecodeString(StripHex(c.String("header")))
if err != nil {
return errors.New(cmn.Fmt("Header (%v) is invalid hex: %v", c.String("header"), err))
}
commitBytes, err := hex.DecodeString(StripHex(c.String("commit")))
if err != nil {
return errors.New(cmn.Fmt("Commit (%v) is invalid hex: %v", c.String("commit"), err))
}
header := new(tmtypes.Header)
commit := new(tmtypes.Commit)
if err := wire.ReadBinaryBytes(headerBytes, &header); err != nil {
return errors.New(cmn.Fmt("Error unmarshalling header: %v", err))
}
if err := wire.ReadBinaryBytes(commitBytes, &commit); err != nil {
return errors.New(cmn.Fmt("Error unmarshalling commit: %v", err))
}
ibcTx := ibc.IBCUpdateChainTx{
Header: *header,
Commit: *commit,
}
fmt.Println("IBCTx:", string(wire.JSONBytes(ibcTx)))
data := []byte(wire.BinaryBytes(struct {
ibc.IBCTx `json:"unwrap"`
}{ibcTx}))
name := "IBC"
return AppTx(c.Parent(), name, data)
}
func cmdIBCPacketCreateTx(c *cli.Context) error {
fromChain, toChain := c.String("from"), c.String("to")
packetType := c.String("type")
payloadBytes, err := hex.DecodeString(StripHex(c.String("payload")))
if err != nil {
return errors.New(cmn.Fmt("Payload (%v) is invalid hex: %v", c.String("payload"), err))
}
sequence, err := getIBCSequence(c)
if err != nil {
return err
}
ibcTx := ibc.IBCPacketCreateTx{
Packet: ibc.Packet{
SrcChainID: fromChain,
DstChainID: toChain,
Sequence: sequence,
Type: packetType,
Payload: payloadBytes,
},
}
fmt.Println("IBCTx:", string(wire.JSONBytes(ibcTx)))
data := []byte(wire.BinaryBytes(struct {
ibc.IBCTx `json:"unwrap"`
}{ibcTx}))
return AppTx(c.Parent().Parent(), "IBC", data)
}
func cmdIBCPacketPostTx(c *cli.Context) error {
fromChain, fromHeight := c.String("from"), c.Int("height")
packetBytes, err := hex.DecodeString(StripHex(c.String("packet")))
if err != nil {
return errors.New(cmn.Fmt("Packet (%v) is invalid hex: %v", c.String("packet"), err))
}
proofBytes, err := hex.DecodeString(StripHex(c.String("proof")))
if err != nil {
return errors.New(cmn.Fmt("Proof (%v) is invalid hex: %v", c.String("proof"), err))
}
var packet ibc.Packet
proof := new(merkle.IAVLProof)
if err := wire.ReadBinaryBytes(packetBytes, &packet); err != nil {
return errors.New(cmn.Fmt("Error unmarshalling packet: %v", err))
}
if err := wire.ReadBinaryBytes(proofBytes, &proof); err != nil {
return errors.New(cmn.Fmt("Error unmarshalling proof: %v", err))
}
ibcTx := ibc.IBCPacketPostTx{
FromChainID: fromChain,
FromChainHeight: uint64(fromHeight),
Packet: packet,
Proof: proof,
}
fmt.Println("IBCTx:", string(wire.JSONBytes(ibcTx)))
data := []byte(wire.BinaryBytes(struct {
ibc.IBCTx `json:"unwrap"`
}{ibcTx}))
return AppTx(c.Parent().Parent(), "IBC", data)
}
func getIBCSequence(c *cli.Context) (uint64, error) {
if c.IsSet("sequence") {
return uint64(c.Int("sequence")), nil
}
// TODO: get sequence
return 0, nil
}
-213
View File
@@ -1,213 +0,0 @@
package commands
import (
"encoding/hex"
"errors"
"fmt"
"strconv"
"github.com/urfave/cli"
cmn "github.com/tendermint/go-common"
"github.com/tendermint/go-merkle"
"github.com/tendermint/go-wire"
tmtypes "github.com/tendermint/tendermint/types"
)
var (
QueryCmd = cli.Command{
Name: "query",
Usage: "Query the merkle tree",
ArgsUsage: "<key>",
Action: func(c *cli.Context) error {
return cmdQuery(c)
},
Flags: []cli.Flag{
NodeFlag,
},
}
AccountCmd = cli.Command{
Name: "account",
Usage: "Get details of an account",
ArgsUsage: "<address>",
Action: func(c *cli.Context) error {
return cmdAccount(c)
},
Flags: []cli.Flag{
NodeFlag,
},
}
BlockCmd = cli.Command{
Name: "block",
Usage: "Get the header and commit of a block",
ArgsUsage: "<height>",
Action: func(c *cli.Context) error {
return cmdBlock(c)
},
Flags: []cli.Flag{
NodeFlag,
},
}
VerifyCmd = cli.Command{
Name: "verify",
Usage: "Verify the IAVL proof",
Action: func(c *cli.Context) error {
return cmdVerify(c)
},
Flags: []cli.Flag{
ProofFlag,
KeyFlag,
ValueFlag,
RootFlag,
},
}
)
func cmdQuery(c *cli.Context) error {
if len(c.Args()) != 1 {
return errors.New("query command requires an argument ([key])")
}
keyString := c.Args()[0]
key := []byte(keyString)
if isHex(keyString) {
// convert key to bytes
var err error
key, err = hex.DecodeString(StripHex(keyString))
if err != nil {
return errors.New(cmn.Fmt("Query key (%v) is invalid hex: %v", keyString, err))
}
}
resp, err := Query(c.String("node"), key)
if err != nil {
return err
}
if !resp.Code.IsOK() {
return errors.New(cmn.Fmt("Query for key (%v) returned non-zero code (%v): %v", keyString, resp.Code, resp.Log))
}
val := resp.Value
proof := resp.Proof
height := resp.Height
fmt.Println(string(wire.JSONBytes(struct {
Value []byte `json:"value"`
Proof []byte `json:"proof"`
Height uint64 `json:"height"`
}{val, proof, height})))
return nil
}
func cmdAccount(c *cli.Context) error {
if len(c.Args()) != 1 {
return errors.New("account command requires an argument ([address])")
}
addrHex := StripHex(c.Args()[0])
// convert destination address to bytes
addr, err := hex.DecodeString(addrHex)
if err != nil {
return errors.New(cmn.Fmt("Account address (%v) is invalid hex: %v", addrHex, err))
}
acc, err := getAcc(c.String("node"), addr)
if err != nil {
return err
}
fmt.Println(string(wire.JSONBytes(acc)))
return nil
}
func cmdBlock(c *cli.Context) error {
if len(c.Args()) != 1 {
return errors.New("block command requires an argument ([height])")
}
heightString := c.Args()[0]
height, err := strconv.Atoi(heightString)
if err != nil {
return errors.New(cmn.Fmt("Height must be an int, got %v: %v", heightString, err))
}
/*block, err := getBlock(c, height)
if err != nil {
return err
}*/
nextBlock, err := getBlock(c, height+1)
if err != nil {
return err
}
fmt.Println(string(wire.JSONBytes(struct {
Hex BlockHex `json:"hex"`
JSON BlockJSON `json:"json"`
}{
BlockHex{
Header: wire.BinaryBytes(nextBlock.Header),
Commit: wire.BinaryBytes(nextBlock.LastCommit),
},
BlockJSON{
Header: nextBlock.Header,
Commit: nextBlock.LastCommit,
},
})))
return nil
}
type BlockHex struct {
Header []byte `json:"header"`
Commit []byte `json:"commit"`
}
type BlockJSON struct {
Header *tmtypes.Header `json:"header"`
Commit *tmtypes.Commit `json:"commit"`
}
func cmdVerify(c *cli.Context) error {
keyString, valueString := c.String("key"), c.String("value")
var err error
key := []byte(keyString)
if isHex(keyString) {
key, err = hex.DecodeString(StripHex(keyString))
if err != nil {
return errors.New(cmn.Fmt("Key (%v) is invalid hex: %v", keyString, err))
}
}
value := []byte(valueString)
if isHex(valueString) {
value, err = hex.DecodeString(StripHex(valueString))
if err != nil {
return errors.New(cmn.Fmt("Value (%v) is invalid hex: %v", valueString, err))
}
}
root, err := hex.DecodeString(StripHex(c.String("root")))
if err != nil {
return errors.New(cmn.Fmt("Root (%v) is invalid hex: %v", c.String("root"), err))
}
proofBytes, err := hex.DecodeString(StripHex(c.String("proof")))
if err != nil {
return errors.New(cmn.Fmt("Proof (%v) is invalid hex: %v", c.String("proof"), err))
}
proof, err := merkle.ReadProof(proofBytes)
if err != nil {
return errors.New(cmn.Fmt("Error unmarshalling proof: %v", err))
}
if proof.Verify(key, value, root) {
fmt.Println("OK")
} else {
return errors.New("Proof does not verify")
}
return nil
}
-139
View File
@@ -1,139 +0,0 @@
package commands
import (
"errors"
"os"
"path"
"github.com/urfave/cli"
"github.com/tendermint/abci/server"
cmn "github.com/tendermint/go-common"
cfg "github.com/tendermint/go-config"
//logger "github.com/tendermint/go-logger"
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"
"github.com/tendermint/basecoin/plugins/ibc"
"github.com/tendermint/basecoin/types"
)
var config cfg.Config
const EyesCacheSize = 10000
var StartCmd = cli.Command{
Name: "start",
Usage: "Start basecoin",
ArgsUsage: "",
Action: func(c *cli.Context) error {
return cmdStart(c)
},
Flags: []cli.Flag{
AddrFlag,
EyesFlag,
DirFlag,
InProcTMFlag,
ChainIDFlag,
IbcPluginFlag,
},
}
type plugin struct {
name string
init func() types.Plugin
}
var plugins = []plugin{}
// RegisterStartPlugin is used to add another
func RegisterStartPlugin(flag cli.BoolFlag, init func() types.Plugin) {
StartCmd.Flags = append(StartCmd.Flags, flag)
plugins = append(plugins, plugin{name: flag.GetName(), init: init})
}
func cmdStart(c *cli.Context) error {
// Connect to MerkleEyes
var eyesCli *eyes.Client
if c.String("eyes") == "local" {
eyesCli = eyes.NewLocalClient(path.Join(c.String("dir"), "merkleeyes.db"), EyesCacheSize)
} else {
var err error
eyesCli, err = eyes.NewClient(c.String("eyes"))
if err != nil {
return errors.New("connect to MerkleEyes: " + err.Error())
}
}
// Create Basecoin app
basecoinApp := app.NewBasecoin(eyesCli)
if c.Bool("ibc-plugin") {
basecoinApp.RegisterPlugin(ibc.New())
}
// loop through all registered plugins and enable if desired
for _, p := range plugins {
if c.Bool(p.name) {
basecoinApp.RegisterPlugin(p.init())
}
}
// If genesis file exists, set key-value options
genesisFile := path.Join(c.String("dir"), "genesis.json")
if _, err := os.Stat(genesisFile); err == nil {
err := basecoinApp.LoadGenesis(genesisFile)
if err != nil {
return errors.New(cmn.Fmt("%+v", err))
}
}
if c.Bool("in-proc") {
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 {
return errors.New("create listener: " + err.Error())
}
// Wait forever
cmn.TrapSignal(func() {
// Cleanup
svr.Stop()
})
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()
})
}
-225
View File
@@ -1,225 +0,0 @@
package commands
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"
tmtypes "github.com/tendermint/tendermint/types"
)
var (
SendTxCmd = cli.Command{
Name: "sendtx",
Usage: "Broadcast a basecoin SendTx",
ArgsUsage: "",
Action: func(c *cli.Context) error {
return cmdSendTx(c)
},
Flags: []cli.Flag{
NodeFlag,
ChainIDFlag,
FromFlag,
AmountFlag,
CoinFlag,
GasFlag,
FeeFlag,
SeqFlag,
ToFlag,
},
}
AppTxCmd = cli.Command{
Name: "apptx",
Usage: "Broadcast a basecoin AppTx",
ArgsUsage: "",
Action: func(c *cli.Context) error {
return cmdAppTx(c)
},
Flags: []cli.Flag{
NodeFlag,
ChainIDFlag,
FromFlag,
AmountFlag,
CoinFlag,
GasFlag,
FeeFlag,
SeqFlag,
NameFlag,
DataFlag,
},
// Subcommands are dynamically registered with plugins as needed
Subcommands: []cli.Command{},
}
)
// RegisterTxPlugin is used to add another subcommand and create a custom
// apptx encoding. Look at counter.go for an example
func RegisterTxPlugin(cmd cli.Command) {
AppTxCmd.Subcommands = append(AppTxCmd.Subcommands, cmd)
}
func cmdSendTx(c *cli.Context) error {
toHex := c.String("to")
fromFile := c.String("from")
amount := int64(c.Int("amount"))
coin := c.String("coin")
gas, fee := c.Int("gas"), int64(c.Int("fee"))
chainID := c.String("chain_id")
// convert destination address to bytes
to, err := hex.DecodeString(StripHex(toHex))
if err != nil {
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)
// 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)
output := newOutput(to, coin, amount)
tx := &types.SendTx{
Gas: int64(gas),
Fee: types.Coin{coin, fee},
Inputs: []types.TxInput{input},
Outputs: []types.TxOutput{output},
}
// sign that puppy
signBytes := tx.SignBytes(chainID)
tx.Inputs[0].Signature = privVal.Sign(signBytes)
fmt.Println("Signed SendTx:")
fmt.Println(string(wire.JSONBytes(tx)))
// broadcast the transaction to tendermint
if _, err := broadcastTx(c, tx); err != nil {
return err
}
return nil
}
func cmdAppTx(c *cli.Context) error {
// convert data to bytes
dataString := c.String("data")
data := []byte(dataString)
if isHex(dataString) {
data, _ = hex.DecodeString(dataString)
}
name := c.String("name")
return AppTx(c, name, data)
}
func AppTx(c *cli.Context, name string, data []byte) error {
fromFile := c.String("from")
amount := int64(c.Int("amount"))
coin := c.String("coin")
gas, fee := c.Int("gas"), int64(c.Int("fee"))
chainID := c.String("chain_id")
privVal := tmtypes.LoadPrivValidator(fromFile)
sequence, err := getSeq(c, privVal.Address)
if err != nil {
return err
}
input := types.NewTxInput(privVal.PubKey, types.Coins{types.Coin{coin, amount}}, sequence)
tx := &types.AppTx{
Gas: int64(gas),
Fee: types.Coin{coin, fee},
Name: name,
Input: input,
Data: data,
}
tx.Input.Signature = privVal.Sign(tx.SignBytes(chainID))
fmt.Println("Signed AppTx:")
fmt.Println(string(wire.JSONBytes(tx)))
res, err := broadcastTx(c, tx)
if err != nil {
return err
}
fmt.Printf("Response: %X\n", res)
return nil
}
// broadcast the transaction to tendermint
func broadcastTx(c *cli.Context, tx types.Tx) ([]byte, error) {
tmResult := new(ctypes.TMResult)
tmAddr := c.String("node")
clientURI := client.NewClientURI(tmAddr)
// Don't you hate having to do this?
// How many times have I lost an hour over this trick?!
txBytes := []byte(wire.BinaryBytes(struct {
types.Tx `json:"unwrap"`
}{tx}))
_, err := clientURI.Call("broadcast_tx_commit", map[string]interface{}{"tx": txBytes}, tmResult)
if err != nil {
return nil, errors.New(cmn.Fmt("Error on broadcast tx: %v", err))
}
res := (*tmResult).(*ctypes.ResultBroadcastTxCommit)
// if it fails check, we don't even get a delivertx back!
if !res.CheckTx.Code.IsOK() {
r := res.CheckTx
return nil, errors.New(cmn.Fmt("BroadcastTxCommit got non-zero exit code: %v. %X; %s", r.Code, r.Data, r.Log))
}
if !res.DeliverTx.Code.IsOK() {
r := res.DeliverTx
return nil, errors.New(cmn.Fmt("BroadcastTxCommit got non-zero exit code: %v. %X; %s", r.Code, r.Data, r.Log))
}
return res.DeliverTx.Data, 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("node")
acc, err := getAcc(tmAddr, address)
if err != nil {
return 0, err
}
return acc.Sequence + 1, nil
}
func newOutput(to []byte, coin string, amount int64) types.TxOutput {
return types.TxOutput{
Address: to,
Coins: types.Coins{
types.Coin{
Denom: coin,
Amount: amount,
},
},
}
}
-94
View File
@@ -1,94 +0,0 @@
package commands
import (
"encoding/hex"
"errors"
"github.com/urfave/cli"
"github.com/tendermint/basecoin/types"
abci "github.com/tendermint/abci/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"
tmtypes "github.com/tendermint/tendermint/types"
)
// Returns true for non-empty hex-string prefixed with "0x"
func isHex(s string) bool {
if len(s) > 2 && s[:2] == "0x" {
_, err := hex.DecodeString(s[2:])
if err != nil {
return false
}
return true
}
return false
}
func StripHex(s string) string {
if isHex(s) {
return s[2:]
}
return s
}
func Query(tmAddr string, key []byte) (*abci.ResponseQuery, error) {
clientURI := client.NewClientURI(tmAddr)
tmResult := new(ctypes.TMResult)
params := map[string]interface{}{
"path": "/key",
"data": key,
"prove": true,
}
_, 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))
}
return &res.Response, nil
}
// fetch the account by querying the app
func getAcc(tmAddr string, address []byte) (*types.Account, error) {
key := append([]byte("base/a/"), address...)
response, err := Query(tmAddr, key)
if err != nil {
return nil, err
}
accountBytes := response.Value
if len(accountBytes) == 0 {
return nil, errors.New(cmn.Fmt("Account bytes are empty 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
}
func getBlock(c *cli.Context, height int) (*tmtypes.Block, error) {
tmResult := new(ctypes.TMResult)
tmAddr := c.String("node")
clientURI := client.NewClientURI(tmAddr)
_, err := clientURI.Call("block", map[string]interface{}{"height": height}, tmResult)
if err != nil {
return nil, errors.New(cmn.Fmt("Error on broadcast tx: %v", err))
}
res := (*tmResult).(*ctypes.ResultBlock)
return res.Block, nil
}
+4 -6
View File
@@ -3,7 +3,7 @@ package main
import (
"os"
"github.com/tendermint/basecoin/cmd/basecoin/commands"
"github.com/tendermint/basecoin/cmd/commands"
"github.com/urfave/cli"
)
@@ -14,12 +14,10 @@ func main() {
app.Version = "0.1.0"
app.Commands = []cli.Command{
commands.StartCmd,
commands.SendTxCmd,
commands.AppTxCmd,
commands.IbcCmd,
commands.TxCmd,
commands.QueryCmd,
commands.VerifyCmd,
commands.BlockCmd,
commands.VerifyCmd, // TODO: move to merkleeyes?
commands.BlockCmd, // TODO: move to adam?
commands.AccountCmd,
}
app.Run(os.Args)