commit
6af6e6a4a1
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -1,5 +1,16 @@
|
||||||
# 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
|
||||||
|
- Much more testing
|
||||||
|
|
||||||
## 0.3.1 (March 23, 2017)
|
## 0.3.1 (March 23, 2017)
|
||||||
|
|
||||||
IMPROVEMENTS:
|
IMPROVEMENTS:
|
||||||
|
|
10
Makefile
10
Makefile
|
@ -1,22 +1,22 @@
|
||||||
all: test install
|
all: test install
|
||||||
|
|
||||||
NOVENDOR = go list github.com/tendermint/basecoin/... | grep -v /vendor/
|
NOVENDOR = go list ./... | grep -v /vendor/
|
||||||
|
|
||||||
build:
|
build:
|
||||||
go build github.com/tendermint/basecoin/cmd/...
|
go build ./cmd/...
|
||||||
|
|
||||||
install:
|
install:
|
||||||
go install github.com/tendermint/basecoin/cmd/...
|
go install ./cmd/...
|
||||||
|
|
||||||
test:
|
test:
|
||||||
go test `${NOVENDOR}`
|
go test `${NOVENDOR}`
|
||||||
#go run tests/tendermint/*.go
|
#go run tests/tendermint/*.go
|
||||||
|
|
||||||
get_deps:
|
get_deps:
|
||||||
go get -d github.com/tendermint/basecoin/...
|
go get -d ./...
|
||||||
|
|
||||||
update_deps:
|
update_deps:
|
||||||
go get -d -u github.com/tendermint/basecoin/...
|
go get -d -u ./...
|
||||||
|
|
||||||
get_vendor_deps:
|
get_vendor_deps:
|
||||||
go get github.com/Masterminds/glide
|
go get github.com/Masterminds/glide
|
||||||
|
|
14
app/app.go
14
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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -37,14 +37,14 @@ func NewBasecoin(eyesCli *eyes.Client) *Basecoin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For testing, not thread safe!
|
// XXX For testing, not thread safe!
|
||||||
func (app *Basecoin) GetState() *sm.State {
|
func (app *Basecoin) GetState() *sm.State {
|
||||||
return app.state.CacheWrap()
|
return app.state.CacheWrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
|
@ -136,7 +136,7 @@ func (app *Basecoin) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQu
|
||||||
// handle special path for account info
|
// handle special path for account info
|
||||||
if reqQuery.Path == "/account" {
|
if reqQuery.Path == "/account" {
|
||||||
reqQuery.Path = "/key"
|
reqQuery.Path = "/key"
|
||||||
reqQuery.Data = append([]byte("base/a/"), reqQuery.Data...)
|
reqQuery.Data = sm.AccountKey(reqQuery.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
resQuery, err := app.eyesCli.QuerySync(reqQuery)
|
resQuery, err := app.eyesCli.QuerySync(reqQuery)
|
||||||
|
|
|
@ -0,0 +1,182 @@
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
|
"github.com/tendermint/basecoin/types"
|
||||||
|
"github.com/tendermint/go-wire"
|
||||||
|
eyes "github.com/tendermint/merkleeyes/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
//--------------------------------------------------------
|
||||||
|
// test environment is a list of input and output accounts
|
||||||
|
|
||||||
|
type appTest struct {
|
||||||
|
t *testing.T
|
||||||
|
chainID string
|
||||||
|
app *Basecoin
|
||||||
|
accIn types.PrivAccount
|
||||||
|
accOut types.PrivAccount
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAppTest(t *testing.T) *appTest {
|
||||||
|
at := &appTest{
|
||||||
|
t: t,
|
||||||
|
chainID: "test_chain_id",
|
||||||
|
}
|
||||||
|
at.reset()
|
||||||
|
return at
|
||||||
|
}
|
||||||
|
|
||||||
|
// make a tx sending 5mycoin from each accIn to accOut
|
||||||
|
func (at *appTest) getTx(seq int) *types.SendTx {
|
||||||
|
tx := types.GetTx(seq, at.accOut, at.accIn)
|
||||||
|
types.SignTx(at.chainID, tx, at.accIn)
|
||||||
|
return tx
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the account on the app through SetOption
|
||||||
|
func (at *appTest) acc2app(acc types.Account) {
|
||||||
|
accBytes, err := json.Marshal(acc)
|
||||||
|
require.Nil(at.t, err)
|
||||||
|
res := at.app.SetOption("base/account", string(accBytes))
|
||||||
|
require.EqualValues(at.t, res, "Success")
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the in and out accs to be one account each with 7mycoin
|
||||||
|
func (at *appTest) reset() {
|
||||||
|
at.accIn = types.MakeAcc("input0")
|
||||||
|
at.accOut = types.MakeAcc("output0")
|
||||||
|
|
||||||
|
eyesCli := eyes.NewLocalClient("", 0)
|
||||||
|
at.app = NewBasecoin(eyesCli)
|
||||||
|
|
||||||
|
res := at.app.SetOption("base/chain_id", at.chainID)
|
||||||
|
require.EqualValues(at.t, res, "Success")
|
||||||
|
|
||||||
|
at.acc2app(at.accIn.Account)
|
||||||
|
at.acc2app(at.accOut.Account)
|
||||||
|
|
||||||
|
resabci := at.app.Commit()
|
||||||
|
require.True(at.t, resabci.IsOK(), resabci)
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the final balance and expected balance for input and output accounts
|
||||||
|
func (at *appTest) exec(tx *types.SendTx, checkTx bool) (res abci.Result, inputGot, inputExp, outputGot, outputExpected types.Coins) {
|
||||||
|
|
||||||
|
initBalIn := at.app.GetState().GetAccount(at.accIn.Account.PubKey.Address()).Balance
|
||||||
|
initBalOut := at.app.GetState().GetAccount(at.accOut.Account.PubKey.Address()).Balance
|
||||||
|
|
||||||
|
txBytes := []byte(wire.BinaryBytes(struct{ types.Tx }{tx}))
|
||||||
|
if checkTx {
|
||||||
|
res = at.app.CheckTx(txBytes)
|
||||||
|
} else {
|
||||||
|
res = at.app.DeliverTx(txBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
endBalIn := at.app.GetState().GetAccount(at.accIn.Account.PubKey.Address()).Balance
|
||||||
|
endBalOut := at.app.GetState().GetAccount(at.accOut.Account.PubKey.Address()).Balance
|
||||||
|
decrBalInExp := tx.Outputs[0].Coins.Plus(types.Coins{tx.Fee})
|
||||||
|
return res, endBalIn, initBalIn.Minus(decrBalInExp), endBalOut, initBalOut.Plus(tx.Outputs[0].Coins)
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------
|
||||||
|
|
||||||
|
func TestSplitKey(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
prefix, suffix := splitKey("foo/bar")
|
||||||
|
assert.EqualValues("foo", prefix)
|
||||||
|
assert.EqualValues("bar", suffix)
|
||||||
|
|
||||||
|
prefix, suffix = splitKey("foobar")
|
||||||
|
assert.EqualValues("foobar", prefix)
|
||||||
|
assert.EqualValues("", suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetOption(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
eyesCli := eyes.NewLocalClient("", 0)
|
||||||
|
app := NewBasecoin(eyesCli)
|
||||||
|
|
||||||
|
//testing ChainID
|
||||||
|
chainID := "testChain"
|
||||||
|
res := app.SetOption("base/chain_id", chainID)
|
||||||
|
assert.EqualValues(app.GetState().GetChainID(), chainID)
|
||||||
|
assert.EqualValues(res, "Success")
|
||||||
|
|
||||||
|
accIn := types.MakeAcc("input0")
|
||||||
|
accsInBytes, err := json.Marshal(accIn.Account)
|
||||||
|
assert.Nil(err)
|
||||||
|
res = app.SetOption("base/account", string(accsInBytes))
|
||||||
|
assert.EqualValues(res, "Success")
|
||||||
|
|
||||||
|
res = app.SetOption("base/dslfkgjdas", "")
|
||||||
|
assert.NotEqual(res, "Success")
|
||||||
|
|
||||||
|
res = app.SetOption("dslfkgjdas", "")
|
||||||
|
assert.NotEqual(res, "Success")
|
||||||
|
|
||||||
|
res = app.SetOption("dslfkgjdas/szfdjzs", "")
|
||||||
|
assert.NotEqual(res, "Success")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test CheckTx and DeliverTx with insufficient and sufficient balance
|
||||||
|
func TestTx(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
at := newAppTest(t)
|
||||||
|
|
||||||
|
//Bad Balance
|
||||||
|
at.accIn.Balance = types.Coins{{"mycoin", 2}}
|
||||||
|
at.acc2app(at.accIn.Account)
|
||||||
|
res, _, _, _, _ := at.exec(at.getTx(1), true)
|
||||||
|
assert.True(res.IsErr(), "ExecTx/Bad CheckTx: Expected error return from ExecTx, returned: %v", res)
|
||||||
|
res, inGot, inExp, outGot, outExp := at.exec(at.getTx(1), false)
|
||||||
|
assert.True(res.IsErr(), "ExecTx/Bad DeliverTx: Expected error return from ExecTx, returned: %v", res)
|
||||||
|
assert.False(inGot.IsEqual(inExp), "ExecTx/Bad DeliverTx: shouldn't be equal, inGot: %v, inExp: %v", inGot, inExp)
|
||||||
|
assert.False(outGot.IsEqual(outExp), "ExecTx/Bad DeliverTx: shouldn't be equal, outGot: %v, outExp: %v", outGot, outExp)
|
||||||
|
|
||||||
|
//Regular CheckTx
|
||||||
|
at.reset()
|
||||||
|
res, _, _, _, _ = at.exec(at.getTx(1), true)
|
||||||
|
assert.True(res.IsOK(), "ExecTx/Good CheckTx: Expected OK return from ExecTx, Error: %v", res)
|
||||||
|
|
||||||
|
//Regular DeliverTx
|
||||||
|
at.reset()
|
||||||
|
res, inGot, inExp, outGot, outExp = at.exec(at.getTx(1), false)
|
||||||
|
assert.True(res.IsOK(), "ExecTx/Good DeliverTx: Expected OK return from ExecTx, Error: %v", res)
|
||||||
|
assert.True(inGot.IsEqual(inExp),
|
||||||
|
"ExecTx/good DeliverTx: unexpected change in input coins, inGot: %v, inExp: %v", inGot, inExp)
|
||||||
|
assert.True(outGot.IsEqual(outExp),
|
||||||
|
"ExecTx/good DeliverTx: unexpected change in output coins, outGot: %v, outExp: %v", outGot, outExp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQuery(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
at := newAppTest(t)
|
||||||
|
|
||||||
|
res, _, _, _, _ := at.exec(at.getTx(1), false)
|
||||||
|
assert.True(res.IsOK(), "Commit, DeliverTx: Expected OK return from DeliverTx, Error: %v", res)
|
||||||
|
|
||||||
|
resQueryPreCommit := at.app.Query(abci.RequestQuery{
|
||||||
|
Path: "/account",
|
||||||
|
Data: at.accIn.Account.PubKey.Address(),
|
||||||
|
})
|
||||||
|
|
||||||
|
res = at.app.Commit()
|
||||||
|
assert.True(res.IsOK(), res)
|
||||||
|
|
||||||
|
resQueryPostCommit := at.app.Query(abci.RequestQuery{
|
||||||
|
Path: "/account",
|
||||||
|
Data: at.accIn.Account.PubKey.Address(),
|
||||||
|
})
|
||||||
|
fmt.Println(resQueryPreCommit)
|
||||||
|
fmt.Println(resQueryPostCommit)
|
||||||
|
assert.NotEqual(resQueryPreCommit, resQueryPostCommit, "Query should change before/after commit")
|
||||||
|
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mod1 + mod2 + mod3 + mod4) > 0 {
|
||||||
log.Notice("Initialized Basecoin", "genesis", genesisFile, "key", key1File)
|
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,40 +8,41 @@ 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 {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println(keyJSON)
|
fmt.Println(string(keyJSON))
|
||||||
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"
|
||||||
"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 := ParseCoin(fee)
|
feeCoin, err := types.ParseCoin(feeFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
amountCoins, err := 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 := ParseCoin(fee)
|
feeCoin, err := types.ParseCoin(feeFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
amountCoins, err := 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)
|
||||||
clientURI := client.NewClientURI(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 := clientURI.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,13 +2,13 @@ package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"path"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"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"
|
||||||
|
@ -16,21 +16,101 @@ import (
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
||||||
cmn "github.com/tendermint/go-common"
|
cmn "github.com/tendermint/go-common"
|
||||||
client "github.com/tendermint/go-rpc/client"
|
client "github.com/tendermint/go-rpc/client"
|
||||||
"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"
|
||||||
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" {
|
||||||
|
@ -50,46 +130,8 @@ func StripHex(s string) string {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
//regex codes for extracting coins from CLI input
|
|
||||||
var reDenom = regexp.MustCompile("([^\\d\\W]+)")
|
|
||||||
var reAmt = regexp.MustCompile("(\\d+)")
|
|
||||||
|
|
||||||
func ParseCoin(str string) (types.Coin, error) {
|
|
||||||
|
|
||||||
var coin types.Coin
|
|
||||||
|
|
||||||
if len(str) > 0 {
|
|
||||||
amt, err := strconv.Atoi(reAmt.FindString(str))
|
|
||||||
if err != nil {
|
|
||||||
return coin, err
|
|
||||||
}
|
|
||||||
denom := reDenom.FindString(str)
|
|
||||||
coin = types.Coin{denom, int64(amt)}
|
|
||||||
}
|
|
||||||
|
|
||||||
return coin, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseCoins(str string) (types.Coins, error) {
|
|
||||||
|
|
||||||
split := strings.Split(str, ",")
|
|
||||||
var coins []types.Coin
|
|
||||||
|
|
||||||
for _, el := range split {
|
|
||||||
if len(el) > 0 {
|
|
||||||
coin, err := ParseCoin(el)
|
|
||||||
if err != nil {
|
|
||||||
return coins, err
|
|
||||||
}
|
|
||||||
coins = append(coins, coin)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return coins, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Query(tmAddr string, key []byte) (*abci.ResponseQuery, error) {
|
func Query(tmAddr string, key []byte) (*abci.ResponseQuery, error) {
|
||||||
clientURI := client.NewClientURI(tmAddr)
|
uriClient := client.NewURIClient(tmAddr)
|
||||||
tmResult := new(ctypes.TMResult)
|
tmResult := new(ctypes.TMResult)
|
||||||
|
|
||||||
params := map[string]interface{}{
|
params := map[string]interface{}{
|
||||||
|
@ -97,13 +139,13 @@ func Query(tmAddr string, key []byte) (*abci.ResponseQuery, error) {
|
||||||
"data": key,
|
"data": key,
|
||||||
"prove": true,
|
"prove": true,
|
||||||
}
|
}
|
||||||
_, err := clientURI.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
|
||||||
}
|
}
|
||||||
|
@ -120,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)
|
||||||
clientURI := client.NewClientURI(tmAddr)
|
|
||||||
|
|
||||||
method := "commit"
|
method := "commit"
|
||||||
_, err := clientURI.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
|
||||||
|
|
|
@ -5,10 +5,10 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/tendermint/basecoin/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHex(t *testing.T) {
|
func TestHex(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
//test isHex
|
//test isHex
|
||||||
hexNoPrefix := hex.EncodeToString([]byte("foobar"))
|
hexNoPrefix := hex.EncodeToString([]byte("foobar"))
|
||||||
|
@ -16,65 +16,9 @@ func TestHex(t *testing.T) {
|
||||||
str := "foobar"
|
str := "foobar"
|
||||||
strWPrefix := "0xfoobar"
|
strWPrefix := "0xfoobar"
|
||||||
|
|
||||||
//define the list of coin tests
|
assert.True(isHex(hexWPrefix), "isHex not identifying hex with 0x prefix")
|
||||||
var testList = []struct {
|
assert.False(isHex(hexNoPrefix), "isHex shouldn't identify hex without 0x prefix")
|
||||||
testPass bool
|
assert.False(isHex(str), "isHex shouldn't identify non-hex string")
|
||||||
errMsg string
|
assert.False(isHex(strWPrefix), "isHex shouldn't identify non-hex string with 0x prefix")
|
||||||
}{
|
assert.True(StripHex(hexWPrefix) == hexNoPrefix, "StripHex doesn't remove first two characters")
|
||||||
{isHex(hexWPrefix), "isHex not identifying hex with 0x prefix"},
|
|
||||||
{!isHex(hexNoPrefix), "isHex shouldn't identify hex without 0x prefix"},
|
|
||||||
{!isHex(str), "isHex shouldn't identify non-hex string"},
|
|
||||||
{!isHex(strWPrefix), "isHex shouldn't identify non-hex string with 0x prefix"},
|
|
||||||
{StripHex(hexWPrefix) == hexNoPrefix, "StripHex doesn't remove first two characters"},
|
|
||||||
}
|
|
||||||
|
|
||||||
//execute the tests
|
|
||||||
for _, tl := range testList {
|
|
||||||
assert.True(t, tl.testPass, tl.errMsg)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//Test the parse coin and parse coins functionality
|
|
||||||
func TestParse(t *testing.T) {
|
|
||||||
|
|
||||||
makeCoin := func(str string) types.Coin {
|
|
||||||
coin, err := ParseCoin(str)
|
|
||||||
if err != nil {
|
|
||||||
panic(err.Error())
|
|
||||||
}
|
|
||||||
return coin
|
|
||||||
}
|
|
||||||
|
|
||||||
makeCoins := func(str string) types.Coins {
|
|
||||||
coin, err := ParseCoins(str)
|
|
||||||
if err != nil {
|
|
||||||
panic(err.Error())
|
|
||||||
}
|
|
||||||
return coin
|
|
||||||
}
|
|
||||||
|
|
||||||
//define the list of coin tests
|
|
||||||
var testList = []struct {
|
|
||||||
testPass bool
|
|
||||||
errMsg string
|
|
||||||
}{
|
|
||||||
//testing ParseCoin Function
|
|
||||||
{types.Coin{} == makeCoin(""), "parseCoin makes bad empty coin"},
|
|
||||||
{types.Coin{"fooCoin", 1} == makeCoin("1fooCoin"), "parseCoin makes bad coins"},
|
|
||||||
{types.Coin{"barCoin", 10} == makeCoin("10 barCoin"), "parseCoin makes bad coins"},
|
|
||||||
|
|
||||||
//testing ParseCoins Function
|
|
||||||
{types.Coins{{"fooCoin", 1}}.IsEqual(makeCoins("1fooCoin")),
|
|
||||||
"parseCoins doesn't parse a single coin"},
|
|
||||||
{types.Coins{{"barCoin", 99}, {"fooCoin", 1}}.IsEqual(makeCoins("99barCoin,1fooCoin")),
|
|
||||||
"parseCoins doesn't properly parse two coins"},
|
|
||||||
{types.Coins{{"barCoin", 99}, {"fooCoin", 1}}.IsEqual(makeCoins("99 barCoin, 1 fooCoin")),
|
|
||||||
"parseCoins doesn't properly parse two coins which use spaces"},
|
|
||||||
}
|
|
||||||
|
|
||||||
//execute the tests
|
|
||||||
for _, tl := range testList {
|
|
||||||
assert.True(t, tl.testPass, tl.errMsg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -11,6 +11,8 @@ learn a new project quickly as they all have the same enforced layout,
|
||||||
programming following these conventions allows for interoperability with much
|
programming following these conventions allows for interoperability with much
|
||||||
of the go tooling, and a much more fluid development experience.
|
of the go tooling, and a much more fluid development experience.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
First of all, you should read through [Effective
|
First of all, you should read through [Effective
|
||||||
Go](https://golang.org/doc/effective_go.html) to get a feel for the language
|
Go](https://golang.org/doc/effective_go.html) to get a feel for the language
|
||||||
and the constructs. And maybe pick up a book, read a tutorial, or do what you
|
and the constructs. And maybe pick up a book, read a tutorial, or do what you
|
||||||
|
@ -36,6 +38,8 @@ github repos. If you put your code outside of GOPATH/src or have a path other
|
||||||
than the url of the repo, you can expect errors. There are ways to do this,
|
than the url of the repo, you can expect errors. There are ways to do this,
|
||||||
but quite complex and not worth the bother.
|
but quite complex and not worth the bother.
|
||||||
|
|
||||||
|
## Compiling from source
|
||||||
|
|
||||||
Third, every repo in `$GOPATH/src` is checkout out of a version control system
|
Third, every repo in `$GOPATH/src` is checkout out of a version control system
|
||||||
(commonly git), and you can go into those directories and manipulate them like
|
(commonly git), and you can go into those directories and manipulate them like
|
||||||
any git repo (`git checkout develop`, `git pull`, `git remote set-url origin
|
any git repo (`git checkout develop`, `git pull`, `git remote set-url origin
|
||||||
|
@ -56,6 +60,8 @@ something like `go install github.com/tendermint/basecoin/cmd/basecoin` or to
|
||||||
compile all the commands `go install github.com/tendermint/basecoin/cmd/...`
|
compile all the commands `go install github.com/tendermint/basecoin/cmd/...`
|
||||||
(... is a go tooling shortcut for all subdirs, like `*`).
|
(... is a go tooling shortcut for all subdirs, like `*`).
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
Fifth, there isn't good dependency management built into go. By default, when
|
Fifth, there isn't good dependency management built into go. By default, when
|
||||||
compiling a go program which imports another repo, go will compile using the
|
compiling a go program which imports another repo, go will compile using the
|
||||||
latest master branch, or whichever version you have checked out and located.
|
latest master branch, or whichever version you have checked out and located.
|
||||||
|
@ -80,6 +86,47 @@ install` will compile all commands. `make test` is good to run the test suite
|
||||||
and make sure things are working with your environment... failing tests are
|
and make sure things are working with your environment... failing tests are
|
||||||
much easier to debug than a malfunctioning program.
|
much easier to debug than a malfunctioning program.
|
||||||
|
|
||||||
|
## Custom versions.
|
||||||
|
|
||||||
|
Sometimes compiling the master branch isn't enough. Let's say you just heard the new `develop` branch of tendermint has an awesome new feature and you want to try it out before it makes it into master (in a few weeks). That is pretty simple. Simply go into the `tendermint`, `basecoin`, etc. repo and compile as above. (Note in tendermint and basecoin, make install automatically gets the newest vendor deps, so you can skip that step)
|
||||||
|
|
||||||
|
```
|
||||||
|
git checkout develop # or other branch
|
||||||
|
git pull
|
||||||
|
make get_vendor_deps
|
||||||
|
make install
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
|
||||||
|
Great! Now when I run `tendermint` I have the newest of the new, the develop branch! But please not that this branch is not considered production ready and may have issues. This should only be done if you want to develop code for the future and run locally.
|
||||||
|
|
||||||
|
But wait, I want to mix and match. There is a bugfix in `go-p2p:persistent_peer` that I want to use with tendermint. How to compile this. I will show with a simple example, please update the repo and commit numbers for your usecase. Also, make sure these branches are compatible, so if `persistent_peer` is close to `master` it should work. But if it is 15 commits ahead, you will probably need the `develop` branch of tendermint to compile with it. But I assume you know your way around git and can figure that out.
|
||||||
|
|
||||||
|
In the dependent repo:
|
||||||
|
```
|
||||||
|
cd $GOPATH/src/github.com/tendermint/go-p2p
|
||||||
|
git checkout persistent_peer
|
||||||
|
git pull
|
||||||
|
# double-check this makes sense or if it is too far off
|
||||||
|
git log --oneline --decorate --graph
|
||||||
|
# get the full commit number here
|
||||||
|
git log | head -1
|
||||||
|
```
|
||||||
|
|
||||||
|
In the main repo (tendermint, basecoin, ...) where the binary will be built:
|
||||||
|
```
|
||||||
|
cd $GOPATH/src/github.com/tendermint/tendermin
|
||||||
|
git checkout master
|
||||||
|
git pull
|
||||||
|
# -> edit glide.lock, set the version of go-p2p (for example)
|
||||||
|
# to the commit number you got above (the 40 char version)
|
||||||
|
make get_vendor_deps
|
||||||
|
make install
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
|
||||||
|
Great, now you just compiled the master branch of tendermint along with the bugfix for one of the dependencies! Maybe you don't have to wait until the next bugfix release after all.
|
||||||
|
|
||||||
Okay, that's it, with this info you should be able to follow along and
|
Okay, that's it, with this info you should be able to follow along and
|
||||||
trouble-shoot any issues you have with the rest of the guide.
|
trouble-shoot any issues you have with the rest of the guide.
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -13,6 +13,7 @@ the correct way to install is:
|
||||||
|
|
||||||
```
|
```
|
||||||
cd $GOPATH/src/github.com/tendermint/basecoin
|
cd $GOPATH/src/github.com/tendermint/basecoin
|
||||||
|
git pull origin master
|
||||||
make get_vendor_deps
|
make get_vendor_deps
|
||||||
make install
|
make install
|
||||||
```
|
```
|
||||||
|
|
|
@ -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: c71c0d6c409bfddb4c4b471d8445f59cebd2cd41e1635a90d6facd81bd09a5e0
|
hash: c6e5febc35b5fd1003066820defb8a089db048b407239dad9faf44553fdc15e8
|
||||||
updated: 2017-03-14T17:02:44.512359631-04: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
|
||||||
|
@ -43,7 +50,7 @@ imports:
|
||||||
- leveldb/table
|
- leveldb/table
|
||||||
- leveldb/util
|
- leveldb/util
|
||||||
- name: github.com/tendermint/abci
|
- name: github.com/tendermint/abci
|
||||||
version: af792eac777de757cd496349a5f6b5313738fcbc
|
version: 56e13d87f4e3ec1ea756957d6b23caa6ebcf0998
|
||||||
subpackages:
|
subpackages:
|
||||||
- client
|
- client
|
||||||
- example/dummy
|
- example/dummy
|
||||||
|
@ -59,15 +66,15 @@ imports:
|
||||||
- name: github.com/tendermint/go-clist
|
- name: github.com/tendermint/go-clist
|
||||||
version: 3baa390bbaf7634251c42ad69a8682e7e3990552
|
version: 3baa390bbaf7634251c42ad69a8682e7e3990552
|
||||||
- name: github.com/tendermint/go-common
|
- name: github.com/tendermint/go-common
|
||||||
version: dcb015dff6c7af21e65c8e2f3b450df19d38c777
|
version: f9e3db037330c8a8d61d3966de8473eaf01154fa
|
||||||
- name: github.com/tendermint/go-config
|
- name: github.com/tendermint/go-config
|
||||||
version: 620dcbbd7d587cf3599dedbf329b64311b0c307a
|
version: 620dcbbd7d587cf3599dedbf329b64311b0c307a
|
||||||
- name: github.com/tendermint/go-crypto
|
- name: github.com/tendermint/go-crypto
|
||||||
version: 3f47cfac5fcd9e0f1727c7db980b3559913b3e3a
|
version: 0ca2c6fdb0706001ca4c4b9b80c9f428e8cf39da
|
||||||
- name: github.com/tendermint/go-data
|
- name: github.com/tendermint/go-data
|
||||||
version: 32271140e8fd5abdbb22e268d7a02421fa382f0b
|
version: e7fcc6d081ec8518912fcdc103188275f83a3ee5
|
||||||
- name: github.com/tendermint/go-db
|
- name: github.com/tendermint/go-db
|
||||||
version: eac3f2bc147023957c8bf69432a4e6c4dc5c3f72
|
version: 9643f60bc2578693844aacf380a7c32e4c029fee
|
||||||
- name: github.com/tendermint/go-events
|
- name: github.com/tendermint/go-events
|
||||||
version: f8ffbfb2be3483e9e7927495590a727f51c0c11f
|
version: f8ffbfb2be3483e9e7927495590a727f51c0c11f
|
||||||
- name: github.com/tendermint/go-flowrate
|
- name: github.com/tendermint/go-flowrate
|
||||||
|
@ -79,17 +86,17 @@ imports:
|
||||||
- name: github.com/tendermint/go-merkle
|
- name: github.com/tendermint/go-merkle
|
||||||
version: 714d4d04557fd068a7c2a1748241ce8428015a96
|
version: 714d4d04557fd068a7c2a1748241ce8428015a96
|
||||||
- name: github.com/tendermint/go-p2p
|
- name: github.com/tendermint/go-p2p
|
||||||
version: 97a5ed2d1a17eaee8717b8a32cfaf7a9a82a273d
|
version: 17124989a93774833df33107fbf17157a7f8ef31
|
||||||
subpackages:
|
subpackages:
|
||||||
- upnp
|
- upnp
|
||||||
- name: github.com/tendermint/go-rpc
|
- name: github.com/tendermint/go-rpc
|
||||||
version: fcea0cda21f64889be00a0f4b6d13266b1a76ee7
|
version: 559613689d56eaa423b19a3a4158546beb4857de
|
||||||
subpackages:
|
subpackages:
|
||||||
- client
|
- client
|
||||||
- server
|
- server
|
||||||
- types
|
- types
|
||||||
- name: github.com/tendermint/go-wire
|
- name: github.com/tendermint/go-wire
|
||||||
version: f530b7af7a8b06e612c2063bff6ace49060a085e
|
version: c1c9a57ab8038448ddea1714c0698f8051e5748c
|
||||||
- name: github.com/tendermint/log15
|
- name: github.com/tendermint/log15
|
||||||
version: ae0f3d6450da9eac7074b439c8e1c3cabf0d5ce6
|
version: ae0f3d6450da9eac7074b439c8e1c3cabf0d5ce6
|
||||||
subpackages:
|
subpackages:
|
||||||
|
@ -100,9 +107,10 @@ imports:
|
||||||
- app
|
- app
|
||||||
- client
|
- client
|
||||||
- name: github.com/tendermint/tendermint
|
- name: github.com/tendermint/tendermint
|
||||||
version: d4f625455109d88e7f55a999fdb25e208f174802
|
version: e8cad948e366cd1d0a9ebef642073f4ade9899e9
|
||||||
subpackages:
|
subpackages:
|
||||||
- blockchain
|
- blockchain
|
||||||
|
- cmd/tendermint/commands
|
||||||
- config/tendermint
|
- config/tendermint
|
||||||
- consensus
|
- consensus
|
||||||
- mempool
|
- mempool
|
||||||
|
@ -112,12 +120,13 @@ imports:
|
||||||
- rpc/core/types
|
- rpc/core/types
|
||||||
- rpc/grpc
|
- rpc/grpc
|
||||||
- state
|
- state
|
||||||
|
- state/txindex
|
||||||
|
- state/txindex/kv
|
||||||
|
- 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
|
||||||
|
@ -128,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
|
||||||
|
@ -138,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
|
||||||
|
@ -153,6 +173,7 @@ imports:
|
||||||
- naming
|
- naming
|
||||||
- peer
|
- peer
|
||||||
- stats
|
- stats
|
||||||
|
- status
|
||||||
- tap
|
- tap
|
||||||
- transport
|
- transport
|
||||||
testImports:
|
testImports:
|
||||||
|
|
20
glide.yaml
20
glide.yaml
|
@ -1,22 +1,24 @@
|
||||||
package: github.com/tendermint/basecoin
|
package: github.com/tendermint/basecoin
|
||||||
import:
|
import:
|
||||||
- package: github.com/tendermint/go-common
|
- package: github.com/tendermint/go-common
|
||||||
version: master
|
version: develop
|
||||||
- package: github.com/tendermint/go-crypto
|
- package: github.com/tendermint/go-crypto
|
||||||
version: master
|
version: develop
|
||||||
- package: github.com/tendermint/go-events
|
- package: github.com/tendermint/go-events
|
||||||
version: master
|
version: develop
|
||||||
- package: github.com/tendermint/go-logger
|
- package: github.com/tendermint/go-logger
|
||||||
version: master
|
version: develop
|
||||||
|
- package: github.com/tendermint/go-data
|
||||||
|
version: develop
|
||||||
- package: github.com/tendermint/go-rpc
|
- package: github.com/tendermint/go-rpc
|
||||||
version: master
|
version: develop
|
||||||
- package: github.com/tendermint/go-wire
|
- package: github.com/tendermint/go-wire
|
||||||
version: master
|
version: develop
|
||||||
- package: github.com/tendermint/merkleeyes
|
- package: github.com/tendermint/merkleeyes
|
||||||
version: master
|
version: develop
|
||||||
- package: github.com/tendermint/tendermint
|
- package: github.com/tendermint/tendermint
|
||||||
version: master
|
version: develop
|
||||||
- package: github.com/tendermint/abci
|
- package: github.com/tendermint/abci
|
||||||
version: master
|
version: develop
|
||||||
- package: github.com/gorilla/websocket
|
- package: github.com/gorilla/websocket
|
||||||
version: v1.1.0
|
version: v1.1.0
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCounterPlugin(t *testing.T) {
|
func TestCounterPlugin(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
// Basecoin initialization
|
// Basecoin initialization
|
||||||
eyesCli := eyescli.NewLocalClient("", 0)
|
eyesCli := eyescli.NewLocalClient("", 0)
|
||||||
|
@ -64,39 +65,40 @@ func TestCounterPlugin(t *testing.T) {
|
||||||
|
|
||||||
// Test a basic send, no fee
|
// Test a basic send, no fee
|
||||||
res := DeliverCounterTx(0, types.Coin{}, types.Coins{{"", 1}}, 1, types.Coins{})
|
res := DeliverCounterTx(0, types.Coin{}, types.Coins{{"", 1}}, 1, types.Coins{})
|
||||||
assert.True(t, res.IsOK(), res.String())
|
assert.True(res.IsOK(), res.String())
|
||||||
|
|
||||||
// Test fee prevented transaction
|
// Test fee prevented transaction
|
||||||
res = DeliverCounterTx(0, types.Coin{"", 2}, types.Coins{{"", 1}}, 2, types.Coins{})
|
res = DeliverCounterTx(0, types.Coin{"", 2}, types.Coins{{"", 1}}, 2, types.Coins{})
|
||||||
assert.True(t, res.IsErr(), res.String())
|
assert.True(res.IsErr(), res.String())
|
||||||
|
|
||||||
// Test input equals fee
|
// Test input equals fee
|
||||||
res = DeliverCounterTx(0, types.Coin{"", 2}, types.Coins{{"", 2}}, 2, types.Coins{})
|
res = DeliverCounterTx(0, types.Coin{"", 2}, types.Coins{{"", 2}}, 2, types.Coins{})
|
||||||
assert.True(t, res.IsOK(), res.String())
|
assert.True(res.IsOK(), res.String())
|
||||||
|
|
||||||
// Test more input than fee
|
// Test more input than fee
|
||||||
res = DeliverCounterTx(0, types.Coin{"", 2}, types.Coins{{"", 3}}, 3, types.Coins{})
|
res = DeliverCounterTx(0, types.Coin{"", 2}, types.Coins{{"", 3}}, 3, types.Coins{})
|
||||||
assert.True(t, res.IsOK(), res.String())
|
assert.True(res.IsOK(), res.String())
|
||||||
|
|
||||||
// Test input equals fee+appFee
|
// Test input equals fee+appFee
|
||||||
res = DeliverCounterTx(0, types.Coin{"", 1}, types.Coins{{"", 3}, {"gold", 1}}, 4, types.Coins{{"", 2}, {"gold", 1}})
|
res = DeliverCounterTx(0, types.Coin{"", 1}, types.Coins{{"", 3}, {"gold", 1}}, 4, types.Coins{{"", 2}, {"gold", 1}})
|
||||||
assert.True(t, res.IsOK(), res.String())
|
assert.True(res.IsOK(), res.String())
|
||||||
|
|
||||||
// Test fee+appFee prevented transaction, not enough ""
|
// Test fee+appFee prevented transaction, not enough ""
|
||||||
res = DeliverCounterTx(0, types.Coin{"", 1}, types.Coins{{"", 2}, {"gold", 1}}, 5, types.Coins{{"", 2}, {"gold", 1}})
|
res = DeliverCounterTx(0, types.Coin{"", 1}, types.Coins{{"", 2}, {"gold", 1}}, 5, types.Coins{{"", 2}, {"gold", 1}})
|
||||||
assert.True(t, res.IsErr(), res.String())
|
assert.True(res.IsErr(), res.String())
|
||||||
|
|
||||||
// Test fee+appFee prevented transaction, not enough "gold"
|
// Test fee+appFee prevented transaction, not enough "gold"
|
||||||
res = DeliverCounterTx(0, types.Coin{"", 1}, types.Coins{{"", 3}, {"gold", 1}}, 5, types.Coins{{"", 2}, {"gold", 2}})
|
res = DeliverCounterTx(0, types.Coin{"", 1}, types.Coins{{"", 3}, {"gold", 1}}, 5, types.Coins{{"", 2}, {"gold", 2}})
|
||||||
assert.True(t, res.IsErr(), res.String())
|
assert.True(res.IsErr(), res.String())
|
||||||
|
|
||||||
// Test more input than fee, more ""
|
// Test more input than fee, more ""
|
||||||
res = DeliverCounterTx(0, types.Coin{"", 1}, types.Coins{{"", 4}, {"gold", 1}}, 6, types.Coins{{"", 2}, {"gold", 1}})
|
res = DeliverCounterTx(0, types.Coin{"", 1}, types.Coins{{"", 4}, {"gold", 1}}, 6, types.Coins{{"", 2}, {"gold", 1}})
|
||||||
assert.True(t, res.IsOK(), res.String())
|
assert.True(res.IsOK(), res.String())
|
||||||
|
|
||||||
// Test more input than fee, more "gold"
|
// Test more input than fee, more "gold"
|
||||||
res = DeliverCounterTx(0, types.Coin{"", 1}, types.Coins{{"", 3}, {"gold", 2}}, 7, types.Coins{{"", 2}, {"gold", 1}})
|
res = DeliverCounterTx(0, types.Coin{"", 1}, types.Coins{{"", 3}, {"gold", 2}}, 7, types.Coins{{"", 2}, {"gold", 1}})
|
||||||
assert.True(t, res.IsOK(), res.String())
|
assert.True(res.IsOK(), res.String())
|
||||||
|
|
||||||
|
// REF: DeliverCounterTx(gas, fee, inputCoins, inputSequence, appFee) {w
|
||||||
|
|
||||||
// REF: DeliverCounterTx(gas, fee, inputCoins, inputSequence, appFee) {
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,7 @@ func (pas PrivAccountsByAddress) Swap(i, j int) {
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
|
|
||||||
func TestIBCPlugin(t *testing.T) {
|
func TestIBCPlugin(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
eyesClient := eyes.NewLocalClient("", 0)
|
eyesClient := eyes.NewLocalClient("", 0)
|
||||||
store := types.NewKVCache(eyesClient)
|
store := types.NewKVCache(eyesClient)
|
||||||
|
@ -88,7 +89,7 @@ func TestIBCPlugin(t *testing.T) {
|
||||||
Genesis: "<THIS IS NOT JSON>",
|
Genesis: "<THIS IS NOT JSON>",
|
||||||
},
|
},
|
||||||
}}))
|
}}))
|
||||||
assert.Equal(t, IBCCodeEncodingError, res.Code)
|
assert.Equal(IBCCodeEncodingError, res.Code)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||||
store.ClearLogLines()
|
store.ClearLogLines()
|
||||||
|
|
||||||
|
@ -99,7 +100,7 @@ func TestIBCPlugin(t *testing.T) {
|
||||||
Genesis: string(genDocJSON_1),
|
Genesis: string(genDocJSON_1),
|
||||||
},
|
},
|
||||||
}}))
|
}}))
|
||||||
assert.True(t, res.IsOK(), res.Log)
|
assert.True(res.IsOK(), res.Log)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||||
store.ClearLogLines()
|
store.ClearLogLines()
|
||||||
|
|
||||||
|
@ -110,7 +111,7 @@ func TestIBCPlugin(t *testing.T) {
|
||||||
Genesis: string(genDocJSON_1),
|
Genesis: string(genDocJSON_1),
|
||||||
},
|
},
|
||||||
}}))
|
}}))
|
||||||
assert.Equal(t, IBCCodeChainAlreadyExists, res.Code, res.Log)
|
assert.Equal(IBCCodeChainAlreadyExists, res.Code, res.Log)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||||
store.ClearLogLines()
|
store.ClearLogLines()
|
||||||
|
|
||||||
|
@ -125,7 +126,7 @@ func TestIBCPlugin(t *testing.T) {
|
||||||
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCPacketCreateTx{
|
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCPacketCreateTx{
|
||||||
Packet: packet,
|
Packet: packet,
|
||||||
}}))
|
}}))
|
||||||
assert.Equal(t, abci.CodeType_OK, res.Code, res.Log)
|
assert.Equal(abci.CodeType_OK, res.Code, res.Log)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||||
store.ClearLogLines()
|
store.ClearLogLines()
|
||||||
|
|
||||||
|
@ -133,7 +134,7 @@ func TestIBCPlugin(t *testing.T) {
|
||||||
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCPacketCreateTx{
|
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCPacketCreateTx{
|
||||||
Packet: packet,
|
Packet: packet,
|
||||||
}}))
|
}}))
|
||||||
assert.Equal(t, IBCCodePacketAlreadyExists, res.Code, res.Log)
|
assert.Equal(IBCCodePacketAlreadyExists, res.Code, res.Log)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||||
store.ClearLogLines()
|
store.ClearLogLines()
|
||||||
|
|
||||||
|
@ -175,7 +176,7 @@ func TestIBCPlugin(t *testing.T) {
|
||||||
Header: header,
|
Header: header,
|
||||||
Commit: commit,
|
Commit: commit,
|
||||||
}}))
|
}}))
|
||||||
assert.Equal(t, abci.CodeType_OK, res.Code, res.Log)
|
assert.Equal(abci.CodeType_OK, res.Code, res.Log)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||||
store.ClearLogLines()
|
store.ClearLogLines()
|
||||||
|
|
||||||
|
@ -190,10 +191,10 @@ func TestIBCPlugin(t *testing.T) {
|
||||||
Data: packetKey,
|
Data: packetKey,
|
||||||
Prove: true,
|
Prove: true,
|
||||||
})
|
})
|
||||||
assert.Nil(t, err)
|
assert.Nil(err)
|
||||||
var proof *merkle.IAVLProof
|
var proof *merkle.IAVLProof
|
||||||
err = wire.ReadBinaryBytes(resQuery.Proof, &proof)
|
err = wire.ReadBinaryBytes(resQuery.Proof, &proof)
|
||||||
assert.Nil(t, err)
|
assert.Nil(err)
|
||||||
|
|
||||||
// Post a packet
|
// Post a packet
|
||||||
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCPacketPostTx{
|
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCPacketPostTx{
|
||||||
|
@ -202,12 +203,13 @@ func TestIBCPlugin(t *testing.T) {
|
||||||
Packet: packet,
|
Packet: packet,
|
||||||
Proof: proof,
|
Proof: proof,
|
||||||
}}))
|
}}))
|
||||||
assert.Equal(t, abci.CodeType_OK, res.Code, res.Log)
|
assert.Equal(abci.CodeType_OK, res.Code, res.Log)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||||
store.ClearLogLines()
|
store.ClearLogLines()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIBCPluginBadCommit(t *testing.T) {
|
func TestIBCPluginBadCommit(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
eyesClient := eyes.NewLocalClient("", 0)
|
eyesClient := eyes.NewLocalClient("", 0)
|
||||||
store := types.NewKVCache(eyesClient)
|
store := types.NewKVCache(eyesClient)
|
||||||
|
@ -231,7 +233,7 @@ func TestIBCPluginBadCommit(t *testing.T) {
|
||||||
Genesis: string(genDocJSON_1),
|
Genesis: string(genDocJSON_1),
|
||||||
},
|
},
|
||||||
}}))
|
}}))
|
||||||
assert.True(t, res.IsOK(), res.Log)
|
assert.True(res.IsOK(), res.Log)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||||
store.ClearLogLines()
|
store.ClearLogLines()
|
||||||
|
|
||||||
|
@ -273,13 +275,14 @@ func TestIBCPluginBadCommit(t *testing.T) {
|
||||||
Header: header,
|
Header: header,
|
||||||
Commit: commit,
|
Commit: commit,
|
||||||
}}))
|
}}))
|
||||||
assert.Equal(t, IBCCodeInvalidCommit, res.Code, res.Log)
|
assert.Equal(IBCCodeInvalidCommit, res.Code, res.Log)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||||
store.ClearLogLines()
|
store.ClearLogLines()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIBCPluginBadProof(t *testing.T) {
|
func TestIBCPluginBadProof(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
eyesClient := eyes.NewLocalClient("", 0)
|
eyesClient := eyes.NewLocalClient("", 0)
|
||||||
store := types.NewKVCache(eyesClient)
|
store := types.NewKVCache(eyesClient)
|
||||||
|
@ -303,7 +306,7 @@ func TestIBCPluginBadProof(t *testing.T) {
|
||||||
Genesis: string(genDocJSON_1),
|
Genesis: string(genDocJSON_1),
|
||||||
},
|
},
|
||||||
}}))
|
}}))
|
||||||
assert.True(t, res.IsOK(), res.Log)
|
assert.True(res.IsOK(), res.Log)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||||
store.ClearLogLines()
|
store.ClearLogLines()
|
||||||
|
|
||||||
|
@ -318,7 +321,7 @@ func TestIBCPluginBadProof(t *testing.T) {
|
||||||
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCPacketCreateTx{
|
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCPacketCreateTx{
|
||||||
Packet: packet,
|
Packet: packet,
|
||||||
}}))
|
}}))
|
||||||
assert.Equal(t, abci.CodeType_OK, res.Code, res.Log)
|
assert.Equal(abci.CodeType_OK, res.Code, res.Log)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||||
store.ClearLogLines()
|
store.ClearLogLines()
|
||||||
|
|
||||||
|
@ -360,7 +363,7 @@ func TestIBCPluginBadProof(t *testing.T) {
|
||||||
Header: header,
|
Header: header,
|
||||||
Commit: commit,
|
Commit: commit,
|
||||||
}}))
|
}}))
|
||||||
assert.Equal(t, abci.CodeType_OK, res.Code, res.Log)
|
assert.Equal(abci.CodeType_OK, res.Code, res.Log)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||||
store.ClearLogLines()
|
store.ClearLogLines()
|
||||||
|
|
||||||
|
@ -375,10 +378,10 @@ func TestIBCPluginBadProof(t *testing.T) {
|
||||||
Data: packetKey,
|
Data: packetKey,
|
||||||
Prove: true,
|
Prove: true,
|
||||||
})
|
})
|
||||||
assert.Nil(t, err)
|
assert.Nil(err)
|
||||||
var proof *merkle.IAVLProof
|
var proof *merkle.IAVLProof
|
||||||
err = wire.ReadBinaryBytes(resQuery.Proof, &proof)
|
err = wire.ReadBinaryBytes(resQuery.Proof, &proof)
|
||||||
assert.Nil(t, err)
|
assert.Nil(err)
|
||||||
|
|
||||||
// Mutate the proof
|
// Mutate the proof
|
||||||
proof.InnerNodes[0].Height += 1
|
proof.InnerNodes[0].Height += 1
|
||||||
|
@ -390,7 +393,7 @@ func TestIBCPluginBadProof(t *testing.T) {
|
||||||
Packet: packet,
|
Packet: packet,
|
||||||
Proof: proof,
|
Proof: proof,
|
||||||
}}))
|
}}))
|
||||||
assert.Equal(t, IBCCodeInvalidProof, res.Code, res.Log)
|
assert.Equal(IBCCodeInvalidProof, res.Code, res.Log)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||||
store.ClearLogLines()
|
store.ClearLogLines()
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,8 @@ Address: D9B727742AA29FA638DC63D70813C976014C4CE0
|
||||||
*/
|
*/
|
||||||
func main() {
|
func main() {
|
||||||
tAcc := tests.PrivAccountFromSecret("test")
|
tAcc := tests.PrivAccountFromSecret("test")
|
||||||
fmt.Println("PrivKey:", fmt.Sprintf("%X", tAcc.PrivKey.Bytes()))
|
fmt.Printf("PrivKey:%X\n", tAcc.PrivKey.Bytes())
|
||||||
fmt.Println("PubKey:", fmt.Sprintf("%X", tAcc.Account.PubKey.Bytes()))
|
fmt.Printf("PubKey:%X\n", tAcc.Account.PubKey.Bytes())
|
||||||
fmt.Println("Address:", fmt.Sprintf("%X", tAcc.Account.PubKey.Address()))
|
fmt.Printf("Address:%X\n", tAcc.Account.PubKey.Address())
|
||||||
fmt.Println(string(wire.JSONBytesPretty(tAcc)))
|
fmt.Println(string(wire.JSONBytesPretty(tAcc)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,312 @@
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
|
"github.com/tendermint/basecoin/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
//--------------------------------------------------------
|
||||||
|
// test environment is a bunch of lists of accountns
|
||||||
|
|
||||||
|
type execTest struct {
|
||||||
|
chainID string
|
||||||
|
store types.KVStore
|
||||||
|
state *State
|
||||||
|
accIn types.PrivAccount
|
||||||
|
accOut types.PrivAccount
|
||||||
|
}
|
||||||
|
|
||||||
|
func newExecTest() *execTest {
|
||||||
|
et := &execTest{
|
||||||
|
chainID: "test_chain_id",
|
||||||
|
}
|
||||||
|
et.reset()
|
||||||
|
return et
|
||||||
|
}
|
||||||
|
|
||||||
|
func (et *execTest) signTx(tx *types.SendTx, accsIn ...types.PrivAccount) {
|
||||||
|
types.SignTx(et.chainID, tx, accsIn...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make tx from accsIn to et.accOut
|
||||||
|
func (et *execTest) getTx(seq int, accOut types.PrivAccount, accsIn ...types.PrivAccount) *types.SendTx {
|
||||||
|
return types.GetTx(seq, accOut, accsIn...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the final balance and expected balance for input and output accounts
|
||||||
|
func (et *execTest) exec(tx *types.SendTx, checkTx bool) (res abci.Result, inGot, inExp, outGot, outExp types.Coins) {
|
||||||
|
initBalIn := et.state.GetAccount(et.accIn.Account.PubKey.Address()).Balance
|
||||||
|
initBalOut := et.state.GetAccount(et.accOut.Account.PubKey.Address()).Balance
|
||||||
|
|
||||||
|
res = ExecTx(et.state, nil, tx, checkTx, nil)
|
||||||
|
|
||||||
|
endBalIn := et.state.GetAccount(et.accIn.Account.PubKey.Address()).Balance
|
||||||
|
endBalOut := et.state.GetAccount(et.accOut.Account.PubKey.Address()).Balance
|
||||||
|
decrBalInExp := tx.Outputs[0].Coins.Plus(types.Coins{tx.Fee}) //expected decrease in balance In
|
||||||
|
return res, endBalIn, initBalIn.Minus(decrBalInExp), endBalOut, initBalOut.Plus(tx.Outputs[0].Coins)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (et *execTest) acc2State(accs ...types.PrivAccount) {
|
||||||
|
for _, acc := range accs {
|
||||||
|
et.state.SetAccount(acc.Account.PubKey.Address(), &acc.Account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//reset everything. state is empty
|
||||||
|
func (et *execTest) reset() {
|
||||||
|
et.accIn = types.MakeAcc("foo")
|
||||||
|
et.accOut = types.MakeAcc("bar")
|
||||||
|
|
||||||
|
et.store = types.NewMemKVStore()
|
||||||
|
et.state = NewState(et.store)
|
||||||
|
et.state.SetChainID(et.chainID)
|
||||||
|
|
||||||
|
// NOTE we dont run acc2State here
|
||||||
|
// so we can test non-existing accounts
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------
|
||||||
|
|
||||||
|
func TestGetInputs(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
et := newExecTest()
|
||||||
|
|
||||||
|
//nil submissions
|
||||||
|
acc, res := getInputs(nil, nil)
|
||||||
|
assert.True(res.IsOK(), "getInputs: error on nil submission")
|
||||||
|
assert.Zero(len(acc), "getInputs: accounts returned on nil submission")
|
||||||
|
|
||||||
|
//test getInputs for registered, non-registered account
|
||||||
|
et.reset()
|
||||||
|
txs := types.Accs2TxInputs(1, et.accIn)
|
||||||
|
acc, res = getInputs(et.state, txs)
|
||||||
|
assert.True(res.IsErr(), "getInputs: expected error when using getInput with non-registered Input")
|
||||||
|
|
||||||
|
et.acc2State(et.accIn)
|
||||||
|
acc, res = getInputs(et.state, txs)
|
||||||
|
assert.True(res.IsOK(), "getInputs: expected to getInput from registered Input")
|
||||||
|
|
||||||
|
//test sending duplicate accounts
|
||||||
|
et.reset()
|
||||||
|
et.acc2State(et.accIn, et.accIn, et.accIn)
|
||||||
|
txs = types.Accs2TxInputs(1, et.accIn, et.accIn, et.accIn)
|
||||||
|
acc, res = getInputs(et.state, txs)
|
||||||
|
assert.True(res.IsErr(), "getInputs: expected error when sending duplicate accounts")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetOrMakeOutputs(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
et := newExecTest()
|
||||||
|
|
||||||
|
//nil submissions
|
||||||
|
acc, res := getOrMakeOutputs(nil, nil, nil)
|
||||||
|
assert.True(res.IsOK(), "getOrMakeOutputs: error on nil submission")
|
||||||
|
assert.Zero(len(acc), "getOrMakeOutputs: accounts returned on nil submission")
|
||||||
|
|
||||||
|
//test sending duplicate accounts
|
||||||
|
et.reset()
|
||||||
|
txs := types.Accs2TxOutputs(et.accIn, et.accIn, et.accIn)
|
||||||
|
_, res = getOrMakeOutputs(et.state, nil, txs)
|
||||||
|
assert.True(res.IsErr(), "getOrMakeOutputs: expected error when sending duplicate accounts")
|
||||||
|
|
||||||
|
//test sending to existing/new account
|
||||||
|
et.reset()
|
||||||
|
txs1 := types.Accs2TxOutputs(et.accIn)
|
||||||
|
txs2 := types.Accs2TxOutputs(et.accOut)
|
||||||
|
|
||||||
|
et.acc2State(et.accIn)
|
||||||
|
_, res = getOrMakeOutputs(et.state, nil, txs1)
|
||||||
|
assert.True(res.IsOK(), "getOrMakeOutputs: error when sending to existing account")
|
||||||
|
|
||||||
|
mapRes2, res := getOrMakeOutputs(et.state, nil, txs2)
|
||||||
|
assert.True(res.IsOK(), "getOrMakeOutputs: error when sending to new account")
|
||||||
|
|
||||||
|
//test the map results
|
||||||
|
_, map2ok := mapRes2[string(txs2[0].Address)]
|
||||||
|
assert.True(map2ok, "getOrMakeOutputs: account output does not contain new account map item")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateInputsBasic(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
et := newExecTest()
|
||||||
|
|
||||||
|
//validate input basic
|
||||||
|
txs := types.Accs2TxInputs(1, et.accIn)
|
||||||
|
res := validateInputsBasic(txs)
|
||||||
|
assert.True(res.IsOK(), "validateInputsBasic: expected no error on good tx input. Error: %v", res.Error())
|
||||||
|
|
||||||
|
txs[0].Coins[0].Amount = 0
|
||||||
|
res = validateInputsBasic(txs)
|
||||||
|
assert.True(res.IsErr(), "validateInputsBasic: expected error on bad tx input")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateInputsAdvanced(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
et := newExecTest()
|
||||||
|
|
||||||
|
//create three temp accounts for the test
|
||||||
|
accIn1 := types.MakeAcc("foox")
|
||||||
|
accIn2 := types.MakeAcc("fooy")
|
||||||
|
accIn3 := types.MakeAcc("fooz")
|
||||||
|
|
||||||
|
//validate inputs advanced
|
||||||
|
txs := et.getTx(1, et.accOut, accIn1, accIn2, accIn3)
|
||||||
|
|
||||||
|
et.acc2State(accIn1, accIn2, accIn3, et.accOut)
|
||||||
|
accMap, res := getInputs(et.state, txs.Inputs)
|
||||||
|
assert.True(res.IsOK(), "validateInputsAdvanced: error retrieving accMap. Error: %v", res.Error())
|
||||||
|
signBytes := txs.SignBytes(et.chainID)
|
||||||
|
|
||||||
|
//test bad case, unsigned
|
||||||
|
totalCoins, res := validateInputsAdvanced(accMap, signBytes, txs.Inputs)
|
||||||
|
assert.True(res.IsErr(), "validateInputsAdvanced: expected an error on an unsigned tx input")
|
||||||
|
|
||||||
|
//test good case sgined
|
||||||
|
et.signTx(txs, accIn1, accIn2, accIn3, et.accOut)
|
||||||
|
totalCoins, res = validateInputsAdvanced(accMap, signBytes, txs.Inputs)
|
||||||
|
assert.True(res.IsOK(), "validateInputsAdvanced: expected no error on good tx input. Error: %v", res.Error())
|
||||||
|
|
||||||
|
txsTotalCoins := txs.Inputs[0].Coins.
|
||||||
|
Plus(txs.Inputs[1].Coins).
|
||||||
|
Plus(txs.Inputs[2].Coins)
|
||||||
|
|
||||||
|
assert.True(totalCoins.IsEqual(txsTotalCoins),
|
||||||
|
"ValidateInputsAdvanced: transaction total coins are not equal: got %v, expected %v", txsTotalCoins, totalCoins)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateInputAdvanced(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
et := newExecTest()
|
||||||
|
|
||||||
|
//validate input advanced
|
||||||
|
txs := et.getTx(1, et.accOut, et.accIn)
|
||||||
|
|
||||||
|
et.acc2State(et.accIn, et.accOut)
|
||||||
|
signBytes := txs.SignBytes(et.chainID)
|
||||||
|
|
||||||
|
//unsigned case
|
||||||
|
res := validateInputAdvanced(&et.accIn.Account, signBytes, txs.Inputs[0])
|
||||||
|
assert.True(res.IsErr(), "validateInputAdvanced: expected error on tx input without signature")
|
||||||
|
|
||||||
|
//good signed case
|
||||||
|
et.signTx(txs, et.accIn, et.accOut)
|
||||||
|
res = validateInputAdvanced(&et.accIn.Account, signBytes, txs.Inputs[0])
|
||||||
|
assert.True(res.IsOK(), "validateInputAdvanced: expected no error on good tx input. Error: %v", res.Error())
|
||||||
|
|
||||||
|
//bad sequence case
|
||||||
|
et.accIn.Sequence = 1
|
||||||
|
et.signTx(txs, et.accIn, et.accOut)
|
||||||
|
res = validateInputAdvanced(&et.accIn.Account, signBytes, txs.Inputs[0])
|
||||||
|
assert.Equal(abci.CodeType_BaseInvalidSequence, res.Code, "validateInputAdvanced: expected error on tx input with bad sequence")
|
||||||
|
et.accIn.Sequence = 0 //restore sequence
|
||||||
|
|
||||||
|
//bad balance case
|
||||||
|
et.accIn.Balance = types.Coins{{"mycoin", 2}}
|
||||||
|
et.signTx(txs, et.accIn, et.accOut)
|
||||||
|
res = validateInputAdvanced(&et.accIn.Account, signBytes, txs.Inputs[0])
|
||||||
|
assert.Equal(abci.CodeType_BaseInsufficientFunds, res.Code,
|
||||||
|
"validateInputAdvanced: expected error on tx input with insufficient funds %v", et.accIn.Sequence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateOutputsAdvanced(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
et := newExecTest()
|
||||||
|
|
||||||
|
//validateOutputsBasic
|
||||||
|
txs := types.Accs2TxOutputs(et.accIn)
|
||||||
|
res := validateOutputsBasic(txs)
|
||||||
|
assert.True(res.IsOK(), "validateOutputsBasic: expected no error on good tx output. Error: %v", res.Error())
|
||||||
|
|
||||||
|
txs[0].Coins[0].Amount = 0
|
||||||
|
res = validateOutputsBasic(txs)
|
||||||
|
assert.True(res.IsErr(), "validateInputBasic: expected error on bad tx output. Error: %v", res.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSumOutput(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
et := newExecTest()
|
||||||
|
|
||||||
|
//SumOutput
|
||||||
|
txs := types.Accs2TxOutputs(et.accIn, et.accOut)
|
||||||
|
total := sumOutputs(txs)
|
||||||
|
assert.True(total.IsEqual(txs[0].Coins.Plus(txs[1].Coins)), "sumOutputs: total coins are not equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAdjustBy(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
et := newExecTest()
|
||||||
|
|
||||||
|
//adjustByInputs/adjustByOutputs
|
||||||
|
//sending transaction from accIn to accOut
|
||||||
|
initBalIn := et.accIn.Account.Balance
|
||||||
|
initBalOut := et.accOut.Account.Balance
|
||||||
|
et.acc2State(et.accIn, et.accOut)
|
||||||
|
|
||||||
|
txIn := types.Accs2TxInputs(1, et.accIn)
|
||||||
|
txOut := types.Accs2TxOutputs(et.accOut)
|
||||||
|
accMap, _ := getInputs(et.state, txIn)
|
||||||
|
accMap, _ = getOrMakeOutputs(et.state, accMap, txOut)
|
||||||
|
|
||||||
|
adjustByInputs(et.state, accMap, txIn)
|
||||||
|
adjustByOutputs(et.state, accMap, txOut, false)
|
||||||
|
|
||||||
|
endBalIn := accMap[string(et.accIn.Account.PubKey.Address())].Balance
|
||||||
|
endBalOut := accMap[string(et.accOut.Account.PubKey.Address())].Balance
|
||||||
|
decrBalIn := initBalIn.Minus(endBalIn)
|
||||||
|
incrBalOut := endBalOut.Minus(initBalOut)
|
||||||
|
|
||||||
|
assert.True(decrBalIn.IsEqual(txIn[0].Coins),
|
||||||
|
"adjustByInputs: total coins are not equal. diff: %v, tx: %v", decrBalIn.String(), txIn[0].Coins.String())
|
||||||
|
assert.True(incrBalOut.IsEqual(txOut[0].Coins),
|
||||||
|
"adjustByInputs: total coins are not equal. diff: %v, tx: %v", incrBalOut.String(), txOut[0].Coins.String())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExecTx(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
et := newExecTest()
|
||||||
|
|
||||||
|
//ExecTx
|
||||||
|
txs := et.getTx(1, et.accOut, et.accIn)
|
||||||
|
et.acc2State(et.accIn)
|
||||||
|
et.acc2State(et.accOut)
|
||||||
|
et.signTx(txs, et.accIn)
|
||||||
|
|
||||||
|
//Bad Balance
|
||||||
|
et.accIn.Balance = types.Coins{{"mycoin", 2}}
|
||||||
|
et.acc2State(et.accIn)
|
||||||
|
res, _, _, _, _ := et.exec(txs, true)
|
||||||
|
assert.True(res.IsErr(), "ExecTx/Bad CheckTx: Expected error return from ExecTx, returned: %v", res)
|
||||||
|
|
||||||
|
res, balIn, balInExp, balOut, balOutExp := et.exec(txs, false)
|
||||||
|
assert.True(res.IsErr(), "ExecTx/Bad DeliverTx: Expected error return from ExecTx, returned: %v", res)
|
||||||
|
assert.False(balIn.IsEqual(balInExp),
|
||||||
|
"ExecTx/Bad DeliverTx: balance shouldn't be equal for accIn: got %v, expected: %v", balIn, balInExp)
|
||||||
|
assert.False(balOut.IsEqual(balOutExp),
|
||||||
|
"ExecTx/Bad DeliverTx: balance shouldn't be equal for accOut: got %v, expected: %v", balOut, balOutExp)
|
||||||
|
|
||||||
|
//Regular CheckTx
|
||||||
|
et.reset()
|
||||||
|
et.acc2State(et.accIn)
|
||||||
|
et.acc2State(et.accOut)
|
||||||
|
res, _, _, _, _ = et.exec(txs, true)
|
||||||
|
assert.True(res.IsOK(), "ExecTx/Good CheckTx: Expected OK return from ExecTx, Error: %v", res)
|
||||||
|
|
||||||
|
//Regular DeliverTx
|
||||||
|
et.reset()
|
||||||
|
et.acc2State(et.accIn)
|
||||||
|
et.acc2State(et.accOut)
|
||||||
|
res, balIn, balInExp, balOut, balOutExp = et.exec(txs, false)
|
||||||
|
assert.True(res.IsOK(), "ExecTx/Good DeliverTx: Expected OK return from ExecTx, Error: %v", res)
|
||||||
|
assert.True(balIn.IsEqual(balInExp),
|
||||||
|
"ExecTx/good DeliverTx: unexpected change in input balance, got: %v, expected: %v", balIn, balInExp)
|
||||||
|
assert.True(balOut.IsEqual(balOutExp),
|
||||||
|
"ExecTx/good DeliverTx: unexpected change in output balance, got: %v, expected: %v", balOut, balOutExp)
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestState(t *testing.T) {
|
func TestState(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
//States and Stores for tests
|
//States and Stores for tests
|
||||||
store := types.NewMemKVStore()
|
store := types.NewMemKVStore()
|
||||||
|
@ -63,44 +64,33 @@ func TestState(t *testing.T) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
//define the test list
|
|
||||||
testList := []struct {
|
|
||||||
testPass func() bool
|
|
||||||
errMsg string
|
|
||||||
}{
|
|
||||||
//test chainID
|
//test chainID
|
||||||
{func() bool { state.SetChainID("testchain"); return state.GetChainID() == "testchain" },
|
state.SetChainID("testchain")
|
||||||
"ChainID is improperly stored"},
|
assert.Equal(state.GetChainID(), "testchain", "ChainID is improperly stored")
|
||||||
|
|
||||||
//test basic retrieve
|
//test basic retrieve
|
||||||
{func() bool { setRecords(state); return storeHasAll(state) },
|
setRecords(state)
|
||||||
"state doesn't retrieve after Set"},
|
assert.True(storeHasAll(state), "state doesn't retrieve after Set")
|
||||||
|
|
||||||
// Test account retrieve
|
// Test account retrieve
|
||||||
{func() bool { state.SetAccount(dumAddr, acc); return state.GetAccount(dumAddr).Sequence == 1 },
|
state.SetAccount(dumAddr, acc)
|
||||||
"GetAccount not retrieving"},
|
assert.Equal(state.GetAccount(dumAddr).Sequence, 1, "GetAccount not retrieving")
|
||||||
|
|
||||||
//Test CacheWrap with local mem store
|
//Test CacheWrap with local mem store
|
||||||
{func() bool { reset(); setRecords(cache); return !storeHasAll(store) },
|
reset()
|
||||||
"store retrieving before CacheSync"},
|
setRecords(cache)
|
||||||
{func() bool { cache.CacheSync(); return storeHasAll(store) },
|
assert.False(storeHasAll(store), "store retrieving before CacheSync")
|
||||||
"store doesn't retrieve after CacheSync"},
|
cache.CacheSync()
|
||||||
|
assert.True(storeHasAll(store), "store doesn't retrieve after CacheSync")
|
||||||
|
|
||||||
//Test Commit on state with non-merkle store
|
//Test Commit on state with non-merkle store
|
||||||
{func() bool { return !state.Commit().IsOK() },
|
assert.True(state.Commit().IsErr(), "Commit shouldn't work with non-merkle store")
|
||||||
"Commit shouldn't work with non-merkle store"},
|
|
||||||
|
|
||||||
//Test CacheWrap with merkleeyes client store
|
//Test CacheWrap with merkleeyes client store
|
||||||
{func() bool { useEyesCli(); setRecords(cache); return !storeHasAll(eyesCli) },
|
useEyesCli()
|
||||||
"eyesCli retrieving before Commit"},
|
setRecords(cache)
|
||||||
{func() bool { cache.CacheSync(); return state.Commit().IsOK() },
|
assert.False(storeHasAll(eyesCli), "eyesCli retrieving before Commit")
|
||||||
"Bad Commit"},
|
cache.CacheSync()
|
||||||
{func() bool { return storeHasAll(eyesCli) },
|
assert.True(state.Commit().IsOK(), "Bad Commit")
|
||||||
"eyesCli doesn't retrieve after Commit"},
|
assert.True(storeHasAll(eyesCli), "eyesCli doesn't retrieve after Commit")
|
||||||
}
|
|
||||||
|
|
||||||
//execute the tests
|
|
||||||
for _, tl := range testList {
|
|
||||||
assert.True(t, tl.testPass(), tl.errMsg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@ import (
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
cmn "github.com/tendermint/go-common"
|
cmn "github.com/tendermint/go-common"
|
||||||
crypto "github.com/tendermint/go-crypto"
|
crypto "github.com/tendermint/go-crypto"
|
||||||
"github.com/tendermint/go-rpc/client"
|
rpcclient "github.com/tendermint/go-rpc/client"
|
||||||
"github.com/tendermint/go-rpc/types"
|
"github.com/tendermint/go-rpc/types"
|
||||||
"github.com/tendermint/go-wire"
|
wire "github.com/tendermint/go-wire"
|
||||||
_ "github.com/tendermint/tendermint/rpc/core/types" // Register RPCResponse > Result types
|
_ "github.com/tendermint/tendermint/rpc/core/types" // Register RPCResponse > Result types
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -72,7 +72,8 @@ 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", cmn.Arr(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)
|
||||||
|
@ -122,7 +123,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", cmn.Arr(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)
|
||||||
|
|
|
@ -57,7 +57,7 @@ func TestSendTx(t *testing.T) {
|
||||||
txBytes := wire.BinaryBytes(types.TxS{tx})
|
txBytes := wire.BinaryBytes(types.TxS{tx})
|
||||||
res := bcApp.DeliverTx(txBytes)
|
res := bcApp.DeliverTx(txBytes)
|
||||||
// t.Log(res)
|
// t.Log(res)
|
||||||
assert.False(t, res.IsErr(), "Failed: %v", res.Error())
|
assert.True(t, res.IsOK(), "Failed: %v", res.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSequence(t *testing.T) {
|
func TestSequence(t *testing.T) {
|
||||||
|
@ -108,11 +108,11 @@ func TestSequence(t *testing.T) {
|
||||||
// Write request
|
// Write request
|
||||||
txBytes := wire.BinaryBytes(struct{ types.Tx }{tx})
|
txBytes := wire.BinaryBytes(struct{ types.Tx }{tx})
|
||||||
res := bcApp.DeliverTx(txBytes)
|
res := bcApp.DeliverTx(txBytes)
|
||||||
assert.False(t, res.IsErr(), "DeliverTx error: %v", res.Error())
|
assert.True(t, res.IsOK(), "DeliverTx error: %v", res.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
res := bcApp.Commit()
|
res := bcApp.Commit()
|
||||||
assert.False(t, res.IsErr(), "Failed Commit: %v", res.Error())
|
assert.True(t, res.IsOK(), "Failed Commit: %v", res.Error())
|
||||||
|
|
||||||
t.Log("-------------------- RANDOM SENDS --------------------")
|
t.Log("-------------------- RANDOM SENDS --------------------")
|
||||||
|
|
||||||
|
@ -152,6 +152,6 @@ func TestSequence(t *testing.T) {
|
||||||
// Write request
|
// Write request
|
||||||
txBytes := wire.BinaryBytes(struct{ types.Tx }{tx})
|
txBytes := wire.BinaryBytes(struct{ types.Tx }{tx})
|
||||||
res := bcApp.DeliverTx(txBytes)
|
res := bcApp.DeliverTx(txBytes)
|
||||||
assert.False(t, res.IsErr(), "DeliverTx error: %v", res.Error())
|
assert.True(t, res.IsOK(), "DeliverTx error: %v", res.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,9 @@ func TestNilAccount(t *testing.T) {
|
||||||
|
|
||||||
//test Copy
|
//test Copy
|
||||||
accCopy := acc.Copy()
|
accCopy := acc.Copy()
|
||||||
assert.True(t, &acc != accCopy, "Account Copy Error")
|
//note that the assert.True is used instead of assert.Equal because looking at pointers
|
||||||
assert.True(t, acc.Sequence == accCopy.Sequence)
|
assert.True(t, &acc != accCopy, "Account Copy Error, acc1: %v, acc2: %v", &acc, accCopy)
|
||||||
|
assert.Equal(t, acc.Sequence, accCopy.Sequence)
|
||||||
|
|
||||||
//test sending nils for panic
|
//test sending nils for panic
|
||||||
var nilAcc *Account
|
var nilAcc *Account
|
||||||
|
|
|
@ -2,6 +2,8 @@ package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -11,14 +13,63 @@ type Coin struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (coin Coin) String() string {
|
func (coin Coin) String() string {
|
||||||
return fmt.Sprintf("(%v %v)",
|
return fmt.Sprintf("%v%v", coin.Amount, coin.Denom)
|
||||||
coin.Denom, coin.Amount)
|
}
|
||||||
|
|
||||||
|
//regex codes for extracting coins from string
|
||||||
|
var reDenom = regexp.MustCompile("([^\\d\\W]+)")
|
||||||
|
var reAmt = regexp.MustCompile("(\\d+)")
|
||||||
|
|
||||||
|
func ParseCoin(str string) (Coin, error) {
|
||||||
|
|
||||||
|
var coin Coin
|
||||||
|
|
||||||
|
if len(str) > 0 {
|
||||||
|
amt, err := strconv.Atoi(reAmt.FindString(str))
|
||||||
|
if err != nil {
|
||||||
|
return coin, err
|
||||||
|
}
|
||||||
|
denom := reDenom.FindString(str)
|
||||||
|
coin = Coin{denom, int64(amt)}
|
||||||
|
}
|
||||||
|
|
||||||
|
return coin, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
|
|
||||||
type Coins []Coin
|
type Coins []Coin
|
||||||
|
|
||||||
|
func (coins Coins) String() string {
|
||||||
|
if len(coins) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
out := ""
|
||||||
|
for _, coin := range coins {
|
||||||
|
out += fmt.Sprintf("%v,", coin.String())
|
||||||
|
}
|
||||||
|
return out[:len(out)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseCoins(str string) (Coins, error) {
|
||||||
|
|
||||||
|
split := strings.Split(str, ",")
|
||||||
|
var coins []Coin
|
||||||
|
|
||||||
|
for _, el := range split {
|
||||||
|
if len(el) > 0 {
|
||||||
|
coin, err := ParseCoin(el)
|
||||||
|
if err != nil {
|
||||||
|
return coins, err
|
||||||
|
}
|
||||||
|
coins = append(coins, coin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return coins, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Must be sorted, and not have 0 amounts
|
// Must be sorted, and not have 0 amounts
|
||||||
func (coins Coins) IsValid() bool {
|
func (coins Coins) IsValid() bool {
|
||||||
switch len(coins) {
|
switch len(coins) {
|
||||||
|
|
|
@ -3,12 +3,12 @@ package types
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
cmn "github.com/tendermint/go-common"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCoins(t *testing.T) {
|
func TestCoins(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
//Define the coins to be used in tests
|
//Define the coins to be used in tests
|
||||||
good := Coins{
|
good := Coins{
|
||||||
|
@ -42,24 +42,44 @@ func TestCoins(t *testing.T) {
|
||||||
Coin{"MINERAL", 1},
|
Coin{"MINERAL", 1},
|
||||||
}
|
}
|
||||||
|
|
||||||
//define the list of coin tests
|
assert.True(good.IsValid(), "Coins are valid")
|
||||||
var testList = []struct {
|
assert.True(good.IsPositive(), "Expected coins to be positive: %v", good)
|
||||||
testPass bool
|
assert.True(good.IsGTE(empty), "Expected %v to be >= %v", good, empty)
|
||||||
errMsg string
|
assert.False(neg.IsPositive(), "Expected neg coins to not be positive: %v", neg)
|
||||||
}{
|
assert.Zero(len(sum), "Expected 0 coins")
|
||||||
{good.IsValid(), "Coins are valid"},
|
assert.False(badSort1.IsValid(), "Coins are not sorted")
|
||||||
{good.IsPositive(), cmn.Fmt("Expected coins to be positive: %v", good)},
|
assert.False(badSort2.IsValid(), "Coins are not sorted")
|
||||||
{good.IsGTE(empty), cmn.Fmt("Expected %v to be >= %v", good, empty)},
|
assert.False(badAmt.IsValid(), "Coins cannot include 0 amounts")
|
||||||
{!neg.IsPositive(), cmn.Fmt("Expected neg coins to not be positive: %v", neg)},
|
assert.False(dup.IsValid(), "Duplicate coin")
|
||||||
{len(sum) == 0, "Expected 0 coins"},
|
|
||||||
{!badSort1.IsValid(), "Coins are not sorted"},
|
}
|
||||||
{!badSort2.IsValid(), "Coins are not sorted"},
|
|
||||||
{!badAmt.IsValid(), "Coins cannot include 0 amounts"},
|
//Test the parse coin and parse coins functionality
|
||||||
{!dup.IsValid(), "Duplicate coin"},
|
func TestParse(t *testing.T) {
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
|
||||||
|
makeCoin := func(str string) Coin {
|
||||||
|
coin, err := ParseCoin(str)
|
||||||
|
require.Nil(err)
|
||||||
|
return coin
|
||||||
}
|
}
|
||||||
|
|
||||||
//execute the tests
|
makeCoins := func(str string) Coins {
|
||||||
for _, tl := range testList {
|
coin, err := ParseCoins(str)
|
||||||
assert.True(t, tl.testPass, tl.errMsg)
|
require.Nil(err)
|
||||||
|
return coin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//testing ParseCoin Function
|
||||||
|
assert.Equal(Coin{}, makeCoin(""), "ParseCoin makes bad empty coin")
|
||||||
|
assert.Equal(Coin{"fooCoin", 1}, makeCoin("1fooCoin"), "ParseCoin makes bad coins")
|
||||||
|
assert.Equal(Coin{"barCoin", 10}, makeCoin("10 barCoin"), "ParseCoin makes bad coins")
|
||||||
|
|
||||||
|
//testing ParseCoins Function
|
||||||
|
assert.True(Coins{{"fooCoin", 1}}.IsEqual(makeCoins("1fooCoin")),
|
||||||
|
"ParseCoins doesn't parse a single coin")
|
||||||
|
assert.True(Coins{{"barCoin", 99}, {"fooCoin", 1}}.IsEqual(makeCoins("99barCoin,1fooCoin")),
|
||||||
|
"ParseCoins doesn't properly parse two coins")
|
||||||
|
assert.True(Coins{{"barCoin", 99}, {"fooCoin", 1}}.IsEqual(makeCoins("99 barCoin, 1 fooCoin")),
|
||||||
|
"ParseCoins doesn't properly parse two coins which use spaces")
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestKVStore(t *testing.T) {
|
func TestKVStore(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
//stores to be tested
|
//stores to be tested
|
||||||
ms := NewMemKVStore()
|
ms := NewMemKVStore()
|
||||||
|
@ -40,40 +41,30 @@ func TestKVStore(t *testing.T) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
//define the test list
|
|
||||||
var testList = []struct {
|
|
||||||
testPass func() bool
|
|
||||||
errMsg string
|
|
||||||
}{
|
|
||||||
//test read/write for MemKVStore
|
//test read/write for MemKVStore
|
||||||
{func() bool { setRecords(ms); return storeHasAll(ms) },
|
setRecords(ms)
|
||||||
"MemKVStore doesn't retrieve after Set"},
|
assert.True(storeHasAll(ms), "MemKVStore doesn't retrieve after Set")
|
||||||
|
|
||||||
//test read/write for KVCache
|
//test read/write for KVCache
|
||||||
{func() bool { setRecords(kvc); return storeHasAll(kvc) },
|
setRecords(kvc)
|
||||||
"KVCache doesn't retrieve after Set"},
|
assert.True(storeHasAll(kvc), "KVCache doesn't retrieve after Set")
|
||||||
|
|
||||||
//test reset
|
//test reset
|
||||||
{func() bool { kvc.Reset(); return !storeHasAll(kvc) },
|
kvc.Reset()
|
||||||
"KVCache retrieving after reset"},
|
assert.False(storeHasAll(kvc), "KVCache retrieving after reset")
|
||||||
|
|
||||||
//test sync
|
//test sync
|
||||||
{func() bool { setRecords(kvc); return !storeHasAll(store) },
|
setRecords(kvc)
|
||||||
"store retrieving before synced"},
|
assert.False(storeHasAll(store), "store retrieving before synced")
|
||||||
{func() bool { kvc.Sync(); return storeHasAll(store) },
|
kvc.Sync()
|
||||||
"store isn't retrieving after synced"},
|
assert.True(storeHasAll(store), "store isn't retrieving after synced")
|
||||||
|
|
||||||
//test logging
|
//test logging
|
||||||
{func() bool { return len(kvc.GetLogLines()) == 0 },
|
assert.Zero(len(kvc.GetLogLines()), "logging events existed before using SetLogging")
|
||||||
"logging events existed before using SetLogging"},
|
kvc.SetLogging()
|
||||||
{func() bool { kvc.SetLogging(); setRecords(kvc); return len(kvc.GetLogLines()) == 2 },
|
setRecords(kvc)
|
||||||
"incorrect number of logging events recorded"},
|
assert.Equal(len(kvc.GetLogLines()), 2, "incorrect number of logging events recorded")
|
||||||
{func() bool { kvc.ClearLogLines(); return len(kvc.GetLogLines()) == 0 },
|
kvc.ClearLogLines()
|
||||||
"logging events still exists after ClearLogLines"},
|
assert.Zero(len(kvc.GetLogLines()), "logging events still exists after ClearLogLines")
|
||||||
}
|
|
||||||
|
|
||||||
//execute the tests
|
|
||||||
for _, tl := range testList {
|
|
||||||
assert.True(t, tl.testPass(), tl.errMsg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,25 +32,10 @@ func (d *Dummy) EndBlock(store KVStore, height uint64) (res abci.ResponseEndBloc
|
||||||
//----------------------------------
|
//----------------------------------
|
||||||
|
|
||||||
func TestPlugin(t *testing.T) {
|
func TestPlugin(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
plugins := NewPlugins()
|
plugins := NewPlugins()
|
||||||
|
assert.Zero(len(plugins.GetList()), "plugins object init with a objects")
|
||||||
//define the test list
|
plugins.RegisterPlugin(&Dummy{})
|
||||||
var testList = []struct {
|
assert.Equal(len(plugins.GetList()), 1, "plugin wasn't added to plist after registered")
|
||||||
testPass func() bool
|
assert.Equal(plugins.GetByName("dummy").Name(), "dummy", "plugin wasn't retrieved properly with GetByName")
|
||||||
errMsg string
|
|
||||||
}{
|
|
||||||
{func() bool { return (len(plugins.GetList()) == 0) },
|
|
||||||
"plugins object init with a objects"},
|
|
||||||
{func() bool { plugins.RegisterPlugin(&Dummy{}); return (len(plugins.GetList()) == 1) },
|
|
||||||
"plugin wasn't added to plist after registered"},
|
|
||||||
{func() bool { return (plugins.GetByName("dummy").Name() == "dummy") },
|
|
||||||
"plugin wasn't retrieved properly with GetByName"},
|
|
||||||
}
|
|
||||||
|
|
||||||
//execute the tests
|
|
||||||
for _, tl := range testList {
|
|
||||||
assert.True(t, tl.testPass(), tl.errMsg)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,3 +43,62 @@ func RandAccounts(num int, minAmount int64, maxAmount int64) []PrivAccount {
|
||||||
|
|
||||||
return privAccs
|
return privAccs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//func MakeAccs(secrets ...string) (accs []PrivAccount) {
|
||||||
|
// for _, secret := range secrets {
|
||||||
|
// privAcc := PrivAccountFromSecret(secret)
|
||||||
|
// privAcc.Account.Balance = Coins{{"mycoin", 7}}
|
||||||
|
// accs = append(accs, privAcc)
|
||||||
|
// }
|
||||||
|
// return
|
||||||
|
//}
|
||||||
|
|
||||||
|
func MakeAcc(secret string) PrivAccount {
|
||||||
|
privAcc := PrivAccountFromSecret(secret)
|
||||||
|
privAcc.Account.Balance = Coins{{"mycoin", 7}}
|
||||||
|
return privAcc
|
||||||
|
}
|
||||||
|
|
||||||
|
func Accs2TxInputs(seq int, accs ...PrivAccount) []TxInput {
|
||||||
|
var txs []TxInput
|
||||||
|
for _, acc := range accs {
|
||||||
|
tx := NewTxInput(
|
||||||
|
acc.Account.PubKey,
|
||||||
|
Coins{{"mycoin", 5}},
|
||||||
|
seq)
|
||||||
|
txs = append(txs, tx)
|
||||||
|
}
|
||||||
|
return txs
|
||||||
|
}
|
||||||
|
|
||||||
|
//turn a list of accounts into basic list of transaction outputs
|
||||||
|
func Accs2TxOutputs(accs ...PrivAccount) []TxOutput {
|
||||||
|
var txs []TxOutput
|
||||||
|
for _, acc := range accs {
|
||||||
|
tx := TxOutput{
|
||||||
|
acc.Account.PubKey.Address(),
|
||||||
|
Coins{{"mycoin", 4}}}
|
||||||
|
txs = append(txs, tx)
|
||||||
|
}
|
||||||
|
return txs
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTx(seq int, accOut PrivAccount, accsIn ...PrivAccount) *SendTx {
|
||||||
|
txs := &SendTx{
|
||||||
|
Gas: 0,
|
||||||
|
Fee: Coin{"mycoin", 1},
|
||||||
|
Inputs: Accs2TxInputs(seq, accsIn...),
|
||||||
|
Outputs: Accs2TxOutputs(accOut),
|
||||||
|
}
|
||||||
|
|
||||||
|
return txs
|
||||||
|
}
|
||||||
|
|
||||||
|
func SignTx(chainID string, tx *SendTx, accs ...PrivAccount) {
|
||||||
|
signBytes := tx.SignBytes(chainID)
|
||||||
|
for i, _ := range tx.Inputs {
|
||||||
|
tx.Inputs[i].Signature = crypto.SignatureS{accs[i].Sign(signBytes)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -40,8 +40,8 @@ var txMapper data.Mapper
|
||||||
// register both private key types with go-data (and thus go-wire)
|
// register both private key types with go-data (and thus go-wire)
|
||||||
func init() {
|
func init() {
|
||||||
txMapper = data.NewMapper(TxS{}).
|
txMapper = data.NewMapper(TxS{}).
|
||||||
RegisterInterface(&SendTx{}, TxNameSend, TxTypeSend).
|
RegisterImplementation(&SendTx{}, TxNameSend, TxTypeSend).
|
||||||
RegisterInterface(&AppTx{}, TxNameApp, TxTypeApp)
|
RegisterImplementation(&AppTx{}, TxNameApp, TxTypeApp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TxS add json serialization to Tx
|
// TxS add json serialization to Tx
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"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"
|
|
||||||
crypto "github.com/tendermint/go-crypto"
|
crypto "github.com/tendermint/go-crypto"
|
||||||
data "github.com/tendermint/go-data"
|
data "github.com/tendermint/go-data"
|
||||||
)
|
)
|
||||||
|
@ -40,11 +40,11 @@ func TestSendTxSignable(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
signBytes := sendTx.SignBytes(chainID)
|
signBytes := sendTx.SignBytes(chainID)
|
||||||
signBytesHex := cmn.Fmt("%X", signBytes)
|
signBytesHex := fmt.Sprintf("%X", signBytes)
|
||||||
expected := "010A746573745F636861696E0100000000000000DE00000000000000006F01020106696E7075743101010000000000000030390301093200000106696E70757432010100000000000000006F01DE0000010201076F757470757431010100000000000000014D01076F75747075743201010000000000000001BC"
|
expected := "010A746573745F636861696E0100000000000000DE00000000000000006F01020106696E7075743101010000000000000030390301093200000106696E70757432010100000000000000006F01DE0000010201076F757470757431010100000000000000014D01076F75747075743201010000000000000001BC"
|
||||||
|
|
||||||
assert.True(t, signBytesHex == expected,
|
assert.Equal(t, signBytesHex, expected,
|
||||||
cmn.Fmt("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signBytesHex))
|
"Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signBytesHex)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAppTxSignable(t *testing.T) {
|
func TestAppTxSignable(t *testing.T) {
|
||||||
|
@ -60,14 +60,16 @@ func TestAppTxSignable(t *testing.T) {
|
||||||
Data: []byte("data1"),
|
Data: []byte("data1"),
|
||||||
}
|
}
|
||||||
signBytes := callTx.SignBytes(chainID)
|
signBytes := callTx.SignBytes(chainID)
|
||||||
signBytesHex := cmn.Fmt("%X", signBytes)
|
signBytesHex := fmt.Sprintf("%X", signBytes)
|
||||||
expected := "010A746573745F636861696E0100000000000000DE00000000000000006F0101580106696E70757431010100000000000000303903010932000001056461746131"
|
expected := "010A746573745F636861696E0100000000000000DE00000000000000006F0101580106696E70757431010100000000000000303903010932000001056461746131"
|
||||||
|
|
||||||
assert.True(t, signBytesHex == expected,
|
assert.Equal(t, signBytesHex, expected,
|
||||||
cmn.Fmt("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signBytesHex))
|
"Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signBytesHex)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSendTxJSON(t *testing.T) {
|
func TestSendTxJSON(t *testing.T) {
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
|
||||||
chainID := "test_chain_id"
|
chainID := "test_chain_id"
|
||||||
test1PrivAcc := PrivAccountFromSecret("sendtx1")
|
test1PrivAcc := PrivAccountFromSecret("sendtx1")
|
||||||
test2PrivAcc := PrivAccountFromSecret("sendtx2")
|
test2PrivAcc := PrivAccountFromSecret("sendtx2")
|
||||||
|
@ -89,37 +91,37 @@ func TestSendTxJSON(t *testing.T) {
|
||||||
|
|
||||||
// serialize this as json and back
|
// serialize this as json and back
|
||||||
js, err := data.ToJSON(TxS{tx})
|
js, err := data.ToJSON(TxS{tx})
|
||||||
require.Nil(t, err)
|
require.Nil(err)
|
||||||
// fmt.Println(string(js))
|
// fmt.Println(string(js))
|
||||||
txs := TxS{}
|
txs := TxS{}
|
||||||
err = data.FromJSON(js, &txs)
|
err = data.FromJSON(js, &txs)
|
||||||
require.Nil(t, err)
|
require.Nil(err)
|
||||||
tx2, ok := txs.Tx.(*SendTx)
|
tx2, ok := txs.Tx.(*SendTx)
|
||||||
require.True(t, ok)
|
require.True(ok)
|
||||||
|
|
||||||
// make sure they are the same!
|
// make sure they are the same!
|
||||||
signBytes := tx.SignBytes(chainID)
|
signBytes := tx.SignBytes(chainID)
|
||||||
signBytes2 := tx2.SignBytes(chainID)
|
signBytes2 := tx2.SignBytes(chainID)
|
||||||
assert.Equal(t, signBytes, signBytes2)
|
assert.Equal(signBytes, signBytes2)
|
||||||
assert.Equal(t, tx, tx2)
|
assert.Equal(tx, tx2)
|
||||||
|
|
||||||
// sign this thing
|
// sign this thing
|
||||||
sig := test1PrivAcc.Sign(signBytes)
|
sig := test1PrivAcc.Sign(signBytes)
|
||||||
// we handle both raw sig and wrapped sig the same
|
// we handle both raw sig and wrapped sig the same
|
||||||
tx.SetSignature(test1PrivAcc.PubKey.Address(), sig)
|
tx.SetSignature(test1PrivAcc.PubKey.Address(), sig)
|
||||||
tx2.SetSignature(test1PrivAcc.PubKey.Address(), crypto.SignatureS{sig})
|
tx2.SetSignature(test1PrivAcc.PubKey.Address(), crypto.SignatureS{sig})
|
||||||
assert.Equal(t, tx, tx2)
|
assert.Equal(tx, tx2)
|
||||||
|
|
||||||
// let's marshal / unmarshal this with signature
|
// let's marshal / unmarshal this with signature
|
||||||
js, err = data.ToJSON(TxS{tx})
|
js, err = data.ToJSON(TxS{tx})
|
||||||
require.Nil(t, err)
|
require.Nil(err)
|
||||||
// fmt.Println(string(js))
|
// fmt.Println(string(js))
|
||||||
err = data.FromJSON(js, &txs)
|
err = data.FromJSON(js, &txs)
|
||||||
require.Nil(t, err)
|
require.Nil(err)
|
||||||
tx2, ok = txs.Tx.(*SendTx)
|
tx2, ok = txs.Tx.(*SendTx)
|
||||||
require.True(t, ok)
|
require.True(ok)
|
||||||
|
|
||||||
// and make sure the sig is preserved
|
// and make sure the sig is preserved
|
||||||
assert.Equal(t, tx, tx2)
|
assert.Equal(tx, tx2)
|
||||||
assert.False(t, tx2.Inputs[0].Signature.Empty())
|
assert.False(tx2.Inputs[0].Signature.Empty())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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