commit
488f3e1291
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -1,5 +1,15 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.4.0 (TBD)
|
||||||
|
|
||||||
|
BREAKING CHANGES:
|
||||||
|
|
||||||
|
- CLI now uses Cobra, which forced changes to some of the flag names and orderings
|
||||||
|
|
||||||
|
IMPROVEMENTS:
|
||||||
|
|
||||||
|
- `basecoin init` doesn't generate error if already initialized
|
||||||
|
|
||||||
## 0.3.1 (March 23, 2017)
|
## 0.3.1 (March 23, 2017)
|
||||||
|
|
||||||
IMPROVEMENTS:
|
IMPROVEMENTS:
|
||||||
|
|
12
app/app.go
12
app/app.go
|
@ -5,17 +5,17 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
||||||
sm "github.com/tendermint/basecoin/state"
|
|
||||||
"github.com/tendermint/basecoin/types"
|
|
||||||
. "github.com/tendermint/go-common"
|
. "github.com/tendermint/go-common"
|
||||||
"github.com/tendermint/go-wire"
|
"github.com/tendermint/go-wire"
|
||||||
eyes "github.com/tendermint/merkleeyes/client"
|
eyes "github.com/tendermint/merkleeyes/client"
|
||||||
|
|
||||||
|
sm "github.com/tendermint/basecoin/state"
|
||||||
|
"github.com/tendermint/basecoin/types"
|
||||||
|
"github.com/tendermint/basecoin/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
version = "0.1"
|
maxTxSize = 10240
|
||||||
maxTxSize = 10240
|
|
||||||
|
|
||||||
PluginNameBase = "base"
|
PluginNameBase = "base"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ func (app *Basecoin) GetState() *sm.State {
|
||||||
|
|
||||||
// ABCI::Info
|
// ABCI::Info
|
||||||
func (app *Basecoin) Info() abci.ResponseInfo {
|
func (app *Basecoin) Info() abci.ResponseInfo {
|
||||||
return abci.ResponseInfo{Data: Fmt("Basecoin v%v", version)}
|
return abci.ResponseInfo{Data: Fmt("Basecoin v%v", version.Version)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *Basecoin) RegisterPlugin(plugin types.Plugin) {
|
func (app *Basecoin) RegisterPlugin(plugin types.Plugin) {
|
||||||
|
|
|
@ -2,20 +2,24 @@ package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
cmn "github.com/tendermint/go-common"
|
||||||
"github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
eyescli "github.com/tendermint/merkleeyes/client"
|
eyescli "github.com/tendermint/merkleeyes/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const genesisFilepath = "./testdata/genesis.json"
|
||||||
|
|
||||||
func TestLoadGenesis(t *testing.T) {
|
func TestLoadGenesis(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
|
||||||
eyesCli := eyescli.NewLocalClient("", 0)
|
eyesCli := eyescli.NewLocalClient("", 0)
|
||||||
app := NewBasecoin(eyesCli)
|
app := NewBasecoin(eyesCli)
|
||||||
err := app.LoadGenesis("./testdata/genesis.json")
|
err := app.LoadGenesis(genesisFilepath)
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
|
|
||||||
// check the chain id
|
// check the chain id
|
||||||
|
@ -41,3 +45,24 @@ func TestLoadGenesis(t *testing.T) {
|
||||||
assert.EqualValues(pkbyte, epk[:])
|
assert.EqualValues(pkbyte, epk[:])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseGenesisList(t *testing.T) {
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
|
||||||
|
bytes, err := cmn.ReadFile(genesisFilepath)
|
||||||
|
require.Nil(err, "loading genesis file %+v", err)
|
||||||
|
|
||||||
|
// the basecoin genesis go-data :)
|
||||||
|
genDoc := new(FullGenesisDoc)
|
||||||
|
err = json.Unmarshal(bytes, genDoc)
|
||||||
|
require.Nil(err, "unmarshaling genesis file %+v", err)
|
||||||
|
|
||||||
|
pluginOpts, err := parseGenesisList(genDoc.AppOptions.PluginOptions)
|
||||||
|
require.Nil(err, "%+v", err)
|
||||||
|
genDoc.AppOptions.pluginOptions = pluginOpts
|
||||||
|
|
||||||
|
assert.Equal(genDoc.AppOptions.pluginOptions[0].Key, "plugin1/key1")
|
||||||
|
assert.Equal(genDoc.AppOptions.pluginOptions[1].Key, "plugin1/key2")
|
||||||
|
assert.Equal(genDoc.AppOptions.pluginOptions[0].Value, "value1")
|
||||||
|
assert.Equal(genDoc.AppOptions.pluginOptions[1].Value, "value2")
|
||||||
|
}
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"github.com/spf13/cobra"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/cmd/commands"
|
"github.com/tendermint/basecoin/cmd/commands"
|
||||||
"github.com/tendermint/basecoin/version"
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
app := cli.NewApp()
|
var RootCmd = &cobra.Command{
|
||||||
app.Name = "basecoin"
|
Use: "basecoin",
|
||||||
app.Usage = "basecoin [command] [args...]"
|
Short: "A cryptocurrency framework in Golang based on Tendermint-Core",
|
||||||
app.Version = version.Version
|
}
|
||||||
app.Commands = []cli.Command{
|
|
||||||
|
RootCmd.AddCommand(
|
||||||
commands.InitCmd,
|
commands.InitCmd,
|
||||||
commands.StartCmd,
|
commands.StartCmd,
|
||||||
commands.TxCmd,
|
commands.TxCmd,
|
||||||
|
@ -24,10 +22,8 @@ func main() {
|
||||||
commands.BlockCmd,
|
commands.BlockCmd,
|
||||||
commands.AccountCmd,
|
commands.AccountCmd,
|
||||||
commands.UnsafeResetAllCmd,
|
commands.UnsafeResetAllCmd,
|
||||||
}
|
commands.VersionCmd,
|
||||||
err := app.Run(os.Args)
|
)
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
commands.ExecuteWithDebug(RootCmd)
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,119 +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")
|
|
||||||
|
|
||||||
WithoutTendermintFlag = cli.BoolFlag{
|
|
||||||
Name: "without-tendermint",
|
|
||||||
Usage: "Run the Basecoin app without Tendermint",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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.StringFlag{
|
|
||||||
Name: "amount",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Coins to send in transaction of the format <amt><coin>,<amt2><coin2>,... (eg: 1btc,2gold,5silver)",
|
|
||||||
}
|
|
||||||
|
|
||||||
FromFlag = cli.StringFlag{
|
|
||||||
Name: "from",
|
|
||||||
Value: "key.json",
|
|
||||||
Usage: "Path to a private key to sign the transaction",
|
|
||||||
}
|
|
||||||
|
|
||||||
SeqFlag = cli.IntFlag{
|
|
||||||
Name: "sequence",
|
|
||||||
Value: 0,
|
|
||||||
Usage: "Sequence number for the account",
|
|
||||||
}
|
|
||||||
|
|
||||||
GasFlag = cli.IntFlag{
|
|
||||||
Name: "gas",
|
|
||||||
Value: 0,
|
|
||||||
Usage: "The amount of gas for the transaction",
|
|
||||||
}
|
|
||||||
|
|
||||||
FeeFlag = cli.StringFlag{
|
|
||||||
Name: "fee",
|
|
||||||
Value: "",
|
|
||||||
Usage: "Coins for the transaction fee of the format <amt><coin>",
|
|
||||||
}
|
|
||||||
|
|
||||||
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",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// 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: "",
|
|
||||||
}
|
|
||||||
)
|
|
|
@ -2,15 +2,14 @@ package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/plugins/ibc"
|
"github.com/tendermint/basecoin/plugins/ibc"
|
||||||
|
|
||||||
cmn "github.com/tendermint/go-common"
|
|
||||||
"github.com/tendermint/go-merkle"
|
"github.com/tendermint/go-merkle"
|
||||||
"github.com/tendermint/go-wire"
|
"github.com/tendermint/go-wire"
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
|
@ -21,172 +20,110 @@ func NewIBCPlugin() *ibc.IBCPlugin {
|
||||||
return ibc.New()
|
return ibc.New()
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
//commands
|
||||||
// ibc flags
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
IbcChainIDFlag = cli.StringFlag{
|
IBCTxCmd = &cobra.Command{
|
||||||
Name: "chain_id",
|
Use: "ibc",
|
||||||
Usage: "ChainID for the new blockchain",
|
Short: "An IBC transaction, for InterBlockchain Communication",
|
||||||
Value: "",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IbcGenesisFlag = cli.StringFlag{
|
IBCRegisterTxCmd = &cobra.Command{
|
||||||
Name: "genesis",
|
Use: "register",
|
||||||
Usage: "Genesis file for the new blockchain",
|
Short: "Register a blockchain via IBC",
|
||||||
Value: "",
|
RunE: ibcRegisterTxCmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
IbcHeaderFlag = cli.StringFlag{
|
IBCUpdateTxCmd = &cobra.Command{
|
||||||
Name: "header",
|
Use: "update",
|
||||||
Usage: "Block header for an ibc update",
|
Short: "Update the latest state of a blockchain via IBC",
|
||||||
Value: "",
|
RunE: ibcUpdateTxCmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
IbcCommitFlag = cli.StringFlag{
|
IBCPacketTxCmd = &cobra.Command{
|
||||||
Name: "commit",
|
Use: "packet",
|
||||||
Usage: "Block commit for an ibc update",
|
Short: "Send a new packet via IBC",
|
||||||
Value: "",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IbcFromFlag = cli.StringFlag{
|
IBCPacketCreateTxCmd = &cobra.Command{
|
||||||
Name: "from",
|
Use: "create",
|
||||||
Usage: "Source ChainID",
|
Short: "Create an egress IBC packet",
|
||||||
Value: "",
|
RunE: ibcPacketCreateTxCmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
IbcToFlag = cli.StringFlag{
|
IBCPacketPostTxCmd = &cobra.Command{
|
||||||
Name: "to",
|
Use: "post",
|
||||||
Usage: "Destination ChainID",
|
Short: "Deliver an IBC packet to another chain",
|
||||||
Value: "",
|
RunE: ibcPacketPostTxCmd,
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
//flags
|
||||||
// ibc commands
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
IbcTxCmd = cli.Command{
|
ibcChainIDFlag string
|
||||||
Name: "ibc",
|
ibcGenesisFlag string
|
||||||
Usage: "an IBC transaction, for InterBlockchain Communication",
|
ibcHeaderFlag string
|
||||||
Flags: TxFlags,
|
ibcCommitFlag string
|
||||||
Subcommands: []cli.Command{
|
ibcFromFlag string
|
||||||
IbcRegisterTxCmd,
|
ibcToFlag string
|
||||||
IbcUpdateTxCmd,
|
ibcTypeFlag string
|
||||||
IbcPacketTxCmd,
|
ibcPayloadFlag string
|
||||||
},
|
ibcPacketFlag string
|
||||||
}
|
ibcProofFlag string
|
||||||
|
ibcSequenceFlag int
|
||||||
IbcRegisterTxCmd = cli.Command{
|
ibcHeightFlag int
|
||||||
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",
|
|
||||||
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 init() {
|
||||||
|
|
||||||
|
// register flags
|
||||||
|
registerFlags := []Flag2Register{
|
||||||
|
{&ibcChainIDFlag, "ibc_chain_id", "", "ChainID for the new blockchain"},
|
||||||
|
{&ibcGenesisFlag, "genesis", "", "Genesis file for the new blockchain"},
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFlags := []Flag2Register{
|
||||||
|
{&ibcHeaderFlag, "header", "", "Block header for an ibc update"},
|
||||||
|
{&ibcCommitFlag, "commit", "", "Block commit for an ibc update"},
|
||||||
|
}
|
||||||
|
|
||||||
|
fromFlagReg := Flag2Register{&ibcFromFlag, "ibc_from", "", "Source ChainID"}
|
||||||
|
|
||||||
|
packetCreateFlags := []Flag2Register{
|
||||||
|
fromFlagReg,
|
||||||
|
{&ibcToFlag, "to", "", "Destination ChainID"},
|
||||||
|
{&ibcTypeFlag, "type", "", "IBC packet type (eg. coin)"},
|
||||||
|
{&ibcPayloadFlag, "payload", "", "IBC packet payload"},
|
||||||
|
{&ibcSequenceFlag, "ibc_sequence", -1, "sequence number for IBC packet"},
|
||||||
|
}
|
||||||
|
|
||||||
|
packetPostFlags := []Flag2Register{
|
||||||
|
fromFlagReg,
|
||||||
|
{&ibcHeightFlag, "height", 0, "Height the packet became egress in source chain"},
|
||||||
|
{&ibcPacketFlag, "packet", "", "hex-encoded IBC packet"},
|
||||||
|
{&ibcProofFlag, "proof", "", "hex-encoded proof of IBC packet from source chain"},
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterFlags(IBCRegisterTxCmd, registerFlags)
|
||||||
|
RegisterFlags(IBCUpdateTxCmd, updateFlags)
|
||||||
|
RegisterFlags(IBCPacketCreateTxCmd, packetCreateFlags)
|
||||||
|
RegisterFlags(IBCPacketPostTxCmd, packetPostFlags)
|
||||||
|
|
||||||
|
//register commands
|
||||||
|
IBCTxCmd.AddCommand(IBCRegisterTxCmd, IBCUpdateTxCmd, IBCPacketTxCmd)
|
||||||
|
IBCPacketTxCmd.AddCommand(IBCPacketCreateTxCmd, IBCPacketPostTxCmd)
|
||||||
|
RegisterTxSubcommand(IBCTxCmd)
|
||||||
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
// ibc command implementations
|
// ibc command implementations
|
||||||
|
|
||||||
func cmdIBCRegisterTx(c *cli.Context) error {
|
func ibcRegisterTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
chainID := c.String("chain_id")
|
chainID := ibcChainIDFlag
|
||||||
genesisFile := c.String("genesis")
|
genesisFile := ibcGenesisFlag
|
||||||
parent := c.Parent()
|
|
||||||
|
|
||||||
genesisBytes, err := ioutil.ReadFile(genesisFile)
|
genesisBytes, err := ioutil.ReadFile(genesisFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(cmn.Fmt("Error reading genesis file %v: %v", genesisFile, err))
|
return errors.Errorf("Error reading genesis file %v: %v\n", genesisFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ibcTx := ibc.IBCRegisterChainTx{
|
ibcTx := ibc.IBCRegisterChainTx{
|
||||||
|
@ -203,27 +140,31 @@ func cmdIBCRegisterTx(c *cli.Context) error {
|
||||||
}{ibcTx}))
|
}{ibcTx}))
|
||||||
name := "IBC"
|
name := "IBC"
|
||||||
|
|
||||||
return AppTx(parent, name, data)
|
return AppTx(name, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdIBCUpdateTx(c *cli.Context) error {
|
func ibcUpdateTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
headerBytes, err := hex.DecodeString(StripHex(c.String("header")))
|
headerBytes, err := hex.DecodeString(StripHex(ibcHeaderFlag))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(cmn.Fmt("Header (%v) is invalid hex: %v", c.String("header"), err))
|
return errors.Errorf("Header (%v) is invalid hex: %v\n", ibcHeaderFlag, err)
|
||||||
}
|
}
|
||||||
commitBytes, err := hex.DecodeString(StripHex(c.String("commit")))
|
|
||||||
|
commitBytes, err := hex.DecodeString(StripHex(ibcCommitFlag))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(cmn.Fmt("Commit (%v) is invalid hex: %v", c.String("commit"), err))
|
return errors.Errorf("Commit (%v) is invalid hex: %v\n", ibcCommitFlag, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
header := new(tmtypes.Header)
|
header := new(tmtypes.Header)
|
||||||
commit := new(tmtypes.Commit)
|
commit := new(tmtypes.Commit)
|
||||||
|
|
||||||
if err := wire.ReadBinaryBytes(headerBytes, &header); err != nil {
|
err = wire.ReadBinaryBytes(headerBytes, &header)
|
||||||
return errors.New(cmn.Fmt("Error unmarshalling header: %v", err))
|
if err != nil {
|
||||||
|
return errors.Errorf("Error unmarshalling header: %v\n", err)
|
||||||
}
|
}
|
||||||
if err := wire.ReadBinaryBytes(commitBytes, &commit); err != nil {
|
|
||||||
return errors.New(cmn.Fmt("Error unmarshalling commit: %v", err))
|
err = wire.ReadBinaryBytes(commitBytes, &commit)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Errorf("Error unmarshalling commit: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ibcTx := ibc.IBCUpdateChainTx{
|
ibcTx := ibc.IBCUpdateChainTx{
|
||||||
|
@ -238,19 +179,19 @@ func cmdIBCUpdateTx(c *cli.Context) error {
|
||||||
}{ibcTx}))
|
}{ibcTx}))
|
||||||
name := "IBC"
|
name := "IBC"
|
||||||
|
|
||||||
return AppTx(c.Parent(), name, data)
|
return AppTx(name, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdIBCPacketCreateTx(c *cli.Context) error {
|
func ibcPacketCreateTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
fromChain, toChain := c.String("from"), c.String("to")
|
fromChain, toChain := ibcFromFlag, ibcToFlag
|
||||||
packetType := c.String("type")
|
packetType := ibcTypeFlag
|
||||||
|
|
||||||
payloadBytes, err := hex.DecodeString(StripHex(c.String("payload")))
|
payloadBytes, err := hex.DecodeString(StripHex(ibcPayloadFlag))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(cmn.Fmt("Payload (%v) is invalid hex: %v", c.String("payload"), err))
|
return errors.Errorf("Payload (%v) is invalid hex: %v\n", ibcPayloadFlag, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sequence, err := getIBCSequence(c)
|
sequence, err := ibcSequenceCmd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -271,29 +212,33 @@ func cmdIBCPacketCreateTx(c *cli.Context) error {
|
||||||
ibc.IBCTx `json:"unwrap"`
|
ibc.IBCTx `json:"unwrap"`
|
||||||
}{ibcTx}))
|
}{ibcTx}))
|
||||||
|
|
||||||
return AppTx(c.Parent().Parent(), "IBC", data)
|
return AppTx("IBC", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdIBCPacketPostTx(c *cli.Context) error {
|
func ibcPacketPostTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
fromChain, fromHeight := c.String("from"), c.Int("height")
|
fromChain, fromHeight := ibcFromFlag, ibcHeightFlag
|
||||||
|
|
||||||
packetBytes, err := hex.DecodeString(StripHex(c.String("packet")))
|
packetBytes, err := hex.DecodeString(StripHex(ibcPacketFlag))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(cmn.Fmt("Packet (%v) is invalid hex: %v", c.String("packet"), err))
|
return errors.Errorf("Packet (%v) is invalid hex: %v\n", ibcPacketFlag, err)
|
||||||
}
|
}
|
||||||
proofBytes, err := hex.DecodeString(StripHex(c.String("proof")))
|
|
||||||
|
proofBytes, err := hex.DecodeString(StripHex(ibcProofFlag))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(cmn.Fmt("Proof (%v) is invalid hex: %v", c.String("proof"), err))
|
return errors.Errorf("Proof (%v) is invalid hex: %v\n", ibcProofFlag, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var packet ibc.Packet
|
var packet ibc.Packet
|
||||||
proof := new(merkle.IAVLProof)
|
proof := new(merkle.IAVLProof)
|
||||||
|
|
||||||
if err := wire.ReadBinaryBytes(packetBytes, &packet); err != nil {
|
err = wire.ReadBinaryBytes(packetBytes, &packet)
|
||||||
return errors.New(cmn.Fmt("Error unmarshalling packet: %v", err))
|
if err != nil {
|
||||||
|
return errors.Errorf("Error unmarshalling packet: %v\n", err)
|
||||||
}
|
}
|
||||||
if err := wire.ReadBinaryBytes(proofBytes, &proof); err != nil {
|
|
||||||
return errors.New(cmn.Fmt("Error unmarshalling proof: %v", err))
|
err = wire.ReadBinaryBytes(proofBytes, &proof)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Errorf("Error unmarshalling proof: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ibcTx := ibc.IBCPacketPostTx{
|
ibcTx := ibc.IBCPacketPostTx{
|
||||||
|
@ -309,12 +254,12 @@ func cmdIBCPacketPostTx(c *cli.Context) error {
|
||||||
ibc.IBCTx `json:"unwrap"`
|
ibc.IBCTx `json:"unwrap"`
|
||||||
}{ibcTx}))
|
}{ibcTx}))
|
||||||
|
|
||||||
return AppTx(c.Parent().Parent(), "IBC", data)
|
return AppTx("IBC", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getIBCSequence(c *cli.Context) (uint64, error) {
|
func ibcSequenceCmd() (uint64, error) {
|
||||||
if c.IsSet("sequence") {
|
if ibcSequenceFlag >= 0 {
|
||||||
return uint64(c.Int("sequence")), nil
|
return uint64(ibcSequenceFlag), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: get sequence
|
// TODO: get sequence
|
||||||
|
|
|
@ -2,26 +2,37 @@ package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
cmn "github.com/tendermint/go-common"
|
cmn "github.com/tendermint/go-common"
|
||||||
)
|
)
|
||||||
|
|
||||||
var InitCmd = cli.Command{
|
//commands
|
||||||
Name: "init",
|
var (
|
||||||
Usage: "Initialize a basecoin blockchain",
|
InitCmd = &cobra.Command{
|
||||||
ArgsUsage: "",
|
Use: "init",
|
||||||
Action: func(c *cli.Context) error {
|
Short: "Initialize a basecoin blockchain",
|
||||||
return cmdInit(c)
|
RunE: initCmd,
|
||||||
},
|
}
|
||||||
Flags: []cli.Flag{
|
)
|
||||||
ChainIDFlag,
|
|
||||||
},
|
// returns 1 iff it set a file, otherwise 0 (so we can add them)
|
||||||
|
func setupFile(path, data string, perm os.FileMode) (int, error) {
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
if !os.IsNotExist(err) { //note, os.IsExist(err) != !os.IsNotExist(err)
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(path, []byte(data), perm)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdInit(c *cli.Context) error {
|
func initCmd(cmd *cobra.Command, args []string) error {
|
||||||
rootDir := BasecoinRoot("")
|
rootDir := BasecoinRoot("")
|
||||||
|
|
||||||
cmn.EnsureDir(rootDir, 0777)
|
cmn.EnsureDir(rootDir, 0777)
|
||||||
|
@ -32,25 +43,33 @@ func cmdInit(c *cli.Context) error {
|
||||||
key1File := path.Join(rootDir, "key.json")
|
key1File := path.Join(rootDir, "key.json")
|
||||||
key2File := path.Join(rootDir, "key2.json")
|
key2File := path.Join(rootDir, "key2.json")
|
||||||
|
|
||||||
if err := ioutil.WriteFile(genesisFile, []byte(genesisJSON), 0644); err != nil {
|
mod1, err := setupFile(genesisFile, GenesisJSON, 0644)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ioutil.WriteFile(privValFile, []byte(privValJSON), 0400); err != nil {
|
mod2, err := setupFile(privValFile, PrivValJSON, 0400)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ioutil.WriteFile(key1File, []byte(key1JSON), 0400); err != nil {
|
mod3, err := setupFile(key1File, Key1JSON, 0400)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ioutil.WriteFile(key2File, []byte(key2JSON), 0400); err != nil {
|
mod4, err := setupFile(key2File, Key2JSON, 0400)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Notice("Initialized Basecoin", "genesis", genesisFile, "key", key1File)
|
if (mod1 + mod2 + mod3 + mod4) > 0 {
|
||||||
|
log.Notice("Initialized Basecoin", "genesis", genesisFile, "key", key1File)
|
||||||
|
} else {
|
||||||
|
log.Notice("Already initialized", "priv_validator", privValFile)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const privValJSON = `{
|
var PrivValJSON = `{
|
||||||
"address": "7A956FADD20D3A5B2375042B2959F8AB172A058F",
|
"address": "7A956FADD20D3A5B2375042B2959F8AB172A058F",
|
||||||
"last_height": 0,
|
"last_height": 0,
|
||||||
"last_round": 0,
|
"last_round": 0,
|
||||||
|
@ -67,7 +86,7 @@ const privValJSON = `{
|
||||||
]
|
]
|
||||||
}`
|
}`
|
||||||
|
|
||||||
const genesisJSON = `{
|
var GenesisJSON = `{
|
||||||
"app_hash": "",
|
"app_hash": "",
|
||||||
"chain_id": "test_chain_id",
|
"chain_id": "test_chain_id",
|
||||||
"genesis_time": "0001-01-01T00:00:00.000Z",
|
"genesis_time": "0001-01-01T00:00:00.000Z",
|
||||||
|
@ -97,7 +116,7 @@ const genesisJSON = `{
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
const key1JSON = `{
|
var Key1JSON = `{
|
||||||
"address": "1B1BE55F969F54064628A63B9559E7C21C925165",
|
"address": "1B1BE55F969F54064628A63B9559E7C21C925165",
|
||||||
"priv_key": {
|
"priv_key": {
|
||||||
"type": "ed25519",
|
"type": "ed25519",
|
||||||
|
@ -109,7 +128,7 @@ const key1JSON = `{
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
const key2JSON = `{
|
var Key2JSON = `{
|
||||||
"address": "1DA7C74F9C219229FD54CC9F7386D5A3839F0090",
|
"address": "1DA7C74F9C219229FD54CC9F7386D5A3839F0090",
|
||||||
"priv_key": {
|
"priv_key": {
|
||||||
"type": "ed25519",
|
"type": "ed25519",
|
||||||
|
|
|
@ -8,31 +8,27 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
//"github.com/pkg/errors"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
cmn "github.com/tendermint/go-common"
|
|
||||||
"github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//commands
|
||||||
var (
|
var (
|
||||||
KeyCmd = cli.Command{
|
KeyCmd = &cobra.Command{
|
||||||
Name: "key",
|
Use: "key",
|
||||||
Usage: "Manage keys",
|
Short: "Manage keys",
|
||||||
ArgsUsage: "",
|
|
||||||
Subcommands: []cli.Command{NewKeyCmd},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NewKeyCmd = cli.Command{
|
NewKeyCmd = &cobra.Command{
|
||||||
Name: "new",
|
Use: "new",
|
||||||
Usage: "Create a new private key",
|
Short: "Create a new private key",
|
||||||
ArgsUsage: "",
|
RunE: newKeyCmd,
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
return cmdNewKey(c)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func cmdNewKey(c *cli.Context) error {
|
func newKeyCmd(cmd *cobra.Command, args []string) error {
|
||||||
key := genKey()
|
key := genKey()
|
||||||
keyJSON, err := json.MarshalIndent(key, "", "\t")
|
keyJSON, err := json.MarshalIndent(key, "", "\t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -42,6 +38,11 @@ func cmdNewKey(c *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
//register commands
|
||||||
|
KeyCmd.AddCommand(NewKeyCmd)
|
||||||
|
}
|
||||||
|
|
||||||
//---------------------------------------------
|
//---------------------------------------------
|
||||||
// simple implementation of a key
|
// simple implementation of a key
|
||||||
|
|
||||||
|
@ -84,16 +85,18 @@ func genKey() *Key {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadKey(keyFile string) *Key {
|
func LoadKey(keyFile string) (*Key, error) {
|
||||||
filePath := path.Join(BasecoinRoot(""), keyFile)
|
filePath := path.Join(BasecoinRoot(""), keyFile)
|
||||||
keyJSONBytes, err := ioutil.ReadFile(filePath)
|
keyJSONBytes, err := ioutil.ReadFile(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmn.Exit(err.Error())
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
key := new(Key)
|
key := new(Key)
|
||||||
err = json.Unmarshal(keyJSONBytes, key)
|
err = json.Unmarshal(keyJSONBytes, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmn.Exit(cmn.Fmt("Error reading key from %v: %v\n", filePath, err))
|
return nil, fmt.Errorf("Error reading key from %v: %v\n", filePath, err) //never stack trace
|
||||||
}
|
}
|
||||||
return key
|
|
||||||
|
return key, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/tendermint/basecoin/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type plugin struct {
|
||||||
|
name string
|
||||||
|
newPlugin func() types.Plugin
|
||||||
|
}
|
||||||
|
|
||||||
|
var plugins = []plugin{}
|
||||||
|
|
||||||
|
// RegisterStartPlugin is used to enable a plugin
|
||||||
|
func RegisterStartPlugin(name string, newPlugin func() types.Plugin) {
|
||||||
|
plugins = append(plugins, plugin{name: name, newPlugin: newPlugin})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register a subcommand of QueryCmd for plugin specific query functionality
|
||||||
|
func RegisterQuerySubcommand(cmd *cobra.Command) {
|
||||||
|
QueryCmd.AddCommand(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register a subcommand of TxCmd to craft transactions for plugins
|
||||||
|
func RegisterTxSubcommand(cmd *cobra.Command) {
|
||||||
|
TxCmd.AddCommand(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
//Returns a version command based on version input
|
||||||
|
func QuickVersionCmd(version string) *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "version",
|
||||||
|
Short: "Show version info",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println(version)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,97 +2,96 @@ package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
cmn "github.com/tendermint/go-common"
|
|
||||||
"github.com/tendermint/go-merkle"
|
"github.com/tendermint/go-merkle"
|
||||||
"github.com/tendermint/go-wire"
|
"github.com/tendermint/go-wire"
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//commands
|
||||||
var (
|
var (
|
||||||
QueryCmd = cli.Command{
|
QueryCmd = &cobra.Command{
|
||||||
Name: "query",
|
Use: "query [key]",
|
||||||
Usage: "Query the merkle tree",
|
Short: "Query the merkle tree",
|
||||||
ArgsUsage: "<key>",
|
RunE: queryCmd,
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
return cmdQuery(c)
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NodeFlag,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountCmd = cli.Command{
|
AccountCmd = &cobra.Command{
|
||||||
Name: "account",
|
Use: "account [address]",
|
||||||
Usage: "Get details of an account",
|
Short: "Get details of an account",
|
||||||
ArgsUsage: "<address>",
|
RunE: accountCmd,
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
return cmdAccount(c)
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NodeFlag,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockCmd = cli.Command{
|
BlockCmd = &cobra.Command{
|
||||||
Name: "block",
|
Use: "block [height]",
|
||||||
Usage: "Get the header and commit of a block",
|
Short: "Get the header and commit of a block",
|
||||||
ArgsUsage: "<height>",
|
RunE: blockCmd,
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
return cmdBlock(c)
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
NodeFlag,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VerifyCmd = cli.Command{
|
VerifyCmd = &cobra.Command{
|
||||||
Name: "verify",
|
Use: "verify",
|
||||||
Usage: "Verify the IAVL proof",
|
Short: "Verify the IAVL proof",
|
||||||
Action: func(c *cli.Context) error {
|
RunE: verifyCmd,
|
||||||
return cmdVerify(c)
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
ProofFlag,
|
|
||||||
KeyFlag,
|
|
||||||
ValueFlag,
|
|
||||||
RootFlag,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Register a subcommand of QueryCmd for plugin specific query functionality
|
//flags
|
||||||
func RegisterQuerySubcommand(cmd cli.Command) {
|
var (
|
||||||
QueryCmd.Subcommands = append(QueryCmd.Subcommands, cmd)
|
nodeFlag string
|
||||||
|
proofFlag string
|
||||||
|
keyFlag string
|
||||||
|
valueFlag string
|
||||||
|
rootFlag string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
commonFlags := []Flag2Register{
|
||||||
|
{&nodeFlag, "node", "tcp://localhost:46657", "Tendermint RPC address"},
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyFlags := []Flag2Register{
|
||||||
|
{&proofFlag, "proof", "", "hex-encoded IAVL proof"},
|
||||||
|
{&keyFlag, "key", "", "key to the IAVL tree"},
|
||||||
|
{&valueFlag, "value", "", "value in the IAVL tree"},
|
||||||
|
{&rootFlag, "root", "", "root hash of the IAVL tree"},
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterFlags(QueryCmd, commonFlags)
|
||||||
|
RegisterFlags(AccountCmd, commonFlags)
|
||||||
|
RegisterFlags(BlockCmd, commonFlags)
|
||||||
|
RegisterFlags(VerifyCmd, verifyFlags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdQuery(c *cli.Context) error {
|
func queryCmd(cmd *cobra.Command, args []string) error {
|
||||||
if len(c.Args()) != 1 {
|
|
||||||
return errors.New("query command requires an argument ([key])")
|
if len(args) != 1 {
|
||||||
|
return fmt.Errorf("query command requires an argument ([key])") //never stack trace
|
||||||
}
|
}
|
||||||
keyString := c.Args()[0]
|
|
||||||
|
keyString := args[0]
|
||||||
key := []byte(keyString)
|
key := []byte(keyString)
|
||||||
if isHex(keyString) {
|
if isHex(keyString) {
|
||||||
// convert key to bytes
|
// convert key to bytes
|
||||||
var err error
|
var err error
|
||||||
key, err = hex.DecodeString(StripHex(keyString))
|
key, err = hex.DecodeString(StripHex(keyString))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(cmn.Fmt("Query key (%v) is invalid hex: %v", keyString, err))
|
return errors.Errorf("Query key (%v) is invalid hex: %v\n", keyString, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := Query(c.String("node"), key)
|
resp, err := Query(nodeFlag, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return errors.Errorf("Query returns error: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !resp.Code.IsOK() {
|
if !resp.Code.IsOK() {
|
||||||
return errors.New(cmn.Fmt("Query for key (%v) returned non-zero code (%v): %v", keyString, resp.Code, resp.Log))
|
return errors.Errorf("Query for key (%v) returned non-zero code (%v): %v", keyString, resp.Code, resp.Log)
|
||||||
}
|
}
|
||||||
|
|
||||||
val := resp.Value
|
val := resp.Value
|
||||||
|
@ -104,23 +103,24 @@ func cmdQuery(c *cli.Context) error {
|
||||||
Proof []byte `json:"proof"`
|
Proof []byte `json:"proof"`
|
||||||
Height uint64 `json:"height"`
|
Height uint64 `json:"height"`
|
||||||
}{val, proof, height})))
|
}{val, proof, height})))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdAccount(c *cli.Context) error {
|
func accountCmd(cmd *cobra.Command, args []string) error {
|
||||||
if len(c.Args()) != 1 {
|
|
||||||
return errors.New("account command requires an argument ([address])")
|
if len(args) != 1 {
|
||||||
|
return fmt.Errorf("account command requires an argument ([address])") //never stack trace
|
||||||
}
|
}
|
||||||
addrHex := StripHex(c.Args()[0])
|
|
||||||
|
addrHex := StripHex(args[0])
|
||||||
|
|
||||||
// convert destination address to bytes
|
// convert destination address to bytes
|
||||||
addr, err := hex.DecodeString(addrHex)
|
addr, err := hex.DecodeString(addrHex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(cmn.Fmt("Account address (%v) is invalid hex: %v", addrHex, err))
|
return errors.Errorf("Account address (%v) is invalid hex: %v\n", addrHex, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
acc, err := getAcc(c.String("node"), addr)
|
acc, err := getAcc(nodeFlag, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -128,17 +128,19 @@ func cmdAccount(c *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdBlock(c *cli.Context) error {
|
func blockCmd(cmd *cobra.Command, args []string) error {
|
||||||
if len(c.Args()) != 1 {
|
|
||||||
return errors.New("block command requires an argument ([height])")
|
if len(args) != 1 {
|
||||||
}
|
return fmt.Errorf("block command requires an argument ([height])") //never stack trace
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
header, commit, err := getHeaderAndCommit(c, height)
|
heightString := args[0]
|
||||||
|
height, err := strconv.Atoi(heightString)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Errorf("Height must be an int, got %v: %v\n", heightString, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
header, commit, err := getHeaderAndCommit(nodeFlag, height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -156,7 +158,6 @@ func cmdBlock(c *cli.Context) error {
|
||||||
Commit: commit,
|
Commit: commit,
|
||||||
},
|
},
|
||||||
})))
|
})))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,15 +171,16 @@ type BlockJSON struct {
|
||||||
Commit *tmtypes.Commit `json:"commit"`
|
Commit *tmtypes.Commit `json:"commit"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdVerify(c *cli.Context) error {
|
func verifyCmd(cmd *cobra.Command, args []string) error {
|
||||||
keyString, valueString := c.String("key"), c.String("value")
|
|
||||||
|
keyString, valueString := keyFlag, valueFlag
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
key := []byte(keyString)
|
key := []byte(keyString)
|
||||||
if isHex(keyString) {
|
if isHex(keyString) {
|
||||||
key, err = hex.DecodeString(StripHex(keyString))
|
key, err = hex.DecodeString(StripHex(keyString))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(cmn.Fmt("Key (%v) is invalid hex: %v", keyString, err))
|
return errors.Errorf("Key (%v) is invalid hex: %v\n", keyString, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,23 +188,23 @@ func cmdVerify(c *cli.Context) error {
|
||||||
if isHex(valueString) {
|
if isHex(valueString) {
|
||||||
value, err = hex.DecodeString(StripHex(valueString))
|
value, err = hex.DecodeString(StripHex(valueString))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(cmn.Fmt("Value (%v) is invalid hex: %v", valueString, err))
|
return errors.Errorf("Value (%v) is invalid hex: %v\n", valueString, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
root, err := hex.DecodeString(StripHex(c.String("root")))
|
root, err := hex.DecodeString(StripHex(rootFlag))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(cmn.Fmt("Root (%v) is invalid hex: %v", c.String("root"), err))
|
return errors.Errorf("Root (%v) is invalid hex: %v\n", rootFlag, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
proofBytes, err := hex.DecodeString(StripHex(c.String("proof")))
|
proofBytes, err := hex.DecodeString(StripHex(proofFlag))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(cmn.Fmt("Proof (%v) is invalid hex: %v", c.String("proof"), err))
|
return errors.Errorf("Proof (%v) is invalid hex: %v\n", proofFlag, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
proof, err := merkle.ReadProof(proofBytes)
|
proof, err := merkle.ReadProof(proofBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(cmn.Fmt("Error unmarshalling proof: %v", err))
|
return errors.Errorf("Error unmarshalling proof: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if proof.Verify(key, value, root) {
|
if proof.Verify(key, value, root) {
|
||||||
|
|
|
@ -1,47 +1,25 @@
|
||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
tmcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||||
tmcfg "github.com/tendermint/tendermint/config/tendermint"
|
tmcfg "github.com/tendermint/tendermint/config/tendermint"
|
||||||
types "github.com/tendermint/tendermint/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var UnsafeResetAllCmd = cli.Command{
|
var UnsafeResetAllCmd = &cobra.Command{
|
||||||
Name: "unsafe_reset_all",
|
Use: "unsafe_reset_all",
|
||||||
Usage: "Reset all blockchain data",
|
Short: "Reset all blockchain data",
|
||||||
ArgsUsage: "",
|
RunE: unsafeResetAllCmd,
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
return cmdUnsafeResetAll(c)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdUnsafeResetAll(c *cli.Context) error {
|
func unsafeResetAllCmd(cmd *cobra.Command, args []string) error {
|
||||||
basecoinDir := BasecoinRoot("")
|
basecoinDir := BasecoinRoot("")
|
||||||
tmDir := path.Join(basecoinDir)
|
tmDir := path.Join(basecoinDir)
|
||||||
tmConfig := tmcfg.GetConfig(tmDir)
|
tmConfig := tmcfg.GetConfig(tmDir)
|
||||||
|
|
||||||
// Get and Reset PrivValidator
|
tmcmd.ResetAll(tmConfig, log)
|
||||||
var privValidator *types.PrivValidator
|
|
||||||
privValidatorFile := tmConfig.GetString("priv_validator_file")
|
|
||||||
if _, err := os.Stat(privValidatorFile); err == nil {
|
|
||||||
privValidator = types.LoadPrivValidator(privValidatorFile)
|
|
||||||
privValidator.Reset()
|
|
||||||
log.Notice("Reset PrivValidator", "file", privValidatorFile)
|
|
||||||
} else {
|
|
||||||
privValidator = types.GenPrivValidator()
|
|
||||||
privValidator.SetFile(privValidatorFile)
|
|
||||||
privValidator.Save()
|
|
||||||
log.Notice("Generated PrivValidator", "file", privValidatorFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove all tendermint data
|
|
||||||
tmDataDir := tmConfig.GetString("db_dir")
|
|
||||||
os.RemoveAll(tmDataDir)
|
|
||||||
log.Notice("Removed all data", "dir", tmDataDir)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/tendermint/abci/server"
|
"github.com/tendermint/abci/server"
|
||||||
cmn "github.com/tendermint/go-common"
|
cmn "github.com/tendermint/go-common"
|
||||||
|
@ -18,50 +18,48 @@ import (
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/app"
|
"github.com/tendermint/basecoin/app"
|
||||||
"github.com/tendermint/basecoin/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var StartCmd = &cobra.Command{
|
||||||
|
Use: "start",
|
||||||
|
Short: "Start basecoin",
|
||||||
|
RunE: startCmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
//flags
|
||||||
|
var (
|
||||||
|
addrFlag string
|
||||||
|
eyesFlag string
|
||||||
|
dirFlag string
|
||||||
|
withoutTendermintFlag bool
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: move to config file
|
||||||
const EyesCacheSize = 10000
|
const EyesCacheSize = 10000
|
||||||
|
|
||||||
var StartCmd = cli.Command{
|
func init() {
|
||||||
Name: "start",
|
|
||||||
Usage: "Start basecoin",
|
flags := []Flag2Register{
|
||||||
ArgsUsage: "",
|
{&addrFlag, "address", "tcp://0.0.0.0:46658", "Listen address"},
|
||||||
Action: func(c *cli.Context) error {
|
{&eyesFlag, "eyes", "local", "MerkleEyes address, or 'local' for embedded"},
|
||||||
return cmdStart(c)
|
{&dirFlag, "dir", ".", "Root directory"},
|
||||||
},
|
{&withoutTendermintFlag, "without-tendermint", false, "Run Tendermint in-process with the App"},
|
||||||
Flags: []cli.Flag{
|
}
|
||||||
AddrFlag,
|
RegisterFlags(StartCmd, flags)
|
||||||
EyesFlag,
|
|
||||||
WithoutTendermintFlag,
|
|
||||||
ChainIDFlag,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type plugin struct {
|
func startCmd(cmd *cobra.Command, args []string) error {
|
||||||
name string
|
|
||||||
newPlugin func() types.Plugin
|
|
||||||
}
|
|
||||||
|
|
||||||
var plugins = []plugin{}
|
|
||||||
|
|
||||||
// RegisterStartPlugin is used to enable a plugin
|
|
||||||
func RegisterStartPlugin(name string, newPlugin func() types.Plugin) {
|
|
||||||
plugins = append(plugins, plugin{name: name, newPlugin: newPlugin})
|
|
||||||
}
|
|
||||||
|
|
||||||
func cmdStart(c *cli.Context) error {
|
|
||||||
basecoinDir := BasecoinRoot("")
|
basecoinDir := BasecoinRoot("")
|
||||||
|
|
||||||
// Connect to MerkleEyes
|
// Connect to MerkleEyes
|
||||||
var eyesCli *eyes.Client
|
var eyesCli *eyes.Client
|
||||||
if c.String("eyes") == "local" {
|
if eyesFlag == "local" {
|
||||||
eyesCli = eyes.NewLocalClient(path.Join(basecoinDir, "data", "merkleeyes.db"), EyesCacheSize)
|
eyesCli = eyes.NewLocalClient(path.Join(basecoinDir, "data", "merkleeyes.db"), EyesCacheSize)
|
||||||
} else {
|
} else {
|
||||||
var err error
|
var err error
|
||||||
eyesCli, err = eyes.NewClient(c.String("eyes"))
|
eyesCli, err = eyes.NewClient(eyesFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("connect to MerkleEyes: " + err.Error())
|
return errors.Errorf("Error connecting to MerkleEyes: %v\n", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +82,7 @@ func cmdStart(c *cli.Context) error {
|
||||||
if _, err := os.Stat(genesisFile); err == nil {
|
if _, err := os.Stat(genesisFile); err == nil {
|
||||||
err := basecoinApp.LoadGenesis(genesisFile)
|
err := basecoinApp.LoadGenesis(genesisFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New(cmn.Fmt("%+v", err))
|
return errors.Errorf("Error in LoadGenesis: %v\n", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("No genesis file at %s, skipping...\n", genesisFile)
|
fmt.Printf("No genesis file at %s, skipping...\n", genesisFile)
|
||||||
|
@ -92,40 +90,38 @@ func cmdStart(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
chainID := basecoinApp.GetState().GetChainID()
|
chainID := basecoinApp.GetState().GetChainID()
|
||||||
if c.Bool("without-tendermint") {
|
if withoutTendermintFlag {
|
||||||
log.Notice("Starting Basecoin without Tendermint", "chain_id", chainID)
|
log.Notice("Starting Basecoin without Tendermint", "chain_id", chainID)
|
||||||
// run just the abci app/server
|
// run just the abci app/server
|
||||||
return startBasecoinABCI(c, basecoinApp)
|
return startBasecoinABCI(basecoinApp)
|
||||||
} else {
|
} else {
|
||||||
log.Notice("Starting Basecoin with Tendermint", "chain_id", chainID)
|
log.Notice("Starting Basecoin with Tendermint", "chain_id", chainID)
|
||||||
// start the app with tendermint in-process
|
// start the app with tendermint in-process
|
||||||
return startTendermint(basecoinDir, basecoinApp)
|
return startTendermint(basecoinDir, basecoinApp)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func startBasecoinABCI(c *cli.Context, basecoinApp *app.Basecoin) error {
|
func startBasecoinABCI(basecoinApp *app.Basecoin) error {
|
||||||
|
|
||||||
// Start the ABCI listener
|
// Start the ABCI listener
|
||||||
svr, err := server.NewServer(c.String("address"), "socket", basecoinApp)
|
svr, err := server.NewServer(addrFlag, "socket", basecoinApp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("create listener: " + err.Error())
|
return errors.Errorf("Error creating listener: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait forever
|
// Wait forever
|
||||||
cmn.TrapSignal(func() {
|
cmn.TrapSignal(func() {
|
||||||
// Cleanup
|
// Cleanup
|
||||||
svr.Stop()
|
svr.Stop()
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func startTendermint(dir string, basecoinApp *app.Basecoin) error {
|
func startTendermint(dir string, basecoinApp *app.Basecoin) error {
|
||||||
|
|
||||||
// Get configuration
|
// Get configuration
|
||||||
tmConfig := tmcfg.GetConfig(dir)
|
tmConfig := tmcfg.GetConfig(dir)
|
||||||
|
|
||||||
// logger.SetLogLevel("notice") //config.GetString("log_level"))
|
// logger.SetLogLevel("notice") //config.GetString("log_level"))
|
||||||
|
|
||||||
// parseFlags(config, args[1:]) // Command line overrides
|
// parseFlags(config, args[1:]) // Command line overrides
|
||||||
|
|
||||||
// Create & start tendermint node
|
// Create & start tendermint node
|
||||||
|
@ -143,6 +139,5 @@ func startTendermint(dir string, basecoinApp *app.Basecoin) error {
|
||||||
// Cleanup
|
// Cleanup
|
||||||
n.Stop()
|
n.Stop()
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,101 +2,111 @@ package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
crypto "github.com/tendermint/go-crypto"
|
crypto "github.com/tendermint/go-crypto"
|
||||||
|
|
||||||
cmn "github.com/tendermint/go-common"
|
|
||||||
client "github.com/tendermint/go-rpc/client"
|
client "github.com/tendermint/go-rpc/client"
|
||||||
wire "github.com/tendermint/go-wire"
|
wire "github.com/tendermint/go-wire"
|
||||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var TxFlags = []cli.Flag{
|
//commands
|
||||||
NodeFlag,
|
|
||||||
ChainIDFlag,
|
|
||||||
|
|
||||||
FromFlag,
|
|
||||||
|
|
||||||
AmountFlag,
|
|
||||||
GasFlag,
|
|
||||||
FeeFlag,
|
|
||||||
SeqFlag,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TxCmd = cli.Command{
|
TxCmd = &cobra.Command{
|
||||||
Name: "tx",
|
Use: "tx",
|
||||||
Usage: "Create, sign, and broadcast a transaction",
|
Short: "Create, sign, and broadcast a transaction",
|
||||||
ArgsUsage: "",
|
|
||||||
Subcommands: []cli.Command{
|
|
||||||
SendTxCmd,
|
|
||||||
AppTxCmd,
|
|
||||||
IbcTxCmd,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SendTxCmd = cli.Command{
|
SendTxCmd = &cobra.Command{
|
||||||
Name: "send",
|
Use: "send",
|
||||||
Usage: "a SendTx transaction, for sending tokens around",
|
Short: "A SendTx transaction, for sending tokens around",
|
||||||
ArgsUsage: "",
|
RunE: sendTxCmd,
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
return cmdSendTx(c)
|
|
||||||
},
|
|
||||||
Flags: append(TxFlags, ToFlag),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AppTxCmd = cli.Command{
|
AppTxCmd = &cobra.Command{
|
||||||
Name: "app",
|
Use: "app",
|
||||||
Usage: "an AppTx transaction, for sending raw data to plugins",
|
Short: "An AppTx transaction, for sending raw data to plugins",
|
||||||
ArgsUsage: "",
|
RunE: appTxCmd,
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
return cmdAppTx(c)
|
|
||||||
},
|
|
||||||
Flags: append(TxFlags, NameFlag, DataFlag),
|
|
||||||
// Subcommands are dynamically registered with plugins as needed
|
|
||||||
Subcommands: []cli.Command{},
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Register a subcommand of TxCmd to craft transactions for plugins
|
var (
|
||||||
func RegisterTxSubcommand(cmd cli.Command) {
|
//persistent flags
|
||||||
TxCmd.Subcommands = append(TxCmd.Subcommands, cmd)
|
txNodeFlag string
|
||||||
|
amountFlag string
|
||||||
|
fromFlag string
|
||||||
|
seqFlag int
|
||||||
|
gasFlag int
|
||||||
|
feeFlag string
|
||||||
|
chainIDFlag string
|
||||||
|
|
||||||
|
//non-persistent flags
|
||||||
|
toFlag string
|
||||||
|
dataFlag string
|
||||||
|
nameFlag string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
// register flags
|
||||||
|
cmdTxFlags := []Flag2Register{
|
||||||
|
{&txNodeFlag, "node", "tcp://localhost:46657", "Tendermint RPC address"},
|
||||||
|
{&chainIDFlag, "chain_id", "test_chain_id", "ID of the chain for replay protection"},
|
||||||
|
{&fromFlag, "from", "key.json", "Path to a private key to sign the transaction"},
|
||||||
|
{&amountFlag, "amount", "", "Coins to send in transaction of the format <amt><coin>,<amt2><coin2>,... (eg: 1btc,2gold,5silver)"},
|
||||||
|
{&gasFlag, "gas", 0, "The amount of gas for the transaction"},
|
||||||
|
{&feeFlag, "fee", "", "Coins for the transaction fee of the format <amt><coin>"},
|
||||||
|
{&seqFlag, "sequence", -1, "Sequence number for the account (-1 to autocalculate)"},
|
||||||
|
}
|
||||||
|
|
||||||
|
sendTxFlags := []Flag2Register{
|
||||||
|
{&toFlag, "to", "", "Destination address for the transaction"},
|
||||||
|
}
|
||||||
|
|
||||||
|
appTxFlags := []Flag2Register{
|
||||||
|
{&nameFlag, "name", "", "Plugin to send the transaction to"},
|
||||||
|
{&dataFlag, "data", "", "Data to send with the transaction"},
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterPersistentFlags(TxCmd, cmdTxFlags)
|
||||||
|
RegisterFlags(SendTxCmd, sendTxFlags)
|
||||||
|
RegisterFlags(AppTxCmd, appTxFlags)
|
||||||
|
|
||||||
|
//register commands
|
||||||
|
TxCmd.AddCommand(SendTxCmd, AppTxCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdSendTx(c *cli.Context) error {
|
func sendTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
toHex := c.String("to")
|
|
||||||
fromFile := c.String("from")
|
|
||||||
amount := c.String("amount")
|
|
||||||
gas := int64(c.Int("gas"))
|
|
||||||
fee := c.String("fee")
|
|
||||||
chainID := c.String("chain_id")
|
|
||||||
|
|
||||||
// convert destination address to bytes
|
// convert destination address to bytes
|
||||||
to, err := hex.DecodeString(StripHex(toHex))
|
to, err := hex.DecodeString(StripHex(toFlag))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("To address is invalid hex: " + err.Error())
|
return errors.Errorf("To address is invalid hex: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// load the priv key
|
// load the priv key
|
||||||
privKey := LoadKey(fromFile)
|
privKey, err := LoadKey(fromFlag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// get the sequence number for the tx
|
// get the sequence number for the tx
|
||||||
sequence, err := getSeq(c, privKey.Address[:])
|
sequence, err := getSeq(privKey.Address[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
//parse the fee and amounts into coin types
|
//parse the fee and amounts into coin types
|
||||||
feeCoin, err := types.ParseCoin(fee)
|
feeCoin, err := types.ParseCoin(feeFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
amountCoins, err := types.ParseCoins(amount)
|
amountCoins, err := types.ParseCoins(amountFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -105,21 +115,21 @@ func cmdSendTx(c *cli.Context) error {
|
||||||
input := types.NewTxInput(privKey.PubKey, amountCoins, sequence)
|
input := types.NewTxInput(privKey.PubKey, amountCoins, sequence)
|
||||||
output := newOutput(to, amountCoins)
|
output := newOutput(to, amountCoins)
|
||||||
tx := &types.SendTx{
|
tx := &types.SendTx{
|
||||||
Gas: gas,
|
Gas: int64(gasFlag),
|
||||||
Fee: feeCoin,
|
Fee: feeCoin,
|
||||||
Inputs: []types.TxInput{input},
|
Inputs: []types.TxInput{input},
|
||||||
Outputs: []types.TxOutput{output},
|
Outputs: []types.TxOutput{output},
|
||||||
}
|
}
|
||||||
|
|
||||||
// sign that puppy
|
// sign that puppy
|
||||||
signBytes := tx.SignBytes(chainID)
|
signBytes := tx.SignBytes(chainIDFlag)
|
||||||
tx.Inputs[0].Signature = crypto.SignatureS{privKey.Sign(signBytes)}
|
tx.Inputs[0].Signature = crypto.SignatureS{privKey.Sign(signBytes)}
|
||||||
|
|
||||||
fmt.Println("Signed SendTx:")
|
fmt.Println("Signed SendTx:")
|
||||||
fmt.Println(string(wire.JSONBytes(tx)))
|
fmt.Println(string(wire.JSONBytes(tx)))
|
||||||
|
|
||||||
// broadcast the transaction to tendermint
|
// broadcast the transaction to tendermint
|
||||||
data, log, err := broadcastTx(c, tx)
|
data, log, err := broadcastTx(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -127,100 +137,103 @@ func cmdSendTx(c *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdAppTx(c *cli.Context) error {
|
func appTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
// convert data to bytes
|
// convert data to bytes
|
||||||
dataString := c.String("data")
|
data := []byte(dataFlag)
|
||||||
data := []byte(dataString)
|
if isHex(dataFlag) {
|
||||||
if isHex(dataString) {
|
data, _ = hex.DecodeString(dataFlag)
|
||||||
data, _ = hex.DecodeString(dataString)
|
|
||||||
}
|
}
|
||||||
name := c.String("name")
|
name := nameFlag
|
||||||
return AppTx(c, name, data)
|
return AppTx(name, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AppTx(c *cli.Context, name string, data []byte) error {
|
func AppTx(name string, data []byte) error {
|
||||||
fromFile := c.String("from")
|
|
||||||
amount := c.String("amount")
|
|
||||||
fee := c.String("fee")
|
|
||||||
gas := int64(c.Int("gas"))
|
|
||||||
chainID := c.String("chain_id")
|
|
||||||
|
|
||||||
privKey := LoadKey(fromFile)
|
privKey, err := LoadKey(fromFlag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
sequence, err := getSeq(c, privKey.Address[:])
|
sequence, err := getSeq(privKey.Address[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
//parse the fee and amounts into coin types
|
//parse the fee and amounts into coin types
|
||||||
feeCoin, err := types.ParseCoin(fee)
|
feeCoin, err := types.ParseCoin(feeFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
amountCoins, err := types.ParseCoins(amount)
|
|
||||||
|
amountCoins, err := types.ParseCoins(amountFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
input := types.NewTxInput(privKey.PubKey, amountCoins, sequence)
|
input := types.NewTxInput(privKey.PubKey, amountCoins, sequence)
|
||||||
tx := &types.AppTx{
|
tx := &types.AppTx{
|
||||||
Gas: gas,
|
Gas: int64(gasFlag),
|
||||||
Fee: feeCoin,
|
Fee: feeCoin,
|
||||||
Name: name,
|
Name: name,
|
||||||
Input: input,
|
Input: input,
|
||||||
Data: data,
|
Data: data,
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.Input.Signature = crypto.SignatureS{privKey.Sign(tx.SignBytes(chainID))}
|
tx.Input.Signature = crypto.SignatureS{privKey.Sign(tx.SignBytes(chainIDFlag))}
|
||||||
|
|
||||||
fmt.Println("Signed AppTx:")
|
fmt.Println("Signed AppTx:")
|
||||||
fmt.Println(string(wire.JSONBytes(tx)))
|
fmt.Println(string(wire.JSONBytes(tx)))
|
||||||
|
|
||||||
data, log, err := broadcastTx(c, tx)
|
data, log, err := broadcastTx(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Printf("Response: %X ; %s\n", data, log)
|
fmt.Printf("Response: %X ; %s\n", data, log)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// broadcast the transaction to tendermint
|
// broadcast the transaction to tendermint
|
||||||
func broadcastTx(c *cli.Context, tx types.Tx) ([]byte, string, error) {
|
func broadcastTx(tx types.Tx) ([]byte, string, error) {
|
||||||
|
|
||||||
tmResult := new(ctypes.TMResult)
|
tmResult := new(ctypes.TMResult)
|
||||||
tmAddr := c.String("node")
|
uriClient := client.NewURIClient(txNodeFlag)
|
||||||
uriClient := client.NewURIClient(tmAddr)
|
|
||||||
|
|
||||||
// Don't you hate having to do this?
|
// Don't you hate having to do this?
|
||||||
// How many times have I lost an hour over this trick?!
|
// How many times have I lost an hour over this trick?!
|
||||||
txBytes := []byte(wire.BinaryBytes(struct {
|
txBytes := []byte(wire.BinaryBytes(struct {
|
||||||
types.Tx `json:"unwrap"`
|
types.Tx `json:"unwrap"`
|
||||||
}{tx}))
|
}{tx}))
|
||||||
|
|
||||||
_, err := uriClient.Call("broadcast_tx_commit", map[string]interface{}{"tx": txBytes}, tmResult)
|
_, err := uriClient.Call("broadcast_tx_commit", map[string]interface{}{"tx": txBytes}, tmResult)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", errors.New(cmn.Fmt("Error on broadcast tx: %v", err))
|
return nil, "", errors.Errorf("Error on broadcast tx: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
res := (*tmResult).(*ctypes.ResultBroadcastTxCommit)
|
res := (*tmResult).(*ctypes.ResultBroadcastTxCommit)
|
||||||
|
|
||||||
// if it fails check, we don't even get a delivertx back!
|
// if it fails check, we don't even get a delivertx back!
|
||||||
if !res.CheckTx.Code.IsOK() {
|
if !res.CheckTx.Code.IsOK() {
|
||||||
r := res.CheckTx
|
r := res.CheckTx
|
||||||
return nil, "", errors.New(cmn.Fmt("BroadcastTxCommit got non-zero exit code: %v. %X; %s", r.Code, r.Data, r.Log))
|
return nil, "", errors.Errorf("BroadcastTxCommit got non-zero exit code: %v. %X; %s", r.Code, r.Data, r.Log)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !res.DeliverTx.Code.IsOK() {
|
if !res.DeliverTx.Code.IsOK() {
|
||||||
r := res.DeliverTx
|
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 nil, "", errors.Errorf("BroadcastTxCommit got non-zero exit code: %v. %X; %s", r.Code, r.Data, r.Log)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.DeliverTx.Data, res.DeliverTx.Log, nil
|
return res.DeliverTx.Data, res.DeliverTx.Log, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the sequence flag is set, return it;
|
// if the sequence flag is set, return it;
|
||||||
// else, fetch the account by querying the app and return the sequence number
|
// else, fetch the account by querying the app and return the sequence number
|
||||||
func getSeq(c *cli.Context, address []byte) (int, error) {
|
func getSeq(address []byte) (int, error) {
|
||||||
if c.IsSet("sequence") {
|
|
||||||
return c.Int("sequence"), nil
|
if seqFlag >= 0 {
|
||||||
|
return seqFlag, nil
|
||||||
}
|
}
|
||||||
tmAddr := c.String("node")
|
|
||||||
acc, err := getAcc(tmAddr, address)
|
acc, err := getAcc(txNodeFlag, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -232,5 +245,4 @@ func newOutput(to []byte, amount types.Coins) types.TxOutput {
|
||||||
Address: to,
|
Address: to,
|
||||||
Coins: amount,
|
Coins: amount,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,13 @@ package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/state"
|
"github.com/tendermint/basecoin/state"
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
|
@ -18,16 +21,96 @@ import (
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//This variable can be overwritten by plugin applications
|
||||||
|
// if they require a different working directory
|
||||||
|
var DefaultHome = ".basecoin"
|
||||||
|
|
||||||
func BasecoinRoot(rootDir string) string {
|
func BasecoinRoot(rootDir string) string {
|
||||||
if rootDir == "" {
|
if rootDir == "" {
|
||||||
rootDir = os.Getenv("BCHOME")
|
rootDir = os.Getenv("BCHOME")
|
||||||
}
|
}
|
||||||
if rootDir == "" {
|
if rootDir == "" {
|
||||||
rootDir = os.Getenv("HOME") + "/.basecoin"
|
rootDir = path.Join(os.Getenv("HOME"), DefaultHome)
|
||||||
}
|
}
|
||||||
return rootDir
|
return rootDir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Add debugging flag and execute the root command
|
||||||
|
func ExecuteWithDebug(RootCmd *cobra.Command) {
|
||||||
|
|
||||||
|
var debug bool
|
||||||
|
RootCmd.SilenceUsage = true
|
||||||
|
RootCmd.PersistentFlags().BoolVar(&debug, "debug", false, "enables stack trace error messages")
|
||||||
|
|
||||||
|
//note that Execute() prints the error if encountered, so no need to reprint the error,
|
||||||
|
// only if we want the full stack trace
|
||||||
|
if err := RootCmd.Execute(); err != nil && debug {
|
||||||
|
cmn.Exit(fmt.Sprintf("%+v\n", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Quickly registering flags can be quickly achieved through using the utility functions
|
||||||
|
//RegisterFlags, and RegisterPersistentFlags. Ex:
|
||||||
|
// flags := []Flag2Register{
|
||||||
|
// {&myStringFlag, "mystringflag", "foobar", "description of what this flag does"},
|
||||||
|
// {&myBoolFlag, "myboolflag", false, "description of what this flag does"},
|
||||||
|
// {&myInt64Flag, "myintflag", 333, "description of what this flag does"},
|
||||||
|
// }
|
||||||
|
// RegisterFlags(MyCobraCmd, flags)
|
||||||
|
type Flag2Register struct {
|
||||||
|
Pointer interface{}
|
||||||
|
Use string
|
||||||
|
Value interface{}
|
||||||
|
Desc string
|
||||||
|
}
|
||||||
|
|
||||||
|
//register flag utils
|
||||||
|
func RegisterFlags(c *cobra.Command, flags []Flag2Register) {
|
||||||
|
registerFlags(c, flags, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterPersistentFlags(c *cobra.Command, flags []Flag2Register) {
|
||||||
|
registerFlags(c, flags, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerFlags(c *cobra.Command, flags []Flag2Register, persistent bool) {
|
||||||
|
|
||||||
|
var flagset *pflag.FlagSet
|
||||||
|
if persistent {
|
||||||
|
flagset = c.PersistentFlags()
|
||||||
|
} else {
|
||||||
|
flagset = c.Flags()
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range flags {
|
||||||
|
|
||||||
|
ok := false
|
||||||
|
|
||||||
|
switch f.Value.(type) {
|
||||||
|
case string:
|
||||||
|
if _, ok = f.Pointer.(*string); ok {
|
||||||
|
flagset.StringVar(f.Pointer.(*string), f.Use, f.Value.(string), f.Desc)
|
||||||
|
}
|
||||||
|
case int:
|
||||||
|
if _, ok = f.Pointer.(*int); ok {
|
||||||
|
flagset.IntVar(f.Pointer.(*int), f.Use, f.Value.(int), f.Desc)
|
||||||
|
}
|
||||||
|
case uint64:
|
||||||
|
if _, ok = f.Pointer.(*uint64); ok {
|
||||||
|
flagset.Uint64Var(f.Pointer.(*uint64), f.Use, f.Value.(uint64), f.Desc)
|
||||||
|
}
|
||||||
|
case bool:
|
||||||
|
if _, ok = f.Pointer.(*bool); ok {
|
||||||
|
flagset.BoolVar(f.Pointer.(*bool), f.Use, f.Value.(bool), f.Desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
panic("improper use of RegisterFlags")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Returns true for non-empty hex-string prefixed with "0x"
|
// Returns true for non-empty hex-string prefixed with "0x"
|
||||||
func isHex(s string) bool {
|
func isHex(s string) bool {
|
||||||
if len(s) > 2 && s[:2] == "0x" {
|
if len(s) > 2 && s[:2] == "0x" {
|
||||||
|
@ -58,11 +141,11 @@ func Query(tmAddr string, key []byte) (*abci.ResponseQuery, error) {
|
||||||
}
|
}
|
||||||
_, err := uriClient.Call("abci_query", params, tmResult)
|
_, err := uriClient.Call("abci_query", params, tmResult)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New(cmn.Fmt("Error calling /abci_query: %v", err))
|
return nil, errors.Errorf("Error calling /abci_query: %v", err)
|
||||||
}
|
}
|
||||||
res := (*tmResult).(*ctypes.ResultABCIQuery)
|
res := (*tmResult).(*ctypes.ResultABCIQuery)
|
||||||
if !res.Response.Code.IsOK() {
|
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 nil, errors.Errorf("Query got non-zero exit code: %v. %s", res.Response.Code, res.Response.Log)
|
||||||
}
|
}
|
||||||
return &res.Response, nil
|
return &res.Response, nil
|
||||||
}
|
}
|
||||||
|
@ -79,28 +162,27 @@ func getAcc(tmAddr string, address []byte) (*types.Account, error) {
|
||||||
accountBytes := response.Value
|
accountBytes := response.Value
|
||||||
|
|
||||||
if len(accountBytes) == 0 {
|
if len(accountBytes) == 0 {
|
||||||
return nil, errors.New(cmn.Fmt("Account bytes are empty for address: %X ", address))
|
return nil, fmt.Errorf("Account bytes are empty for address: %X ", address) //never stack trace
|
||||||
}
|
}
|
||||||
|
|
||||||
var acc *types.Account
|
var acc *types.Account
|
||||||
err = wire.ReadBinaryBytes(accountBytes, &acc)
|
err = wire.ReadBinaryBytes(accountBytes, &acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New(cmn.Fmt("Error reading account %X error: %v",
|
return nil, errors.Errorf("Error reading account %X error: %v",
|
||||||
accountBytes, err.Error()))
|
accountBytes, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return acc, nil
|
return acc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHeaderAndCommit(c *cli.Context, height int) (*tmtypes.Header, *tmtypes.Commit, error) {
|
func getHeaderAndCommit(tmAddr string, height int) (*tmtypes.Header, *tmtypes.Commit, error) {
|
||||||
tmResult := new(ctypes.TMResult)
|
tmResult := new(ctypes.TMResult)
|
||||||
tmAddr := c.String("node")
|
|
||||||
uriClient := client.NewURIClient(tmAddr)
|
uriClient := client.NewURIClient(tmAddr)
|
||||||
|
|
||||||
method := "commit"
|
method := "commit"
|
||||||
_, err := uriClient.Call(method, map[string]interface{}{"height": height}, tmResult)
|
_, err := uriClient.Call(method, map[string]interface{}{"height": height}, tmResult)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errors.New(cmn.Fmt("Error on %s: %v", method, err))
|
return nil, nil, errors.Errorf("Error on %s: %v", method, err)
|
||||||
}
|
}
|
||||||
resCommit := (*tmResult).(*ctypes.ResultCommit)
|
resCommit := (*tmResult).(*ctypes.ResultCommit)
|
||||||
header := resCommit.Header
|
header := resCommit.Header
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/tendermint/basecoin/version"
|
||||||
|
)
|
||||||
|
|
||||||
|
var VersionCmd = &cobra.Command{
|
||||||
|
Use: "version",
|
||||||
|
Short: "Show version info",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println(version.Version)
|
||||||
|
},
|
||||||
|
}
|
|
@ -3,46 +3,46 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
wire "github.com/tendermint/go-wire"
|
wire "github.com/tendermint/go-wire"
|
||||||
"github.com/urfave/cli"
|
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/cmd/commands"
|
"github.com/tendermint/basecoin/cmd/commands"
|
||||||
"github.com/tendermint/basecoin/plugins/counter"
|
"github.com/tendermint/basecoin/plugins/counter"
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//commands
|
||||||
|
var CounterTxCmd = &cobra.Command{
|
||||||
|
Use: "counter",
|
||||||
|
Short: "Create, sign, and broadcast a transaction to the counter plugin",
|
||||||
|
RunE: counterTxCmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
//flags
|
||||||
|
var (
|
||||||
|
validFlag bool
|
||||||
|
countFeeFlag string
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
|
CounterTxCmd.Flags().BoolVar(&validFlag, "valid", false, "Set valid field in CounterTx")
|
||||||
|
CounterTxCmd.Flags().StringVar(&countFeeFlag, "countfee", "", "Coins for the counter fee of the format <amt><coin>")
|
||||||
|
|
||||||
commands.RegisterTxSubcommand(CounterTxCmd)
|
commands.RegisterTxSubcommand(CounterTxCmd)
|
||||||
commands.RegisterStartPlugin("counter", func() types.Plugin { return counter.New() })
|
commands.RegisterStartPlugin("counter", func() types.Plugin { return counter.New() })
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
func counterTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
ValidFlag = cli.BoolFlag{
|
|
||||||
Name: "valid",
|
|
||||||
Usage: "Set valid field in CounterTx",
|
|
||||||
}
|
|
||||||
|
|
||||||
CounterTxCmd = cli.Command{
|
countFee, err := types.ParseCoins(countFeeFlag)
|
||||||
Name: "counter",
|
if err != nil {
|
||||||
Usage: "Create, sign, and broadcast a transaction to the counter plugin",
|
return err
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
return cmdCounterTx(c)
|
|
||||||
},
|
|
||||||
Flags: append(commands.TxFlags, ValidFlag),
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
func cmdCounterTx(c *cli.Context) error {
|
|
||||||
valid := c.Bool("valid")
|
|
||||||
|
|
||||||
counterTx := counter.CounterTx{
|
counterTx := counter.CounterTx{
|
||||||
Valid: valid,
|
Valid: validFlag,
|
||||||
Fee: types.Coins{
|
Fee: countFee,
|
||||||
{
|
|
||||||
Denom: c.String("coin"),
|
|
||||||
Amount: int64(c.Int("fee")),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("CounterTx:", string(wire.JSONBytes(counterTx)))
|
fmt.Println("CounterTx:", string(wire.JSONBytes(counterTx)))
|
||||||
|
@ -50,5 +50,5 @@ func cmdCounterTx(c *cli.Context) error {
|
||||||
data := wire.BinaryBytes(counterTx)
|
data := wire.BinaryBytes(counterTx)
|
||||||
name := "counter"
|
name := "counter"
|
||||||
|
|
||||||
return commands.AppTx(c, name, data)
|
return commands.AppTx(name, data)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,28 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/cmd/commands"
|
"github.com/tendermint/basecoin/cmd/commands"
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
app := cli.NewApp()
|
|
||||||
app.Name = "counter"
|
var RootCmd = &cobra.Command{
|
||||||
app.Usage = "counter [command] [args...]"
|
Use: "counter",
|
||||||
app.Version = "0.1.0"
|
Short: "demo plugin for basecoin",
|
||||||
app.Commands = []cli.Command{
|
}
|
||||||
|
|
||||||
|
RootCmd.AddCommand(
|
||||||
commands.StartCmd,
|
commands.StartCmd,
|
||||||
commands.TxCmd,
|
commands.TxCmd,
|
||||||
commands.KeyCmd,
|
|
||||||
commands.QueryCmd,
|
commands.QueryCmd,
|
||||||
|
commands.KeyCmd,
|
||||||
|
commands.VerifyCmd,
|
||||||
|
commands.BlockCmd,
|
||||||
commands.AccountCmd,
|
commands.AccountCmd,
|
||||||
}
|
commands.QuickVersionCmd("0.1.0"),
|
||||||
app.Run(os.Args)
|
)
|
||||||
|
|
||||||
|
commands.ExecuteWithDebug(RootCmd)
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,14 +103,14 @@ sleep 3
|
||||||
echo "... registering chain1 on chain2"
|
echo "... registering chain1 on chain2"
|
||||||
echo ""
|
echo ""
|
||||||
# register chain1 on chain2
|
# register chain1 on chain2
|
||||||
basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 register --chain_id $CHAIN_ID1 --genesis ./data/chain1/genesis.json
|
basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 register --ibc_chain_id $CHAIN_ID1 --genesis ./data/chain1/genesis.json
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "... creating egress packet on chain1"
|
echo "... creating egress packet on chain1"
|
||||||
echo ""
|
echo ""
|
||||||
# create a packet on chain1 destined for chain2
|
# create a packet on chain1 destined for chain2
|
||||||
PAYLOAD="DEADBEEF" #TODO
|
PAYLOAD="DEADBEEF" #TODO
|
||||||
basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS1 packet create --from $CHAIN_ID1 --to $CHAIN_ID2 --type coin --payload $PAYLOAD --sequence 1
|
basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS1 packet create --ibc_from $CHAIN_ID1 --to $CHAIN_ID2 --type coin --payload $PAYLOAD --ibc_sequence 1
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "... querying for packet data"
|
echo "... querying for packet data"
|
||||||
|
@ -162,7 +162,7 @@ echo ""
|
||||||
echo "... posting packet from chain1 on chain2"
|
echo "... posting packet from chain1 on chain2"
|
||||||
echo ""
|
echo ""
|
||||||
# post the packet from chain1 to chain2
|
# post the packet from chain1 to chain2
|
||||||
basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 packet post --from $CHAIN_ID1 --height $HEIGHT --packet 0x$PACKET --proof 0x$PROOF
|
basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 packet post --ibc_from $CHAIN_ID1 --height $HEIGHT --packet 0x$PACKET --proof 0x$PROOF
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "... checking if the packet is present on chain2"
|
echo "... checking if the packet is present on chain2"
|
||||||
|
|
|
@ -6,10 +6,11 @@ Here, we will demonstrate how to extend the blockchain and CLI to support a simp
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Creating a new plugin and CLI to support it requires a little bit of boilerplate, but not much.
|
Creating a new plugin and CLI to support it requires a little bit of
|
||||||
For convenience, we've implemented an extremely simple example plugin that can be easily modified.
|
boilerplate, but not much. For convenience, we've implemented an extremely
|
||||||
The example is under `docs/guide/src/example-plugin`.
|
simple example plugin that can be easily modified. The example is under
|
||||||
To build your own plugin, copy this folder to a new location and start modifying it there.
|
`docs/guide/src/example-plugin`. To build your own plugin, copy this folder to
|
||||||
|
a new location and start modifying it there.
|
||||||
|
|
||||||
Let's take a look at the files in `docs/guide/src/example-plugin`:
|
Let's take a look at the files in `docs/guide/src/example-plugin`:
|
||||||
|
|
||||||
|
@ -25,94 +26,104 @@ The `main.go` is very simple and does not need to be changed:
|
||||||
|
|
||||||
```golang
|
```golang
|
||||||
func main() {
|
func main() {
|
||||||
app := cli.NewApp()
|
//Initialize example-plugin root command
|
||||||
app.Name = "example-plugin"
|
var RootCmd = &cobra.Command{
|
||||||
app.Usage = "example-plugin [command] [args...]"
|
Use: "example-plugin",
|
||||||
app.Version = "0.1.0"
|
Short: "example-plugin usage description",
|
||||||
app.Commands = []cli.Command{
|
}
|
||||||
|
|
||||||
|
//Add the default basecoin commands to the root command
|
||||||
|
RootCmd.AddCommand(
|
||||||
|
commands.InitCmd,
|
||||||
commands.StartCmd,
|
commands.StartCmd,
|
||||||
commands.TxCmd,
|
commands.TxCmd,
|
||||||
commands.KeyCmd,
|
|
||||||
commands.QueryCmd,
|
commands.QueryCmd,
|
||||||
|
commands.KeyCmd,
|
||||||
|
commands.VerifyCmd,
|
||||||
|
commands.BlockCmd,
|
||||||
commands.AccountCmd,
|
commands.AccountCmd,
|
||||||
}
|
commands.UnsafeResetAllCmd,
|
||||||
app.Run(os.Args)
|
)
|
||||||
|
|
||||||
|
//Run the root command
|
||||||
|
commands.ExecuteWithDebug(RootCmd)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
It creates the CLI, exactly like the `basecoin` one.
|
It creates the CLI, exactly like the `basecoin` one. However, if we want our
|
||||||
However, if we want our plugin to be active,
|
plugin to be active, we need to make sure it is registered with the
|
||||||
we need to make sure it is registered with the application.
|
application. In addition, if we want to send transactions to our plugin, we
|
||||||
In addition, if we want to send transactions to our plugin,
|
need to add a new command to the CLI. This is where the `cmd.go` comes in.
|
||||||
we need to add a new command to the CLI.
|
|
||||||
This is where the `cmd.go` comes in.
|
|
||||||
|
|
||||||
### cmd.go
|
### cmd.go
|
||||||
|
|
||||||
First, we register the plugin:
|
First we define the new CLI command and associated flag variables.
|
||||||
|
|
||||||
|
```golang
|
||||||
|
var (
|
||||||
|
//CLI Flags
|
||||||
|
validFlag bool
|
||||||
|
|
||||||
|
//CLI Plugin Commands
|
||||||
|
ExamplePluginTxCmd = &cobra.Command{
|
||||||
|
Use: "example",
|
||||||
|
Short: "Create, sign, and broadcast a transaction to the example plugin",
|
||||||
|
RunE: examplePluginTxCmd,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
Next within the `init` function we register our plugin's flags and register our
|
||||||
|
custom plugin command with the root command. This creates a new subcommand
|
||||||
|
under `tx` (defined below), and ensures the plugin is activated when we start
|
||||||
|
the app.
|
||||||
|
|
||||||
```golang
|
```golang
|
||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
|
//Set the Plugin Flags
|
||||||
|
ExamplePluginTxCmd.Flags().BoolVar(&validFlag, "valid", false, "Set this to make transaction valid")
|
||||||
|
|
||||||
|
//Register a plugin specific CLI command as a subcommand of the tx command
|
||||||
commands.RegisterTxSubcommand(ExamplePluginTxCmd)
|
commands.RegisterTxSubcommand(ExamplePluginTxCmd)
|
||||||
|
|
||||||
|
//Register the example with basecoin at start
|
||||||
commands.RegisterStartPlugin("example-plugin", func() types.Plugin { return NewExamplePlugin() })
|
commands.RegisterStartPlugin("example-plugin", func() types.Plugin { return NewExamplePlugin() })
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This creates a new subcommand under `tx` (defined below),
|
We now define the actual function which is called by our CLI command.
|
||||||
and ensures the plugin is activated when we start the app.
|
|
||||||
Now we actually define the new command:
|
```golang
|
||||||
|
func examplePluginTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
|
exampleTx := ExamplePluginTx{validFlag}
|
||||||
|
exampleTxBytes := wire.BinaryBytes(exampleTx)
|
||||||
|
return commands.AppTx("example-plugin", exampleTxBytes)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Our function is a simple command with one boolean flag. However, it actually
|
||||||
|
inherits the persistent flags from the Basecoin framework. These persistent
|
||||||
|
flags use pointers to these variables stored in `cmd/commands/tx.go`:
|
||||||
|
|
||||||
```golang
|
```golang
|
||||||
var (
|
var (
|
||||||
ExampleFlag = cli.BoolFlag{
|
//persistent flags
|
||||||
Name: "valid",
|
txNodeFlag string
|
||||||
Usage: "Set this to make the transaction valid",
|
amountFlag string
|
||||||
}
|
fromFlag string
|
||||||
|
seqFlag int
|
||||||
|
gasFlag int
|
||||||
|
feeFlag string
|
||||||
|
chainIDFlag string
|
||||||
|
|
||||||
ExamplePluginTxCmd = cli.Command{
|
//non-persistent flags
|
||||||
Name: "example",
|
toFlag string
|
||||||
Usage: "Create, sign, and broadcast a transaction to the example plugin",
|
dataFlag string
|
||||||
Action: func(c *cli.Context) error {
|
nameFlag string
|
||||||
return cmdExamplePluginTx(c)
|
|
||||||
},
|
|
||||||
Flags: append(commands.TxFlags, ExampleFlag),
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func cmdExamplePluginTx(c *cli.Context) error {
|
|
||||||
exampleFlag := c.Bool("valid")
|
|
||||||
exampleTx := ExamplePluginTx{exampleFlag}
|
|
||||||
return commands.AppTx(c, "example-plugin", wire.BinaryBytes(exampleTx))
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
It's a simple command with one flag, which is just a boolean.
|
|
||||||
However, it actually inherits more flags from the Basecoin framework:
|
|
||||||
|
|
||||||
```golang
|
|
||||||
Flags: append(commands.TxFlags, ExampleFlag),
|
|
||||||
```
|
|
||||||
|
|
||||||
The `commands.TxFlags` is defined in `cmd/commands/tx.go`:
|
|
||||||
|
|
||||||
```golang
|
|
||||||
var TxFlags = []cli.Flag{
|
|
||||||
NodeFlag,
|
|
||||||
ChainIDFlag,
|
|
||||||
|
|
||||||
FromFlag,
|
|
||||||
|
|
||||||
AmountFlag,
|
|
||||||
CoinFlag,
|
|
||||||
GasFlag,
|
|
||||||
FeeFlag,
|
|
||||||
SeqFlag,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
It adds all the default flags for a Basecoin transaction.
|
|
||||||
|
|
||||||
If we now compile and run our program, we can see all the options:
|
If we now compile and run our program, we can see all the options:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -124,48 +135,51 @@ example-plugin tx example --help
|
||||||
The output:
|
The output:
|
||||||
|
|
||||||
```
|
```
|
||||||
NAME:
|
Create, sign, and broadcast a transaction to the example plugin
|
||||||
example-plugin tx example - Create, sign, and broadcast a transaction to the example plugin
|
|
||||||
|
|
||||||
USAGE:
|
Usage:
|
||||||
example-plugin tx example [command options] [arguments...]
|
example-plugin tx example [flags]
|
||||||
|
|
||||||
OPTIONS:
|
Flags:
|
||||||
--node value Tendermint RPC address (default: "tcp://localhost:46657")
|
--valid Set this to make transaction valid
|
||||||
--chain_id value ID of the chain for replay protection (default: "test_chain_id")
|
|
||||||
--from value Path to a private key to sign the transaction (default: "key.json")
|
Global Flags:
|
||||||
--amount value Coins to send in transaction of the format <amt><coin>,<amt2><coin2>,... (eg: 1btc,2gold,5silver)
|
--amount string Coins to send in transaction of the format <amt><coin>,<amt2><coin2>,... (eg: 1btc,2gold,5silver},
|
||||||
--gas value The amount of gas for the transaction (default: 0)
|
--chain_id string ID of the chain for replay protection (default "test_chain_id")
|
||||||
--fee value Coins for the transaction fee of the format <amt><coin>
|
--fee string Coins for the transaction fee of the format <amt><coin>
|
||||||
--sequence value Sequence number for the account (default: 0)
|
--from string Path to a private key to sign the transaction (default "key.json")
|
||||||
--valid Set this to make the transaction valid
|
--gas int The amount of gas for the transaction
|
||||||
|
--node string Tendermint RPC address (default "tcp://localhost:46657")
|
||||||
|
--sequence int Sequence number for the account (-1 to autocalculate}, (default -1)
|
||||||
```
|
```
|
||||||
|
|
||||||
Cool, eh?
|
Cool, eh?
|
||||||
|
|
||||||
Before we move on to `plugin.go`, let's look at the `cmdExamplePluginTx` function in `cmd.go`:
|
Before we move on to `plugin.go`, let's look at the `examplePluginTxCmd`
|
||||||
|
function in `cmd.go`:
|
||||||
|
|
||||||
```golang
|
```golang
|
||||||
func cmdExamplePluginTx(c *cli.Context) error {
|
func examplePluginTxCmd(cmd *cobra.Command, args []string) {
|
||||||
exampleFlag := c.Bool("valid")
|
exampleTx := ExamplePluginTx{validFlag}
|
||||||
exampleTx := ExamplePluginTx{exampleFlag}
|
exampleTxBytes := wire.BinaryBytes(exampleTx)
|
||||||
return commands.AppTx(c, "example-plugin", wire.BinaryBytes(exampleTx))
|
commands.AppTx("example-plugin", exampleTxBytes)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
We read the flag from the CLI library, and then create the example transaction.
|
We read the flag from the CLI library, and then create the example transaction.
|
||||||
Remember that Basecoin itself only knows about two transaction types, `SendTx` and `AppTx`.
|
Remember that Basecoin itself only knows about two transaction types, `SendTx`
|
||||||
All plugin data must be serialized (ie. encoded as a byte-array)
|
and `AppTx`. All plugin data must be serialized (ie. encoded as a byte-array)
|
||||||
and sent as data in an `AppTx`. The `commands.AppTx` function does this for us -
|
and sent as data in an `AppTx`. The `commands.AppTx` function does this for us
|
||||||
it creates an `AppTx` with the corresponding data, signs it, and sends it on to the blockchain.
|
- it creates an `AppTx` with the corresponding data, signs it, and sends it on
|
||||||
|
to the blockchain.
|
||||||
|
|
||||||
### plugin.go
|
### plugin.go
|
||||||
|
|
||||||
Ok, now we're ready to actually look at the implementation of the plugin in `plugin.go`.
|
Ok, now we're ready to actually look at the implementation of the plugin in
|
||||||
Note I'll leave out some of the methods as they don't serve any purpose for this example,
|
`plugin.go`. Note I'll leave out some of the methods as they don't serve any
|
||||||
but are necessary boilerplate.
|
purpose for this example, but are necessary boilerplate. Your plugin may have
|
||||||
Your plugin may have additional requirements that utilize these other methods.
|
additional requirements that utilize these other methods. Here's what's
|
||||||
Here's what's relevant for us:
|
relevant for us:
|
||||||
|
|
||||||
```golang
|
```golang
|
||||||
type ExamplePluginState struct {
|
type ExamplePluginState struct {
|
||||||
|
@ -200,14 +214,16 @@ func (ep *ExamplePlugin) SetOption(store types.KVStore, key string, value string
|
||||||
|
|
||||||
func (ep *ExamplePlugin) RunTx(store types.KVStore, ctx types.CallContext, txBytes []byte) (res abci.Result) {
|
func (ep *ExamplePlugin) RunTx(store types.KVStore, ctx types.CallContext, txBytes []byte) (res abci.Result) {
|
||||||
|
|
||||||
// Decode tx
|
// Decode txBytes using go-wire. Attempt to write the txBytes to the variable
|
||||||
|
// tx, if the txBytes have not been properly encoded from a ExamplePluginTx
|
||||||
|
// struct wire will produce an error.
|
||||||
var tx ExamplePluginTx
|
var tx ExamplePluginTx
|
||||||
err := wire.ReadBinaryBytes(txBytes, &tx)
|
err := wire.ReadBinaryBytes(txBytes, &tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return abci.ErrBaseEncodingError.AppendLog("Error decoding tx: " + err.Error())
|
return abci.ErrBaseEncodingError.AppendLog("Error decoding tx: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate tx
|
// Perform Transaction Validation
|
||||||
if !tx.Valid {
|
if !tx.Valid {
|
||||||
return abci.ErrInternalError.AppendLog("Valid must be true")
|
return abci.ErrInternalError.AppendLog("Valid must be true")
|
||||||
}
|
}
|
||||||
|
@ -215,8 +231,10 @@ func (ep *ExamplePlugin) RunTx(store types.KVStore, ctx types.CallContext, txByt
|
||||||
// Load PluginState
|
// Load PluginState
|
||||||
var pluginState ExamplePluginState
|
var pluginState ExamplePluginState
|
||||||
stateBytes := store.Get(ep.StateKey())
|
stateBytes := store.Get(ep.StateKey())
|
||||||
|
// If the state does not exist, stateBytes will be initialized
|
||||||
|
// as an empty byte array with length of zero
|
||||||
if len(stateBytes) > 0 {
|
if len(stateBytes) > 0 {
|
||||||
err = wire.ReadBinaryBytes(stateBytes, &pluginState)
|
err = wire.ReadBinaryBytes(stateBytes, &pluginState) //decode using go-wire
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return abci.ErrInternalError.AppendLog("Error decoding state: " + err.Error())
|
return abci.ErrInternalError.AppendLog("Error decoding state: " + err.Error())
|
||||||
}
|
}
|
||||||
|
@ -233,12 +251,11 @@ func (ep *ExamplePlugin) RunTx(store types.KVStore, ctx types.CallContext, txByt
|
||||||
```
|
```
|
||||||
|
|
||||||
All we're doing here is defining a state and transaction type for our plugin,
|
All we're doing here is defining a state and transaction type for our plugin,
|
||||||
and then using the `RunTx` method to define how the transaction updates the state.
|
and then using the `RunTx` method to define how the transaction updates the
|
||||||
Let's break down `RunTx` in parts. First, we deserialize the transaction:
|
state. Let's break down `RunTx` in parts. First, we deserialize the
|
||||||
|
transaction:
|
||||||
|
|
||||||
```golang
|
```golang
|
||||||
// Decode tx
|
|
||||||
var tx ExamplePluginTx
|
var tx ExamplePluginTx
|
||||||
err := wire.ReadBinaryBytes(txBytes, &tx)
|
err := wire.ReadBinaryBytes(txBytes, &tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -246,27 +263,26 @@ if err != nil {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The transaction is expected to be serialized according to Tendermint's "wire" format,
|
The transaction is expected to be serialized according to Tendermint's "wire"
|
||||||
as defined in the `github.com/tendermint/go-wire` package.
|
format, as defined in the `github.com/tendermint/go-wire` package. If it's not
|
||||||
If it's not encoded properly, we return an error.
|
encoded properly, we return an error.
|
||||||
|
|
||||||
|
|
||||||
If the transaction deserializes correctly, we can now check if it's valid:
|
If the transaction deserializes correctly, we can now check if it's valid:
|
||||||
|
|
||||||
```golang
|
```golang
|
||||||
// Validate tx
|
|
||||||
if !tx.Valid {
|
if !tx.Valid {
|
||||||
return abci.ErrInternalError.AppendLog("Valid must be true")
|
return abci.ErrInternalError.AppendLog("Valid must be true")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The transaction is valid if the `Valid` field is set, otherwise it's not - simple as that.
|
The transaction is valid if the `Valid` field is set, otherwise it's not -
|
||||||
Finally, we can update the state. In this example, the state simply counts how many valid transactions
|
simple as that. Finally, we can update the state. In this example, the state
|
||||||
we've processed. But the state itself is serialized and kept in some `store`, which is typically a Merkle tree.
|
simply counts how many valid transactions we've processed. But the state itself
|
||||||
So first we have to load the state from the store and deserialize it:
|
is serialized and kept in some `store`, which is typically a Merkle tree. So
|
||||||
|
first we have to load the state from the store and deserialize it:
|
||||||
|
|
||||||
```golang
|
```golang
|
||||||
// Load PluginState
|
|
||||||
var pluginState ExamplePluginState
|
var pluginState ExamplePluginState
|
||||||
stateBytes := store.Get(ep.StateKey())
|
stateBytes := store.Get(ep.StateKey())
|
||||||
if len(stateBytes) > 0 {
|
if len(stateBytes) > 0 {
|
||||||
|
@ -369,5 +385,5 @@ basecoin CLI to activate the plugin on the blockchain and to send transactions t
|
||||||
Hopefully by now you have some ideas for your own plugin, and feel comfortable implementing them.
|
Hopefully by now you have some ideas for your own plugin, and feel comfortable implementing them.
|
||||||
|
|
||||||
In the [next tutorial](more-examples.md), we tour through some other plugin examples,
|
In the [next tutorial](more-examples.md), we tour through some other plugin examples,
|
||||||
adding features for minting new coins, voting, and changing the Tendermint validator set.
|
addin mple-plugin query ExamplePlugin.Statefeatures for minting new coins, voting, and changing the Tendermint validator set.
|
||||||
But first, you may want to learn a bit more about [the design of the plugin system](plugin-design.md)
|
But first, you may want to learn a bit more about [the design of the plugin system](plugin-design.md)
|
||||||
|
|
|
@ -13,7 +13,8 @@ You may also want to see the tutorials on [a simple example plugin](example-plug
|
||||||
and the list of [more advanced plugins](more-examples.md).
|
and the list of [more advanced plugins](more-examples.md).
|
||||||
|
|
||||||
The IBC plugin defines a new set of transactions as subtypes of the `AppTx`.
|
The IBC plugin defines a new set of transactions as subtypes of the `AppTx`.
|
||||||
The plugin's functionality is accessed by setting the `AppTx.Name` field to `"IBC"`, and setting the `Data` field to the serialized IBC transaction type.
|
The plugin's functionality is accessed by setting the `AppTx.Name` field to `"IBC"`,
|
||||||
|
and setting the `Data` field to the serialized IBC transaction type.
|
||||||
|
|
||||||
We'll demonstrate exactly how this works below.
|
We'll demonstrate exactly how this works below.
|
||||||
|
|
||||||
|
@ -33,7 +34,8 @@ contains the votes responsible for committing the previous block, and a field
|
||||||
in the block header called `AppHash`, which refers to the Merkle root hash of
|
in the block header called `AppHash`, which refers to the Merkle root hash of
|
||||||
the application after processing the transactions from the previous block. So,
|
the application after processing the transactions from the previous block. So,
|
||||||
if we want to verify the `AppHash` from height H, we need the signatures from `LastCommit`
|
if we want to verify the `AppHash` from height H, we need the signatures from `LastCommit`
|
||||||
at height H+1. (And remember that this `AppHash` only contains the results from all transactions up to and including block H-1)
|
at height H+1. (And remember that this `AppHash` only contains the results from all
|
||||||
|
transactions up to and including block H-1)
|
||||||
|
|
||||||
Unlike Proof-of-Work, the light-client protocol does not need to download and
|
Unlike Proof-of-Work, the light-client protocol does not need to download and
|
||||||
check all the headers in the blockchain - the client can always jump straight
|
check all the headers in the blockchain - the client can always jump straight
|
||||||
|
|
|
@ -43,8 +43,8 @@ type Plugin interface {
|
||||||
// Other ABCI message handlers
|
// Other ABCI message handlers
|
||||||
SetOption(store KVStore, key string, value string) (log string)
|
SetOption(store KVStore, key string, value string) (log string)
|
||||||
InitChain(store KVStore, vals []*abci.Validator)
|
InitChain(store KVStore, vals []*abci.Validator)
|
||||||
BeginBlock(store KVStore, height uint64)
|
BeginBlock(store KVStore, hash []byte, header *abci.Header)
|
||||||
EndBlock(store KVStore, height uint64) []*abci.Validator
|
EndBlock(store KVStore, height uint64) (res abci.ResponseEndBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
type CallContext struct {
|
type CallContext struct {
|
||||||
|
|
|
@ -1,16 +1,32 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
wire "github.com/tendermint/go-wire"
|
wire "github.com/tendermint/go-wire"
|
||||||
"github.com/urfave/cli"
|
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/cmd/commands"
|
"github.com/tendermint/basecoin/cmd/commands"
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
//CLI Flags
|
||||||
|
validFlag bool
|
||||||
|
|
||||||
|
//CLI Plugin Commands
|
||||||
|
ExamplePluginTxCmd = &cobra.Command{
|
||||||
|
Use: "example",
|
||||||
|
Short: "Create, sign, and broadcast a transaction to the example plugin",
|
||||||
|
RunE: examplePluginTxCmd,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
//Called during CLI initialization
|
//Called during CLI initialization
|
||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
|
//Set the Plugin Flags
|
||||||
|
ExamplePluginTxCmd.Flags().BoolVar(&validFlag, "valid", false, "Set this to make transaction valid")
|
||||||
|
|
||||||
//Register a plugin specific CLI command as a subcommand of the tx command
|
//Register a plugin specific CLI command as a subcommand of the tx command
|
||||||
commands.RegisterTxSubcommand(ExamplePluginTxCmd)
|
commands.RegisterTxSubcommand(ExamplePluginTxCmd)
|
||||||
|
|
||||||
|
@ -18,32 +34,12 @@ func init() {
|
||||||
commands.RegisterStartPlugin("example-plugin", func() types.Plugin { return NewExamplePlugin() })
|
commands.RegisterStartPlugin("example-plugin", func() types.Plugin { return NewExamplePlugin() })
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
//CLI Flags
|
|
||||||
ExampleFlag = cli.BoolFlag{
|
|
||||||
Name: "valid",
|
|
||||||
Usage: "Set this to make the transaction valid",
|
|
||||||
}
|
|
||||||
|
|
||||||
//CLI Plugin Commands
|
|
||||||
ExamplePluginTxCmd = cli.Command{
|
|
||||||
Name: "example",
|
|
||||||
Usage: "Create, sign, and broadcast a transaction to the example plugin",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
return cmdExamplePluginTx(c)
|
|
||||||
},
|
|
||||||
Flags: append(commands.TxFlags, ExampleFlag),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
//Send a transaction
|
//Send a transaction
|
||||||
func cmdExamplePluginTx(c *cli.Context) error {
|
func examplePluginTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
//Retrieve any flag results
|
|
||||||
exampleFlag := c.Bool("valid")
|
|
||||||
|
|
||||||
// Create a transaction using the flag.
|
// Create a transaction using the flag.
|
||||||
// The tx passes on custom information to the plugin
|
// The tx passes on custom information to the plugin
|
||||||
exampleTx := ExamplePluginTx{exampleFlag}
|
exampleTx := ExamplePluginTx{validFlag}
|
||||||
|
|
||||||
// The tx is passed to the plugin in the form of
|
// The tx is passed to the plugin in the form of
|
||||||
// a byte array. This is achieved by serializing the object using go-wire.
|
// a byte array. This is achieved by serializing the object using go-wire.
|
||||||
|
@ -62,5 +58,5 @@ func cmdExamplePluginTx(c *cli.Context) error {
|
||||||
// - Once deserialized, the tx is passed to `state.ExecTx` (state/execution.go)
|
// - Once deserialized, the tx is passed to `state.ExecTx` (state/execution.go)
|
||||||
// - If the tx passes various checks, the `tx.Data` is forwarded as `txBytes` to `plugin.RunTx` (docs/guide/src/example-plugin/plugin.go)
|
// - If the tx passes various checks, the `tx.Data` is forwarded as `txBytes` to `plugin.RunTx` (docs/guide/src/example-plugin/plugin.go)
|
||||||
// - Finally, it deserialized back to the ExamplePluginTx
|
// - Finally, it deserialized back to the ExamplePluginTx
|
||||||
return commands.AppTx(c, "example-plugin", exampleTxBytes)
|
return commands.AppTx("example-plugin", exampleTxBytes)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,32 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/cmd/commands"
|
"github.com/tendermint/basecoin/cmd/commands"
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
//Initialize an instance of basecoin with default basecoin commands
|
|
||||||
app := cli.NewApp()
|
//Initialize example-plugin root command
|
||||||
app.Name = "example-plugin"
|
var RootCmd = &cobra.Command{
|
||||||
app.Usage = "example-plugin [command] [args...]"
|
Use: "example-plugin",
|
||||||
app.Version = "0.1.0"
|
Short: "example-plugin usage description",
|
||||||
app.Commands = []cli.Command{
|
}
|
||||||
|
|
||||||
|
//Add the default basecoin commands to the root command
|
||||||
|
RootCmd.AddCommand(
|
||||||
|
commands.InitCmd,
|
||||||
commands.StartCmd,
|
commands.StartCmd,
|
||||||
commands.TxCmd,
|
commands.TxCmd,
|
||||||
commands.KeyCmd,
|
|
||||||
commands.QueryCmd,
|
commands.QueryCmd,
|
||||||
|
commands.KeyCmd,
|
||||||
|
commands.VerifyCmd,
|
||||||
|
commands.BlockCmd,
|
||||||
commands.AccountCmd,
|
commands.AccountCmd,
|
||||||
}
|
commands.UnsafeResetAllCmd,
|
||||||
app.Run(os.Args)
|
)
|
||||||
|
|
||||||
|
//Run the root command
|
||||||
|
commands.ExecuteWithDebug(RootCmd)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,41 @@
|
||||||
hash: c023dbd97e1ea0a525e33738f03afd6be61f997f1c2592a5d9928fdcecc71361
|
hash: c6e5febc35b5fd1003066820defb8a089db048b407239dad9faf44553fdc15e8
|
||||||
updated: 2017-04-21T17:38:13.194966906+02:00
|
updated: 2017-04-21T12:55:42.7004558-04:00
|
||||||
imports:
|
imports:
|
||||||
- name: github.com/btcsuite/btcd
|
- name: github.com/btcsuite/btcd
|
||||||
version: 583684b21bfbde9b5fc4403916fd7c807feb0289
|
version: 4b348c1d33373d672edd83fc576892d0e46686d2
|
||||||
subpackages:
|
subpackages:
|
||||||
- btcec
|
- btcec
|
||||||
- name: github.com/BurntSushi/toml
|
- name: github.com/BurntSushi/toml
|
||||||
version: e643e9ef00b049d75de26e61109c5ea01885cd21
|
version: b26d9c308763d68093482582cea63d69be07a0f0
|
||||||
- name: github.com/ebuchman/fail-test
|
- name: github.com/ebuchman/fail-test
|
||||||
version: 95f809107225be108efcf10a3509e4ea6ceef3c4
|
version: 95f809107225be108efcf10a3509e4ea6ceef3c4
|
||||||
- name: github.com/go-stack/stack
|
- name: github.com/go-stack/stack
|
||||||
version: 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82
|
version: 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82
|
||||||
- name: github.com/golang/protobuf
|
- name: github.com/golang/protobuf
|
||||||
version: c9c7427a2a70d2eb3bafa0ab2dc163e45f143317
|
version: 2bba0603135d7d7f5cb73b2125beeda19c09f4ef
|
||||||
subpackages:
|
subpackages:
|
||||||
- proto
|
- proto
|
||||||
|
- ptypes/any
|
||||||
- name: github.com/golang/snappy
|
- name: github.com/golang/snappy
|
||||||
version: 553a641470496b2327abcac10b36396bd98e45c9
|
version: 553a641470496b2327abcac10b36396bd98e45c9
|
||||||
- name: github.com/gorilla/websocket
|
- name: github.com/gorilla/websocket
|
||||||
version: 3ab3a8b8831546bd18fd182c20687ca853b2bb13
|
version: 3ab3a8b8831546bd18fd182c20687ca853b2bb13
|
||||||
|
- name: github.com/inconshreveable/mousetrap
|
||||||
|
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||||
- name: github.com/jmhodges/levigo
|
- name: github.com/jmhodges/levigo
|
||||||
version: c42d9e0ca023e2198120196f842701bb4c55d7b9
|
version: c42d9e0ca023e2198120196f842701bb4c55d7b9
|
||||||
- name: github.com/mattn/go-colorable
|
- name: github.com/mattn/go-colorable
|
||||||
version: a392f450ea64cee2b268dfaacdc2502b50a22b18
|
version: ded68f7a9561c023e790de24279db7ebf473ea80
|
||||||
- name: github.com/mattn/go-isatty
|
- name: github.com/mattn/go-isatty
|
||||||
version: 57fdcb988a5c543893cc61bce354a6e24ab70022
|
version: fc9e8d8ef48496124e79ae0df75490096eccf6fe
|
||||||
- name: github.com/pkg/errors
|
- name: github.com/pkg/errors
|
||||||
version: bfd5150e4e41705ded2129ec33379de1cb90b513
|
version: ff09b135c25aae272398c51a07235b90a75aa4f0
|
||||||
|
- name: github.com/spf13/cobra
|
||||||
|
version: 10f6b9d7e1631a54ad07c5c0fb71c28a1abfd3c2
|
||||||
|
- name: github.com/spf13/pflag
|
||||||
|
version: 2300d0f8576fe575f71aaa5b9bbe4e1b0dc2eb51
|
||||||
- name: github.com/syndtr/goleveldb
|
- name: github.com/syndtr/goleveldb
|
||||||
version: 3c5717caf1475fd25964109a0fc640bd150fce43
|
version: 8c81ea47d4c41a385645e133e15510fc6a2a74b4
|
||||||
subpackages:
|
subpackages:
|
||||||
- leveldb
|
- leveldb
|
||||||
- leveldb/cache
|
- leveldb/cache
|
||||||
|
@ -83,7 +90,7 @@ imports:
|
||||||
subpackages:
|
subpackages:
|
||||||
- upnp
|
- upnp
|
||||||
- name: github.com/tendermint/go-rpc
|
- name: github.com/tendermint/go-rpc
|
||||||
version: 9d18cbe74e66f875afa36d2fa3be280e4a2dc9e6
|
version: 559613689d56eaa423b19a3a4158546beb4857de
|
||||||
subpackages:
|
subpackages:
|
||||||
- client
|
- client
|
||||||
- server
|
- server
|
||||||
|
@ -100,9 +107,10 @@ imports:
|
||||||
- app
|
- app
|
||||||
- client
|
- client
|
||||||
- name: github.com/tendermint/tendermint
|
- name: github.com/tendermint/tendermint
|
||||||
version: 7cf773e2d37b2b5a08bc94fb125cfd346b834824
|
version: e8cad948e366cd1d0a9ebef642073f4ade9899e9
|
||||||
subpackages:
|
subpackages:
|
||||||
- blockchain
|
- blockchain
|
||||||
|
- cmd/tendermint/commands
|
||||||
- config/tendermint
|
- config/tendermint
|
||||||
- consensus
|
- consensus
|
||||||
- mempool
|
- mempool
|
||||||
|
@ -117,10 +125,8 @@ imports:
|
||||||
- state/txindex/null
|
- state/txindex/null
|
||||||
- types
|
- types
|
||||||
- version
|
- version
|
||||||
- name: github.com/urfave/cli
|
|
||||||
version: 0bdeddeeb0f650497d603c4ad7b20cfe685682f6
|
|
||||||
- name: golang.org/x/crypto
|
- name: golang.org/x/crypto
|
||||||
version: 728b753d0135da6801d45a38e6f43ff55779c5c2
|
version: 96846453c37f0876340a66a47f3f75b1f3a6cd2d
|
||||||
subpackages:
|
subpackages:
|
||||||
- curve25519
|
- curve25519
|
||||||
- nacl/box
|
- nacl/box
|
||||||
|
@ -131,7 +137,7 @@ imports:
|
||||||
- ripemd160
|
- ripemd160
|
||||||
- salsa20/salsa
|
- salsa20/salsa
|
||||||
- name: golang.org/x/net
|
- name: golang.org/x/net
|
||||||
version: a6577fac2d73be281a500b310739095313165611
|
version: c8c74377599bd978aee1cf3b9b63a8634051cec2
|
||||||
subpackages:
|
subpackages:
|
||||||
- context
|
- context
|
||||||
- http2
|
- http2
|
||||||
|
@ -141,11 +147,22 @@ imports:
|
||||||
- lex/httplex
|
- lex/httplex
|
||||||
- trace
|
- trace
|
||||||
- name: golang.org/x/sys
|
- name: golang.org/x/sys
|
||||||
version: 99f16d856c9836c42d24e7ab64ea72916925fa97
|
version: ea9bcade75cb975a0b9738936568ab388b845617
|
||||||
subpackages:
|
subpackages:
|
||||||
- unix
|
- unix
|
||||||
|
- name: golang.org/x/text
|
||||||
|
version: 19e3104b43db45fca0303f489a9536087b184802
|
||||||
|
subpackages:
|
||||||
|
- secure/bidirule
|
||||||
|
- transform
|
||||||
|
- unicode/bidi
|
||||||
|
- unicode/norm
|
||||||
|
- name: google.golang.org/genproto
|
||||||
|
version: 411e09b969b1170a9f0c467558eb4c4c110d9c77
|
||||||
|
subpackages:
|
||||||
|
- googleapis/rpc/status
|
||||||
- name: google.golang.org/grpc
|
- name: google.golang.org/grpc
|
||||||
version: 0713829b980f4ddd276689a36235c5fcc82a21bf
|
version: 6914ab1e338c92da4218a23d27fcd03d0ad78d46
|
||||||
subpackages:
|
subpackages:
|
||||||
- codes
|
- codes
|
||||||
- credentials
|
- credentials
|
||||||
|
@ -156,6 +173,7 @@ imports:
|
||||||
- naming
|
- naming
|
||||||
- peer
|
- peer
|
||||||
- stats
|
- stats
|
||||||
|
- status
|
||||||
- tap
|
- tap
|
||||||
- transport
|
- transport
|
||||||
testImports:
|
testImports:
|
||||||
|
|
|
@ -8,6 +8,8 @@ import:
|
||||||
version: develop
|
version: develop
|
||||||
- package: github.com/tendermint/go-logger
|
- package: github.com/tendermint/go-logger
|
||||||
version: develop
|
version: develop
|
||||||
|
- package: github.com/tendermint/go-data
|
||||||
|
version: develop
|
||||||
- package: github.com/tendermint/go-rpc
|
- package: github.com/tendermint/go-rpc
|
||||||
version: develop
|
version: develop
|
||||||
- package: github.com/tendermint/go-wire
|
- package: github.com/tendermint/go-wire
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
. "github.com/tendermint/go-common"
|
cmn "github.com/tendermint/go-common"
|
||||||
"github.com/tendermint/go-rpc/client"
|
"github.com/tendermint/go-rpc/client"
|
||||||
"github.com/tendermint/go-rpc/types"
|
"github.com/tendermint/go-rpc/types"
|
||||||
"github.com/tendermint/go-wire"
|
"github.com/tendermint/go-wire"
|
||||||
|
@ -21,7 +21,7 @@ func main() {
|
||||||
|
|
||||||
_, err := ws.Start()
|
_, err := ws.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Exit(err.Error())
|
cmn.Exit(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read a bunch of responses
|
// Read a bunch of responses
|
||||||
|
@ -50,7 +50,7 @@ func main() {
|
||||||
reqBytes := wire.JSONBytes(request)
|
reqBytes := wire.JSONBytes(request)
|
||||||
err = ws.WriteMessage(websocket.TextMessage, reqBytes)
|
err = ws.WriteMessage(websocket.TextMessage, reqBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Exit("writing websocket request: " + err.Error())
|
cmn.Exit("writing websocket request: " + err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,7 @@ func main() {
|
||||||
// Write request
|
// Write request
|
||||||
txBytes := wire.BinaryBytes(struct{ types.Tx }{tx})
|
txBytes := wire.BinaryBytes(struct{ types.Tx }{tx})
|
||||||
request := rpctypes.NewRPCRequest("fakeid", "broadcast_tx_sync", map[string]interface{}{"tx": txBytes})
|
request := rpctypes.NewRPCRequest("fakeid", "broadcast_tx_sync", map[string]interface{}{"tx": txBytes})
|
||||||
|
//request := rpctypes.NewRPCRequest("fakeid", "broadcast_tx_sync", map[string]interface{}{"tx": txBytes})
|
||||||
reqBytes := wire.JSONBytes(request)
|
reqBytes := wire.JSONBytes(request)
|
||||||
//fmt.Print(".")
|
//fmt.Print(".")
|
||||||
err := ws.WriteMessage(websocket.TextMessage, reqBytes)
|
err := ws.WriteMessage(websocket.TextMessage, reqBytes)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package version
|
package version
|
||||||
|
|
||||||
const Maj = "0"
|
const Maj = "0"
|
||||||
const Min = "3"
|
const Min = "4"
|
||||||
const Fix = "1"
|
const Fix = "0"
|
||||||
|
|
||||||
const Version = Maj + "." + Min + "." + Fix
|
const Version = Maj + "." + Min + "." + Fix
|
||||||
|
|
Loading…
Reference in New Issue