Merge branch 'develop'
This commit is contained in:
commit
7b39417b99
25
CHANGELOG.md
25
CHANGELOG.md
|
@ -1,5 +1,30 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.5.0 (May 27, 2017)
|
||||||
|
|
||||||
|
BREAKING CHANGES:
|
||||||
|
- only those related to the tendermint 0.9 -> 0.10 upgrade
|
||||||
|
|
||||||
|
ENHANCEMENTS:
|
||||||
|
- basecoin cli
|
||||||
|
- integrates tendermint 0.10.0 and unifies cli (init, unsafe_reset_all, ...)
|
||||||
|
- integrate viper, all command line flags can also be defined in environmental variables or config.toml
|
||||||
|
- genesis file
|
||||||
|
- you can define accounts with either address or pub_key
|
||||||
|
- sorts coins for you, so no silent errors if not in alphabetical order
|
||||||
|
- [light-client](https://github.com/tendermint/light-client) integration
|
||||||
|
- no longer must you trust the node you connect to, prove everything!
|
||||||
|
- new [basecli command](./cmd/basecli/README.md)
|
||||||
|
- integrated [key management](https://github.com/tendermint/go-crypto/blob/master/cmd/README.md), stored encrypted locally
|
||||||
|
- tracks validator set changes and proves everything from one initial validator seed
|
||||||
|
- `basecli proof state` gets complete proofs for any abci state
|
||||||
|
- `basecli proof tx` gets complete proof where a tx was stored in the chain
|
||||||
|
- `basecli proxy` exposes tendermint rpc, but only passes through results after doing complete verification
|
||||||
|
|
||||||
|
BUG FIXES:
|
||||||
|
- no more silently ignored error with invalid coin names (eg. "17.22foo coin" used to parse as "17 foo", not warning/error)
|
||||||
|
|
||||||
|
|
||||||
## 0.4.1 (April 26, 2017)
|
## 0.4.1 (April 26, 2017)
|
||||||
|
|
||||||
BUG FIXES:
|
BUG FIXES:
|
||||||
|
|
28
app/app.go
28
app/app.go
|
@ -1,13 +1,15 @@
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
||||||
. "github.com/tendermint/go-common"
|
wire "github.com/tendermint/go-wire"
|
||||||
"github.com/tendermint/go-wire"
|
|
||||||
eyes "github.com/tendermint/merkleeyes/client"
|
eyes "github.com/tendermint/merkleeyes/client"
|
||||||
|
. "github.com/tendermint/tmlibs/common"
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
sm "github.com/tendermint/basecoin/state"
|
sm "github.com/tendermint/basecoin/state"
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
|
@ -24,6 +26,7 @@ type Basecoin struct {
|
||||||
state *sm.State
|
state *sm.State
|
||||||
cacheState *sm.State
|
cacheState *sm.State
|
||||||
plugins *types.Plugins
|
plugins *types.Plugins
|
||||||
|
logger log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBasecoin(eyesCli *eyes.Client) *Basecoin {
|
func NewBasecoin(eyesCli *eyes.Client) *Basecoin {
|
||||||
|
@ -34,9 +37,15 @@ func NewBasecoin(eyesCli *eyes.Client) *Basecoin {
|
||||||
state: state,
|
state: state,
|
||||||
cacheState: nil,
|
cacheState: nil,
|
||||||
plugins: plugins,
|
plugins: plugins,
|
||||||
|
logger: log.NewNopLogger(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (app *Basecoin) SetLogger(l log.Logger) {
|
||||||
|
app.logger = l
|
||||||
|
app.state.SetLogger(l.With("module", "state"))
|
||||||
|
}
|
||||||
|
|
||||||
// XXX 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()
|
||||||
|
@ -60,7 +69,7 @@ func (app *Basecoin) SetOption(key string, value string) string {
|
||||||
if plugin == nil {
|
if plugin == nil {
|
||||||
return "Invalid plugin name: " + pluginName
|
return "Invalid plugin name: " + pluginName
|
||||||
}
|
}
|
||||||
log.Notice("SetOption on plugin", "plugin", pluginName, "key", key, "value", value)
|
app.logger.Info("SetOption on plugin", "plugin", pluginName, "key", key, "value", value)
|
||||||
return plugin.SetOption(app.state, key, value)
|
return plugin.SetOption(app.state, key, value)
|
||||||
} else {
|
} else {
|
||||||
// Set option on basecoin
|
// Set option on basecoin
|
||||||
|
@ -69,13 +78,18 @@ func (app *Basecoin) SetOption(key string, value string) string {
|
||||||
app.state.SetChainID(value)
|
app.state.SetChainID(value)
|
||||||
return "Success"
|
return "Success"
|
||||||
case "account":
|
case "account":
|
||||||
var acc types.Account
|
var acc GenesisAccount
|
||||||
err := json.Unmarshal([]byte(value), &acc)
|
err := json.Unmarshal([]byte(value), &acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "Error decoding acc message: " + err.Error()
|
return "Error decoding acc message: " + err.Error()
|
||||||
}
|
}
|
||||||
app.state.SetAccount(acc.PubKey.Address(), &acc)
|
acc.Balance.Sort()
|
||||||
log.Notice("SetAccount", "addr", acc.PubKey.Address(), "acc", acc)
|
addr, err := acc.GetAddr()
|
||||||
|
if err != nil {
|
||||||
|
return "Invalid address: " + err.Error()
|
||||||
|
}
|
||||||
|
app.state.SetAccount(addr, acc.ToAccount())
|
||||||
|
app.logger.Info("SetAccount", "addr", hex.EncodeToString(addr), "acc", acc)
|
||||||
|
|
||||||
return "Success"
|
return "Success"
|
||||||
}
|
}
|
||||||
|
@ -136,7 +150,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 = sm.AccountKey(reqQuery.Data)
|
reqQuery.Data = types.AccountKey(reqQuery.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
resQuery, err := app.eyesCli.QuerySync(reqQuery)
|
resQuery, err := app.eyesCli.QuerySync(reqQuery)
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -10,8 +10,9 @@ import (
|
||||||
|
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
"github.com/tendermint/go-wire"
|
wire "github.com/tendermint/go-wire"
|
||||||
eyes "github.com/tendermint/merkleeyes/client"
|
eyes "github.com/tendermint/merkleeyes/client"
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
//--------------------------------------------------------
|
//--------------------------------------------------------
|
||||||
|
@ -36,7 +37,7 @@ func newAppTest(t *testing.T) *appTest {
|
||||||
|
|
||||||
// make a tx sending 5mycoin from each accIn to accOut
|
// make a tx sending 5mycoin from each accIn to accOut
|
||||||
func (at *appTest) getTx(seq int) *types.SendTx {
|
func (at *appTest) getTx(seq int) *types.SendTx {
|
||||||
tx := types.GetTx(seq, at.accOut, at.accIn)
|
tx := types.MakeSendTx(seq, at.accOut, at.accIn)
|
||||||
types.SignTx(at.chainID, tx, at.accIn)
|
types.SignTx(at.chainID, tx, at.accIn)
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
|
@ -56,6 +57,7 @@ func (at *appTest) reset() {
|
||||||
|
|
||||||
eyesCli := eyes.NewLocalClient("", 0)
|
eyesCli := eyes.NewLocalClient("", 0)
|
||||||
at.app = NewBasecoin(eyesCli)
|
at.app = NewBasecoin(eyesCli)
|
||||||
|
at.app.SetLogger(log.TestingLogger().With("module", "app"))
|
||||||
|
|
||||||
res := at.app.SetOption("base/chain_id", at.chainID)
|
res := at.app.SetOption("base/chain_id", at.chainID)
|
||||||
require.EqualValues(at.t, res, "Success")
|
require.EqualValues(at.t, res, "Success")
|
||||||
|
@ -101,9 +103,11 @@ func TestSplitKey(t *testing.T) {
|
||||||
|
|
||||||
func TestSetOption(t *testing.T) {
|
func TestSetOption(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
eyesCli := eyes.NewLocalClient("", 0)
|
eyesCli := eyes.NewLocalClient("", 0)
|
||||||
app := NewBasecoin(eyesCli)
|
app := NewBasecoin(eyesCli)
|
||||||
|
app.SetLogger(log.TestingLogger().With("module", "app"))
|
||||||
|
|
||||||
//testing ChainID
|
//testing ChainID
|
||||||
chainID := "testChain"
|
chainID := "testChain"
|
||||||
|
@ -111,11 +115,43 @@ func TestSetOption(t *testing.T) {
|
||||||
assert.EqualValues(app.GetState().GetChainID(), chainID)
|
assert.EqualValues(app.GetState().GetChainID(), chainID)
|
||||||
assert.EqualValues(res, "Success")
|
assert.EqualValues(res, "Success")
|
||||||
|
|
||||||
|
// make a nice account...
|
||||||
accIn := types.MakeAcc("input0")
|
accIn := types.MakeAcc("input0")
|
||||||
accsInBytes, err := json.Marshal(accIn.Account)
|
accsInBytes, err := json.Marshal(accIn.Account)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
res = app.SetOption("base/account", string(accsInBytes))
|
res = app.SetOption("base/account", string(accsInBytes))
|
||||||
assert.EqualValues(res, "Success")
|
require.EqualValues(res, "Success")
|
||||||
|
// make sure it is set correctly, with some balance
|
||||||
|
acct := types.GetAccount(app.GetState(), accIn.PubKey.Address())
|
||||||
|
require.NotNil(acct)
|
||||||
|
assert.Equal(accIn.Balance, acct.Balance)
|
||||||
|
|
||||||
|
// let's parse an account with badly sorted coins...
|
||||||
|
unsortAddr, err := hex.DecodeString("C471FB670E44D219EE6DF2FC284BE38793ACBCE1")
|
||||||
|
require.Nil(err)
|
||||||
|
unsortCoins := types.Coins{{"BTC", 789}, {"eth", 123}}
|
||||||
|
unsortAcc := `{
|
||||||
|
"pub_key": {
|
||||||
|
"type": "ed25519",
|
||||||
|
"data": "AD084F0572C116D618B36F2EB08240D1BAB4B51716CCE0E7734B89C8936DCE9A"
|
||||||
|
},
|
||||||
|
"coins": [
|
||||||
|
{
|
||||||
|
"denom": "eth",
|
||||||
|
"amount": 123
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"denom": "BTC",
|
||||||
|
"amount": 789
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
res = app.SetOption("base/account", unsortAcc)
|
||||||
|
require.EqualValues(res, "Success")
|
||||||
|
acct = types.GetAccount(app.GetState(), unsortAddr)
|
||||||
|
require.NotNil(acct)
|
||||||
|
assert.True(acct.Balance.IsValid())
|
||||||
|
assert.Equal(unsortCoins, acct.Balance)
|
||||||
|
|
||||||
res = app.SetOption("base/dslfkgjdas", "")
|
res = app.SetOption("base/dslfkgjdas", "")
|
||||||
assert.NotEqual(res, "Success")
|
assert.NotEqual(res, "Success")
|
||||||
|
@ -125,6 +161,7 @@ func TestSetOption(t *testing.T) {
|
||||||
|
|
||||||
res = app.SetOption("dslfkgjdas/szfdjzs", "")
|
res = app.SetOption("dslfkgjdas/szfdjzs", "")
|
||||||
assert.NotEqual(res, "Success")
|
assert.NotEqual(res, "Success")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test CheckTx and DeliverTx with insufficient and sufficient balance
|
// Test CheckTx and DeliverTx with insufficient and sufficient balance
|
||||||
|
@ -176,7 +213,5 @@ func TestQuery(t *testing.T) {
|
||||||
Path: "/account",
|
Path: "/account",
|
||||||
Data: at.accIn.Account.PubKey.Address(),
|
Data: at.accIn.Account.PubKey.Address(),
|
||||||
})
|
})
|
||||||
fmt.Println(resQueryPreCommit)
|
|
||||||
fmt.Println(resQueryPostCommit)
|
|
||||||
assert.NotEqual(resQueryPreCommit, resQueryPostCommit, "Query should change before/after commit")
|
assert.NotEqual(resQueryPreCommit, resQueryPostCommit, "Query should change before/after commit")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
cmn "github.com/tendermint/go-common"
|
crypto "github.com/tendermint/go-crypto"
|
||||||
//tmtypes "github.com/tendermint/tendermint/types"
|
"github.com/tendermint/go-wire/data"
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (app *Basecoin) LoadGenesis(path string) error {
|
func (app *Basecoin) LoadGenesis(path string) error {
|
||||||
|
@ -26,13 +29,13 @@ func (app *Basecoin) LoadGenesis(path string) error {
|
||||||
}
|
}
|
||||||
r := app.SetOption("base/account", string(accBytes))
|
r := app.SetOption("base/account", string(accBytes))
|
||||||
// TODO: SetOption returns an error
|
// TODO: SetOption returns an error
|
||||||
log.Notice("Done setting Account via SetOption", "result", r)
|
app.logger.Info("Done setting Account via SetOption", "result", r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// set plugin options
|
// set plugin options
|
||||||
for _, kv := range genDoc.AppOptions.pluginOptions {
|
for _, kv := range genDoc.AppOptions.pluginOptions {
|
||||||
r := app.SetOption(kv.Key, kv.Value)
|
r := app.SetOption(kv.Key, kv.Value)
|
||||||
log.Notice("Done setting Plugin key-value pair via SetOption", "result", r, "k", kv.Key, "v", kv.Value)
|
app.logger.Info("Done setting Plugin key-value pair via SetOption", "result", r, "k", kv.Key, "v", kv.Value)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -49,7 +52,7 @@ type FullGenesisDoc struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type GenesisDoc struct {
|
type GenesisDoc struct {
|
||||||
Accounts []types.Account `json:"accounts"`
|
Accounts []GenesisAccount `json:"accounts"`
|
||||||
PluginOptions []json.RawMessage `json:"plugin_options"`
|
PluginOptions []json.RawMessage `json:"plugin_options"`
|
||||||
|
|
||||||
pluginOptions []keyValue // unmarshaled rawmessages
|
pluginOptions []keyValue // unmarshaled rawmessages
|
||||||
|
@ -61,11 +64,7 @@ func loadGenesis(filePath string) (*FullGenesisDoc, error) {
|
||||||
return nil, errors.Wrap(err, "loading genesis file")
|
return nil, errors.Wrap(err, "loading genesis file")
|
||||||
}
|
}
|
||||||
|
|
||||||
// the tendermint genesis is go-wire
|
// the basecoin genesis go-wire/data :)
|
||||||
// tmGenesis := new(tmtypes.GenesisDoc)
|
|
||||||
// err = wire.ReadJSONBytes(bytes, tmGenesis)
|
|
||||||
|
|
||||||
// the basecoin genesis go-data :)
|
|
||||||
genDoc := new(FullGenesisDoc)
|
genDoc := new(FullGenesisDoc)
|
||||||
err = json.Unmarshal(bytes, genDoc)
|
err = json.Unmarshal(bytes, genDoc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -102,3 +101,40 @@ func parseGenesisList(kvz_ []json.RawMessage) (kvz []keyValue, err error) {
|
||||||
}
|
}
|
||||||
return kvz, nil
|
return kvz, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**** code to parse accounts from genesis docs ***/
|
||||||
|
|
||||||
|
type GenesisAccount struct {
|
||||||
|
Address data.Bytes `json:"address"`
|
||||||
|
// this from types.Account (don't know how to embed this properly)
|
||||||
|
PubKey crypto.PubKey `json:"pub_key"` // May be nil, if not known.
|
||||||
|
Sequence int `json:"sequence"`
|
||||||
|
Balance types.Coins `json:"coins"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g GenesisAccount) ToAccount() *types.Account {
|
||||||
|
return &types.Account{
|
||||||
|
PubKey: g.PubKey,
|
||||||
|
Sequence: g.Sequence,
|
||||||
|
Balance: g.Balance,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g GenesisAccount) GetAddr() ([]byte, error) {
|
||||||
|
noAddr, noPk := len(g.Address) == 0, g.PubKey.Empty()
|
||||||
|
|
||||||
|
if noAddr {
|
||||||
|
if noPk {
|
||||||
|
return nil, errors.New("No address given")
|
||||||
|
}
|
||||||
|
return g.PubKey.Address(), nil
|
||||||
|
}
|
||||||
|
if noPk { // but is addr...
|
||||||
|
return g.Address, nil
|
||||||
|
}
|
||||||
|
// now, we have both, make sure they check out
|
||||||
|
if bytes.Equal(g.Address, g.PubKey.Address()) {
|
||||||
|
return g.Address, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("Address and pubkey don't match")
|
||||||
|
}
|
||||||
|
|
|
@ -7,12 +7,14 @@ import (
|
||||||
|
|
||||||
"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/basecoin/types"
|
||||||
"github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
eyescli "github.com/tendermint/merkleeyes/client"
|
eyescli "github.com/tendermint/merkleeyes/client"
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
const genesisFilepath = "./testdata/genesis.json"
|
const genesisFilepath = "./testdata/genesis.json"
|
||||||
|
const genesisAcctFilepath = "./testdata/genesis2.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)
|
||||||
|
@ -34,11 +36,15 @@ func TestLoadGenesis(t *testing.T) {
|
||||||
|
|
||||||
// make sure balance is proper
|
// make sure balance is proper
|
||||||
assert.Equal(2, len(acct.Balance))
|
assert.Equal(2, len(acct.Balance))
|
||||||
assert.EqualValues(12345, acct.Balance[0].Amount)
|
assert.True(acct.Balance.IsValid())
|
||||||
assert.EqualValues("blank", acct.Balance[0].Denom)
|
// note, that we now sort them to be valid
|
||||||
|
assert.EqualValues(654321, acct.Balance[0].Amount)
|
||||||
|
assert.EqualValues("ETH", acct.Balance[0].Denom)
|
||||||
|
assert.EqualValues(12345, acct.Balance[1].Amount)
|
||||||
|
assert.EqualValues("blank", acct.Balance[1].Denom)
|
||||||
|
|
||||||
// and public key is parsed properly
|
// and public key is parsed properly
|
||||||
apk := acct.PubKey.PubKey
|
apk := acct.PubKey.Unwrap()
|
||||||
require.NotNil(apk)
|
require.NotNil(apk)
|
||||||
epk, ok := apk.(crypto.PubKeyEd25519)
|
epk, ok := apk.(crypto.PubKeyEd25519)
|
||||||
if assert.True(ok) {
|
if assert.True(ok) {
|
||||||
|
@ -46,13 +52,58 @@ func TestLoadGenesis(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fix for issue #89, change the parse format for accounts in genesis.json
|
||||||
|
func TestLoadGenesisAccountAddress(t *testing.T) {
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
|
||||||
|
eyesCli := eyescli.NewLocalClient("", 0)
|
||||||
|
app := NewBasecoin(eyesCli)
|
||||||
|
err := app.LoadGenesis(genesisAcctFilepath)
|
||||||
|
require.Nil(err, "%+v", err)
|
||||||
|
|
||||||
|
// check the chain id
|
||||||
|
assert.Equal("addr_accounts_chain", app.GetState().GetChainID())
|
||||||
|
|
||||||
|
// make sure the accounts were set properly
|
||||||
|
cases := []struct {
|
||||||
|
addr string
|
||||||
|
exists bool
|
||||||
|
hasPubkey bool
|
||||||
|
coins types.Coins
|
||||||
|
}{
|
||||||
|
// this comes from a public key, should be stored proper (alice)
|
||||||
|
{"62035D628DE7543332544AA60D90D3693B6AD51B", true, true, types.Coins{{"one", 111}}},
|
||||||
|
// this comes from an address, should be stored proper (bob)
|
||||||
|
{"C471FB670E44D219EE6DF2FC284BE38793ACBCE1", true, false, types.Coins{{"two", 222}}},
|
||||||
|
// this one had a mismatched address and pubkey, should not store under either (carl)
|
||||||
|
{"1234ABCDD18E8EFE3FFC4B0506BF9BF8E5B0D9E9", false, false, nil}, // this is given addr
|
||||||
|
{"700BEC5ED18E8EFE3FFC4B0506BF9BF8E5B0D9E9", false, false, nil}, // this is addr of the given pubkey
|
||||||
|
// this comes from a secp256k1 public key, should be stored proper (sam)
|
||||||
|
{"979F080B1DD046C452C2A8A250D18646C6B669D4", true, true, types.Coins{{"four", 444}}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
addr, err := hex.DecodeString(tc.addr)
|
||||||
|
require.Nil(err, tc.addr)
|
||||||
|
acct := app.GetState().GetAccount(addr)
|
||||||
|
if !tc.exists {
|
||||||
|
assert.Nil(acct, tc.addr)
|
||||||
|
} else if assert.NotNil(acct, tc.addr) {
|
||||||
|
// it should and does exist...
|
||||||
|
assert.True(acct.Balance.IsValid())
|
||||||
|
assert.Equal(tc.coins, acct.Balance)
|
||||||
|
assert.Equal(!tc.hasPubkey, acct.PubKey.Empty(), tc.addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestParseGenesisList(t *testing.T) {
|
func TestParseGenesisList(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
|
||||||
bytes, err := cmn.ReadFile(genesisFilepath)
|
bytes, err := cmn.ReadFile(genesisFilepath)
|
||||||
require.Nil(err, "loading genesis file %+v", err)
|
require.Nil(err, "loading genesis file %+v", err)
|
||||||
|
|
||||||
// the basecoin genesis go-data :)
|
// the basecoin genesis go-wire/data :)
|
||||||
genDoc := new(FullGenesisDoc)
|
genDoc := new(FullGenesisDoc)
|
||||||
err = json.Unmarshal(bytes, genDoc)
|
err = json.Unmarshal(bytes, genDoc)
|
||||||
require.Nil(err, "unmarshaling genesis file %+v", err)
|
require.Nil(err, "unmarshaling genesis file %+v", err)
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/tendermint/go-logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
var log = logger.New("module", "app")
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
{
|
||||||
|
"chain_id": "addr_accounts_chain",
|
||||||
|
"app_options": {
|
||||||
|
"accounts": [{
|
||||||
|
"name": "alice",
|
||||||
|
"pub_key": {
|
||||||
|
"type": "ed25519",
|
||||||
|
"data": "DBD9A46C45868F0A37C92B53113C09B048FBD87B5FBC2F8B199052973B8FAA36"
|
||||||
|
},
|
||||||
|
"coins": [
|
||||||
|
{
|
||||||
|
"denom": "one",
|
||||||
|
"amount": 111
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"name": "bob",
|
||||||
|
"address": "C471FB670E44D219EE6DF2FC284BE38793ACBCE1",
|
||||||
|
"coins": [
|
||||||
|
{
|
||||||
|
"denom": "two",
|
||||||
|
"amount": 222
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"name": "carl",
|
||||||
|
"address": "1234ABCDD18E8EFE3FFC4B0506BF9BF8E5B0D9E9",
|
||||||
|
"pub_key": {
|
||||||
|
"type": "ed25519",
|
||||||
|
"data": "177C0AC45E86257F0708DC085D592AB22AAEECD1D26381B757F7C96135921858"
|
||||||
|
},
|
||||||
|
"coins": [
|
||||||
|
{
|
||||||
|
"denom": "three",
|
||||||
|
"amount": 333
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"name": "sam",
|
||||||
|
"pub_key": {
|
||||||
|
"type": "secp256k1",
|
||||||
|
"data": "02AA8342F63CCCCE6DDB128525BA048CE0B2993DA3B4308746E1F216361A87651E"
|
||||||
|
},
|
||||||
|
"coins": [
|
||||||
|
{
|
||||||
|
"denom": "four",
|
||||||
|
"amount": 444
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
18
circle.yml
18
circle.yml
|
@ -1,26 +1,26 @@
|
||||||
machine:
|
machine:
|
||||||
environment:
|
environment:
|
||||||
GOPATH: /home/ubuntu/.go_workspace
|
GOPATH: "$HOME/.go_workspace"
|
||||||
REPO: $GOPATH/src/github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME
|
PROJECT_PARENT_PATH: "$GOPATH/src/github.com/$CIRCLE_PROJECT_USERNAME"
|
||||||
|
REPO: "$PROJECT_PARENT_PATH/$CIRCLE_PROJECT_REPONAME"
|
||||||
|
PATH: "$GOPATH/bin:$PATH"
|
||||||
hosts:
|
hosts:
|
||||||
circlehost: 127.0.0.1
|
circlehost: 127.0.0.1
|
||||||
localhost: 127.0.0.1
|
localhost: 127.0.0.1
|
||||||
|
|
||||||
checkout:
|
|
||||||
post:
|
|
||||||
- rm -rf $REPO
|
|
||||||
- mkdir -p $HOME/.go_workspace/src/github.com/$CIRCLE_PROJECT_USERNAME
|
|
||||||
- mv $HOME/$CIRCLE_PROJECT_REPONAME $REPO
|
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
override:
|
override:
|
||||||
- go get github.com/Masterminds/glide
|
- go get github.com/Masterminds/glide
|
||||||
- go version
|
- go version
|
||||||
- glide --version
|
- glide --version
|
||||||
- "cd $REPO && glide install && go install ./cmd/basecoin"
|
- mkdir -p "$PROJECT_PARENT_PATH"
|
||||||
|
- ln -sf "$HOME/$CIRCLE_PROJECT_REPONAME/" "$REPO"
|
||||||
|
- env
|
||||||
|
|
||||||
test:
|
test:
|
||||||
override:
|
override:
|
||||||
|
- "cd $REPO && glide install && go install ./cmd/basecoin"
|
||||||
|
- ls $GOPATH/bin
|
||||||
- "cd $REPO && make test"
|
- "cd $REPO && make test"
|
||||||
- "cd $REPO/demo && bash start.sh"
|
- "cd $REPO/demo && bash start.sh"
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
# Run your own (super) lightweight node
|
||||||
|
|
||||||
|
In addition to providing command-line tooling that goes cryptographic verification
|
||||||
|
on all the data your receive from the node, we have implemented a proxy mode, that
|
||||||
|
allows you to run a super lightweight node. It does not follow the chain on
|
||||||
|
every block or even every header, but only as needed. But still providing the
|
||||||
|
same security as running a full non-validator node on your local machine.
|
||||||
|
|
||||||
|
Basically, it runs as a proxy that exposes the same rpc interface as the full node
|
||||||
|
and connects to a (potentially untrusted) full node. Every response is cryptographically
|
||||||
|
verified before being passed through, returning an error if it doesn't match.
|
||||||
|
|
||||||
|
You can expect 2 rpc calls for every query plus <= 1 query for each validator set
|
||||||
|
change. Going offline for a while allows you to verify multiple validator set changes
|
||||||
|
with one call. Cuz at 1 block/sec and 1000 tx/block, it just doesn't make sense
|
||||||
|
to run a full node just to get security
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Just initialize your client with the proper validator set as in the [README](README.md)
|
||||||
|
|
||||||
|
```
|
||||||
|
$ export BCHOME=~/.lightnode
|
||||||
|
$ basecli init --node tcp://<host>:<port> --chainid <chain>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
```
|
||||||
|
$ basecli proxy --serve tcp://localhost:7890
|
||||||
|
...
|
||||||
|
curl localhost:7890/status
|
||||||
|
curl localhost:7890/block\?height=20
|
||||||
|
```
|
||||||
|
|
||||||
|
You can even subscribe to events over websockets and they are all verified
|
||||||
|
before passing them though. Though if you want every block, you might as
|
||||||
|
well run a full (nonvalidating) node.
|
||||||
|
|
||||||
|
## Seeds
|
||||||
|
|
||||||
|
Every time the validator set changes, the light node verifies if it is legal,
|
||||||
|
and then creates a seed at that point. These "seeds" are verified checkpoints
|
||||||
|
that we can trace any proof back to, starting with one on `init`.
|
||||||
|
|
||||||
|
To make sure you are based on the most recent header, you can run:
|
||||||
|
|
||||||
|
```
|
||||||
|
basecli seeds update
|
||||||
|
basecli seeds show
|
||||||
|
```
|
||||||
|
|
||||||
|
## Feedback
|
||||||
|
|
||||||
|
This is the first release of basecli and the light-weight proxy. It is secure, but
|
||||||
|
may not be useful for your workflow. Please try it out and open github issues
|
||||||
|
for any enhancements or bugs you find. I am aiming to make this a very useful
|
||||||
|
tool by tendermint 0.11, for which I need community feedback.
|
|
@ -0,0 +1,63 @@
|
||||||
|
# Basic run through of using basecli....
|
||||||
|
|
||||||
|
To keep things clear, let's have two shells...
|
||||||
|
|
||||||
|
`$` is for basecoin (server), `%` is for basecli (client)
|
||||||
|
|
||||||
|
## Set up a clean basecoin, but don't start the chain
|
||||||
|
|
||||||
|
```
|
||||||
|
$ export BCHOME=~/.demoserve
|
||||||
|
$ basecoin init
|
||||||
|
```
|
||||||
|
|
||||||
|
## Set up your basecli with a new key
|
||||||
|
|
||||||
|
```
|
||||||
|
% export BCHOME=~/.democli
|
||||||
|
% basecli keys new demo
|
||||||
|
% basecli keys get demo -o json
|
||||||
|
```
|
||||||
|
|
||||||
|
And set up a few more keys for fun...
|
||||||
|
|
||||||
|
```
|
||||||
|
% basecli keys new buddy
|
||||||
|
% basecli keys list
|
||||||
|
% ME=`basecli keys get demo -o json | jq .address | tr -d '"'`
|
||||||
|
% YOU=`basecli keys get buddy -o json | jq .address | tr -d '"'`
|
||||||
|
```
|
||||||
|
|
||||||
|
## Update genesis so you are rich, and start
|
||||||
|
|
||||||
|
```
|
||||||
|
$ vi $BCHOME/genesis.json
|
||||||
|
-> cut/paste your pubkey from the results above
|
||||||
|
|
||||||
|
$ basecoin start
|
||||||
|
```
|
||||||
|
|
||||||
|
## Connect your basecli the first time
|
||||||
|
|
||||||
|
```
|
||||||
|
% basecli init --chainid test_chain_id --node tcp://localhost:46657
|
||||||
|
```
|
||||||
|
|
||||||
|
## Check your balances...
|
||||||
|
|
||||||
|
```
|
||||||
|
% basecli proof state get --app=account --key=$ME
|
||||||
|
% basecli proof state get --app=account --key=$YOU
|
||||||
|
```
|
||||||
|
|
||||||
|
## Send the money
|
||||||
|
|
||||||
|
```
|
||||||
|
% basecli tx send --name demo --amount 1000mycoin --sequence 1 --to $YOU
|
||||||
|
-> copy hash to HASH
|
||||||
|
% basecli proof tx get --key $HASH
|
||||||
|
|
||||||
|
% basecli proof tx get --key $HASH --app base
|
||||||
|
% basecli proof state get --key $YOU --app account
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,232 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
flag "github.com/spf13/pflag"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
crypto "github.com/tendermint/go-crypto"
|
||||||
|
wire "github.com/tendermint/go-wire"
|
||||||
|
lightclient "github.com/tendermint/light-client"
|
||||||
|
"github.com/tendermint/light-client/commands"
|
||||||
|
"github.com/tendermint/light-client/proofs"
|
||||||
|
|
||||||
|
btypes "github.com/tendermint/basecoin/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AccountPresenter struct{}
|
||||||
|
|
||||||
|
func (_ AccountPresenter) MakeKey(str string) ([]byte, error) {
|
||||||
|
res, err := hex.DecodeString(str)
|
||||||
|
if err == nil {
|
||||||
|
res = btypes.AccountKey(res)
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_ AccountPresenter) ParseData(raw []byte) (interface{}, error) {
|
||||||
|
var acc *btypes.Account
|
||||||
|
err := wire.ReadBinaryBytes(raw, &acc)
|
||||||
|
return acc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseTxPresenter struct {
|
||||||
|
proofs.RawPresenter // this handles MakeKey as hex bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_ BaseTxPresenter) ParseData(raw []byte) (interface{}, error) {
|
||||||
|
var tx btypes.TxS
|
||||||
|
err := wire.ReadBinaryBytes(raw, &tx)
|
||||||
|
return tx, err
|
||||||
|
}
|
||||||
|
|
||||||
|
/******** SendTx *********/
|
||||||
|
|
||||||
|
type SendTxMaker struct{}
|
||||||
|
|
||||||
|
func (m SendTxMaker) MakeReader() (lightclient.TxReader, error) {
|
||||||
|
chainID := viper.GetString(commands.ChainFlag)
|
||||||
|
return SendTxReader{ChainID: chainID}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type SendFlags struct {
|
||||||
|
To string
|
||||||
|
Amount string
|
||||||
|
Fee string
|
||||||
|
Gas int64
|
||||||
|
Sequence int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m SendTxMaker) Flags() (*flag.FlagSet, interface{}) {
|
||||||
|
fs := flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
|
|
||||||
|
fs.String("to", "", "Destination address for the bits")
|
||||||
|
fs.String("amount", "", "Coins to send in the format <amt><coin>,<amt><coin>...")
|
||||||
|
fs.String("fee", "", "Coins for the transaction fee of the format <amt><coin>")
|
||||||
|
fs.Int64("gas", 0, "Amount of gas for this transaction")
|
||||||
|
fs.Int("sequence", -1, "Sequence number for this transaction")
|
||||||
|
return fs, &SendFlags{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendTXReader allows us to create SendTx
|
||||||
|
type SendTxReader struct {
|
||||||
|
ChainID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t SendTxReader) ReadTxJSON(data []byte, pk crypto.PubKey) (interface{}, error) {
|
||||||
|
// TODO: use pk info to help construct data
|
||||||
|
var tx btypes.SendTx
|
||||||
|
err := json.Unmarshal(data, &tx)
|
||||||
|
send := SendTx{
|
||||||
|
chainID: t.ChainID,
|
||||||
|
Tx: &tx,
|
||||||
|
}
|
||||||
|
return &send, errors.Wrap(err, "parse sendtx")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t SendTxReader) ReadTxFlags(flags interface{}, pk crypto.PubKey) (interface{}, error) {
|
||||||
|
data := flags.(*SendFlags)
|
||||||
|
|
||||||
|
// parse to and from addresses
|
||||||
|
to, err := hex.DecodeString(StripHex(data.To))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Errorf("To address is invalid hex: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//parse the fee and amounts into coin types
|
||||||
|
feeCoin, err := btypes.ParseCoin(data.Fee)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
amountCoins, err := btypes.ParseCoins(data.Amount)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// get addr if available
|
||||||
|
var addr []byte
|
||||||
|
if !pk.Empty() {
|
||||||
|
addr = pk.Address()
|
||||||
|
}
|
||||||
|
|
||||||
|
// craft the tx
|
||||||
|
input := btypes.TxInput{
|
||||||
|
Address: addr,
|
||||||
|
Coins: amountCoins,
|
||||||
|
Sequence: data.Sequence,
|
||||||
|
}
|
||||||
|
if data.Sequence == 1 {
|
||||||
|
input.PubKey = pk
|
||||||
|
}
|
||||||
|
output := btypes.TxOutput{
|
||||||
|
Address: to,
|
||||||
|
Coins: amountCoins,
|
||||||
|
}
|
||||||
|
tx := btypes.SendTx{
|
||||||
|
Gas: data.Gas,
|
||||||
|
Fee: feeCoin,
|
||||||
|
Inputs: []btypes.TxInput{input},
|
||||||
|
Outputs: []btypes.TxOutput{output},
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrap it in the proper signer thing...
|
||||||
|
send := SendTx{
|
||||||
|
chainID: t.ChainID,
|
||||||
|
Tx: &tx,
|
||||||
|
}
|
||||||
|
return &send, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/******** AppTx *********/
|
||||||
|
|
||||||
|
type AppFlags struct {
|
||||||
|
Fee string
|
||||||
|
Gas int64
|
||||||
|
Amount string
|
||||||
|
Sequence int
|
||||||
|
}
|
||||||
|
|
||||||
|
func AppFlagSet() (*flag.FlagSet, AppFlags) {
|
||||||
|
fs := flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
|
|
||||||
|
fs.String("amount", "", "Coins to send in the format <amt><coin>,<amt><coin>...")
|
||||||
|
fs.String("fee", "", "Coins for the transaction fee of the format <amt><coin>")
|
||||||
|
fs.Int64("gas", 0, "Amount of gas for this transaction")
|
||||||
|
fs.Int("sequence", -1, "Sequence number for this transaction")
|
||||||
|
return fs, AppFlags{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppTxReader allows us to create AppTx
|
||||||
|
type AppTxReader struct {
|
||||||
|
ChainID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t AppTxReader) ReadTxJSON(data []byte, pk crypto.PubKey) (interface{}, error) {
|
||||||
|
return nil, errors.New("Not implemented...")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t AppTxReader) ReadTxFlags(data *AppFlags, app string, appData []byte, pk crypto.PubKey) (interface{}, error) {
|
||||||
|
//parse the fee and amounts into coin types
|
||||||
|
feeCoin, err := btypes.ParseCoin(data.Fee)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
amountCoins, err := btypes.ParseCoins(data.Amount)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// get addr if available
|
||||||
|
var addr []byte
|
||||||
|
if !pk.Empty() {
|
||||||
|
addr = pk.Address()
|
||||||
|
}
|
||||||
|
|
||||||
|
// craft the tx
|
||||||
|
input := btypes.TxInput{
|
||||||
|
Address: addr,
|
||||||
|
Coins: amountCoins,
|
||||||
|
Sequence: data.Sequence,
|
||||||
|
}
|
||||||
|
if data.Sequence == 1 {
|
||||||
|
input.PubKey = pk
|
||||||
|
}
|
||||||
|
tx := btypes.AppTx{
|
||||||
|
Gas: data.Gas,
|
||||||
|
Fee: feeCoin,
|
||||||
|
Input: input,
|
||||||
|
Name: app,
|
||||||
|
Data: appData,
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrap it in the proper signer thing...
|
||||||
|
send := AppTx{
|
||||||
|
chainID: t.ChainID,
|
||||||
|
Tx: &tx,
|
||||||
|
}
|
||||||
|
return &send, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/** TODO copied from basecoin cli - put in common somewhere? **/
|
||||||
|
|
||||||
|
// Returns true for non-empty hex-string prefixed with "0x"
|
||||||
|
func isHex(s string) bool {
|
||||||
|
if len(s) > 2 && s[:2] == "0x" {
|
||||||
|
_, err := hex.DecodeString(s[2:])
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func StripHex(s string) string {
|
||||||
|
if isHex(s) {
|
||||||
|
return s[2:]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
crypto "github.com/tendermint/go-crypto"
|
||||||
|
keys "github.com/tendermint/go-crypto/keys"
|
||||||
|
wire "github.com/tendermint/go-wire"
|
||||||
|
|
||||||
|
bc "github.com/tendermint/basecoin/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AppTx struct {
|
||||||
|
chainID string
|
||||||
|
signers []crypto.PubKey
|
||||||
|
Tx *bc.AppTx
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ keys.Signable = &AppTx{}
|
||||||
|
|
||||||
|
// SignBytes returned the unsigned bytes, needing a signature
|
||||||
|
func (s *AppTx) SignBytes() []byte {
|
||||||
|
return s.Tx.SignBytes(s.chainID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign will add a signature and pubkey.
|
||||||
|
//
|
||||||
|
// Depending on the Signable, one may be able to call this multiple times for multisig
|
||||||
|
// Returns error if called with invalid data or too many times
|
||||||
|
func (s *AppTx) Sign(pubkey crypto.PubKey, sig crypto.Signature) error {
|
||||||
|
if len(s.signers) > 0 {
|
||||||
|
return errors.New("AppTx already signed")
|
||||||
|
}
|
||||||
|
s.Tx.SetSignature(sig)
|
||||||
|
s.signers = []crypto.PubKey{pubkey}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signers will return the public key(s) that signed if the signature
|
||||||
|
// is valid, or an error if there is any issue with the signature,
|
||||||
|
// including if there are no signatures
|
||||||
|
func (s *AppTx) Signers() ([]crypto.PubKey, error) {
|
||||||
|
if len(s.signers) == 0 {
|
||||||
|
return nil, errors.New("No signatures on AppTx")
|
||||||
|
}
|
||||||
|
return s.signers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxBytes returns the transaction data as well as all signatures
|
||||||
|
// It should return an error if Sign was never called
|
||||||
|
func (s *AppTx) TxBytes() ([]byte, error) {
|
||||||
|
// TODO: verify it is signed
|
||||||
|
|
||||||
|
// Code and comment from: basecoin/cmd/commands/tx.go
|
||||||
|
// Don't you hate having to do this?
|
||||||
|
// How many times have I lost an hour over this trick?!
|
||||||
|
txBytes := wire.BinaryBytes(bc.TxS{s.Tx})
|
||||||
|
return txBytes, nil
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
bc "github.com/tendermint/basecoin/types"
|
||||||
|
crypto "github.com/tendermint/go-crypto"
|
||||||
|
keys "github.com/tendermint/go-crypto/keys"
|
||||||
|
wire "github.com/tendermint/go-wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SendTx struct {
|
||||||
|
chainID string
|
||||||
|
signers []crypto.PubKey
|
||||||
|
Tx *bc.SendTx
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ keys.Signable = &SendTx{}
|
||||||
|
|
||||||
|
// SignBytes returned the unsigned bytes, needing a signature
|
||||||
|
func (s *SendTx) SignBytes() []byte {
|
||||||
|
return s.Tx.SignBytes(s.chainID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign will add a signature and pubkey.
|
||||||
|
//
|
||||||
|
// Depending on the Signable, one may be able to call this multiple times for multisig
|
||||||
|
// Returns error if called with invalid data or too many times
|
||||||
|
func (s *SendTx) Sign(pubkey crypto.PubKey, sig crypto.Signature) error {
|
||||||
|
addr := pubkey.Address()
|
||||||
|
set := s.Tx.SetSignature(addr, sig)
|
||||||
|
if !set {
|
||||||
|
return errors.Errorf("Cannot add signature for address %X", addr)
|
||||||
|
}
|
||||||
|
s.signers = append(s.signers, pubkey)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signers will return the public key(s) that signed if the signature
|
||||||
|
// is valid, or an error if there is any issue with the signature,
|
||||||
|
// including if there are no signatures
|
||||||
|
func (s *SendTx) Signers() ([]crypto.PubKey, error) {
|
||||||
|
if len(s.signers) == 0 {
|
||||||
|
return nil, errors.New("No signatures on SendTx")
|
||||||
|
}
|
||||||
|
return s.signers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxBytes returns the transaction data as well as all signatures
|
||||||
|
// It should return an error if Sign was never called
|
||||||
|
func (s *SendTx) TxBytes() ([]byte, error) {
|
||||||
|
// TODO: verify it is signed
|
||||||
|
|
||||||
|
// Code and comment from: basecoin/cmd/commands/tx.go
|
||||||
|
// Don't you hate having to do this?
|
||||||
|
// How many times have I lost an hour over this trick?!
|
||||||
|
txBytes := wire.BinaryBytes(struct {
|
||||||
|
bc.Tx `json:"unwrap"`
|
||||||
|
}{s.Tx})
|
||||||
|
return txBytes, nil
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
package counter
|
||||||
|
|
||||||
|
import (
|
||||||
|
flag "github.com/spf13/pflag"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
|
crypto "github.com/tendermint/go-crypto"
|
||||||
|
wire "github.com/tendermint/go-wire"
|
||||||
|
lightclient "github.com/tendermint/light-client"
|
||||||
|
"github.com/tendermint/light-client/commands"
|
||||||
|
"github.com/tendermint/light-client/commands/txs"
|
||||||
|
|
||||||
|
bcmd "github.com/tendermint/basecoin/cmd/basecli/commands"
|
||||||
|
"github.com/tendermint/basecoin/plugins/counter"
|
||||||
|
btypes "github.com/tendermint/basecoin/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CounterPresenter struct{}
|
||||||
|
|
||||||
|
func (_ CounterPresenter) MakeKey(str string) ([]byte, error) {
|
||||||
|
key := counter.New().StateKey()
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_ CounterPresenter) ParseData(raw []byte) (interface{}, error) {
|
||||||
|
var cp counter.CounterPluginState
|
||||||
|
err := wire.ReadBinaryBytes(raw, &cp)
|
||||||
|
return cp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
/**** build out the tx ****/
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ txs.ReaderMaker = CounterTxMaker{}
|
||||||
|
_ lightclient.TxReader = CounterTxReader{}
|
||||||
|
)
|
||||||
|
|
||||||
|
type CounterTxMaker struct{}
|
||||||
|
|
||||||
|
func (m CounterTxMaker) MakeReader() (lightclient.TxReader, error) {
|
||||||
|
chainID := viper.GetString(commands.ChainFlag)
|
||||||
|
return CounterTxReader{bcmd.AppTxReader{ChainID: chainID}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// define flags
|
||||||
|
|
||||||
|
type CounterFlags struct {
|
||||||
|
bcmd.AppFlags `mapstructure:",squash"`
|
||||||
|
Valid bool
|
||||||
|
CountFee string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m CounterTxMaker) Flags() (*flag.FlagSet, interface{}) {
|
||||||
|
fs, app := bcmd.AppFlagSet()
|
||||||
|
fs.String("countfee", "", "Coins to send in the format <amt><coin>,<amt><coin>...")
|
||||||
|
fs.Bool("valid", false, "Is count valid?")
|
||||||
|
return fs, &CounterFlags{AppFlags: app}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse flags
|
||||||
|
|
||||||
|
type CounterTxReader struct {
|
||||||
|
App bcmd.AppTxReader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t CounterTxReader) ReadTxJSON(data []byte, pk crypto.PubKey) (interface{}, error) {
|
||||||
|
// TODO: something. maybe?
|
||||||
|
return t.App.ReadTxJSON(data, pk)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t CounterTxReader) ReadTxFlags(flags interface{}, pk crypto.PubKey) (interface{}, error) {
|
||||||
|
data := flags.(*CounterFlags)
|
||||||
|
countFee, err := btypes.ParseCoins(data.CountFee)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := counter.CounterTx{
|
||||||
|
Valid: viper.GetBool("valid"),
|
||||||
|
Fee: countFee,
|
||||||
|
}
|
||||||
|
txBytes := wire.BinaryBytes(ctx)
|
||||||
|
|
||||||
|
return t.App.ReadTxFlags(&data.AppFlags, counter.New().Name(), txBytes, pk)
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
keycmd "github.com/tendermint/go-crypto/cmd"
|
||||||
|
"github.com/tendermint/light-client/commands"
|
||||||
|
"github.com/tendermint/light-client/commands/proofs"
|
||||||
|
"github.com/tendermint/light-client/commands/proxy"
|
||||||
|
"github.com/tendermint/light-client/commands/seeds"
|
||||||
|
"github.com/tendermint/light-client/commands/txs"
|
||||||
|
"github.com/tendermint/tmlibs/cli"
|
||||||
|
|
||||||
|
bcmd "github.com/tendermint/basecoin/cmd/basecli/commands"
|
||||||
|
bcount "github.com/tendermint/basecoin/cmd/basecli/counter"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BaseCli represents the base command when called without any subcommands
|
||||||
|
var BaseCli = &cobra.Command{
|
||||||
|
Use: "basecli",
|
||||||
|
Short: "Light client for tendermint",
|
||||||
|
Long: `Basecli is an version of tmcli including custom logic to
|
||||||
|
present a nice (not raw hex) interface to the basecoin blockchain structure.
|
||||||
|
|
||||||
|
This is a useful tool, but also serves to demonstrate how one can configure
|
||||||
|
tmcli to work for any custom abci app.
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
commands.AddBasicFlags(BaseCli)
|
||||||
|
|
||||||
|
//initialize proofs and txs
|
||||||
|
proofs.StatePresenters.Register("account", bcmd.AccountPresenter{})
|
||||||
|
proofs.TxPresenters.Register("base", bcmd.BaseTxPresenter{})
|
||||||
|
proofs.StatePresenters.Register("counter", bcount.CounterPresenter{})
|
||||||
|
|
||||||
|
txs.Register("send", bcmd.SendTxMaker{})
|
||||||
|
txs.Register("counter", bcount.CounterTxMaker{})
|
||||||
|
|
||||||
|
// set up the various commands to use
|
||||||
|
BaseCli.AddCommand(
|
||||||
|
keycmd.RootCmd,
|
||||||
|
commands.InitCmd,
|
||||||
|
seeds.RootCmd,
|
||||||
|
proofs.RootCmd,
|
||||||
|
txs.RootCmd,
|
||||||
|
proxy.RootCmd,
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd := cli.PrepareMainCmd(BaseCli, "BC", os.ExpandEnv("$HOME/.basecli"))
|
||||||
|
cmd.Execute()
|
||||||
|
}
|
|
@ -1,9 +1,12 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/cmd/commands"
|
"github.com/tendermint/basecoin/cmd/commands"
|
||||||
|
"github.com/tendermint/tmlibs/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -15,6 +18,7 @@ func main() {
|
||||||
RootCmd.AddCommand(
|
RootCmd.AddCommand(
|
||||||
commands.InitCmd,
|
commands.InitCmd,
|
||||||
commands.StartCmd,
|
commands.StartCmd,
|
||||||
|
commands.RelayCmd,
|
||||||
commands.TxCmd,
|
commands.TxCmd,
|
||||||
commands.QueryCmd,
|
commands.QueryCmd,
|
||||||
commands.KeyCmd,
|
commands.KeyCmd,
|
||||||
|
@ -25,5 +29,8 @@ func main() {
|
||||||
commands.VersionCmd,
|
commands.VersionCmd,
|
||||||
)
|
)
|
||||||
|
|
||||||
commands.ExecuteWithDebug(RootCmd)
|
cmd := cli.PrepareMainCmd(RootCmd, "BC", os.ExpandEnv("$HOME/.basecoin"))
|
||||||
|
if err := cmd.Execute(); err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@ import (
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/plugins/ibc"
|
"github.com/tendermint/basecoin/plugins/ibc"
|
||||||
|
|
||||||
"github.com/tendermint/go-merkle"
|
|
||||||
"github.com/tendermint/go-wire"
|
"github.com/tendermint/go-wire"
|
||||||
|
"github.com/tendermint/merkleeyes/iavl"
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -196,13 +196,18 @@ func ibcPacketCreateTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var payload ibc.Payload
|
||||||
|
if err := wire.ReadBinaryBytes(payloadBytes, &payload); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
ibcTx := ibc.IBCPacketCreateTx{
|
ibcTx := ibc.IBCPacketCreateTx{
|
||||||
Packet: ibc.Packet{
|
Packet: ibc.Packet{
|
||||||
SrcChainID: fromChain,
|
SrcChainID: fromChain,
|
||||||
DstChainID: toChain,
|
DstChainID: toChain,
|
||||||
Sequence: sequence,
|
Sequence: sequence,
|
||||||
Type: packetType,
|
Type: packetType,
|
||||||
Payload: payloadBytes,
|
Payload: payload,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,7 +234,7 @@ func ibcPacketPostTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var packet ibc.Packet
|
var packet ibc.Packet
|
||||||
proof := new(merkle.IAVLProof)
|
proof := new(iavl.IAVLProof)
|
||||||
|
|
||||||
err = wire.ReadBinaryBytes(packetBytes, &packet)
|
err = wire.ReadBinaryBytes(packetBytes, &packet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -6,8 +6,6 @@ import (
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
cmn "github.com/tendermint/go-common"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//commands
|
//commands
|
||||||
|
@ -33,15 +31,17 @@ func setupFile(path, data string, perm os.FileMode) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func initCmd(cmd *cobra.Command, args []string) error {
|
func initCmd(cmd *cobra.Command, args []string) error {
|
||||||
rootDir := BasecoinRoot("")
|
// this will ensure that config.toml is there if not yet created, and create dir
|
||||||
|
cfg, err := getTendermintConfig()
|
||||||
cmn.EnsureDir(rootDir, 0777)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// initalize basecoin
|
// initalize basecoin
|
||||||
genesisFile := path.Join(rootDir, "genesis.json")
|
genesisFile := cfg.GenesisFile()
|
||||||
privValFile := path.Join(rootDir, "priv_validator.json")
|
privValFile := cfg.PrivValidatorFile()
|
||||||
key1File := path.Join(rootDir, "key.json")
|
key1File := path.Join(cfg.RootDir, "key.json")
|
||||||
key2File := path.Join(rootDir, "key2.json")
|
key2File := path.Join(cfg.RootDir, "key2.json")
|
||||||
|
|
||||||
mod1, err := setupFile(genesisFile, GenesisJSON, 0644)
|
mod1, err := setupFile(genesisFile, GenesisJSON, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -61,29 +61,29 @@ func initCmd(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mod1 + mod2 + mod3 + mod4) > 0 {
|
if (mod1 + mod2 + mod3 + mod4) > 0 {
|
||||||
log.Notice("Initialized Basecoin", "genesis", genesisFile, "key", key1File)
|
logger.Info("Initialized Basecoin", "genesis", genesisFile, "key", key1File)
|
||||||
} else {
|
} else {
|
||||||
log.Notice("Already initialized", "priv_validator", privValFile)
|
logger.Info("Already initialized", "priv_validator", privValFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var PrivValJSON = `{
|
var PrivValJSON = `{
|
||||||
"address": "7A956FADD20D3A5B2375042B2959F8AB172A058F",
|
"address": "7A956FADD20D3A5B2375042B2959F8AB172A058F",
|
||||||
"last_height": 0,
|
"last_height": 0,
|
||||||
"last_round": 0,
|
"last_round": 0,
|
||||||
"last_signature": null,
|
"last_signature": null,
|
||||||
"last_signbytes": "",
|
"last_signbytes": "",
|
||||||
"last_step": 0,
|
"last_step": 0,
|
||||||
"priv_key": [
|
"priv_key": {
|
||||||
1,
|
"type": "ed25519",
|
||||||
"D07ABE82A8B15559A983B2DB5D4842B2B6E4D6AF58B080005662F424F17D68C17B90EA87E7DC0C7145C8C48C08992BE271C7234134343E8A8E8008E617DE7B30"
|
"data": "D07ABE82A8B15559A983B2DB5D4842B2B6E4D6AF58B080005662F424F17D68C17B90EA87E7DC0C7145C8C48C08992BE271C7234134343E8A8E8008E617DE7B30"
|
||||||
],
|
},
|
||||||
"pub_key": [
|
"pub_key": {
|
||||||
1,
|
"type": "ed25519",
|
||||||
"7B90EA87E7DC0C7145C8C48C08992BE271C7234134343E8A8E8008E617DE7B30"
|
"data": "7B90EA87E7DC0C7145C8C48C08992BE271C7234134343E8A8E8008E617DE7B30"
|
||||||
]
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
var GenesisJSON = `{
|
var GenesisJSON = `{
|
||||||
|
@ -94,10 +94,10 @@ var GenesisJSON = `{
|
||||||
{
|
{
|
||||||
"amount": 10,
|
"amount": 10,
|
||||||
"name": "",
|
"name": "",
|
||||||
"pub_key": [
|
"pub_key": {
|
||||||
1,
|
"type": "ed25519",
|
||||||
"7B90EA87E7DC0C7145C8C48C08992BE271C7234134343E8A8E8008E617DE7B30"
|
"data": "7B90EA87E7DC0C7145C8C48C08992BE271C7234134343E8A8E8008E617DE7B30"
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"app_options": {
|
"app_options": {
|
||||||
|
@ -117,25 +117,25 @@ var GenesisJSON = `{
|
||||||
}`
|
}`
|
||||||
|
|
||||||
var Key1JSON = `{
|
var Key1JSON = `{
|
||||||
"address": "1B1BE55F969F54064628A63B9559E7C21C925165",
|
"address": "1B1BE55F969F54064628A63B9559E7C21C925165",
|
||||||
"priv_key": {
|
"priv_key": {
|
||||||
"type": "ed25519",
|
"type": "ed25519",
|
||||||
"data": "C70D6934B4F55F1B7BC33B56B9CA8A2061384AFC19E91E44B40C4BBA182953D1619D3678599971ED29C7529DDD4DA537B97129893598A17C82E3AC9A8BA95279"
|
"data": "C70D6934B4F55F1B7BC33B56B9CA8A2061384AFC19E91E44B40C4BBA182953D1619D3678599971ED29C7529DDD4DA537B97129893598A17C82E3AC9A8BA95279"
|
||||||
},
|
},
|
||||||
"pub_key": {
|
"pub_key": {
|
||||||
"type": "ed25519",
|
"type": "ed25519",
|
||||||
"data": "619D3678599971ED29C7529DDD4DA537B97129893598A17C82E3AC9A8BA95279"
|
"data": "619D3678599971ED29C7529DDD4DA537B97129893598A17C82E3AC9A8BA95279"
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
var Key2JSON = `{
|
var Key2JSON = `{
|
||||||
"address": "1DA7C74F9C219229FD54CC9F7386D5A3839F0090",
|
"address": "1DA7C74F9C219229FD54CC9F7386D5A3839F0090",
|
||||||
"priv_key": {
|
"priv_key": {
|
||||||
"type": "ed25519",
|
"type": "ed25519",
|
||||||
"data": "34BAE9E65CE8245FAD035A0E3EED9401BDE8785FFB3199ACCF8F5B5DDF7486A8352195DA90CB0B90C24295B90AEBA25A5A71BC61BAB2FE2387241D439698B7B8"
|
"data": "34BAE9E65CE8245FAD035A0E3EED9401BDE8785FFB3199ACCF8F5B5DDF7486A8352195DA90CB0B90C24295B90AEBA25A5A71BC61BAB2FE2387241D439698B7B8"
|
||||||
},
|
},
|
||||||
"pub_key": {
|
"pub_key": {
|
||||||
"type": "ed25519",
|
"type": "ed25519",
|
||||||
"data": "352195DA90CB0B90C24295B90AEBA25A5A71BC61BAB2FE2387241D439698B7B8"
|
"data": "352195DA90CB0B90C24295B90AEBA25A5A71BC61BAB2FE2387241D439698B7B8"
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
|
@ -10,8 +10,10 @@ import (
|
||||||
|
|
||||||
//"github.com/pkg/errors"
|
//"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
|
"github.com/tendermint/tmlibs/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
//commands
|
//commands
|
||||||
|
@ -62,9 +64,9 @@ func (a *Address) UnmarshalJSON(addrHex []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Key struct {
|
type Key struct {
|
||||||
Address Address `json:"address"`
|
Address Address `json:"address"`
|
||||||
PubKey crypto.PubKeyS `json:"pub_key"`
|
PubKey crypto.PubKey `json:"pub_key"`
|
||||||
PrivKey crypto.PrivKeyS `json:"priv_key"`
|
PrivKey crypto.PrivKey `json:"priv_key"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements Signer
|
// Implements Signer
|
||||||
|
@ -75,18 +77,25 @@ func (k *Key) Sign(msg []byte) crypto.Signature {
|
||||||
// Generates a new validator with private key.
|
// Generates a new validator with private key.
|
||||||
func genKey() *Key {
|
func genKey() *Key {
|
||||||
privKey := crypto.GenPrivKeyEd25519()
|
privKey := crypto.GenPrivKeyEd25519()
|
||||||
addrBytes := privKey.PubKey().Address()
|
pubKey := privKey.PubKey()
|
||||||
|
addrBytes := pubKey.Address()
|
||||||
var addr Address
|
var addr Address
|
||||||
copy(addr[:], addrBytes)
|
copy(addr[:], addrBytes)
|
||||||
return &Key{
|
return &Key{
|
||||||
Address: addr,
|
Address: addr,
|
||||||
PubKey: crypto.PubKeyS{privKey.PubKey()},
|
PubKey: pubKey,
|
||||||
PrivKey: crypto.PrivKeyS{privKey},
|
PrivKey: privKey.Wrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadKey(keyFile string) (*Key, error) {
|
func LoadKey(keyFile string) (*Key, error) {
|
||||||
filePath := path.Join(BasecoinRoot(""), keyFile)
|
filePath := keyFile
|
||||||
|
|
||||||
|
if !strings.HasPrefix(keyFile, "/") && !strings.HasPrefix(keyFile, ".") {
|
||||||
|
rootDir := viper.GetString(cli.HomeFlag)
|
||||||
|
filePath = path.Join(rootDir, keyFile)
|
||||||
|
}
|
||||||
|
|
||||||
keyJSONBytes, err := ioutil.ReadFile(filePath)
|
keyJSONBytes, err := ioutil.ReadFile(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
package commands
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/tendermint/go-logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
var log = logger.New("module", "commands")
|
|
|
@ -8,8 +8,9 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/tendermint/go-merkle"
|
|
||||||
"github.com/tendermint/go-wire"
|
"github.com/tendermint/go-wire"
|
||||||
|
"github.com/tendermint/merkleeyes/iavl"
|
||||||
|
"github.com/tendermint/tendermint/rpc/client"
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -120,7 +121,8 @@ func accountCmd(cmd *cobra.Command, args []string) error {
|
||||||
return errors.Errorf("Account address (%v) is invalid hex: %v\n", addrHex, err)
|
return errors.Errorf("Account address (%v) is invalid hex: %v\n", addrHex, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
acc, err := getAcc(nodeFlag, addr)
|
httpClient := client.NewHTTP(nodeFlag, "/websocket")
|
||||||
|
acc, err := getAccWithClient(httpClient, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -202,7 +204,7 @@ func verifyCmd(cmd *cobra.Command, args []string) error {
|
||||||
return errors.Errorf("Proof (%v) is invalid hex: %v\n", proofFlag, err)
|
return errors.Errorf("Proof (%v) is invalid hex: %v\n", proofFlag, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
proof, err := merkle.ReadProof(proofBytes)
|
proof, err := iavl.ReadProof(proofBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Errorf("Error unmarshalling proof: %v\n", err)
|
return errors.Errorf("Error unmarshalling proof: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,247 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
// "github.com/spf13/viper"
|
||||||
|
// "github.com/tendermint/tmlibs/cli"
|
||||||
|
// "github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
|
"github.com/tendermint/go-wire"
|
||||||
|
"github.com/tendermint/merkleeyes/iavl"
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
|
|
||||||
|
"github.com/tendermint/basecoin/plugins/ibc"
|
||||||
|
"github.com/tendermint/basecoin/types"
|
||||||
|
"github.com/tendermint/tendermint/rpc/client"
|
||||||
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var RelayCmd = &cobra.Command{
|
||||||
|
Use: "relay",
|
||||||
|
Short: "Start basecoin relayer to relay IBC packets between chains",
|
||||||
|
RunE: relayCmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
//flags
|
||||||
|
var (
|
||||||
|
chain1AddrFlag string
|
||||||
|
chain2AddrFlag string
|
||||||
|
|
||||||
|
chain1IDFlag string
|
||||||
|
chain2IDFlag string
|
||||||
|
|
||||||
|
fromFileFlag string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
flags := []Flag2Register{
|
||||||
|
{&chain1AddrFlag, "chain1-addr", "tcp://localhost:46657", "Node address for chain1"},
|
||||||
|
{&chain2AddrFlag, "chain2-addr", "tcp://localhost:36657", "Node address for chain2"},
|
||||||
|
{&chain1IDFlag, "chain1-id", "test_chain_1", "ChainID for chain1"},
|
||||||
|
{&chain2IDFlag, "chain2-id", "test_chain_2", "ChainID for chain2"},
|
||||||
|
{&fromFileFlag, "from", "key.json", "Path to a private key to sign the transaction"},
|
||||||
|
}
|
||||||
|
RegisterFlags(RelayCmd, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loop(addr1, addr2, id1, id2 string) {
|
||||||
|
nextSeq := 0
|
||||||
|
|
||||||
|
// load the priv key
|
||||||
|
privKey, err := LoadKey(fromFlag)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err.Error())
|
||||||
|
cmn.PanicCrisis(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// relay from chain1 to chain2
|
||||||
|
thisRelayer := newRelayer(privKey, id2, addr2)
|
||||||
|
|
||||||
|
logger.Info(fmt.Sprintf("Relaying from chain %v on %v to chain %v on %v", id1, addr1, id2, addr2))
|
||||||
|
|
||||||
|
httpClient := client.NewHTTP(addr1, "/websocket")
|
||||||
|
|
||||||
|
OUTER:
|
||||||
|
for {
|
||||||
|
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
// get the latest ibc packet sequence number
|
||||||
|
key := fmt.Sprintf("ibc,egress,%v,%v", id1, id2)
|
||||||
|
query, err := queryWithClient(httpClient, []byte(key))
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("Error querying for latest sequence", "key", key, "error", err.Error())
|
||||||
|
continue OUTER
|
||||||
|
}
|
||||||
|
if len(query.Value) == 0 {
|
||||||
|
// nothing yet
|
||||||
|
continue OUTER
|
||||||
|
}
|
||||||
|
|
||||||
|
seq, err := strconv.ParseUint(string(query.Value), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("Error parsing sequence number from query", "query.Value", query.Value, "error", err.Error())
|
||||||
|
continue OUTER
|
||||||
|
}
|
||||||
|
seq -= 1 // seq is the packet count. -1 because 0-indexed
|
||||||
|
|
||||||
|
if nextSeq <= int(seq) {
|
||||||
|
logger.Info("Got new packets", "last-sequence", nextSeq-1, "new-sequence", seq)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get all packets since the last one we relayed
|
||||||
|
for ; nextSeq <= int(seq); nextSeq++ {
|
||||||
|
key := fmt.Sprintf("ibc,egress,%v,%v,%d", id1, id2, nextSeq)
|
||||||
|
query, err := queryWithClient(httpClient, []byte(key))
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("Error querying for packet", "seqeuence", nextSeq, "key", key, "error", err.Error())
|
||||||
|
continue OUTER
|
||||||
|
}
|
||||||
|
|
||||||
|
var packet ibc.Packet
|
||||||
|
err = wire.ReadBinaryBytes(query.Value, &packet)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("Error unmarshalling packet", "key", key, "query.Value", query.Value, "error", err.Error())
|
||||||
|
continue OUTER
|
||||||
|
}
|
||||||
|
|
||||||
|
proof := new(iavl.IAVLProof)
|
||||||
|
err = wire.ReadBinaryBytes(query.Proof, &proof)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("Error unmarshalling proof", "query.Proof", query.Proof, "error", err.Error())
|
||||||
|
continue OUTER
|
||||||
|
}
|
||||||
|
|
||||||
|
// query.Height is actually for the next block,
|
||||||
|
// so wait a block before we fetch the header & commit
|
||||||
|
if err := waitForBlock(httpClient); err != nil {
|
||||||
|
logger.Error("Error waiting for a block", "addr", addr1, "error", err.Error())
|
||||||
|
continue OUTER
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the header and commit from the height the query was done at
|
||||||
|
res, err := httpClient.Commit(int(query.Height))
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("Error fetching header and commits", "height", query.Height, "error", err.Error())
|
||||||
|
continue OUTER
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the chain state on the other chain
|
||||||
|
updateTx := ibc.IBCUpdateChainTx{
|
||||||
|
Header: *res.Header,
|
||||||
|
Commit: *res.Commit,
|
||||||
|
}
|
||||||
|
logger.Info("Updating chain", "src-chain", id1, "height", res.Header.Height, "appHash", res.Header.AppHash)
|
||||||
|
if err := thisRelayer.appTx(updateTx); err != nil {
|
||||||
|
logger.Error("Error creating/sending IBCUpdateChainTx", "error", err.Error())
|
||||||
|
continue OUTER
|
||||||
|
}
|
||||||
|
|
||||||
|
// relay the packet and proof
|
||||||
|
logger.Info("Relaying packet", "src-chain", id1, "height", query.Height, "sequence", nextSeq)
|
||||||
|
postTx := ibc.IBCPacketPostTx{
|
||||||
|
FromChainID: id1,
|
||||||
|
FromChainHeight: query.Height,
|
||||||
|
Packet: packet,
|
||||||
|
Proof: proof,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := thisRelayer.appTx(postTx); err != nil {
|
||||||
|
logger.Error("Error creating/sending IBCPacketPostTx", "error", err.Error())
|
||||||
|
// dont `continue OUTER` here. the error might be eg. Already exists
|
||||||
|
// TODO: catch this programmatically ?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type relayer struct {
|
||||||
|
privKey *Key
|
||||||
|
chainID string
|
||||||
|
nodeAddr string
|
||||||
|
client *client.HTTP
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRelayer(privKey *Key, chainID, nodeAddr string) *relayer {
|
||||||
|
httpClient := client.NewHTTP(nodeAddr, "/websocket")
|
||||||
|
return &relayer{
|
||||||
|
privKey: privKey,
|
||||||
|
chainID: chainID,
|
||||||
|
nodeAddr: nodeAddr,
|
||||||
|
client: httpClient,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *relayer) appTx(ibcTx ibc.IBCTx) error {
|
||||||
|
acc, err := getAccWithClient(r.client, r.privKey.Address[:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sequence := acc.Sequence + 1
|
||||||
|
|
||||||
|
data := []byte(wire.BinaryBytes(struct {
|
||||||
|
ibc.IBCTx `json:"unwrap"`
|
||||||
|
}{ibcTx}))
|
||||||
|
|
||||||
|
smallCoins := types.Coin{"mycoin", 1}
|
||||||
|
|
||||||
|
input := types.NewTxInput(r.privKey.PubKey, types.Coins{smallCoins}, sequence)
|
||||||
|
tx := &types.AppTx{
|
||||||
|
Gas: 0,
|
||||||
|
Fee: smallCoins,
|
||||||
|
Name: "IBC",
|
||||||
|
Input: input,
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.Input.Signature = r.privKey.Sign(tx.SignBytes(r.chainID))
|
||||||
|
txBytes := []byte(wire.BinaryBytes(struct {
|
||||||
|
types.Tx `json:"unwrap"`
|
||||||
|
}{tx}))
|
||||||
|
|
||||||
|
data, log, err := broadcastTxWithClient(r.client, txBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, _ = data, log
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// broadcast the transaction to tendermint
|
||||||
|
func broadcastTxWithClient(httpClient *client.HTTP, tx tmtypes.Tx) ([]byte, string, error) {
|
||||||
|
res, err := httpClient.BroadcastTxCommit(tx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", errors.Errorf("Error on broadcast tx: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !res.CheckTx.Code.IsOK() {
|
||||||
|
r := res.CheckTx
|
||||||
|
return nil, "", errors.Errorf("BroadcastTxCommit got non-zero exit code: %v. %X; %s", r.Code, r.Data, r.Log)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !res.DeliverTx.Code.IsOK() {
|
||||||
|
r := res.DeliverTx
|
||||||
|
return nil, "", errors.Errorf("BroadcastTxCommit got non-zero exit code: %v. %X; %s", r.Code, r.Data, r.Log)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.DeliverTx.Data, res.DeliverTx.Log, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func relayCmd(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
|
go loop(chain1AddrFlag, chain2AddrFlag, chain1IDFlag, chain2IDFlag)
|
||||||
|
go loop(chain2AddrFlag, chain1AddrFlag, chain2IDFlag, chain1IDFlag)
|
||||||
|
|
||||||
|
cmn.TrapSignal(func() {
|
||||||
|
// TODO: Cleanup
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
tmcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
tmcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||||
tmcfg "github.com/tendermint/tendermint/config/tendermint"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var UnsafeResetAllCmd = &cobra.Command{
|
var UnsafeResetAllCmd = &cobra.Command{
|
||||||
|
@ -14,8 +13,10 @@ var UnsafeResetAllCmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
func unsafeResetAllCmd(cmd *cobra.Command, args []string) error {
|
func unsafeResetAllCmd(cmd *cobra.Command, args []string) error {
|
||||||
basecoinDir := BasecoinRoot("")
|
cfg, err := getTendermintConfig()
|
||||||
tmConfig := tmcfg.GetConfig(basecoinDir)
|
if err != nil {
|
||||||
tmcmd.ResetAll(tmConfig, log)
|
return err
|
||||||
|
}
|
||||||
|
tmcmd.ResetAll(cfg.DBDir(), cfg.PrivValidatorFile(), logger)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "main")
|
||||||
|
)
|
|
@ -7,15 +7,18 @@ import (
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/tendermint/abci/server"
|
"github.com/tendermint/abci/server"
|
||||||
cmn "github.com/tendermint/go-common"
|
|
||||||
eyes "github.com/tendermint/merkleeyes/client"
|
eyes "github.com/tendermint/merkleeyes/client"
|
||||||
|
"github.com/tendermint/tmlibs/cli"
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
tmcfg "github.com/tendermint/tendermint/config/tendermint"
|
"github.com/tendermint/tendermint/config"
|
||||||
"github.com/tendermint/tendermint/node"
|
"github.com/tendermint/tendermint/node"
|
||||||
"github.com/tendermint/tendermint/proxy"
|
"github.com/tendermint/tendermint/proxy"
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/app"
|
"github.com/tendermint/basecoin/app"
|
||||||
)
|
)
|
||||||
|
@ -49,12 +52,12 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func startCmd(cmd *cobra.Command, args []string) error {
|
func startCmd(cmd *cobra.Command, args []string) error {
|
||||||
basecoinDir := BasecoinRoot("")
|
rootDir := viper.GetString(cli.HomeFlag)
|
||||||
|
|
||||||
// Connect to MerkleEyes
|
// Connect to MerkleEyes
|
||||||
var eyesCli *eyes.Client
|
var eyesCli *eyes.Client
|
||||||
if eyesFlag == "local" {
|
if eyesFlag == "local" {
|
||||||
eyesCli = eyes.NewLocalClient(path.Join(basecoinDir, "data", "merkleeyes.db"), EyesCacheSize)
|
eyesCli = eyes.NewLocalClient(path.Join(rootDir, "data", "merkleeyes.db"), EyesCacheSize)
|
||||||
} else {
|
} else {
|
||||||
var err error
|
var err error
|
||||||
eyesCli, err = eyes.NewClient(eyesFlag)
|
eyesCli, err = eyes.NewClient(eyesFlag)
|
||||||
|
@ -65,6 +68,7 @@ func startCmd(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
// Create Basecoin app
|
// Create Basecoin app
|
||||||
basecoinApp := app.NewBasecoin(eyesCli)
|
basecoinApp := app.NewBasecoin(eyesCli)
|
||||||
|
basecoinApp.SetLogger(logger.With("module", "app"))
|
||||||
|
|
||||||
// register IBC plugn
|
// register IBC plugn
|
||||||
basecoinApp.RegisterPlugin(NewIBCPlugin())
|
basecoinApp.RegisterPlugin(NewIBCPlugin())
|
||||||
|
@ -78,7 +82,7 @@ func startCmd(cmd *cobra.Command, args []string) error {
|
||||||
// else, assume it's been loaded
|
// else, assume it's been loaded
|
||||||
if basecoinApp.GetState().GetChainID() == "" {
|
if basecoinApp.GetState().GetChainID() == "" {
|
||||||
// If genesis file exists, set key-value options
|
// If genesis file exists, set key-value options
|
||||||
genesisFile := path.Join(basecoinDir, "genesis.json")
|
genesisFile := path.Join(rootDir, "genesis.json")
|
||||||
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 {
|
||||||
|
@ -91,23 +95,24 @@ func startCmd(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
chainID := basecoinApp.GetState().GetChainID()
|
chainID := basecoinApp.GetState().GetChainID()
|
||||||
if withoutTendermintFlag {
|
if withoutTendermintFlag {
|
||||||
log.Notice("Starting Basecoin without Tendermint", "chain_id", chainID)
|
logger.Info("Starting Basecoin without Tendermint", "chain_id", chainID)
|
||||||
// run just the abci app/server
|
// run just the abci app/server
|
||||||
return startBasecoinABCI(basecoinApp)
|
return startBasecoinABCI(basecoinApp)
|
||||||
} else {
|
} else {
|
||||||
log.Notice("Starting Basecoin with Tendermint", "chain_id", chainID)
|
logger.Info("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(rootDir, basecoinApp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func startBasecoinABCI(basecoinApp *app.Basecoin) error {
|
func startBasecoinABCI(basecoinApp *app.Basecoin) error {
|
||||||
|
|
||||||
// Start the ABCI listener
|
// Start the ABCI listener
|
||||||
svr, err := server.NewServer(addrFlag, "socket", basecoinApp)
|
svr, err := server.NewServer(addrFlag, "socket", basecoinApp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Errorf("Error creating listener: %v\n", err)
|
return errors.Errorf("Error creating listener: %v\n", err)
|
||||||
}
|
}
|
||||||
|
svr.SetLogger(logger.With("module", "abci-server"))
|
||||||
|
svr.Start()
|
||||||
|
|
||||||
// Wait forever
|
// Wait forever
|
||||||
cmn.TrapSignal(func() {
|
cmn.TrapSignal(func() {
|
||||||
|
@ -117,27 +122,41 @@ func startBasecoinABCI(basecoinApp *app.Basecoin) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getTendermintConfig() (*config.Config, error) {
|
||||||
|
cfg := config.DefaultConfig()
|
||||||
|
err := viper.Unmarshal(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cfg.SetRoot(cfg.RootDir)
|
||||||
|
config.EnsureRoot(cfg.RootDir)
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
func startTendermint(dir string, basecoinApp *app.Basecoin) error {
|
func startTendermint(dir string, basecoinApp *app.Basecoin) error {
|
||||||
|
cfg, err := getTendermintConfig()
|
||||||
// Get configuration
|
|
||||||
tmConfig := tmcfg.GetConfig(dir)
|
|
||||||
// logger.SetLogLevel("notice") //config.GetString("log_level"))
|
|
||||||
// parseFlags(config, args[1:]) // Command line overrides
|
|
||||||
|
|
||||||
// Create & start tendermint node
|
|
||||||
privValidatorFile := tmConfig.GetString("priv_validator_file")
|
|
||||||
privValidator := tmtypes.LoadOrGenPrivValidator(privValidatorFile)
|
|
||||||
n := node.NewNode(tmConfig, privValidator, proxy.NewLocalClientCreator(basecoinApp))
|
|
||||||
|
|
||||||
_, err := n.Start()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait forever
|
// TODO: parse the log level from the config properly (multi modules)
|
||||||
cmn.TrapSignal(func() {
|
// but some tm code must be refactored for better usability
|
||||||
// Cleanup
|
lvl, err := log.AllowLevel(cfg.LogLevel)
|
||||||
n.Stop()
|
if err != nil {
|
||||||
})
|
return err
|
||||||
|
}
|
||||||
|
tmLogger := log.NewFilter(logger, lvl)
|
||||||
|
|
||||||
|
// Create & start tendermint node
|
||||||
|
privValidator := types.LoadOrGenPrivValidator(cfg.PrivValidatorFile(), tmLogger)
|
||||||
|
n := node.NewNode(cfg, privValidator, proxy.NewLocalClientCreator(basecoinApp), tmLogger.With("module", "node"))
|
||||||
|
|
||||||
|
_, err = n.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trap signal, run forever.
|
||||||
|
n.RunForever()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,15 @@ package commands
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
crypto "github.com/tendermint/go-crypto"
|
|
||||||
|
|
||||||
client "github.com/tendermint/go-rpc/client"
|
|
||||||
wire "github.com/tendermint/go-wire"
|
wire "github.com/tendermint/go-wire"
|
||||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
"github.com/tendermint/tendermint/rpc/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
//commands
|
//commands
|
||||||
|
@ -60,7 +59,7 @@ func init() {
|
||||||
{&fromFlag, "from", "key.json", "Path to a private key to sign the transaction"},
|
{&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)"},
|
{&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"},
|
{&gasFlag, "gas", 0, "The amount of gas for the transaction"},
|
||||||
{&feeFlag, "fee", "", "Coins for the transaction fee of the format <amt><coin>"},
|
{&feeFlag, "fee", "0coin", "Coins for the transaction fee of the format <amt><coin>"},
|
||||||
{&seqFlag, "sequence", -1, "Sequence number for the account (-1 to autocalculate)"},
|
{&seqFlag, "sequence", -1, "Sequence number for the account (-1 to autocalculate)"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,12 +82,29 @@ func init() {
|
||||||
|
|
||||||
func sendTxCmd(cmd *cobra.Command, args []string) error {
|
func sendTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
|
var toHex string
|
||||||
|
var chainPrefix string
|
||||||
|
spl := strings.Split(toFlag, "/")
|
||||||
|
switch len(spl) {
|
||||||
|
case 1:
|
||||||
|
toHex = spl[0]
|
||||||
|
case 2:
|
||||||
|
chainPrefix = spl[0]
|
||||||
|
toHex = spl[1]
|
||||||
|
default:
|
||||||
|
return errors.Errorf("To address has too many slashes")
|
||||||
|
}
|
||||||
|
|
||||||
// convert destination address to bytes
|
// convert destination address to bytes
|
||||||
to, err := hex.DecodeString(StripHex(toFlag))
|
to, err := hex.DecodeString(StripHex(toHex))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Errorf("To address is invalid hex: %v\n", err)
|
return errors.Errorf("To address is invalid hex: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if chainPrefix != "" {
|
||||||
|
to = []byte(chainPrefix + "/" + string(to))
|
||||||
|
}
|
||||||
|
|
||||||
// load the priv key
|
// load the priv key
|
||||||
privKey, err := LoadKey(fromFlag)
|
privKey, err := LoadKey(fromFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -123,7 +139,7 @@ func sendTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
// sign that puppy
|
// sign that puppy
|
||||||
signBytes := tx.SignBytes(chainIDFlag)
|
signBytes := tx.SignBytes(chainIDFlag)
|
||||||
tx.Inputs[0].Signature = crypto.SignatureS{privKey.Sign(signBytes)}
|
tx.Inputs[0].Signature = privKey.Sign(signBytes)
|
||||||
|
|
||||||
fmt.Println("Signed SendTx:")
|
fmt.Println("Signed SendTx:")
|
||||||
fmt.Println(string(wire.JSONBytes(tx)))
|
fmt.Println(string(wire.JSONBytes(tx)))
|
||||||
|
@ -179,7 +195,7 @@ func AppTx(name string, data []byte) error {
|
||||||
Data: data,
|
Data: data,
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.Input.Signature = crypto.SignatureS{privKey.Sign(tx.SignBytes(chainIDFlag))}
|
tx.Input.Signature = 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)))
|
||||||
|
@ -194,23 +210,17 @@ func AppTx(name string, data []byte) error {
|
||||||
|
|
||||||
// broadcast the transaction to tendermint
|
// broadcast the transaction to tendermint
|
||||||
func broadcastTx(tx types.Tx) ([]byte, string, error) {
|
func broadcastTx(tx types.Tx) ([]byte, string, error) {
|
||||||
|
httpClient := client.NewHTTP(txNodeFlag, "/websocket")
|
||||||
tmResult := new(ctypes.TMResult)
|
|
||||||
uriClient := client.NewURIClient(txNodeFlag)
|
|
||||||
|
|
||||||
// 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}))
|
||||||
|
res, err := httpClient.BroadcastTxCommit(txBytes)
|
||||||
_, err := uriClient.Call("broadcast_tx_commit", map[string]interface{}{"tx": txBytes}, tmResult)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", errors.Errorf("Error on broadcast tx: %v", err)
|
return nil, "", errors.Errorf("Error on broadcast tx: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|
@ -228,12 +238,12 @@ func broadcastTx(tx types.Tx) ([]byte, string, error) {
|
||||||
// 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(address []byte) (int, error) {
|
func getSeq(address []byte) (int, error) {
|
||||||
|
|
||||||
if seqFlag >= 0 {
|
if seqFlag >= 0 {
|
||||||
return seqFlag, nil
|
return seqFlag, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
acc, err := getAcc(txNodeFlag, address)
|
httpClient := client.NewHTTP(txNodeFlag, "/websocket")
|
||||||
|
acc, err := getAccWithClient(httpClient, address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,52 +3,20 @@ package commands
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/state"
|
abci "github.com/tendermint/abci/types"
|
||||||
|
wire "github.com/tendermint/go-wire"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
|
|
||||||
abci "github.com/tendermint/abci/types"
|
client "github.com/tendermint/tendermint/rpc/client"
|
||||||
cmn "github.com/tendermint/go-common"
|
|
||||||
client "github.com/tendermint/go-rpc/client"
|
|
||||||
wire "github.com/tendermint/go-wire"
|
|
||||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
//This variable can be overwritten by plugin applications
|
|
||||||
// if they require a different working directory
|
|
||||||
var DefaultHome = ".basecoin"
|
|
||||||
|
|
||||||
func BasecoinRoot(rootDir string) string {
|
|
||||||
if rootDir == "" {
|
|
||||||
rootDir = os.Getenv("BCHOME")
|
|
||||||
}
|
|
||||||
if rootDir == "" {
|
|
||||||
rootDir = path.Join(os.Getenv("HOME"), DefaultHome)
|
|
||||||
}
|
|
||||||
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
|
//Quickly registering flags can be quickly achieved through using the utility functions
|
||||||
//RegisterFlags, and RegisterPersistentFlags. Ex:
|
//RegisterFlags, and RegisterPersistentFlags. Ex:
|
||||||
// flags := []Flag2Register{
|
// flags := []Flag2Register{
|
||||||
|
@ -130,31 +98,27 @@ func StripHex(s string) string {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func Query(tmAddr string, key []byte) (*abci.ResponseQuery, error) {
|
func Query(tmAddr string, key []byte) (*abci.ResultQuery, error) {
|
||||||
uriClient := client.NewURIClient(tmAddr)
|
httpClient := client.NewHTTP(tmAddr, "/websocket")
|
||||||
tmResult := new(ctypes.TMResult)
|
return queryWithClient(httpClient, key)
|
||||||
|
}
|
||||||
|
|
||||||
params := map[string]interface{}{
|
func queryWithClient(httpClient *client.HTTP, key []byte) (*abci.ResultQuery, error) {
|
||||||
"path": "/key",
|
res, err := httpClient.ABCIQuery("/key", key, true)
|
||||||
"data": key,
|
|
||||||
"prove": true,
|
|
||||||
}
|
|
||||||
_, err := uriClient.Call("abci_query", params, tmResult)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Errorf("Error calling /abci_query: %v", err)
|
return nil, errors.Errorf("Error calling /abci_query: %v", err)
|
||||||
}
|
}
|
||||||
res := (*tmResult).(*ctypes.ResultABCIQuery)
|
if !res.Code.IsOK() {
|
||||||
if !res.Response.Code.IsOK() {
|
return nil, errors.Errorf("Query got non-zero exit code: %v. %s", res.Code, res.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.ResultQuery, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetch the account by querying the app
|
// fetch the account by querying the app
|
||||||
func getAcc(tmAddr string, address []byte) (*types.Account, error) {
|
func getAccWithClient(httpClient *client.HTTP, address []byte) (*types.Account, error) {
|
||||||
|
|
||||||
key := state.AccountKey(address)
|
key := types.AccountKey(address)
|
||||||
response, err := Query(tmAddr, key)
|
response, err := queryWithClient(httpClient, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -176,17 +140,33 @@ func getAcc(tmAddr string, address []byte) (*types.Account, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHeaderAndCommit(tmAddr string, height int) (*tmtypes.Header, *tmtypes.Commit, error) {
|
func getHeaderAndCommit(tmAddr string, height int) (*tmtypes.Header, *tmtypes.Commit, error) {
|
||||||
tmResult := new(ctypes.TMResult)
|
httpClient := client.NewHTTP(tmAddr, "/websocket")
|
||||||
uriClient := client.NewURIClient(tmAddr)
|
res, err := httpClient.Commit(height)
|
||||||
|
|
||||||
method := "commit"
|
|
||||||
_, err := uriClient.Call(method, map[string]interface{}{"height": height}, tmResult)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errors.Errorf("Error on %s: %v", method, err)
|
return nil, nil, errors.Errorf("Error on commit: %v", err)
|
||||||
}
|
}
|
||||||
resCommit := (*tmResult).(*ctypes.ResultCommit)
|
header := res.Header
|
||||||
header := resCommit.Header
|
commit := res.Commit
|
||||||
commit := resCommit.Commit
|
|
||||||
|
|
||||||
return header, commit, nil
|
return header, commit, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func waitForBlock(httpClient *client.HTTP) error {
|
||||||
|
res, err := httpClient.Status()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
lastHeight := res.LatestBlockHeight
|
||||||
|
for {
|
||||||
|
res, err := httpClient.Status()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.LatestBlockHeight > lastHeight {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,19 +1,22 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/cmd/commands"
|
"github.com/tendermint/basecoin/cmd/commands"
|
||||||
|
"github.com/tendermint/tmlibs/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
var RootCmd = &cobra.Command{
|
var RootCmd = &cobra.Command{
|
||||||
Use: "counter",
|
Use: "counter",
|
||||||
Short: "demo plugin for basecoin",
|
Short: "demo plugin for basecoin",
|
||||||
}
|
}
|
||||||
|
|
||||||
RootCmd.AddCommand(
|
RootCmd.AddCommand(
|
||||||
|
commands.InitCmd,
|
||||||
commands.StartCmd,
|
commands.StartCmd,
|
||||||
commands.TxCmd,
|
commands.TxCmd,
|
||||||
commands.QueryCmd,
|
commands.QueryCmd,
|
||||||
|
@ -21,8 +24,10 @@ func main() {
|
||||||
commands.VerifyCmd,
|
commands.VerifyCmd,
|
||||||
commands.BlockCmd,
|
commands.BlockCmd,
|
||||||
commands.AccountCmd,
|
commands.AccountCmd,
|
||||||
commands.QuickVersionCmd("0.1.0"),
|
commands.UnsafeResetAllCmd,
|
||||||
|
commands.VersionCmd,
|
||||||
)
|
)
|
||||||
|
|
||||||
commands.ExecuteWithDebug(RootCmd)
|
cmd := cli.PrepareMainCmd(RootCmd, "BC", os.ExpandEnv("$HOME/.basecoin"))
|
||||||
|
cmd.Execute()
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,5 +7,5 @@ node_laddr = "tcp://0.0.0.0:46656"
|
||||||
seeds = ""
|
seeds = ""
|
||||||
fast_sync = true
|
fast_sync = true
|
||||||
db_backend = "leveldb"
|
db_backend = "leveldb"
|
||||||
log_level = "notice"
|
log_level = "info"
|
||||||
rpc_laddr = "tcp://0.0.0.0:46657"
|
rpc_laddr = "tcp://0.0.0.0:46657"
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
{
|
{
|
||||||
"amount": 10,
|
"amount": 10,
|
||||||
"name": "",
|
"name": "",
|
||||||
"pub_key": [
|
"pub_key": {
|
||||||
1,
|
"type": "ed25519",
|
||||||
"D6EBB92440CF375054AA59BCF0C99D596DEEDFFB2543CAE1BA1908B72CF9676A"
|
"data":"D6EBB92440CF375054AA59BCF0C99D596DEEDFFB2543CAE1BA1908B72CF9676A"
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"app_options": {
|
"app_options": {
|
||||||
|
|
|
@ -1,16 +1 @@
|
||||||
{
|
{"address":"EBB0B4A899973C524A6BB18A161056A55F590F41","pub_key":{"type":"ed25519","data":"D6EBB92440CF375054AA59BCF0C99D596DEEDFFB2543CAE1BA1908B72CF9676A"},"last_height":0,"last_round":0,"last_step":0,"last_signature":null,"priv_key":{"type":"ed25519","data":"5FFDC1EA5FA2CA4A0A5503C86D2D348C5B401AD80FAA1899508F1ED00D8982E8D6EBB92440CF375054AA59BCF0C99D596DEEDFFB2543CAE1BA1908B72CF9676A"}}
|
||||||
"address": "EBB0B4A899973C524A6BB18A161056A55F590F41",
|
|
||||||
"last_height": 0,
|
|
||||||
"last_round": 0,
|
|
||||||
"last_signature": null,
|
|
||||||
"last_signbytes": "",
|
|
||||||
"last_step": 0,
|
|
||||||
"priv_key": [
|
|
||||||
1,
|
|
||||||
"5FFDC1EA5FA2CA4A0A5503C86D2D348C5B401AD80FAA1899508F1ED00D8982E8D6EBB92440CF375054AA59BCF0C99D596DEEDFFB2543CAE1BA1908B72CF9676A"
|
|
||||||
],
|
|
||||||
"pub_key": [
|
|
||||||
1,
|
|
||||||
"D6EBB92440CF375054AA59BCF0C99D596DEEDFFB2543CAE1BA1908B72CF9676A"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -7,5 +7,5 @@ node_laddr = "tcp://0.0.0.0:46656"
|
||||||
seeds = ""
|
seeds = ""
|
||||||
fast_sync = true
|
fast_sync = true
|
||||||
db_backend = "leveldb"
|
db_backend = "leveldb"
|
||||||
log_level = "notice"
|
log_level = "info"
|
||||||
rpc_laddr = "tcp://0.0.0.0:46657"
|
rpc_laddr = "tcp://0.0.0.0:46657"
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
{
|
{
|
||||||
"amount": 10,
|
"amount": 10,
|
||||||
"name": "",
|
"name": "",
|
||||||
"pub_key": [
|
"pub_key": {
|
||||||
1,
|
"type": "ed25519",
|
||||||
"9A76DDE4CA4EE660C073D288DBE4F8A128F23857881A95F18167682D47E7058F"
|
"data": "9A76DDE4CA4EE660C073D288DBE4F8A128F23857881A95F18167682D47E7058F"
|
||||||
]
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"app_options": {
|
"app_options": {
|
||||||
|
|
|
@ -1,16 +1 @@
|
||||||
{
|
{"address":"D42CFCB9C42DF9A73143EEA89255D1DF027B6240","pub_key":{"type":"ed25519","data":"9A76DDE4CA4EE660C073D288DBE4F8A128F23857881A95F18167682D47E7058F"},"last_height":0,"last_round":0,"last_step":0,"last_signature":null,"priv_key":{"type":"ed25519","data":"6353FAF4ADEB03EA496A9EAE5BE56C4C6A851CB705401788184FDC9198413C2C9A76DDE4CA4EE660C073D288DBE4F8A128F23857881A95F18167682D47E7058F"}}
|
||||||
"address": "D42CFCB9C42DF9A73143EEA89255D1DF027B6240",
|
|
||||||
"last_height": 0,
|
|
||||||
"last_round": 0,
|
|
||||||
"last_signature": null,
|
|
||||||
"last_signbytes": "",
|
|
||||||
"last_step": 0,
|
|
||||||
"priv_key": [
|
|
||||||
1,
|
|
||||||
"6353FAF4ADEB03EA496A9EAE5BE56C4C6A851CB705401788184FDC9198413C2C9A76DDE4CA4EE660C073D288DBE4F8A128F23857881A95F18167682D47E7058F"
|
|
||||||
],
|
|
||||||
"pub_key": [
|
|
||||||
1,
|
|
||||||
"9A76DDE4CA4EE660C073D288DBE4F8A128F23857881A95F18167682D47E7058F"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -4,7 +4,8 @@ set -e
|
||||||
cd $GOPATH/src/github.com/tendermint/basecoin/demo
|
cd $GOPATH/src/github.com/tendermint/basecoin/demo
|
||||||
|
|
||||||
LOG_DIR="."
|
LOG_DIR="."
|
||||||
TM_VERSION="v0.9.2"
|
TM_VERSION="develop"
|
||||||
|
#TM_VERSION="v0.10.0"
|
||||||
|
|
||||||
if [[ "$CIRCLECI" == "true" ]]; then
|
if [[ "$CIRCLECI" == "true" ]]; then
|
||||||
# set log dir
|
# set log dir
|
||||||
|
@ -23,6 +24,13 @@ fi
|
||||||
|
|
||||||
set -u
|
set -u
|
||||||
|
|
||||||
|
function ifExit() {
|
||||||
|
if [[ "$?" != 0 ]]; then
|
||||||
|
echo "FAIL"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
function removeQuotes() {
|
function removeQuotes() {
|
||||||
temp="${1%\"}"
|
temp="${1%\"}"
|
||||||
temp="${temp#\"}"
|
temp="${temp#\"}"
|
||||||
|
@ -52,12 +60,12 @@ function waitForNode() {
|
||||||
|
|
||||||
function waitForBlock() {
|
function waitForBlock() {
|
||||||
addr=$1
|
addr=$1
|
||||||
b1=`curl -s $addr/status | jq .result[1].latest_block_height`
|
b1=`curl -s $addr/status | jq .result.latest_block_height`
|
||||||
b2=$b1
|
b2=$b1
|
||||||
while [ "$b2" == "$b1" ]; do
|
while [ "$b2" == "$b1" ]; do
|
||||||
echo "Waiting for node $addr to commit a block ..."
|
echo "Waiting for node $addr to commit a block ..."
|
||||||
sleep 1
|
sleep 1
|
||||||
b2=`curl -s $addr/status | jq .result[1].latest_block_height`
|
b2=`curl -s $addr/status | jq .result.latest_block_height`
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,12 +91,16 @@ echo ""
|
||||||
echo "... starting chains"
|
echo "... starting chains"
|
||||||
echo ""
|
echo ""
|
||||||
# start the first node
|
# start the first node
|
||||||
TMROOT=$BCHOME1 tendermint node --skip_upnp --log_level=info &> $LOG_DIR/chain1_tendermint.log &
|
TMROOT=$BCHOME1 tendermint node --p2p.skip_upnp --log_level=info &> $LOG_DIR/chain1_tendermint.log &
|
||||||
|
ifExit
|
||||||
BCHOME=$BCHOME1 basecoin start --without-tendermint &> $LOG_DIR/chain1_basecoin.log &
|
BCHOME=$BCHOME1 basecoin start --without-tendermint &> $LOG_DIR/chain1_basecoin.log &
|
||||||
|
ifExit
|
||||||
|
|
||||||
# start the second node
|
# start the second node
|
||||||
TMROOT=$BCHOME2 tendermint node --skip_upnp --log_level=info --node_laddr tcp://localhost:36656 --rpc_laddr tcp://localhost:36657 --proxy_app tcp://localhost:36658 &> $LOG_DIR/chain2_tendermint.log &
|
TMROOT=$BCHOME2 tendermint node --p2p.skip_upnp --log_level=info --p2p.laddr tcp://localhost:36656 --rpc_laddr tcp://localhost:36657 --proxy_app tcp://localhost:36658 &> $LOG_DIR/chain2_tendermint.log &
|
||||||
|
ifExit
|
||||||
BCHOME=$BCHOME2 basecoin start --address tcp://localhost:36658 --without-tendermint &> $LOG_DIR/chain2_basecoin.log &
|
BCHOME=$BCHOME2 basecoin start --address tcp://localhost:36658 --without-tendermint &> $LOG_DIR/chain2_basecoin.log &
|
||||||
|
ifExit
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "... waiting for chains to start"
|
echo "... waiting for chains to start"
|
||||||
|
@ -105,19 +117,26 @@ echo "... registering chain1 on chain2"
|
||||||
echo ""
|
echo ""
|
||||||
# register chain1 on chain2
|
# register chain1 on chain2
|
||||||
basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 register --ibc_chain_id $CHAIN_ID1 --genesis $BCHOME1/genesis.json
|
basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 register --ibc_chain_id $CHAIN_ID1 --genesis $BCHOME1/genesis.json
|
||||||
|
ifExit
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "... creating egress packet on chain1"
|
echo "... creating egress packet on chain1"
|
||||||
echo ""
|
echo ""
|
||||||
# create a packet on chain1 destined for chain2
|
# send coins from chain1 to an address on chain2
|
||||||
PAYLOAD="DEADBEEF" #TODO
|
# TODO: dont hardcode the address
|
||||||
basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS1 packet create --ibc_from $CHAIN_ID1 --to $CHAIN_ID2 --type coin --payload $PAYLOAD --ibc_sequence 1
|
basecoin tx send --amount 10mycoin $CHAIN_FLAGS1 --to $CHAIN_ID2/053BA0F19616AFF975C8756A2CBFF04F408B4D47
|
||||||
|
ifExit
|
||||||
|
|
||||||
|
# alternative way to create packets (for testing)
|
||||||
|
# basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS1 packet create --ibc_from $CHAIN_ID1 --to $CHAIN_ID2 --type coin --payload $PAYLOAD --ibc_sequence 0
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "... querying for packet data"
|
echo "... querying for packet data"
|
||||||
echo ""
|
echo ""
|
||||||
# query for the packet data and proof
|
# query for the packet data and proof
|
||||||
QUERY_RESULT=$(basecoin query ibc,egress,$CHAIN_ID1,$CHAIN_ID2,1)
|
# since we only sent one packet, the sequence number is 0
|
||||||
|
QUERY_RESULT=$(basecoin query ibc,egress,$CHAIN_ID1,$CHAIN_ID2,0)
|
||||||
|
ifExit
|
||||||
HEIGHT=$(echo $QUERY_RESULT | jq .height)
|
HEIGHT=$(echo $QUERY_RESULT | jq .height)
|
||||||
PACKET=$(echo $QUERY_RESULT | jq .value)
|
PACKET=$(echo $QUERY_RESULT | jq .value)
|
||||||
PROOF=$(echo $QUERY_RESULT | jq .proof)
|
PROOF=$(echo $QUERY_RESULT | jq .proof)
|
||||||
|
@ -144,6 +163,7 @@ echo "... querying for block data"
|
||||||
echo ""
|
echo ""
|
||||||
# get the header and commit for the height
|
# get the header and commit for the height
|
||||||
HEADER_AND_COMMIT=$(basecoin block $HEIGHT)
|
HEADER_AND_COMMIT=$(basecoin block $HEIGHT)
|
||||||
|
ifExit
|
||||||
HEADER=$(echo $HEADER_AND_COMMIT | jq .hex.header)
|
HEADER=$(echo $HEADER_AND_COMMIT | jq .hex.header)
|
||||||
HEADER=$(removeQuotes $HEADER)
|
HEADER=$(removeQuotes $HEADER)
|
||||||
COMMIT=$(echo $HEADER_AND_COMMIT | jq .hex.commit)
|
COMMIT=$(echo $HEADER_AND_COMMIT | jq .hex.commit)
|
||||||
|
@ -158,18 +178,21 @@ echo "... updating state of chain1 on chain2"
|
||||||
echo ""
|
echo ""
|
||||||
# update the state of chain1 on chain2
|
# update the state of chain1 on chain2
|
||||||
basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 update --header 0x$HEADER --commit 0x$COMMIT
|
basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 update --header 0x$HEADER --commit 0x$COMMIT
|
||||||
|
ifExit
|
||||||
|
|
||||||
echo ""
|
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 --ibc_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
|
||||||
|
ifExit
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "... checking if the packet is present on chain2"
|
echo "... checking if the packet is present on chain2"
|
||||||
echo ""
|
echo ""
|
||||||
# query for the packet on chain2
|
# query for the packet on chain2
|
||||||
basecoin query --node tcp://localhost:36657 ibc,ingress,test_chain_2,test_chain_1,1
|
basecoin query --node tcp://localhost:36657 ibc,ingress,test_chain_2,test_chain_1,0
|
||||||
|
ifExit
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "DONE!"
|
echo "DONE!"
|
||||||
|
|
|
@ -98,7 +98,7 @@ make install
|
||||||
make test
|
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.
|
Great! Now when I run `tendermint` I have the newest of the new, the develop branch! But please note 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.
|
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.
|
||||||
|
|
||||||
|
@ -129,4 +129,3 @@ Great, now you just compiled the master branch of tendermint along with the bugf
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
|
|
@ -111,7 +111,7 @@ type Coin struct {
|
||||||
|
|
||||||
Accounts are serialized and stored in a Merkle tree under the key `base/a/<address>`, where `<address>` is the address of the account.
|
Accounts are serialized and stored in a Merkle tree under the key `base/a/<address>`, where `<address>` is the address of the account.
|
||||||
Typically, the address of the account is the 20-byte `RIPEMD160` hash of the public key, but other formats are acceptable as well,
|
Typically, the address of the account is the 20-byte `RIPEMD160` hash of the public key, but other formats are acceptable as well,
|
||||||
as defined in the [tendermint crypto library](https://github.com/tendermint/go-crypto).
|
as defined in the [Tendermint crypto library](https://github.com/tendermint/go-crypto).
|
||||||
The Merkle tree used in Basecoin is a balanced, binary search tree, which we call an [IAVL tree](https://github.com/tendermint/go-merkle).
|
The Merkle tree used in Basecoin is a balanced, binary search tree, which we call an [IAVL tree](https://github.com/tendermint/go-merkle).
|
||||||
|
|
||||||
## Transactions
|
## Transactions
|
||||||
|
@ -150,8 +150,8 @@ This is slightly different from Ethereum's concept of `Gas` and `GasPrice`,
|
||||||
where `Fee = Gas x GasPrice`. In Basecoin, the `Gas` and `Fee` are independent,
|
where `Fee = Gas x GasPrice`. In Basecoin, the `Gas` and `Fee` are independent,
|
||||||
and the `GasPrice` is implicit.
|
and the `GasPrice` is implicit.
|
||||||
|
|
||||||
In Tendermint, the `Fee` is meant to be used by the validators to inform the ordering
|
In Basecoin, the `Fee` is meant to be used by the validators to inform the ordering
|
||||||
of transactions, like in bitcoin. And the `Gas` is meant to be used by the application
|
of transactions, like in Bitcoin. And the `Gas` is meant to be used by the application
|
||||||
plugin to control its execution. There is currently no means to pass `Fee` information
|
plugin to control its execution. There is currently no means to pass `Fee` information
|
||||||
to the Tendermint validators, but it will come soon...
|
to the Tendermint validators, but it will come soon...
|
||||||
|
|
||||||
|
|
|
@ -144,7 +144,7 @@ to whatever your plugin tool is going to be called.
|
||||||
|
|
||||||
Next is the `cmd.go`. This is where we extend the tool with any new commands and flags we need to send transactions to our plugin.
|
Next is the `cmd.go`. This is where we extend the tool with any new commands and flags we need to send transactions to our plugin.
|
||||||
Note the `init()` function, where we register a new transaction subcommand with `RegisterTxSubcommand`,
|
Note the `init()` function, where we register a new transaction subcommand with `RegisterTxSubcommand`,
|
||||||
and where we load the plugin into the basecoin app with `RegisterStartPlugin`.
|
and where we load the plugin into the Basecoin app with `RegisterStartPlugin`.
|
||||||
|
|
||||||
Finally is the `plugin.go`, where we provide an implementation of the `Plugin` interface.
|
Finally is the `plugin.go`, where we provide an implementation of the `Plugin` interface.
|
||||||
The most important part of the implementation is the `RunTx` method, which determines the meaning of the data
|
The most important part of the implementation is the `RunTx` method, which determines the meaning of the data
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# The Basecoin Tool
|
# The Basecoin Tool
|
||||||
|
|
||||||
In previous tutorials we learned the [basics of the `basecoin` CLI](/docs/guides/basecoin-basics)
|
In previous tutorials we learned the [basics of the `basecoin` CLI](/docs/guide/basecoin-basics.md)
|
||||||
and [how to implement a plugin](/docs/guides/example-plugin).
|
and [how to implement a plugin](/docs/guide/basecoin-plugins.md).
|
||||||
In this tutorial, we provide more details on using the `basecoin` tool.
|
In this tutorial, we provide more details on using the `basecoin` tool.
|
||||||
|
|
||||||
# Data Directory
|
# Data Directory
|
||||||
|
|
|
@ -24,8 +24,6 @@ var (
|
||||||
//Called during CLI initialization
|
//Called during CLI initialization
|
||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
commands.DefaultHome = ".basecoin-example-plugin"
|
|
||||||
|
|
||||||
//Set the Plugin Flags
|
//Set the Plugin Flags
|
||||||
ExamplePluginTxCmd.Flags().BoolVar(&validFlag, "valid", false, "Set this to make transaction valid")
|
ExamplePluginTxCmd.Flags().BoolVar(&validFlag, "valid", false, "Set this to make transaction valid")
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/cmd/commands"
|
"github.com/tendermint/basecoin/cmd/commands"
|
||||||
|
"github.com/tendermint/tmlibs/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -27,6 +30,6 @@ func main() {
|
||||||
commands.UnsafeResetAllCmd,
|
commands.UnsafeResetAllCmd,
|
||||||
)
|
)
|
||||||
|
|
||||||
//Run the root command
|
cmd := cli.PrepareMainCmd(RootCmd, "BC", os.ExpandEnv("$HOME/.basecoin-example-plugin"))
|
||||||
commands.ExecuteWithDebug(RootCmd)
|
cmd.Execute()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +1,92 @@
|
||||||
hash: c6e5febc35b5fd1003066820defb8a089db048b407239dad9faf44553fdc15e8
|
hash: 9d06ae13959cbb2835f5ae400a4b65e4bc329a567c949aec4aeab318c271da39
|
||||||
updated: 2017-04-21T12:55:42.7004558-04:00
|
updated: 2017-05-24T15:11:32.643553723+02:00
|
||||||
imports:
|
imports:
|
||||||
|
- name: github.com/bgentry/speakeasy
|
||||||
|
version: 4aabc24848ce5fd31929f7d1e4ea74d3709c14cd
|
||||||
- name: github.com/btcsuite/btcd
|
- name: github.com/btcsuite/btcd
|
||||||
version: 4b348c1d33373d672edd83fc576892d0e46686d2
|
version: b8df516b4b267acf2de46be593a9d948d1d2c420
|
||||||
subpackages:
|
subpackages:
|
||||||
- btcec
|
- btcec
|
||||||
|
- name: github.com/btcsuite/fastsha256
|
||||||
|
version: 637e656429416087660c84436a2a035d69d54e2e
|
||||||
- name: github.com/BurntSushi/toml
|
- name: github.com/BurntSushi/toml
|
||||||
version: b26d9c308763d68093482582cea63d69be07a0f0
|
version: b26d9c308763d68093482582cea63d69be07a0f0
|
||||||
- name: github.com/ebuchman/fail-test
|
- name: github.com/ebuchman/fail-test
|
||||||
version: 95f809107225be108efcf10a3509e4ea6ceef3c4
|
version: 95f809107225be108efcf10a3509e4ea6ceef3c4
|
||||||
|
- name: github.com/fsnotify/fsnotify
|
||||||
|
version: 4da3e2cfbabc9f751898f250b49f2439785783a1
|
||||||
|
- name: github.com/go-kit/kit
|
||||||
|
version: d67bb4c202e3b91377d1079b110a6c9ce23ab2f8
|
||||||
|
subpackages:
|
||||||
|
- log
|
||||||
|
- log/level
|
||||||
|
- log/term
|
||||||
|
- name: github.com/go-logfmt/logfmt
|
||||||
|
version: 390ab7935ee28ec6b286364bba9b4dd6410cb3d5
|
||||||
|
- name: github.com/go-playground/locales
|
||||||
|
version: 1e5f1161c6416a5ff48840eb8724a394e48cc534
|
||||||
|
subpackages:
|
||||||
|
- currency
|
||||||
|
- name: github.com/go-playground/universal-translator
|
||||||
|
version: 71201497bace774495daed26a3874fd339e0b538
|
||||||
- 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: 2bba0603135d7d7f5cb73b2125beeda19c09f4ef
|
version: 18c9bb3261723cd5401db4d0c9fbc5c3b6c70fe8
|
||||||
subpackages:
|
subpackages:
|
||||||
- proto
|
- proto
|
||||||
- ptypes/any
|
- ptypes/any
|
||||||
- name: github.com/golang/snappy
|
- name: github.com/golang/snappy
|
||||||
version: 553a641470496b2327abcac10b36396bd98e45c9
|
version: 553a641470496b2327abcac10b36396bd98e45c9
|
||||||
|
- name: github.com/gorilla/context
|
||||||
|
version: 08b5f424b9271eedf6f9f0ce86cb9396ed337a42
|
||||||
|
- name: github.com/gorilla/handlers
|
||||||
|
version: 3a5767ca75ece5f7f1440b1d16975247f8d8b221
|
||||||
|
- name: github.com/gorilla/mux
|
||||||
|
version: bcd8bc72b08df0f70df986b97f95590779502d31
|
||||||
- name: github.com/gorilla/websocket
|
- name: github.com/gorilla/websocket
|
||||||
version: 3ab3a8b8831546bd18fd182c20687ca853b2bb13
|
version: a91eba7f97777409bc2c443f5534d41dd20c5720
|
||||||
|
- name: github.com/hashicorp/hcl
|
||||||
|
version: 392dba7d905ed5d04a5794ba89f558b27e2ba1ca
|
||||||
|
subpackages:
|
||||||
|
- hcl/ast
|
||||||
|
- hcl/parser
|
||||||
|
- hcl/scanner
|
||||||
|
- hcl/strconv
|
||||||
|
- hcl/token
|
||||||
|
- json/parser
|
||||||
|
- json/scanner
|
||||||
|
- json/token
|
||||||
- name: github.com/inconshreveable/mousetrap
|
- name: github.com/inconshreveable/mousetrap
|
||||||
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
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/kr/logfmt
|
||||||
version: ded68f7a9561c023e790de24279db7ebf473ea80
|
version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0
|
||||||
- name: github.com/mattn/go-isatty
|
- name: github.com/magiconair/properties
|
||||||
version: fc9e8d8ef48496124e79ae0df75490096eccf6fe
|
version: 51463bfca2576e06c62a8504b5c0f06d61312647
|
||||||
|
- name: github.com/mitchellh/mapstructure
|
||||||
|
version: cc8532a8e9a55ea36402aa21efdf403a60d34096
|
||||||
|
- name: github.com/pelletier/go-buffruneio
|
||||||
|
version: c37440a7cf42ac63b919c752ca73a85067e05992
|
||||||
|
- name: github.com/pelletier/go-toml
|
||||||
|
version: 13d49d4606eb801b8f01ae542b4afc4c6ee3d84a
|
||||||
- name: github.com/pkg/errors
|
- name: github.com/pkg/errors
|
||||||
version: ff09b135c25aae272398c51a07235b90a75aa4f0
|
version: ff09b135c25aae272398c51a07235b90a75aa4f0
|
||||||
|
- name: github.com/spf13/afero
|
||||||
|
version: 9be650865eab0c12963d8753212f4f9c66cdcf12
|
||||||
|
subpackages:
|
||||||
|
- mem
|
||||||
|
- name: github.com/spf13/cast
|
||||||
|
version: acbeb36b902d72a7a4c18e8f3241075e7ab763e4
|
||||||
- name: github.com/spf13/cobra
|
- name: github.com/spf13/cobra
|
||||||
version: 10f6b9d7e1631a54ad07c5c0fb71c28a1abfd3c2
|
version: 3454e0e28e69c1b8effa6b5123c8e4185e20d696
|
||||||
|
- name: github.com/spf13/jwalterweatherman
|
||||||
|
version: 8f07c835e5cc1450c082fe3a439cf87b0cbb2d99
|
||||||
- name: github.com/spf13/pflag
|
- name: github.com/spf13/pflag
|
||||||
version: 2300d0f8576fe575f71aaa5b9bbe4e1b0dc2eb51
|
version: e57e3eeb33f795204c1ca35f56c44f83227c6e66
|
||||||
|
- name: github.com/spf13/viper
|
||||||
|
version: 0967fc9aceab2ce9da34061253ac10fb99bba5b2
|
||||||
- name: github.com/syndtr/goleveldb
|
- name: github.com/syndtr/goleveldb
|
||||||
version: 8c81ea47d4c41a385645e133e15510fc6a2a74b4
|
version: 8c81ea47d4c41a385645e133e15510fc6a2a74b4
|
||||||
subpackages:
|
subpackages:
|
||||||
|
@ -50,7 +103,7 @@ imports:
|
||||||
- leveldb/table
|
- leveldb/table
|
||||||
- leveldb/util
|
- leveldb/util
|
||||||
- name: github.com/tendermint/abci
|
- name: github.com/tendermint/abci
|
||||||
version: 56e13d87f4e3ec1ea756957d6b23caa6ebcf0998
|
version: 5dabeffb35c027d7087a12149685daa68989168b
|
||||||
subpackages:
|
subpackages:
|
||||||
- client
|
- client
|
||||||
- example/dummy
|
- example/dummy
|
||||||
|
@ -61,72 +114,80 @@ imports:
|
||||||
subpackages:
|
subpackages:
|
||||||
- edwards25519
|
- edwards25519
|
||||||
- extra25519
|
- extra25519
|
||||||
- name: github.com/tendermint/go-autofile
|
|
||||||
version: 48b17de82914e1ec2f134ce823ba426337d2c518
|
|
||||||
- name: github.com/tendermint/go-clist
|
|
||||||
version: 3baa390bbaf7634251c42ad69a8682e7e3990552
|
|
||||||
- name: github.com/tendermint/go-common
|
|
||||||
version: f9e3db037330c8a8d61d3966de8473eaf01154fa
|
|
||||||
- name: github.com/tendermint/go-config
|
|
||||||
version: 620dcbbd7d587cf3599dedbf329b64311b0c307a
|
|
||||||
- name: github.com/tendermint/go-crypto
|
- name: github.com/tendermint/go-crypto
|
||||||
version: 0ca2c6fdb0706001ca4c4b9b80c9f428e8cf39da
|
version: 438b16f1f84ef002d7408ecd6fc3a3974cbc9559
|
||||||
- name: github.com/tendermint/go-data
|
|
||||||
version: e7fcc6d081ec8518912fcdc103188275f83a3ee5
|
|
||||||
- name: github.com/tendermint/go-db
|
|
||||||
version: 9643f60bc2578693844aacf380a7c32e4c029fee
|
|
||||||
- name: github.com/tendermint/go-events
|
|
||||||
version: f8ffbfb2be3483e9e7927495590a727f51c0c11f
|
|
||||||
- name: github.com/tendermint/go-flowrate
|
|
||||||
version: a20c98e61957faa93b4014fbd902f20ab9317a6a
|
|
||||||
subpackages:
|
subpackages:
|
||||||
- flowrate
|
- cmd
|
||||||
- name: github.com/tendermint/go-logger
|
- keys
|
||||||
version: cefb3a45c0bf3c493a04e9bcd9b1540528be59f2
|
- keys/cryptostore
|
||||||
- name: github.com/tendermint/go-merkle
|
- keys/server
|
||||||
version: 714d4d04557fd068a7c2a1748241ce8428015a96
|
- keys/server/types
|
||||||
- name: github.com/tendermint/go-p2p
|
- keys/storage/filestorage
|
||||||
version: 17124989a93774833df33107fbf17157a7f8ef31
|
|
||||||
subpackages:
|
|
||||||
- upnp
|
|
||||||
- name: github.com/tendermint/go-rpc
|
|
||||||
version: 559613689d56eaa423b19a3a4158546beb4857de
|
|
||||||
subpackages:
|
|
||||||
- client
|
|
||||||
- server
|
|
||||||
- types
|
|
||||||
- name: github.com/tendermint/go-wire
|
- name: github.com/tendermint/go-wire
|
||||||
version: c1c9a57ab8038448ddea1714c0698f8051e5748c
|
version: 97beaedf0f4dbc035309157c92be3b30cc6e5d74
|
||||||
- name: github.com/tendermint/log15
|
|
||||||
version: ae0f3d6450da9eac7074b439c8e1c3cabf0d5ce6
|
|
||||||
subpackages:
|
subpackages:
|
||||||
- term
|
- data
|
||||||
|
- data/base58
|
||||||
|
- name: github.com/tendermint/light-client
|
||||||
|
version: 478876ca34b360df62f941d5e20cdd608fa0a466
|
||||||
|
subpackages:
|
||||||
|
- certifiers
|
||||||
|
- certifiers/client
|
||||||
|
- certifiers/files
|
||||||
|
- commands
|
||||||
|
- commands/proofs
|
||||||
|
- commands/proxy
|
||||||
|
- commands/seeds
|
||||||
|
- commands/txs
|
||||||
|
- proofs
|
||||||
- name: github.com/tendermint/merkleeyes
|
- name: github.com/tendermint/merkleeyes
|
||||||
version: 9fb76efa5aebe773a598f97e68e75fe53d520e70
|
version: c722818b460381bc5b82e38c73ff6e22a9df624d
|
||||||
subpackages:
|
subpackages:
|
||||||
- app
|
- app
|
||||||
- client
|
- client
|
||||||
|
- iavl
|
||||||
- name: github.com/tendermint/tendermint
|
- name: github.com/tendermint/tendermint
|
||||||
version: 6bcd4242f1f336e2b2ef4f644fabaf56222b34d0
|
version: 11b5d11e9eec170e1d3dce165f0270d5c0759d69
|
||||||
subpackages:
|
subpackages:
|
||||||
- blockchain
|
- blockchain
|
||||||
- cmd/tendermint/commands
|
- cmd/tendermint/commands
|
||||||
- config/tendermint
|
- cmd/tendermint/commands/flags
|
||||||
|
- config
|
||||||
- consensus
|
- consensus
|
||||||
- mempool
|
- mempool
|
||||||
- node
|
- node
|
||||||
|
- p2p
|
||||||
|
- p2p/upnp
|
||||||
- proxy
|
- proxy
|
||||||
|
- rpc/client
|
||||||
- rpc/core
|
- rpc/core
|
||||||
- rpc/core/types
|
- rpc/core/types
|
||||||
- rpc/grpc
|
- rpc/grpc
|
||||||
|
- rpc/lib
|
||||||
|
- rpc/lib/client
|
||||||
|
- rpc/lib/server
|
||||||
|
- rpc/lib/types
|
||||||
- state
|
- state
|
||||||
- state/txindex
|
- state/txindex
|
||||||
- state/txindex/kv
|
- state/txindex/kv
|
||||||
- state/txindex/null
|
- state/txindex/null
|
||||||
- types
|
- types
|
||||||
- version
|
- version
|
||||||
|
- name: github.com/tendermint/tmlibs
|
||||||
|
version: 8af1c70a8be17543eb33e9bfbbcdd8371e3201cc
|
||||||
|
subpackages:
|
||||||
|
- autofile
|
||||||
|
- cli
|
||||||
|
- clist
|
||||||
|
- common
|
||||||
|
- db
|
||||||
|
- events
|
||||||
|
- flowrate
|
||||||
|
- log
|
||||||
|
- logger
|
||||||
|
- merkle
|
||||||
- name: golang.org/x/crypto
|
- name: golang.org/x/crypto
|
||||||
version: 96846453c37f0876340a66a47f3f75b1f3a6cd2d
|
version: c7af5bf2638a1164f2eb5467c39c6cffbd13a02e
|
||||||
subpackages:
|
subpackages:
|
||||||
- curve25519
|
- curve25519
|
||||||
- nacl/box
|
- nacl/box
|
||||||
|
@ -137,7 +198,7 @@ imports:
|
||||||
- ripemd160
|
- ripemd160
|
||||||
- salsa20/salsa
|
- salsa20/salsa
|
||||||
- name: golang.org/x/net
|
- name: golang.org/x/net
|
||||||
version: c8c74377599bd978aee1cf3b9b63a8634051cec2
|
version: feeb485667d1fdabe727840fe00adc22431bc86e
|
||||||
subpackages:
|
subpackages:
|
||||||
- context
|
- context
|
||||||
- http2
|
- http2
|
||||||
|
@ -147,11 +208,11 @@ imports:
|
||||||
- lex/httplex
|
- lex/httplex
|
||||||
- trace
|
- trace
|
||||||
- name: golang.org/x/sys
|
- name: golang.org/x/sys
|
||||||
version: ea9bcade75cb975a0b9738936568ab388b845617
|
version: 9c9d83fe39ed3fd2d9249fcf6b755891fff54b03
|
||||||
subpackages:
|
subpackages:
|
||||||
- unix
|
- unix
|
||||||
- name: golang.org/x/text
|
- name: golang.org/x/text
|
||||||
version: 19e3104b43db45fca0303f489a9536087b184802
|
version: 470f45bf29f4147d6fbd7dfd0a02a848e49f5bf4
|
||||||
subpackages:
|
subpackages:
|
||||||
- secure/bidirule
|
- secure/bidirule
|
||||||
- transform
|
- transform
|
||||||
|
@ -162,10 +223,11 @@ imports:
|
||||||
subpackages:
|
subpackages:
|
||||||
- googleapis/rpc/status
|
- googleapis/rpc/status
|
||||||
- name: google.golang.org/grpc
|
- name: google.golang.org/grpc
|
||||||
version: 6914ab1e338c92da4218a23d27fcd03d0ad78d46
|
version: 844f573616520565fdc6fb4db242321b5456fd6d
|
||||||
subpackages:
|
subpackages:
|
||||||
- codes
|
- codes
|
||||||
- credentials
|
- credentials
|
||||||
|
- grpclb/grpc_lb_v1
|
||||||
- grpclog
|
- grpclog
|
||||||
- internal
|
- internal
|
||||||
- keepalive
|
- keepalive
|
||||||
|
@ -176,6 +238,10 @@ imports:
|
||||||
- status
|
- status
|
||||||
- tap
|
- tap
|
||||||
- transport
|
- transport
|
||||||
|
- name: gopkg.in/go-playground/validator.v9
|
||||||
|
version: 6d8c18553ea1ac493d049edd6f102f52e618f085
|
||||||
|
- name: gopkg.in/yaml.v2
|
||||||
|
version: cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b
|
||||||
testImports:
|
testImports:
|
||||||
- name: github.com/davecgh/go-spew
|
- name: github.com/davecgh/go-spew
|
||||||
version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9
|
version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9
|
||||||
|
|
61
glide.yaml
61
glide.yaml
|
@ -1,24 +1,53 @@
|
||||||
package: github.com/tendermint/basecoin
|
package: github.com/tendermint/basecoin
|
||||||
import:
|
import:
|
||||||
- package: github.com/tendermint/go-common
|
- package: github.com/gorilla/websocket
|
||||||
version: develop
|
- package: github.com/pkg/errors
|
||||||
|
- package: github.com/spf13/cobra
|
||||||
|
- package: github.com/spf13/pflag
|
||||||
|
- package: github.com/spf13/viper
|
||||||
|
- package: github.com/tendermint/abci
|
||||||
|
subpackages:
|
||||||
|
- server
|
||||||
|
- types
|
||||||
- package: github.com/tendermint/go-crypto
|
- package: github.com/tendermint/go-crypto
|
||||||
version: develop
|
subpackages:
|
||||||
- package: github.com/tendermint/go-events
|
- cmd
|
||||||
version: develop
|
- keys
|
||||||
- package: github.com/tendermint/go-logger
|
|
||||||
version: develop
|
|
||||||
- package: github.com/tendermint/go-data
|
|
||||||
version: develop
|
|
||||||
- package: github.com/tendermint/go-rpc
|
|
||||||
version: develop
|
|
||||||
- package: github.com/tendermint/go-wire
|
- package: github.com/tendermint/go-wire
|
||||||
|
subpackages:
|
||||||
|
- data
|
||||||
|
- package: github.com/tendermint/light-client
|
||||||
version: develop
|
version: develop
|
||||||
|
subpackages:
|
||||||
|
- commands
|
||||||
|
- commands/proofs
|
||||||
|
- commands/seeds
|
||||||
|
- commands/txs
|
||||||
|
- proofs
|
||||||
- package: github.com/tendermint/merkleeyes
|
- package: github.com/tendermint/merkleeyes
|
||||||
version: develop
|
subpackages:
|
||||||
|
- client
|
||||||
|
- iavl
|
||||||
- package: github.com/tendermint/tendermint
|
- package: github.com/tendermint/tendermint
|
||||||
version: develop
|
version: develop
|
||||||
- package: github.com/tendermint/abci
|
subpackages:
|
||||||
version: develop
|
- config
|
||||||
- package: github.com/gorilla/websocket
|
- node
|
||||||
version: v1.1.0
|
- proxy
|
||||||
|
- rpc/client
|
||||||
|
- rpc/core/types
|
||||||
|
- rpc/lib/client
|
||||||
|
- rpc/lib/types
|
||||||
|
- types
|
||||||
|
- package: github.com/tendermint/tmlibs
|
||||||
|
subpackages:
|
||||||
|
- cli
|
||||||
|
- common
|
||||||
|
- events
|
||||||
|
- log
|
||||||
|
- logger
|
||||||
|
testImport:
|
||||||
|
- package: github.com/stretchr/testify
|
||||||
|
subpackages:
|
||||||
|
- assert
|
||||||
|
- require
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
||||||
"github.com/tendermint/basecoin/app"
|
"github.com/tendermint/basecoin/app"
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
crypto "github.com/tendermint/go-crypto"
|
|
||||||
"github.com/tendermint/go-wire"
|
"github.com/tendermint/go-wire"
|
||||||
eyescli "github.com/tendermint/merkleeyes/client"
|
eyescli "github.com/tendermint/merkleeyes/client"
|
||||||
)
|
)
|
||||||
|
@ -52,8 +51,7 @@ func TestCounterPlugin(t *testing.T) {
|
||||||
// Sign request
|
// Sign request
|
||||||
signBytes := tx.SignBytes(chainID)
|
signBytes := tx.SignBytes(chainID)
|
||||||
// t.Logf("Sign bytes: %X\n", signBytes)
|
// t.Logf("Sign bytes: %X\n", signBytes)
|
||||||
sig := test1PrivAcc.Sign(signBytes)
|
tx.Input.Signature = test1PrivAcc.Sign(signBytes)
|
||||||
tx.Input.Signature = crypto.SignatureS{sig}
|
|
||||||
// t.Logf("Signed TX bytes: %X\n", wire.BinaryBytes(struct{ types.Tx }{tx}))
|
// t.Logf("Signed TX bytes: %X\n", wire.BinaryBytes(struct{ types.Tx }{tx}))
|
||||||
|
|
||||||
// Write request
|
// Write request
|
||||||
|
|
|
@ -2,15 +2,19 @@ package ibc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
||||||
"github.com/tendermint/basecoin/types"
|
|
||||||
cmn "github.com/tendermint/go-common"
|
|
||||||
merkle "github.com/tendermint/go-merkle"
|
|
||||||
"github.com/tendermint/go-wire"
|
"github.com/tendermint/go-wire"
|
||||||
|
merkle "github.com/tendermint/merkleeyes/iavl"
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
|
|
||||||
|
"github.com/tendermint/basecoin/types"
|
||||||
tm "github.com/tendermint/tendermint/types"
|
tm "github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -51,8 +55,108 @@ type Packet struct {
|
||||||
SrcChainID string
|
SrcChainID string
|
||||||
DstChainID string
|
DstChainID string
|
||||||
Sequence uint64
|
Sequence uint64
|
||||||
Type string
|
Type string // redundant now that Type() is a method on Payload ?
|
||||||
Payload []byte
|
Payload Payload
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPacket(src, dst string, seq uint64, payload Payload) Packet {
|
||||||
|
return Packet{
|
||||||
|
SrcChainID: src,
|
||||||
|
DstChainID: dst,
|
||||||
|
Sequence: seq,
|
||||||
|
Type: payload.Type(),
|
||||||
|
Payload: payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSequenceNumber gets the sequence number for packets being sent from the src chain to the dst chain.
|
||||||
|
// The sequence number counts how many packets have been sent.
|
||||||
|
// The next packet must include the latest sequence number.
|
||||||
|
func GetSequenceNumber(store types.KVStore, src, dst string) uint64 {
|
||||||
|
sequenceKey := toKey(_IBC, _EGRESS, src, dst)
|
||||||
|
seqBytes := store.Get(sequenceKey)
|
||||||
|
if seqBytes == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
seq, err := strconv.ParseUint(string(seqBytes), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
cmn.PanicSanity(err.Error())
|
||||||
|
}
|
||||||
|
return seq
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSequenceNumber sets the sequence number for packets being sent from the src chain to the dst chain
|
||||||
|
func SetSequenceNumber(store types.KVStore, src, dst string, seq uint64) {
|
||||||
|
sequenceKey := toKey(_IBC, _EGRESS, src, dst)
|
||||||
|
store.Set(sequenceKey, []byte(strconv.FormatUint(seq, 10)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveNewIBCPacket creates an IBC packet with the given payload from the src chain to the dst chain
|
||||||
|
// using the correct sequence number. It also increments the sequence number by 1
|
||||||
|
func SaveNewIBCPacket(state types.KVStore, src, dst string, payload Payload) {
|
||||||
|
// fetch sequence number and increment by 1
|
||||||
|
seq := GetSequenceNumber(state, src, dst)
|
||||||
|
SetSequenceNumber(state, src, dst, seq+1)
|
||||||
|
|
||||||
|
// save ibc packet
|
||||||
|
packetKey := toKey(_IBC, _EGRESS, src, dst, cmn.Fmt("%v", seq))
|
||||||
|
packet := NewPacket(src, dst, uint64(seq), payload)
|
||||||
|
save(state, packetKey, packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetIBCPacket(state types.KVStore, src, dst string, seq uint64) (Packet, error) {
|
||||||
|
packetKey := toKey(_IBC, _EGRESS, src, dst, cmn.Fmt("%v", seq))
|
||||||
|
packetBytes := state.Get(packetKey)
|
||||||
|
|
||||||
|
var packet Packet
|
||||||
|
err := wire.ReadBinaryBytes(packetBytes, &packet)
|
||||||
|
return packet, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const (
|
||||||
|
PayloadTypeBytes = byte(0x01)
|
||||||
|
PayloadTypeCoins = byte(0x02)
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = wire.RegisterInterface(
|
||||||
|
struct{ Payload }{},
|
||||||
|
wire.ConcreteType{DataPayload{}, PayloadTypeBytes},
|
||||||
|
wire.ConcreteType{CoinsPayload{}, PayloadTypeCoins},
|
||||||
|
)
|
||||||
|
|
||||||
|
type Payload interface {
|
||||||
|
AssertIsPayload()
|
||||||
|
Type() string
|
||||||
|
ValidateBasic() abci.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (DataPayload) AssertIsPayload() {}
|
||||||
|
func (CoinsPayload) AssertIsPayload() {}
|
||||||
|
|
||||||
|
type DataPayload []byte
|
||||||
|
|
||||||
|
func (p DataPayload) Type() string {
|
||||||
|
return "data"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p DataPayload) ValidateBasic() abci.Result {
|
||||||
|
return abci.OK
|
||||||
|
}
|
||||||
|
|
||||||
|
type CoinsPayload struct {
|
||||||
|
Address []byte
|
||||||
|
Coins types.Coins
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p CoinsPayload) Type() string {
|
||||||
|
return "coin"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p CoinsPayload) ValidateBasic() abci.Result {
|
||||||
|
// TODO: validate
|
||||||
|
return abci.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
|
@ -202,9 +306,8 @@ func (sm *IBCStateMachine) runRegisterChainTx(tx IBCRegisterChainTx) {
|
||||||
chainGen := tx.BlockchainGenesis
|
chainGen := tx.BlockchainGenesis
|
||||||
|
|
||||||
// Parse genesis
|
// Parse genesis
|
||||||
var chainGenDoc = &tm.GenesisDoc{}
|
chainGenDoc := new(tm.GenesisDoc)
|
||||||
var err error
|
err := json.Unmarshal([]byte(chainGen.Genesis), chainGenDoc)
|
||||||
wire.ReadJSONPtr(&chainGenDoc, []byte(chainGen.Genesis), &err)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sm.res.Code = IBCCodeEncodingError
|
sm.res.Code = IBCCodeEncodingError
|
||||||
sm.res.Log = "Genesis doc couldn't be parsed: " + err.Error()
|
sm.res.Log = "Genesis doc couldn't be parsed: " + err.Error()
|
||||||
|
@ -298,8 +401,28 @@ func (sm *IBCStateMachine) runPacketCreateTx(tx IBCPacketCreateTx) {
|
||||||
sm.res.Log = "Already exists"
|
sm.res.Log = "Already exists"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Execute the payload
|
||||||
|
switch payload := tx.Packet.Payload.(type) {
|
||||||
|
case DataPayload:
|
||||||
|
// do nothing
|
||||||
|
case CoinsPayload:
|
||||||
|
// ensure enough coins were sent in tx to cover the payload coins
|
||||||
|
if !sm.ctx.Coins.IsGTE(payload.Coins) {
|
||||||
|
sm.res.Code = abci.CodeType_InsufficientFunds
|
||||||
|
sm.res.Log = fmt.Sprintf("Not enough funds sent in tx (%v) to send %v via IBC", sm.ctx.Coins, payload.Coins)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// deduct coins from context
|
||||||
|
sm.ctx.Coins = sm.ctx.Coins.Minus(payload.Coins)
|
||||||
|
}
|
||||||
|
|
||||||
// Save new Packet
|
// Save new Packet
|
||||||
save(sm.store, packetKey, packet)
|
save(sm.store, packetKey, packet)
|
||||||
|
|
||||||
|
// set the sequence number
|
||||||
|
SetSequenceNumber(sm.store, packet.SrcChainID, packet.DstChainID, packet.Sequence)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *IBCStateMachine) runPacketPostTx(tx IBCPacketPostTx) {
|
func (sm *IBCStateMachine) runPacketPostTx(tx IBCPacketPostTx) {
|
||||||
|
@ -326,7 +449,7 @@ func (sm *IBCStateMachine) runPacketPostTx(tx IBCPacketPostTx) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save new Packet
|
// Save new Packet (just for fun)
|
||||||
save(sm.store, packetKeyIngress, packet)
|
save(sm.store, packetKeyIngress, packet)
|
||||||
|
|
||||||
// Load Header and make sure it exists
|
// Load Header and make sure it exists
|
||||||
|
@ -355,10 +478,24 @@ func (sm *IBCStateMachine) runPacketPostTx(tx IBCPacketPostTx) {
|
||||||
ok := proof.Verify(packetKeyEgress, packetBytes, header.AppHash)
|
ok := proof.Verify(packetKeyEgress, packetBytes, header.AppHash)
|
||||||
if !ok {
|
if !ok {
|
||||||
sm.res.Code = IBCCodeInvalidProof
|
sm.res.Code = IBCCodeInvalidProof
|
||||||
sm.res.Log = "Proof is invalid"
|
sm.res.Log = fmt.Sprintf("Proof is invalid. key: %s; packetByes %X; header %v; proof %v", packetKeyEgress, packetBytes, header, proof)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Execute payload
|
||||||
|
switch payload := packet.Payload.(type) {
|
||||||
|
case DataPayload:
|
||||||
|
// do nothing
|
||||||
|
case CoinsPayload:
|
||||||
|
// Add coins to destination account
|
||||||
|
acc := types.GetAccount(sm.store, payload.Address)
|
||||||
|
if acc == nil {
|
||||||
|
acc = &types.Account{}
|
||||||
|
}
|
||||||
|
acc.Balance = acc.Balance.Plus(payload.Coins)
|
||||||
|
types.SetAccount(sm.store, payload.Address, acc)
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,22 @@ package ibc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
||||||
"github.com/tendermint/basecoin/types"
|
|
||||||
cmn "github.com/tendermint/go-common"
|
|
||||||
crypto "github.com/tendermint/go-crypto"
|
crypto "github.com/tendermint/go-crypto"
|
||||||
"github.com/tendermint/go-merkle"
|
|
||||||
"github.com/tendermint/go-wire"
|
"github.com/tendermint/go-wire"
|
||||||
eyes "github.com/tendermint/merkleeyes/client"
|
eyes "github.com/tendermint/merkleeyes/client"
|
||||||
|
"github.com/tendermint/merkleeyes/iavl"
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
|
|
||||||
|
"github.com/tendermint/basecoin/types"
|
||||||
tm "github.com/tendermint/tendermint/types"
|
tm "github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,7 +34,7 @@ func genGenesisDoc(chainID string, numVals int) (*tm.GenesisDoc, []types.PrivAcc
|
||||||
name := cmn.Fmt("%v_val_%v", chainID, i)
|
name := cmn.Fmt("%v_val_%v", chainID, i)
|
||||||
privAcc := types.PrivAccountFromSecret(name)
|
privAcc := types.PrivAccountFromSecret(name)
|
||||||
genDoc.Validators = append(genDoc.Validators, tm.GenesisValidator{
|
genDoc.Validators = append(genDoc.Validators, tm.GenesisValidator{
|
||||||
PubKey: privAcc.PubKey.PubKey,
|
PubKey: privAcc.PubKey,
|
||||||
Amount: 1,
|
Amount: 1,
|
||||||
Name: name,
|
Name: name,
|
||||||
})
|
})
|
||||||
|
@ -64,23 +68,65 @@ func (pas PrivAccountsByAddress) Swap(i, j int) {
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
|
|
||||||
func TestIBCPlugin(t *testing.T) {
|
var testGenesisDoc = `{
|
||||||
assert := assert.New(t)
|
"app_hash": "",
|
||||||
|
"chain_id": "test_chain_1",
|
||||||
|
"genesis_time": "0001-01-01T00:00:00.000Z",
|
||||||
|
"validators": [
|
||||||
|
{
|
||||||
|
"amount": 10,
|
||||||
|
"name": "",
|
||||||
|
"pub_key": {
|
||||||
|
"type": "ed25519",
|
||||||
|
"data":"D6EBB92440CF375054AA59BCF0C99D596DEEDFFB2543CAE1BA1908B72CF9676A"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"app_options": {
|
||||||
|
"accounts": [
|
||||||
|
{
|
||||||
|
"pub_key": {
|
||||||
|
"type": "ed25519",
|
||||||
|
"data": "B3588BDC92015ED3CDB6F57A86379E8C79A7111063610B7E625487C76496F4DF"
|
||||||
|
},
|
||||||
|
"coins": [
|
||||||
|
{
|
||||||
|
"denom": "mycoin",
|
||||||
|
"amount": 9007199254740992
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
func TestIBCGenesisFromString(t *testing.T) {
|
||||||
|
eyesClient := eyes.NewLocalClient("", 0)
|
||||||
|
store := types.NewKVCache(eyesClient)
|
||||||
|
store.SetLogging() // Log all activity
|
||||||
|
|
||||||
|
ibcPlugin := New()
|
||||||
|
ctx := types.NewCallContext(nil, nil, types.Coins{})
|
||||||
|
|
||||||
|
registerChain(t, ibcPlugin, store, ctx, "test_chain", testGenesisDoc)
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func TestIBCPluginRegister(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
eyesClient := eyes.NewLocalClient("", 0)
|
eyesClient := eyes.NewLocalClient("", 0)
|
||||||
store := types.NewKVCache(eyesClient)
|
store := types.NewKVCache(eyesClient)
|
||||||
store.SetLogging() // Log all activity
|
store.SetLogging() // Log all activity
|
||||||
|
|
||||||
ibcPlugin := New()
|
ibcPlugin := New()
|
||||||
ctx := types.CallContext{
|
ctx := types.NewCallContext(nil, nil, types.Coins{})
|
||||||
CallerAddress: nil,
|
|
||||||
CallerAccount: nil,
|
|
||||||
Coins: types.Coins{},
|
|
||||||
}
|
|
||||||
|
|
||||||
chainID_1 := "test_chain"
|
chainID_1 := "test_chain"
|
||||||
genDoc_1, privAccs_1 := genGenesisDoc(chainID_1, 4)
|
genDoc_1, _ := genGenesisDoc(chainID_1, 4)
|
||||||
genDocJSON_1 := wire.JSONBytesPretty(genDoc_1)
|
genDocJSON_1, err := json.Marshal(genDoc_1)
|
||||||
|
require.Nil(err)
|
||||||
|
|
||||||
// Register a malformed chain
|
// Register a malformed chain
|
||||||
res := ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCRegisterChainTx{
|
res := ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCRegisterChainTx{
|
||||||
|
@ -89,20 +135,10 @@ func TestIBCPlugin(t *testing.T) {
|
||||||
Genesis: "<THIS IS NOT JSON>",
|
Genesis: "<THIS IS NOT JSON>",
|
||||||
},
|
},
|
||||||
}}))
|
}}))
|
||||||
assert.Equal(IBCCodeEncodingError, res.Code)
|
assertAndLog(t, store, res, IBCCodeEncodingError)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
|
||||||
store.ClearLogLines()
|
|
||||||
|
|
||||||
// Successfully register a chain
|
// Successfully register a chain
|
||||||
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCRegisterChainTx{
|
registerChain(t, ibcPlugin, store, ctx, "test_chain", string(genDocJSON_1))
|
||||||
BlockchainGenesis{
|
|
||||||
ChainID: "test_chain",
|
|
||||||
Genesis: string(genDocJSON_1),
|
|
||||||
},
|
|
||||||
}}))
|
|
||||||
assert.True(res.IsOK(), res.Log)
|
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
|
||||||
store.ClearLogLines()
|
|
||||||
|
|
||||||
// Duplicate request fails
|
// Duplicate request fails
|
||||||
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCRegisterChainTx{
|
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCRegisterChainTx{
|
||||||
|
@ -111,74 +147,82 @@ func TestIBCPlugin(t *testing.T) {
|
||||||
Genesis: string(genDocJSON_1),
|
Genesis: string(genDocJSON_1),
|
||||||
},
|
},
|
||||||
}}))
|
}}))
|
||||||
assert.Equal(IBCCodeChainAlreadyExists, res.Code, res.Log)
|
assertAndLog(t, store, res, IBCCodeChainAlreadyExists)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
}
|
||||||
store.ClearLogLines()
|
|
||||||
|
func TestIBCPluginPost(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
eyesClient := eyes.NewLocalClient("", 0)
|
||||||
|
store := types.NewKVCache(eyesClient)
|
||||||
|
store.SetLogging() // Log all activity
|
||||||
|
|
||||||
|
ibcPlugin := New()
|
||||||
|
ctx := types.NewCallContext(nil, nil, types.Coins{})
|
||||||
|
|
||||||
|
chainID_1 := "test_chain"
|
||||||
|
genDoc_1, _ := genGenesisDoc(chainID_1, 4)
|
||||||
|
genDocJSON_1, err := json.Marshal(genDoc_1)
|
||||||
|
require.Nil(err)
|
||||||
|
|
||||||
|
// Register a chain
|
||||||
|
registerChain(t, ibcPlugin, store, ctx, "test_chain", string(genDocJSON_1))
|
||||||
|
|
||||||
// Create a new packet (for testing)
|
// Create a new packet (for testing)
|
||||||
packet := Packet{
|
packet := NewPacket("test_chain", "dst_chain", 0, DataPayload([]byte("hello world")))
|
||||||
SrcChainID: "test_chain",
|
res := ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCPacketCreateTx{
|
||||||
DstChainID: "dst_chain",
|
|
||||||
Sequence: 0,
|
|
||||||
Type: "data",
|
|
||||||
Payload: []byte("hello world"),
|
|
||||||
}
|
|
||||||
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCPacketCreateTx{
|
|
||||||
Packet: packet,
|
Packet: packet,
|
||||||
}}))
|
}}))
|
||||||
assert.Equal(abci.CodeType_OK, res.Code, res.Log)
|
assertAndLog(t, store, res, abci.CodeType_OK)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
|
||||||
store.ClearLogLines()
|
|
||||||
|
|
||||||
// Post a duplicate packet
|
// Post a duplicate packet
|
||||||
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(IBCCodePacketAlreadyExists, res.Code, res.Log)
|
assertAndLog(t, store, res, IBCCodePacketAlreadyExists)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
}
|
||||||
store.ClearLogLines()
|
|
||||||
|
func TestIBCPluginPayloadBytes(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
eyesClient := eyes.NewLocalClient("", 0)
|
||||||
|
store := types.NewKVCache(eyesClient)
|
||||||
|
store.SetLogging() // Log all activity
|
||||||
|
|
||||||
|
ibcPlugin := New()
|
||||||
|
ctx := types.NewCallContext(nil, nil, types.Coins{})
|
||||||
|
|
||||||
|
chainID_1 := "test_chain"
|
||||||
|
genDoc_1, privAccs_1 := genGenesisDoc(chainID_1, 4)
|
||||||
|
genDocJSON_1, err := json.Marshal(genDoc_1)
|
||||||
|
require.Nil(err)
|
||||||
|
|
||||||
|
// Register a chain
|
||||||
|
registerChain(t, ibcPlugin, store, ctx, "test_chain", string(genDocJSON_1))
|
||||||
|
|
||||||
|
// Create a new packet (for testing)
|
||||||
|
packet := NewPacket("test_chain", "dst_chain", 0, DataPayload([]byte("hello world")))
|
||||||
|
res := ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCPacketCreateTx{
|
||||||
|
Packet: packet,
|
||||||
|
}}))
|
||||||
|
assertAndLog(t, store, res, abci.CodeType_OK)
|
||||||
|
|
||||||
// Construct a Header that includes the above packet.
|
// Construct a Header that includes the above packet.
|
||||||
store.Sync()
|
store.Sync()
|
||||||
resCommit := eyesClient.CommitSync()
|
resCommit := eyesClient.CommitSync()
|
||||||
appHash := resCommit.Data
|
appHash := resCommit.Data
|
||||||
header := tm.Header{
|
header := newHeader("test_chain", 999, appHash, []byte("must_exist"))
|
||||||
ChainID: "test_chain",
|
|
||||||
Height: 999,
|
|
||||||
AppHash: appHash,
|
|
||||||
ValidatorsHash: []byte("must_exist"), // TODO make optional
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct a Commit that signs above header
|
// Construct a Commit that signs above header
|
||||||
blockHash := header.Hash()
|
commit := constructCommit(privAccs_1, header)
|
||||||
blockID := tm.BlockID{Hash: blockHash}
|
|
||||||
commit := tm.Commit{
|
|
||||||
BlockID: blockID,
|
|
||||||
Precommits: make([]*tm.Vote, len(privAccs_1)),
|
|
||||||
}
|
|
||||||
for i, privAcc := range privAccs_1 {
|
|
||||||
vote := &tm.Vote{
|
|
||||||
ValidatorAddress: privAcc.Account.PubKey.Address(),
|
|
||||||
ValidatorIndex: i,
|
|
||||||
Height: 999,
|
|
||||||
Round: 0,
|
|
||||||
Type: tm.VoteTypePrecommit,
|
|
||||||
BlockID: tm.BlockID{Hash: blockHash},
|
|
||||||
}
|
|
||||||
vote.Signature = privAcc.PrivKey.Sign(
|
|
||||||
tm.SignBytes("test_chain", vote),
|
|
||||||
)
|
|
||||||
commit.Precommits[i] = vote
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update a chain
|
// Update a chain
|
||||||
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCUpdateChainTx{
|
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCUpdateChainTx{
|
||||||
Header: header,
|
Header: header,
|
||||||
Commit: commit,
|
Commit: commit,
|
||||||
}}))
|
}}))
|
||||||
assert.Equal(abci.CodeType_OK, res.Code, res.Log)
|
assertAndLog(t, store, res, abci.CodeType_OK)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
|
||||||
store.ClearLogLines()
|
|
||||||
|
|
||||||
// Get proof for the packet
|
// Get proof for the packet
|
||||||
packetKey := toKey(_IBC, _EGRESS,
|
packetKey := toKey(_IBC, _EGRESS,
|
||||||
|
@ -192,7 +236,7 @@ func TestIBCPlugin(t *testing.T) {
|
||||||
Prove: true,
|
Prove: true,
|
||||||
})
|
})
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
var proof *merkle.IAVLProof
|
var proof *iavl.IAVLProof
|
||||||
err = wire.ReadBinaryBytes(resQuery.Proof, &proof)
|
err = wire.ReadBinaryBytes(resQuery.Proof, &proof)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
|
@ -203,169 +247,74 @@ func TestIBCPlugin(t *testing.T) {
|
||||||
Packet: packet,
|
Packet: packet,
|
||||||
Proof: proof,
|
Proof: proof,
|
||||||
}}))
|
}}))
|
||||||
assert.Equal(abci.CodeType_OK, res.Code, res.Log)
|
assertAndLog(t, store, res, abci.CodeType_OK)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
|
||||||
store.ClearLogLines()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIBCPluginBadCommit(t *testing.T) {
|
func TestIBCPluginPayloadCoins(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
eyesClient := eyes.NewLocalClient("", 0)
|
eyesClient := eyes.NewLocalClient("", 0)
|
||||||
store := types.NewKVCache(eyesClient)
|
store := types.NewKVCache(eyesClient)
|
||||||
store.SetLogging() // Log all activity
|
store.SetLogging() // Log all activity
|
||||||
|
|
||||||
ibcPlugin := New()
|
ibcPlugin := New()
|
||||||
ctx := types.CallContext{
|
coins := types.Coins{
|
||||||
CallerAddress: nil,
|
types.Coin{
|
||||||
CallerAccount: nil,
|
Denom: "mycoin",
|
||||||
Coins: types.Coins{},
|
Amount: 100,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
ctx := types.NewCallContext(nil, nil, coins)
|
||||||
|
|
||||||
chainID_1 := "test_chain"
|
chainID_1 := "test_chain"
|
||||||
genDoc_1, privAccs_1 := genGenesisDoc(chainID_1, 4)
|
genDoc_1, privAccs_1 := genGenesisDoc(chainID_1, 4)
|
||||||
genDocJSON_1 := wire.JSONBytesPretty(genDoc_1)
|
genDocJSON_1, err := json.Marshal(genDoc_1)
|
||||||
|
require.Nil(err)
|
||||||
|
|
||||||
// Successfully register a chain
|
// Register a chain
|
||||||
res := ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCRegisterChainTx{
|
registerChain(t, ibcPlugin, store, ctx, "test_chain", string(genDocJSON_1))
|
||||||
BlockchainGenesis{
|
|
||||||
ChainID: "test_chain",
|
// send coins to this addr on the other chain
|
||||||
Genesis: string(genDocJSON_1),
|
destinationAddr := []byte("some address")
|
||||||
},
|
coinsBad := types.Coins{types.Coin{"mycoin", 200}}
|
||||||
|
coinsGood := types.Coins{types.Coin{"mycoin", 1}}
|
||||||
|
|
||||||
|
// Try to send too many coins
|
||||||
|
packet := NewPacket("test_chain", "dst_chain", 0, CoinsPayload{
|
||||||
|
Address: destinationAddr,
|
||||||
|
Coins: coinsBad,
|
||||||
|
})
|
||||||
|
res := ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCPacketCreateTx{
|
||||||
|
Packet: packet,
|
||||||
}}))
|
}}))
|
||||||
assert.True(res.IsOK(), res.Log)
|
assertAndLog(t, store, res, abci.CodeType_InsufficientFunds)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
|
||||||
store.ClearLogLines()
|
|
||||||
|
|
||||||
// Construct a Header
|
// Send a small enough number of coins
|
||||||
header := tm.Header{
|
packet = NewPacket("test_chain", "dst_chain", 0, CoinsPayload{
|
||||||
ChainID: "test_chain",
|
Address: destinationAddr,
|
||||||
Height: 999,
|
Coins: coinsGood,
|
||||||
ValidatorsHash: []byte("must_exist"), // TODO make optional
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// Construct a Commit that signs above header
|
|
||||||
blockHash := header.Hash()
|
|
||||||
blockID := tm.BlockID{Hash: blockHash}
|
|
||||||
commit := tm.Commit{
|
|
||||||
BlockID: blockID,
|
|
||||||
Precommits: make([]*tm.Vote, len(privAccs_1)),
|
|
||||||
}
|
|
||||||
for i, privAcc := range privAccs_1 {
|
|
||||||
vote := &tm.Vote{
|
|
||||||
ValidatorAddress: privAcc.Account.PubKey.Address(),
|
|
||||||
ValidatorIndex: i,
|
|
||||||
Height: 999,
|
|
||||||
Round: 0,
|
|
||||||
Type: tm.VoteTypePrecommit,
|
|
||||||
BlockID: tm.BlockID{Hash: blockHash},
|
|
||||||
}
|
|
||||||
vote.Signature = privAcc.PrivKey.Sign(
|
|
||||||
tm.SignBytes("test_chain", vote),
|
|
||||||
)
|
|
||||||
commit.Precommits[i] = vote
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update a chain with a broken commit
|
|
||||||
// Modify the first byte of the first signature
|
|
||||||
sig := commit.Precommits[0].Signature.(crypto.SignatureEd25519)
|
|
||||||
sig[0] += 1
|
|
||||||
commit.Precommits[0].Signature = sig
|
|
||||||
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCUpdateChainTx{
|
|
||||||
Header: header,
|
|
||||||
Commit: commit,
|
|
||||||
}}))
|
|
||||||
assert.Equal(IBCCodeInvalidCommit, res.Code, res.Log)
|
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
|
||||||
store.ClearLogLines()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIBCPluginBadProof(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
eyesClient := eyes.NewLocalClient("", 0)
|
|
||||||
store := types.NewKVCache(eyesClient)
|
|
||||||
store.SetLogging() // Log all activity
|
|
||||||
|
|
||||||
ibcPlugin := New()
|
|
||||||
ctx := types.CallContext{
|
|
||||||
CallerAddress: nil,
|
|
||||||
CallerAccount: nil,
|
|
||||||
Coins: types.Coins{},
|
|
||||||
}
|
|
||||||
|
|
||||||
chainID_1 := "test_chain"
|
|
||||||
genDoc_1, privAccs_1 := genGenesisDoc(chainID_1, 4)
|
|
||||||
genDocJSON_1 := wire.JSONBytesPretty(genDoc_1)
|
|
||||||
|
|
||||||
// Successfully register a chain
|
|
||||||
res := ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCRegisterChainTx{
|
|
||||||
BlockchainGenesis{
|
|
||||||
ChainID: "test_chain",
|
|
||||||
Genesis: string(genDocJSON_1),
|
|
||||||
},
|
|
||||||
}}))
|
|
||||||
assert.True(res.IsOK(), res.Log)
|
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
|
||||||
store.ClearLogLines()
|
|
||||||
|
|
||||||
// Create a new packet (for testing)
|
|
||||||
packet := Packet{
|
|
||||||
SrcChainID: "test_chain",
|
|
||||||
DstChainID: "dst_chain",
|
|
||||||
Sequence: 0,
|
|
||||||
Type: "data",
|
|
||||||
Payload: []byte("hello world"),
|
|
||||||
}
|
|
||||||
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(abci.CodeType_OK, res.Code, res.Log)
|
assertAndLog(t, store, res, abci.CodeType_OK)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
|
||||||
store.ClearLogLines()
|
|
||||||
|
|
||||||
// Construct a Header that includes the above packet.
|
// Construct a Header that includes the above packet.
|
||||||
store.Sync()
|
store.Sync()
|
||||||
resCommit := eyesClient.CommitSync()
|
resCommit := eyesClient.CommitSync()
|
||||||
appHash := resCommit.Data
|
appHash := resCommit.Data
|
||||||
header := tm.Header{
|
header := newHeader("test_chain", 999, appHash, []byte("must_exist"))
|
||||||
ChainID: "test_chain",
|
|
||||||
Height: 999,
|
|
||||||
AppHash: appHash,
|
|
||||||
ValidatorsHash: []byte("must_exist"), // TODO make optional
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct a Commit that signs above header
|
// Construct a Commit that signs above header
|
||||||
blockHash := header.Hash()
|
commit := constructCommit(privAccs_1, header)
|
||||||
blockID := tm.BlockID{Hash: blockHash}
|
|
||||||
commit := tm.Commit{
|
|
||||||
BlockID: blockID,
|
|
||||||
Precommits: make([]*tm.Vote, len(privAccs_1)),
|
|
||||||
}
|
|
||||||
for i, privAcc := range privAccs_1 {
|
|
||||||
vote := &tm.Vote{
|
|
||||||
ValidatorAddress: privAcc.Account.PubKey.Address(),
|
|
||||||
ValidatorIndex: i,
|
|
||||||
Height: 999,
|
|
||||||
Round: 0,
|
|
||||||
Type: tm.VoteTypePrecommit,
|
|
||||||
BlockID: tm.BlockID{Hash: blockHash},
|
|
||||||
}
|
|
||||||
vote.Signature = privAcc.PrivKey.Sign(
|
|
||||||
tm.SignBytes("test_chain", vote),
|
|
||||||
)
|
|
||||||
commit.Precommits[i] = vote
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update a chain
|
// Update a chain
|
||||||
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCUpdateChainTx{
|
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCUpdateChainTx{
|
||||||
Header: header,
|
Header: header,
|
||||||
Commit: commit,
|
Commit: commit,
|
||||||
}}))
|
}}))
|
||||||
assert.Equal(abci.CodeType_OK, res.Code, res.Log)
|
assertAndLog(t, store, res, abci.CodeType_OK)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
|
||||||
store.ClearLogLines()
|
|
||||||
|
|
||||||
// Get proof for the packet
|
// Get proof for the packet
|
||||||
packetKey := toKey(_IBC, _EGRESS,
|
packetKey := toKey(_IBC, _EGRESS,
|
||||||
|
@ -379,7 +328,120 @@ func TestIBCPluginBadProof(t *testing.T) {
|
||||||
Prove: true,
|
Prove: true,
|
||||||
})
|
})
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
var proof *merkle.IAVLProof
|
var proof *iavl.IAVLProof
|
||||||
|
err = wire.ReadBinaryBytes(resQuery.Proof, &proof)
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
// Account should be empty before the tx
|
||||||
|
acc := types.GetAccount(store, destinationAddr)
|
||||||
|
assert.Nil(acc)
|
||||||
|
|
||||||
|
// Post a packet
|
||||||
|
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCPacketPostTx{
|
||||||
|
FromChainID: "test_chain",
|
||||||
|
FromChainHeight: 999,
|
||||||
|
Packet: packet,
|
||||||
|
Proof: proof,
|
||||||
|
}}))
|
||||||
|
assertAndLog(t, store, res, abci.CodeType_OK)
|
||||||
|
|
||||||
|
// Account should now have some coins
|
||||||
|
acc = types.GetAccount(store, destinationAddr)
|
||||||
|
assert.Equal(acc.Balance, coinsGood)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIBCPluginBadCommit(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
eyesClient := eyes.NewLocalClient("", 0)
|
||||||
|
store := types.NewKVCache(eyesClient)
|
||||||
|
store.SetLogging() // Log all activity
|
||||||
|
|
||||||
|
ibcPlugin := New()
|
||||||
|
ctx := types.NewCallContext(nil, nil, types.Coins{})
|
||||||
|
|
||||||
|
chainID_1 := "test_chain"
|
||||||
|
genDoc_1, privAccs_1 := genGenesisDoc(chainID_1, 4)
|
||||||
|
genDocJSON_1, err := json.Marshal(genDoc_1)
|
||||||
|
require.Nil(err)
|
||||||
|
|
||||||
|
// Successfully register a chain
|
||||||
|
registerChain(t, ibcPlugin, store, ctx, "test_chain", string(genDocJSON_1))
|
||||||
|
|
||||||
|
// Construct a Header
|
||||||
|
header := newHeader("test_chain", 999, nil, []byte("must_exist"))
|
||||||
|
|
||||||
|
// Construct a Commit that signs above header
|
||||||
|
commit := constructCommit(privAccs_1, header)
|
||||||
|
|
||||||
|
// Update a chain with a broken commit
|
||||||
|
// Modify the first byte of the first signature
|
||||||
|
sig := commit.Precommits[0].Signature.Unwrap().(crypto.SignatureEd25519)
|
||||||
|
sig[0] += 1
|
||||||
|
commit.Precommits[0].Signature = sig.Wrap()
|
||||||
|
res := ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCUpdateChainTx{
|
||||||
|
Header: header,
|
||||||
|
Commit: commit,
|
||||||
|
}}))
|
||||||
|
assertAndLog(t, store, res, IBCCodeInvalidCommit)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIBCPluginBadProof(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
eyesClient := eyes.NewLocalClient("", 0)
|
||||||
|
store := types.NewKVCache(eyesClient)
|
||||||
|
store.SetLogging() // Log all activity
|
||||||
|
|
||||||
|
ibcPlugin := New()
|
||||||
|
ctx := types.NewCallContext(nil, nil, types.Coins{})
|
||||||
|
|
||||||
|
chainID_1 := "test_chain"
|
||||||
|
genDoc_1, privAccs_1 := genGenesisDoc(chainID_1, 4)
|
||||||
|
genDocJSON_1, err := json.Marshal(genDoc_1)
|
||||||
|
require.Nil(err)
|
||||||
|
|
||||||
|
// Successfully register a chain
|
||||||
|
registerChain(t, ibcPlugin, store, ctx, "test_chain", string(genDocJSON_1))
|
||||||
|
|
||||||
|
// Create a new packet (for testing)
|
||||||
|
packet := NewPacket("test_chain", "dst_chain", 0, DataPayload([]byte("hello world")))
|
||||||
|
res := ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCPacketCreateTx{
|
||||||
|
Packet: packet,
|
||||||
|
}}))
|
||||||
|
assertAndLog(t, store, res, abci.CodeType_OK)
|
||||||
|
|
||||||
|
// Construct a Header that includes the above packet.
|
||||||
|
store.Sync()
|
||||||
|
resCommit := eyesClient.CommitSync()
|
||||||
|
appHash := resCommit.Data
|
||||||
|
header := newHeader("test_chain", 999, appHash, []byte("must_exist"))
|
||||||
|
|
||||||
|
// Construct a Commit that signs above header
|
||||||
|
commit := constructCommit(privAccs_1, header)
|
||||||
|
|
||||||
|
// Update a chain
|
||||||
|
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCUpdateChainTx{
|
||||||
|
Header: header,
|
||||||
|
Commit: commit,
|
||||||
|
}}))
|
||||||
|
assertAndLog(t, store, res, abci.CodeType_OK)
|
||||||
|
|
||||||
|
// Get proof for the packet
|
||||||
|
packetKey := toKey(_IBC, _EGRESS,
|
||||||
|
packet.SrcChainID,
|
||||||
|
packet.DstChainID,
|
||||||
|
cmn.Fmt("%v", packet.Sequence),
|
||||||
|
)
|
||||||
|
resQuery, err := eyesClient.QuerySync(abci.RequestQuery{
|
||||||
|
Path: "/store",
|
||||||
|
Data: packetKey,
|
||||||
|
Prove: true,
|
||||||
|
})
|
||||||
|
assert.Nil(err)
|
||||||
|
var proof *iavl.IAVLProof
|
||||||
err = wire.ReadBinaryBytes(resQuery.Proof, &proof)
|
err = wire.ReadBinaryBytes(resQuery.Proof, &proof)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
|
|
||||||
|
@ -393,7 +455,58 @@ func TestIBCPluginBadProof(t *testing.T) {
|
||||||
Packet: packet,
|
Packet: packet,
|
||||||
Proof: proof,
|
Proof: proof,
|
||||||
}}))
|
}}))
|
||||||
assert.Equal(IBCCodeInvalidProof, res.Code, res.Log)
|
assertAndLog(t, store, res, IBCCodeInvalidProof)
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------
|
||||||
|
// utils
|
||||||
|
|
||||||
|
func assertAndLog(t *testing.T, store *types.KVCache, res abci.Result, codeExpected abci.CodeType) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
assert.Equal(codeExpected, res.Code, res.Log)
|
||||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||||
store.ClearLogLines()
|
store.ClearLogLines()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newHeader(chainID string, height int, appHash, valHash []byte) tm.Header {
|
||||||
|
return tm.Header{
|
||||||
|
ChainID: chainID,
|
||||||
|
Height: height,
|
||||||
|
AppHash: appHash,
|
||||||
|
ValidatorsHash: valHash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerChain(t *testing.T, ibcPlugin *IBCPlugin, store *types.KVCache, ctx types.CallContext, chainID, genDoc string) {
|
||||||
|
res := ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCRegisterChainTx{
|
||||||
|
BlockchainGenesis{
|
||||||
|
ChainID: chainID,
|
||||||
|
Genesis: genDoc,
|
||||||
|
},
|
||||||
|
}}))
|
||||||
|
assertAndLog(t, store, res, abci.CodeType_OK)
|
||||||
|
}
|
||||||
|
|
||||||
|
func constructCommit(privAccs []types.PrivAccount, header tm.Header) tm.Commit {
|
||||||
|
blockHash := header.Hash()
|
||||||
|
blockID := tm.BlockID{Hash: blockHash}
|
||||||
|
commit := tm.Commit{
|
||||||
|
BlockID: blockID,
|
||||||
|
Precommits: make([]*tm.Vote, len(privAccs)),
|
||||||
|
}
|
||||||
|
for i, privAcc := range privAccs {
|
||||||
|
vote := &tm.Vote{
|
||||||
|
ValidatorAddress: privAcc.Account.PubKey.Address(),
|
||||||
|
ValidatorIndex: i,
|
||||||
|
Height: 999,
|
||||||
|
Round: 0,
|
||||||
|
Type: tm.VoteTypePrecommit,
|
||||||
|
BlockID: tm.BlockID{Hash: blockHash},
|
||||||
|
}
|
||||||
|
vote.Signature = privAcc.PrivKey.Sign(
|
||||||
|
tm.SignBytes("test_chain", vote),
|
||||||
|
)
|
||||||
|
commit.Precommits[i] = vote
|
||||||
|
}
|
||||||
|
return commit
|
||||||
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
cmn "github.com/tendermint/go-common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
"github.com/tendermint/go-rpc/client"
|
"github.com/tendermint/tendermint/rpc/lib/client"
|
||||||
"github.com/tendermint/go-rpc/types"
|
"github.com/tendermint/tendermint/rpc/lib/types"
|
||||||
"github.com/tendermint/go-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
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,14 +2,15 @@ package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
|
"github.com/tendermint/tmlibs/events"
|
||||||
|
|
||||||
|
"github.com/tendermint/basecoin/plugins/ibc"
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
cmn "github.com/tendermint/go-common"
|
|
||||||
"github.com/tendermint/go-events"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// If the tx is invalid, a TMSP error will be returned.
|
// If the tx is invalid, a TMSP error will be returned.
|
||||||
func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc events.Fireable) abci.Result {
|
func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc events.Fireable) abci.Result {
|
||||||
|
|
||||||
chainID := state.GetChainID()
|
chainID := state.GetChainID()
|
||||||
|
|
||||||
// Exec tx
|
// Exec tx
|
||||||
|
@ -95,11 +96,11 @@ func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc e
|
||||||
signBytes := tx.SignBytes(chainID)
|
signBytes := tx.SignBytes(chainID)
|
||||||
res = validateInputAdvanced(inAcc, signBytes, tx.Input)
|
res = validateInputAdvanced(inAcc, signBytes, tx.Input)
|
||||||
if res.IsErr() {
|
if res.IsErr() {
|
||||||
log.Info(cmn.Fmt("validateInputAdvanced failed on %X: %v", tx.Input.Address, res))
|
state.logger.Info(cmn.Fmt("validateInputAdvanced failed on %X: %v", tx.Input.Address, res))
|
||||||
return res.PrependLog("in validateInputAdvanced()")
|
return res.PrependLog("in validateInputAdvanced()")
|
||||||
}
|
}
|
||||||
if !tx.Input.Coins.IsGTE(types.Coins{tx.Fee}) {
|
if !tx.Input.Coins.IsGTE(types.Coins{tx.Fee}) {
|
||||||
log.Info(cmn.Fmt("Sender did not send enough to cover the fee %X", tx.Input.Address))
|
state.logger.Info(cmn.Fmt("Sender did not send enough to cover the fee %X", tx.Input.Address))
|
||||||
return abci.ErrBaseInsufficientFunds.AppendLog(cmn.Fmt("input coins is %v, but fee is %v", tx.Input.Coins, types.Coins{tx.Fee}))
|
return abci.ErrBaseInsufficientFunds.AppendLog(cmn.Fmt("input coins is %v, but fee is %v", tx.Input.Coins, types.Coins{tx.Fee}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +132,7 @@ func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc e
|
||||||
res = plugin.RunTx(cache, ctx, tx.Data)
|
res = plugin.RunTx(cache, ctx, tx.Data)
|
||||||
if res.IsOK() {
|
if res.IsOK() {
|
||||||
cache.CacheSync()
|
cache.CacheSync()
|
||||||
log.Info("Successful execution")
|
state.logger.Info("Successful execution")
|
||||||
// Fire events
|
// Fire events
|
||||||
/*
|
/*
|
||||||
if evc != nil {
|
if evc != nil {
|
||||||
|
@ -144,7 +145,7 @@ func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc e
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
} else {
|
} else {
|
||||||
log.Info("AppTx failed", "error", res)
|
state.logger.Info("AppTx failed", "error", res)
|
||||||
// Just return the coins and return.
|
// Just return the coins and return.
|
||||||
inAccCopy.Balance = inAccCopy.Balance.Plus(coins)
|
inAccCopy.Balance = inAccCopy.Balance.Plus(coins)
|
||||||
// But take the gas
|
// But take the gas
|
||||||
|
@ -190,17 +191,23 @@ func getOrMakeOutputs(state types.AccountGetter, accounts map[string]*types.Acco
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, out := range outs {
|
for _, out := range outs {
|
||||||
|
chain, outAddress, _ := out.ChainAndAddress() // already validated
|
||||||
|
if chain != nil {
|
||||||
|
// we dont need an account for the other chain.
|
||||||
|
// we'll just create an outgoing ibc packet
|
||||||
|
continue
|
||||||
|
}
|
||||||
// Account shouldn't be duplicated
|
// Account shouldn't be duplicated
|
||||||
if _, ok := accounts[string(out.Address)]; ok {
|
if _, ok := accounts[string(outAddress)]; ok {
|
||||||
return nil, abci.ErrBaseDuplicateAddress
|
return nil, abci.ErrBaseDuplicateAddress
|
||||||
}
|
}
|
||||||
acc := state.GetAccount(out.Address)
|
acc := state.GetAccount(outAddress)
|
||||||
// output account may be nil (new)
|
// output account may be nil (new)
|
||||||
if acc == nil {
|
if acc == nil {
|
||||||
// zero value is valid, empty account
|
// zero value is valid, empty account
|
||||||
acc = &types.Account{}
|
acc = &types.Account{}
|
||||||
}
|
}
|
||||||
accounts[string(out.Address)] = acc
|
accounts[string(outAddress)] = acc
|
||||||
}
|
}
|
||||||
return accounts, abci.OK
|
return accounts, abci.OK
|
||||||
}
|
}
|
||||||
|
@ -244,7 +251,7 @@ func validateInputAdvanced(acc *types.Account, signBytes []byte, in types.TxInpu
|
||||||
return abci.ErrBaseInsufficientFunds.AppendLog(cmn.Fmt("balance is %v, tried to send %v", balance, in.Coins))
|
return abci.ErrBaseInsufficientFunds.AppendLog(cmn.Fmt("balance is %v, tried to send %v", balance, in.Coins))
|
||||||
}
|
}
|
||||||
// Check signatures
|
// Check signatures
|
||||||
if !acc.PubKey.VerifyBytes(signBytes, in.Signature.Signature) {
|
if !acc.PubKey.VerifyBytes(signBytes, in.Signature) {
|
||||||
return abci.ErrBaseInvalidSignature.AppendLog(cmn.Fmt("SignBytes: %X", signBytes))
|
return abci.ErrBaseInvalidSignature.AppendLog(cmn.Fmt("SignBytes: %X", signBytes))
|
||||||
}
|
}
|
||||||
return abci.OK
|
return abci.OK
|
||||||
|
@ -282,15 +289,22 @@ func adjustByInputs(state types.AccountSetter, accounts map[string]*types.Accoun
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func adjustByOutputs(state types.AccountSetter, accounts map[string]*types.Account, outs []types.TxOutput, isCheckTx bool) {
|
func adjustByOutputs(state *State, accounts map[string]*types.Account, outs []types.TxOutput, isCheckTx bool) {
|
||||||
for _, out := range outs {
|
for _, out := range outs {
|
||||||
acc := accounts[string(out.Address)]
|
destChain, outAddress, _ := out.ChainAndAddress() // already validated
|
||||||
|
if destChain != nil {
|
||||||
|
payload := ibc.CoinsPayload{outAddress, out.Coins}
|
||||||
|
ibc.SaveNewIBCPacket(state, state.GetChainID(), string(destChain), payload)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
acc := accounts[string(outAddress)]
|
||||||
if acc == nil {
|
if acc == nil {
|
||||||
cmn.PanicSanity("adjustByOutputs() expects account in accounts")
|
cmn.PanicSanity("adjustByOutputs() expects account in accounts")
|
||||||
}
|
}
|
||||||
acc.Balance = acc.Balance.Plus(out.Coins)
|
acc.Balance = acc.Balance.Plus(out.Coins)
|
||||||
if !isCheckTx {
|
if !isCheckTx {
|
||||||
state.SetAccount(out.Address, acc)
|
state.SetAccount(outAddress, acc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,9 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
|
"github.com/tendermint/basecoin/plugins/ibc"
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,11 +35,6 @@ func (et *execTest) signTx(tx *types.SendTx, accsIn ...types.PrivAccount) {
|
||||||
types.SignTx(et.chainID, tx, accsIn...)
|
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
|
// 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) {
|
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
|
initBalIn := et.state.GetAccount(et.accIn.Account.PubKey.Address()).Balance
|
||||||
|
@ -63,6 +61,7 @@ func (et *execTest) reset() {
|
||||||
|
|
||||||
et.store = types.NewMemKVStore()
|
et.store = types.NewMemKVStore()
|
||||||
et.state = NewState(et.store)
|
et.state = NewState(et.store)
|
||||||
|
et.state.SetLogger(log.TestingLogger())
|
||||||
et.state.SetChainID(et.chainID)
|
et.state.SetChainID(et.chainID)
|
||||||
|
|
||||||
// NOTE we dont run acc2State here
|
// NOTE we dont run acc2State here
|
||||||
|
@ -83,19 +82,19 @@ func TestGetInputs(t *testing.T) {
|
||||||
|
|
||||||
//test getInputs for registered, non-registered account
|
//test getInputs for registered, non-registered account
|
||||||
et.reset()
|
et.reset()
|
||||||
txs := types.Accs2TxInputs(1, et.accIn)
|
inputs := types.Accs2TxInputs(1, et.accIn)
|
||||||
acc, res = getInputs(et.state, txs)
|
acc, res = getInputs(et.state, inputs)
|
||||||
assert.True(res.IsErr(), "getInputs: expected error when using getInput with non-registered Input")
|
assert.True(res.IsErr(), "getInputs: expected error when using getInput with non-registered Input")
|
||||||
|
|
||||||
et.acc2State(et.accIn)
|
et.acc2State(et.accIn)
|
||||||
acc, res = getInputs(et.state, txs)
|
acc, res = getInputs(et.state, inputs)
|
||||||
assert.True(res.IsOK(), "getInputs: expected to getInput from registered Input")
|
assert.True(res.IsOK(), "getInputs: expected to getInput from registered Input")
|
||||||
|
|
||||||
//test sending duplicate accounts
|
//test sending duplicate accounts
|
||||||
et.reset()
|
et.reset()
|
||||||
et.acc2State(et.accIn, et.accIn, et.accIn)
|
et.acc2State(et.accIn, et.accIn, et.accIn)
|
||||||
txs = types.Accs2TxInputs(1, et.accIn, et.accIn, et.accIn)
|
inputs = types.Accs2TxInputs(1, et.accIn, et.accIn, et.accIn)
|
||||||
acc, res = getInputs(et.state, txs)
|
acc, res = getInputs(et.state, inputs)
|
||||||
assert.True(res.IsErr(), "getInputs: expected error when sending duplicate accounts")
|
assert.True(res.IsErr(), "getInputs: expected error when sending duplicate accounts")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,24 +109,24 @@ func TestGetOrMakeOutputs(t *testing.T) {
|
||||||
|
|
||||||
//test sending duplicate accounts
|
//test sending duplicate accounts
|
||||||
et.reset()
|
et.reset()
|
||||||
txs := types.Accs2TxOutputs(et.accIn, et.accIn, et.accIn)
|
outputs := types.Accs2TxOutputs(et.accIn, et.accIn, et.accIn)
|
||||||
_, res = getOrMakeOutputs(et.state, nil, txs)
|
_, res = getOrMakeOutputs(et.state, nil, outputs)
|
||||||
assert.True(res.IsErr(), "getOrMakeOutputs: expected error when sending duplicate accounts")
|
assert.True(res.IsErr(), "getOrMakeOutputs: expected error when sending duplicate accounts")
|
||||||
|
|
||||||
//test sending to existing/new account
|
//test sending to existing/new account
|
||||||
et.reset()
|
et.reset()
|
||||||
txs1 := types.Accs2TxOutputs(et.accIn)
|
outputs1 := types.Accs2TxOutputs(et.accIn)
|
||||||
txs2 := types.Accs2TxOutputs(et.accOut)
|
outputs2 := types.Accs2TxOutputs(et.accOut)
|
||||||
|
|
||||||
et.acc2State(et.accIn)
|
et.acc2State(et.accIn)
|
||||||
_, res = getOrMakeOutputs(et.state, nil, txs1)
|
_, res = getOrMakeOutputs(et.state, nil, outputs1)
|
||||||
assert.True(res.IsOK(), "getOrMakeOutputs: error when sending to existing account")
|
assert.True(res.IsOK(), "getOrMakeOutputs: error when sending to existing account")
|
||||||
|
|
||||||
mapRes2, res := getOrMakeOutputs(et.state, nil, txs2)
|
mapRes2, res := getOrMakeOutputs(et.state, nil, outputs2)
|
||||||
assert.True(res.IsOK(), "getOrMakeOutputs: error when sending to new account")
|
assert.True(res.IsOK(), "getOrMakeOutputs: error when sending to new account")
|
||||||
|
|
||||||
//test the map results
|
//test the map results
|
||||||
_, map2ok := mapRes2[string(txs2[0].Address)]
|
_, map2ok := mapRes2[string(outputs2[0].Address)]
|
||||||
assert.True(map2ok, "getOrMakeOutputs: account output does not contain new account map item")
|
assert.True(map2ok, "getOrMakeOutputs: account output does not contain new account map item")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -137,12 +136,12 @@ func TestValidateInputsBasic(t *testing.T) {
|
||||||
et := newExecTest()
|
et := newExecTest()
|
||||||
|
|
||||||
//validate input basic
|
//validate input basic
|
||||||
txs := types.Accs2TxInputs(1, et.accIn)
|
inputs := types.Accs2TxInputs(1, et.accIn)
|
||||||
res := validateInputsBasic(txs)
|
res := validateInputsBasic(inputs)
|
||||||
assert.True(res.IsOK(), "validateInputsBasic: expected no error on good tx input. Error: %v", res.Error())
|
assert.True(res.IsOK(), "validateInputsBasic: expected no error on good tx input. Error: %v", res.Error())
|
||||||
|
|
||||||
txs[0].Coins[0].Amount = 0
|
inputs[0].Coins[0].Amount = 0
|
||||||
res = validateInputsBasic(txs)
|
res = validateInputsBasic(inputs)
|
||||||
assert.True(res.IsErr(), "validateInputsBasic: expected error on bad tx input")
|
assert.True(res.IsErr(), "validateInputsBasic: expected error on bad tx input")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -157,28 +156,28 @@ func TestValidateInputsAdvanced(t *testing.T) {
|
||||||
accIn3 := types.MakeAcc("fooz")
|
accIn3 := types.MakeAcc("fooz")
|
||||||
|
|
||||||
//validate inputs advanced
|
//validate inputs advanced
|
||||||
txs := et.getTx(1, et.accOut, accIn1, accIn2, accIn3)
|
tx := types.MakeSendTx(1, et.accOut, accIn1, accIn2, accIn3)
|
||||||
|
|
||||||
et.acc2State(accIn1, accIn2, accIn3, et.accOut)
|
et.acc2State(accIn1, accIn2, accIn3, et.accOut)
|
||||||
accMap, res := getInputs(et.state, txs.Inputs)
|
accMap, res := getInputs(et.state, tx.Inputs)
|
||||||
assert.True(res.IsOK(), "validateInputsAdvanced: error retrieving accMap. Error: %v", res.Error())
|
assert.True(res.IsOK(), "validateInputsAdvanced: error retrieving accMap. Error: %v", res.Error())
|
||||||
signBytes := txs.SignBytes(et.chainID)
|
signBytes := tx.SignBytes(et.chainID)
|
||||||
|
|
||||||
//test bad case, unsigned
|
//test bad case, unsigned
|
||||||
totalCoins, res := validateInputsAdvanced(accMap, signBytes, txs.Inputs)
|
totalCoins, res := validateInputsAdvanced(accMap, signBytes, tx.Inputs)
|
||||||
assert.True(res.IsErr(), "validateInputsAdvanced: expected an error on an unsigned tx input")
|
assert.True(res.IsErr(), "validateInputsAdvanced: expected an error on an unsigned tx input")
|
||||||
|
|
||||||
//test good case sgined
|
//test good case sgined
|
||||||
et.signTx(txs, accIn1, accIn2, accIn3, et.accOut)
|
et.signTx(tx, accIn1, accIn2, accIn3, et.accOut)
|
||||||
totalCoins, res = validateInputsAdvanced(accMap, signBytes, txs.Inputs)
|
totalCoins, res = validateInputsAdvanced(accMap, signBytes, tx.Inputs)
|
||||||
assert.True(res.IsOK(), "validateInputsAdvanced: expected no error on good tx input. Error: %v", res.Error())
|
assert.True(res.IsOK(), "validateInputsAdvanced: expected no error on good tx input. Error: %v", res.Error())
|
||||||
|
|
||||||
txsTotalCoins := txs.Inputs[0].Coins.
|
txTotalCoins := tx.Inputs[0].Coins.
|
||||||
Plus(txs.Inputs[1].Coins).
|
Plus(tx.Inputs[1].Coins).
|
||||||
Plus(txs.Inputs[2].Coins)
|
Plus(tx.Inputs[2].Coins)
|
||||||
|
|
||||||
assert.True(totalCoins.IsEqual(txsTotalCoins),
|
assert.True(totalCoins.IsEqual(txTotalCoins),
|
||||||
"ValidateInputsAdvanced: transaction total coins are not equal: got %v, expected %v", txsTotalCoins, totalCoins)
|
"ValidateInputsAdvanced: transaction total coins are not equal: got %v, expected %v", txTotalCoins, totalCoins)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidateInputAdvanced(t *testing.T) {
|
func TestValidateInputAdvanced(t *testing.T) {
|
||||||
|
@ -186,31 +185,31 @@ func TestValidateInputAdvanced(t *testing.T) {
|
||||||
et := newExecTest()
|
et := newExecTest()
|
||||||
|
|
||||||
//validate input advanced
|
//validate input advanced
|
||||||
txs := et.getTx(1, et.accOut, et.accIn)
|
tx := types.MakeSendTx(1, et.accOut, et.accIn)
|
||||||
|
|
||||||
et.acc2State(et.accIn, et.accOut)
|
et.acc2State(et.accIn, et.accOut)
|
||||||
signBytes := txs.SignBytes(et.chainID)
|
signBytes := tx.SignBytes(et.chainID)
|
||||||
|
|
||||||
//unsigned case
|
//unsigned case
|
||||||
res := validateInputAdvanced(&et.accIn.Account, signBytes, txs.Inputs[0])
|
res := validateInputAdvanced(&et.accIn.Account, signBytes, tx.Inputs[0])
|
||||||
assert.True(res.IsErr(), "validateInputAdvanced: expected error on tx input without signature")
|
assert.True(res.IsErr(), "validateInputAdvanced: expected error on tx input without signature")
|
||||||
|
|
||||||
//good signed case
|
//good signed case
|
||||||
et.signTx(txs, et.accIn, et.accOut)
|
et.signTx(tx, et.accIn, et.accOut)
|
||||||
res = validateInputAdvanced(&et.accIn.Account, signBytes, txs.Inputs[0])
|
res = validateInputAdvanced(&et.accIn.Account, signBytes, tx.Inputs[0])
|
||||||
assert.True(res.IsOK(), "validateInputAdvanced: expected no error on good tx input. Error: %v", res.Error())
|
assert.True(res.IsOK(), "validateInputAdvanced: expected no error on good tx input. Error: %v", res.Error())
|
||||||
|
|
||||||
//bad sequence case
|
//bad sequence case
|
||||||
et.accIn.Sequence = 1
|
et.accIn.Sequence = 1
|
||||||
et.signTx(txs, et.accIn, et.accOut)
|
et.signTx(tx, et.accIn, et.accOut)
|
||||||
res = validateInputAdvanced(&et.accIn.Account, signBytes, txs.Inputs[0])
|
res = validateInputAdvanced(&et.accIn.Account, signBytes, tx.Inputs[0])
|
||||||
assert.Equal(abci.CodeType_BaseInvalidSequence, res.Code, "validateInputAdvanced: expected error on tx input with bad sequence")
|
assert.Equal(abci.CodeType_BaseInvalidSequence, res.Code, "validateInputAdvanced: expected error on tx input with bad sequence")
|
||||||
et.accIn.Sequence = 0 //restore sequence
|
et.accIn.Sequence = 0 //restore sequence
|
||||||
|
|
||||||
//bad balance case
|
//bad balance case
|
||||||
et.accIn.Balance = types.Coins{{"mycoin", 2}}
|
et.accIn.Balance = types.Coins{{"mycoin", 2}}
|
||||||
et.signTx(txs, et.accIn, et.accOut)
|
et.signTx(tx, et.accIn, et.accOut)
|
||||||
res = validateInputAdvanced(&et.accIn.Account, signBytes, txs.Inputs[0])
|
res = validateInputAdvanced(&et.accIn.Account, signBytes, tx.Inputs[0])
|
||||||
assert.Equal(abci.CodeType_BaseInsufficientFunds, res.Code,
|
assert.Equal(abci.CodeType_BaseInsufficientFunds, res.Code,
|
||||||
"validateInputAdvanced: expected error on tx input with insufficient funds %v", et.accIn.Sequence)
|
"validateInputAdvanced: expected error on tx input with insufficient funds %v", et.accIn.Sequence)
|
||||||
}
|
}
|
||||||
|
@ -220,12 +219,12 @@ func TestValidateOutputsAdvanced(t *testing.T) {
|
||||||
et := newExecTest()
|
et := newExecTest()
|
||||||
|
|
||||||
//validateOutputsBasic
|
//validateOutputsBasic
|
||||||
txs := types.Accs2TxOutputs(et.accIn)
|
tx := types.Accs2TxOutputs(et.accIn)
|
||||||
res := validateOutputsBasic(txs)
|
res := validateOutputsBasic(tx)
|
||||||
assert.True(res.IsOK(), "validateOutputsBasic: expected no error on good tx output. Error: %v", res.Error())
|
assert.True(res.IsOK(), "validateOutputsBasic: expected no error on good tx output. Error: %v", res.Error())
|
||||||
|
|
||||||
txs[0].Coins[0].Amount = 0
|
tx[0].Coins[0].Amount = 0
|
||||||
res = validateOutputsBasic(txs)
|
res = validateOutputsBasic(tx)
|
||||||
assert.True(res.IsErr(), "validateInputBasic: expected error on bad tx output. Error: %v", res.Error())
|
assert.True(res.IsErr(), "validateInputBasic: expected error on bad tx output. Error: %v", res.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,9 +233,9 @@ func TestSumOutput(t *testing.T) {
|
||||||
et := newExecTest()
|
et := newExecTest()
|
||||||
|
|
||||||
//SumOutput
|
//SumOutput
|
||||||
txs := types.Accs2TxOutputs(et.accIn, et.accOut)
|
tx := types.Accs2TxOutputs(et.accIn, et.accOut)
|
||||||
total := sumOutputs(txs)
|
total := sumOutputs(tx)
|
||||||
assert.True(total.IsEqual(txs[0].Coins.Plus(txs[1].Coins)), "sumOutputs: total coins are not equal")
|
assert.True(total.IsEqual(tx[0].Coins.Plus(tx[1].Coins)), "sumOutputs: total coins are not equal")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAdjustBy(t *testing.T) {
|
func TestAdjustBy(t *testing.T) {
|
||||||
|
@ -269,23 +268,23 @@ func TestAdjustBy(t *testing.T) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExecTx(t *testing.T) {
|
func TestSendTx(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
et := newExecTest()
|
et := newExecTest()
|
||||||
|
|
||||||
//ExecTx
|
//ExecTx
|
||||||
txs := et.getTx(1, et.accOut, et.accIn)
|
tx := types.MakeSendTx(1, et.accOut, et.accIn)
|
||||||
et.acc2State(et.accIn)
|
et.acc2State(et.accIn)
|
||||||
et.acc2State(et.accOut)
|
et.acc2State(et.accOut)
|
||||||
et.signTx(txs, et.accIn)
|
et.signTx(tx, et.accIn)
|
||||||
|
|
||||||
//Bad Balance
|
//Bad Balance
|
||||||
et.accIn.Balance = types.Coins{{"mycoin", 2}}
|
et.accIn.Balance = types.Coins{{"mycoin", 2}}
|
||||||
et.acc2State(et.accIn)
|
et.acc2State(et.accIn)
|
||||||
res, _, _, _, _ := et.exec(txs, true)
|
res, _, _, _, _ := et.exec(tx, true)
|
||||||
assert.True(res.IsErr(), "ExecTx/Bad CheckTx: Expected error return from ExecTx, returned: %v", res)
|
assert.True(res.IsErr(), "ExecTx/Bad CheckTx: Expected error return from ExecTx, returned: %v", res)
|
||||||
|
|
||||||
res, balIn, balInExp, balOut, balOutExp := et.exec(txs, false)
|
res, balIn, balInExp, balOut, balOutExp := et.exec(tx, false)
|
||||||
assert.True(res.IsErr(), "ExecTx/Bad DeliverTx: Expected error return from ExecTx, returned: %v", res)
|
assert.True(res.IsErr(), "ExecTx/Bad DeliverTx: Expected error return from ExecTx, returned: %v", res)
|
||||||
assert.False(balIn.IsEqual(balInExp),
|
assert.False(balIn.IsEqual(balInExp),
|
||||||
"ExecTx/Bad DeliverTx: balance shouldn't be equal for accIn: got %v, expected: %v", balIn, balInExp)
|
"ExecTx/Bad DeliverTx: balance shouldn't be equal for accIn: got %v, expected: %v", balIn, balInExp)
|
||||||
|
@ -296,17 +295,59 @@ func TestExecTx(t *testing.T) {
|
||||||
et.reset()
|
et.reset()
|
||||||
et.acc2State(et.accIn)
|
et.acc2State(et.accIn)
|
||||||
et.acc2State(et.accOut)
|
et.acc2State(et.accOut)
|
||||||
res, _, _, _, _ = et.exec(txs, true)
|
res, _, _, _, _ = et.exec(tx, true)
|
||||||
assert.True(res.IsOK(), "ExecTx/Good CheckTx: Expected OK return from ExecTx, Error: %v", res)
|
assert.True(res.IsOK(), "ExecTx/Good CheckTx: Expected OK return from ExecTx, Error: %v", res)
|
||||||
|
|
||||||
//Regular DeliverTx
|
//Regular DeliverTx
|
||||||
et.reset()
|
et.reset()
|
||||||
et.acc2State(et.accIn)
|
et.acc2State(et.accIn)
|
||||||
et.acc2State(et.accOut)
|
et.acc2State(et.accOut)
|
||||||
res, balIn, balInExp, balOut, balOutExp = et.exec(txs, false)
|
res, balIn, balInExp, balOut, balOutExp = et.exec(tx, false)
|
||||||
assert.True(res.IsOK(), "ExecTx/Good DeliverTx: Expected OK return from ExecTx, Error: %v", res)
|
assert.True(res.IsOK(), "ExecTx/Good DeliverTx: Expected OK return from ExecTx, Error: %v", res)
|
||||||
assert.True(balIn.IsEqual(balInExp),
|
assert.True(balIn.IsEqual(balInExp),
|
||||||
"ExecTx/good DeliverTx: unexpected change in input balance, got: %v, expected: %v", balIn, balInExp)
|
"ExecTx/good DeliverTx: unexpected change in input balance, got: %v, expected: %v", balIn, balInExp)
|
||||||
assert.True(balOut.IsEqual(balOutExp),
|
assert.True(balOut.IsEqual(balOutExp),
|
||||||
"ExecTx/good DeliverTx: unexpected change in output balance, got: %v, expected: %v", balOut, balOutExp)
|
"ExecTx/good DeliverTx: unexpected change in output balance, got: %v, expected: %v", balOut, balOutExp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSendTxIBC(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
et := newExecTest()
|
||||||
|
|
||||||
|
//ExecTx
|
||||||
|
chainID2 := "otherchain"
|
||||||
|
tx := types.MakeSendTx(1, et.accOut, et.accIn)
|
||||||
|
dstAddress := tx.Outputs[0].Address
|
||||||
|
tx.Outputs[0].Address = []byte(chainID2 + "/" + string(tx.Outputs[0].Address))
|
||||||
|
et.acc2State(et.accIn)
|
||||||
|
et.signTx(tx, et.accIn)
|
||||||
|
|
||||||
|
//Regular DeliverTx
|
||||||
|
et.reset()
|
||||||
|
et.acc2State(et.accIn)
|
||||||
|
|
||||||
|
initBalIn := et.state.GetAccount(et.accIn.Account.PubKey.Address()).Balance
|
||||||
|
|
||||||
|
res := ExecTx(et.state, nil, tx, false, nil)
|
||||||
|
|
||||||
|
balIn := et.state.GetAccount(et.accIn.Account.PubKey.Address()).Balance
|
||||||
|
decrBalInExp := tx.Outputs[0].Coins.Plus(types.Coins{tx.Fee}) //expected decrease in balance In
|
||||||
|
balInExp := initBalIn.Minus(decrBalInExp)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
packet, err := ibc.GetIBCPacket(et.state, et.chainID, chainID2, 0)
|
||||||
|
assert.Nil(err)
|
||||||
|
|
||||||
|
assert.Equal(packet.SrcChainID, et.chainID)
|
||||||
|
assert.Equal(packet.DstChainID, chainID2)
|
||||||
|
assert.Equal(packet.Sequence, uint64(0))
|
||||||
|
assert.Equal(packet.Type, "coin")
|
||||||
|
|
||||||
|
coins, ok := packet.Payload.(ibc.CoinsPayload)
|
||||||
|
assert.True(ok)
|
||||||
|
assert.Equal(coins.Coins, tx.Outputs[0].Coins)
|
||||||
|
assert.EqualValues(coins.Address, dstAddress)
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
package state
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/tendermint/go-logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
var log = logger.New("module", "state")
|
|
|
@ -3,9 +3,8 @@ package state
|
||||||
import (
|
import (
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
. "github.com/tendermint/go-common"
|
|
||||||
"github.com/tendermint/go-wire"
|
|
||||||
eyes "github.com/tendermint/merkleeyes/client"
|
eyes "github.com/tendermint/merkleeyes/client"
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CONTRACT: State should be quick to copy.
|
// CONTRACT: State should be quick to copy.
|
||||||
|
@ -15,6 +14,7 @@ type State struct {
|
||||||
store types.KVStore
|
store types.KVStore
|
||||||
readCache map[string][]byte // optional, for caching writes to store
|
readCache map[string][]byte // optional, for caching writes to store
|
||||||
writeCache *types.KVCache // optional, for caching writes w/o writing to store
|
writeCache *types.KVCache // optional, for caching writes w/o writing to store
|
||||||
|
logger log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewState(store types.KVStore) *State {
|
func NewState(store types.KVStore) *State {
|
||||||
|
@ -23,9 +23,14 @@ func NewState(store types.KVStore) *State {
|
||||||
store: store,
|
store: store,
|
||||||
readCache: make(map[string][]byte),
|
readCache: make(map[string][]byte),
|
||||||
writeCache: nil,
|
writeCache: nil,
|
||||||
|
logger: log.NewNopLogger(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *State) SetLogger(l log.Logger) {
|
||||||
|
s.logger = l
|
||||||
|
}
|
||||||
|
|
||||||
func (s *State) SetChainID(chainID string) {
|
func (s *State) SetChainID(chainID string) {
|
||||||
s.chainID = chainID
|
s.chainID = chainID
|
||||||
s.store.Set([]byte("base/chain_id"), []byte(chainID))
|
s.store.Set([]byte("base/chain_id"), []byte(chainID))
|
||||||
|
@ -57,11 +62,11 @@ func (s *State) Set(key []byte, value []byte) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) GetAccount(addr []byte) *types.Account {
|
func (s *State) GetAccount(addr []byte) *types.Account {
|
||||||
return GetAccount(s, addr)
|
return types.GetAccount(s, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) SetAccount(addr []byte, acc *types.Account) {
|
func (s *State) SetAccount(addr []byte, acc *types.Account) {
|
||||||
SetAccount(s, addr, acc)
|
types.SetAccount(s, addr, acc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) CacheWrap() *State {
|
func (s *State) CacheWrap() *State {
|
||||||
|
@ -71,6 +76,7 @@ func (s *State) CacheWrap() *State {
|
||||||
store: cache,
|
store: cache,
|
||||||
readCache: nil,
|
readCache: nil,
|
||||||
writeCache: cache,
|
writeCache: cache,
|
||||||
|
logger: s.logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,28 +95,3 @@ func (s *State) Commit() abci.Result {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------
|
|
||||||
|
|
||||||
func AccountKey(addr []byte) []byte {
|
|
||||||
return append([]byte("base/a/"), addr...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAccount(store types.KVStore, addr []byte) *types.Account {
|
|
||||||
data := store.Get(AccountKey(addr))
|
|
||||||
if len(data) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var acc *types.Account
|
|
||||||
err := wire.ReadBinaryBytes(data, &acc)
|
|
||||||
if err != nil {
|
|
||||||
panic(Fmt("Error reading account %X error: %v",
|
|
||||||
data, err.Error()))
|
|
||||||
}
|
|
||||||
return acc
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetAccount(store types.KVStore, addr []byte, acc *types.Account) {
|
|
||||||
accBytes := wire.BinaryBytes(acc)
|
|
||||||
store.Set(AccountKey(addr), accBytes)
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
eyes "github.com/tendermint/merkleeyes/client"
|
eyes "github.com/tendermint/merkleeyes/client"
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
@ -16,6 +17,7 @@ func TestState(t *testing.T) {
|
||||||
//States and Stores for tests
|
//States and Stores for tests
|
||||||
store := types.NewMemKVStore()
|
store := types.NewMemKVStore()
|
||||||
state := NewState(store)
|
state := NewState(store)
|
||||||
|
state.SetLogger(log.TestingLogger())
|
||||||
cache := state.CacheWrap()
|
cache := state.CacheWrap()
|
||||||
eyesCli := eyes.NewLocalClient("", 0)
|
eyesCli := eyes.NewLocalClient("", 0)
|
||||||
|
|
||||||
|
@ -29,12 +31,14 @@ func TestState(t *testing.T) {
|
||||||
reset := func() {
|
reset := func() {
|
||||||
store = types.NewMemKVStore()
|
store = types.NewMemKVStore()
|
||||||
state = NewState(store)
|
state = NewState(store)
|
||||||
|
state.SetLogger(log.TestingLogger())
|
||||||
cache = state.CacheWrap()
|
cache = state.CacheWrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
//set the state to using the eyesCli instead of MemKVStore
|
//set the state to using the eyesCli instead of MemKVStore
|
||||||
useEyesCli := func() {
|
useEyesCli := func() {
|
||||||
state = NewState(eyesCli)
|
state = NewState(eyesCli)
|
||||||
|
state.SetLogger(log.TestingLogger())
|
||||||
cache = state.CacheWrap()
|
cache = state.CacheWrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,11 @@ import (
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
cmn "github.com/tendermint/go-common"
|
|
||||||
crypto "github.com/tendermint/go-crypto"
|
|
||||||
rpcclient "github.com/tendermint/go-rpc/client"
|
|
||||||
"github.com/tendermint/go-rpc/types"
|
|
||||||
wire "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
|
||||||
|
"github.com/tendermint/tendermint/rpc/lib/client"
|
||||||
|
"github.com/tendermint/tendermint/rpc/lib/types"
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -67,16 +66,18 @@ func main() {
|
||||||
// Sign request
|
// Sign request
|
||||||
signBytes := tx.SignBytes(chainID)
|
signBytes := tx.SignBytes(chainID)
|
||||||
sig := root.Sign(signBytes)
|
sig := root.Sign(signBytes)
|
||||||
tx.Inputs[0].Signature = crypto.SignatureS{sig}
|
tx.Inputs[0].Signature = sig
|
||||||
//fmt.Println("tx:", tx)
|
//fmt.Println("tx:", tx)
|
||||||
|
|
||||||
// Write request
|
// Write request
|
||||||
txBytes := wire.BinaryBytes(struct{ types.Tx }{tx})
|
txBytes := wire.BinaryBytes(struct{ types.Tx }{tx})
|
||||||
request := rpctypes.NewRPCRequest("fakeid", "broadcast_tx_sync", map[string]interface{}{"tx": txBytes})
|
request, err := rpctypes.MapToRequest("fakeid", "broadcast_tx_sync", map[string]interface{}{"tx": txBytes})
|
||||||
//request := rpctypes.NewRPCRequest("fakeid", "broadcast_tx_sync", map[string]interface{}{"tx": txBytes})
|
if err != nil {
|
||||||
|
cmn.Exit("cannot encode request: " + err.Error())
|
||||||
|
}
|
||||||
reqBytes := wire.JSONBytes(request)
|
reqBytes := wire.JSONBytes(request)
|
||||||
//fmt.Print(".")
|
//fmt.Print(".")
|
||||||
err := ws.WriteMessage(websocket.TextMessage, reqBytes)
|
err = ws.WriteMessage(websocket.TextMessage, reqBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmn.Exit("writing websocket request: " + err.Error())
|
cmn.Exit("writing websocket request: " + err.Error())
|
||||||
}
|
}
|
||||||
|
@ -118,15 +119,18 @@ func main() {
|
||||||
// Sign request
|
// Sign request
|
||||||
signBytes := tx.SignBytes(chainID)
|
signBytes := tx.SignBytes(chainID)
|
||||||
sig := privAccountA.Sign(signBytes)
|
sig := privAccountA.Sign(signBytes)
|
||||||
tx.Inputs[0].Signature = crypto.SignatureS{sig}
|
tx.Inputs[0].Signature = sig
|
||||||
//fmt.Println("tx:", tx)
|
//fmt.Println("tx:", tx)
|
||||||
|
|
||||||
// Write request
|
// Write request
|
||||||
txBytes := wire.BinaryBytes(struct{ types.Tx }{tx})
|
txBytes := wire.BinaryBytes(struct{ types.Tx }{tx})
|
||||||
request := rpctypes.NewRPCRequest("fakeid", "broadcast_tx_sync", map[string]interface{}{"tx": txBytes})
|
request, err := rpctypes.MapToRequest("fakeid", "broadcast_tx_sync", map[string]interface{}{"tx": txBytes})
|
||||||
|
if err != nil {
|
||||||
|
cmn.Exit("cannot encode request: " + err.Error())
|
||||||
|
}
|
||||||
reqBytes := wire.JSONBytes(request)
|
reqBytes := wire.JSONBytes(request)
|
||||||
//fmt.Print(".")
|
//fmt.Print(".")
|
||||||
err := ws.WriteMessage(websocket.TextMessage, reqBytes)
|
err = ws.WriteMessage(websocket.TextMessage, reqBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmn.Exit("writing websocket request: " + err.Error())
|
cmn.Exit("writing websocket request: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,16 +8,17 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/tendermint/basecoin/app"
|
"github.com/tendermint/basecoin/app"
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
cmn "github.com/tendermint/go-common"
|
wire "github.com/tendermint/go-wire"
|
||||||
crypto "github.com/tendermint/go-crypto"
|
|
||||||
"github.com/tendermint/go-wire"
|
|
||||||
eyescli "github.com/tendermint/merkleeyes/client"
|
eyescli "github.com/tendermint/merkleeyes/client"
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSendTx(t *testing.T) {
|
func TestSendTx(t *testing.T) {
|
||||||
eyesCli := eyescli.NewLocalClient("", 0)
|
eyesCli := eyescli.NewLocalClient("", 0)
|
||||||
chainID := "test_chain_id"
|
chainID := "test_chain_id"
|
||||||
bcApp := app.NewBasecoin(eyesCli)
|
bcApp := app.NewBasecoin(eyesCli)
|
||||||
|
bcApp.SetLogger(log.TestingLogger().With("module", "app"))
|
||||||
bcApp.SetOption("base/chain_id", chainID)
|
bcApp.SetOption("base/chain_id", chainID)
|
||||||
// t.Log(bcApp.Info())
|
// t.Log(bcApp.Info())
|
||||||
|
|
||||||
|
@ -50,7 +51,7 @@ func TestSendTx(t *testing.T) {
|
||||||
signBytes := tx.SignBytes(chainID)
|
signBytes := tx.SignBytes(chainID)
|
||||||
// t.Log("Sign bytes: %X\n", signBytes)
|
// t.Log("Sign bytes: %X\n", signBytes)
|
||||||
sig := test1PrivAcc.Sign(signBytes)
|
sig := test1PrivAcc.Sign(signBytes)
|
||||||
tx.Inputs[0].Signature = crypto.SignatureS{sig}
|
tx.Inputs[0].Signature = sig
|
||||||
// t.Log("Signed TX bytes: %X\n", wire.BinaryBytes(types.TxS{tx}))
|
// t.Log("Signed TX bytes: %X\n", wire.BinaryBytes(types.TxS{tx}))
|
||||||
|
|
||||||
// Write request
|
// Write request
|
||||||
|
@ -102,7 +103,7 @@ func TestSequence(t *testing.T) {
|
||||||
// Sign request
|
// Sign request
|
||||||
signBytes := tx.SignBytes(chainID)
|
signBytes := tx.SignBytes(chainID)
|
||||||
sig := test1PrivAcc.Sign(signBytes)
|
sig := test1PrivAcc.Sign(signBytes)
|
||||||
tx.Inputs[0].Signature = crypto.SignatureS{sig}
|
tx.Inputs[0].Signature = sig
|
||||||
// t.Log("ADDR: %X -> %X\n", tx.Inputs[0].Address, tx.Outputs[0].Address)
|
// t.Log("ADDR: %X -> %X\n", tx.Inputs[0].Address, tx.Outputs[0].Address)
|
||||||
|
|
||||||
// Write request
|
// Write request
|
||||||
|
@ -146,7 +147,7 @@ func TestSequence(t *testing.T) {
|
||||||
// Sign request
|
// Sign request
|
||||||
signBytes := tx.SignBytes(chainID)
|
signBytes := tx.SignBytes(chainID)
|
||||||
sig := privAccountA.Sign(signBytes)
|
sig := privAccountA.Sign(signBytes)
|
||||||
tx.Inputs[0].Signature = crypto.SignatureS{sig}
|
tx.Inputs[0].Signature = sig
|
||||||
// t.Log("ADDR: %X -> %X\n", tx.Inputs[0].Address, tx.Outputs[0].Address)
|
// t.Log("ADDR: %X -> %X\n", tx.Inputs[0].Address, tx.Outputs[0].Address)
|
||||||
|
|
||||||
// Write request
|
// Write request
|
||||||
|
|
|
@ -4,12 +4,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
|
"github.com/tendermint/go-wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Account struct {
|
type Account struct {
|
||||||
PubKey crypto.PubKeyS `json:"pub_key"` // May be nil, if not known.
|
PubKey crypto.PubKey `json:"pub_key"` // May be nil, if not known.
|
||||||
Sequence int `json:"sequence"`
|
Sequence int `json:"sequence"`
|
||||||
Balance Coins `json:"coins"`
|
Balance Coins `json:"coins"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (acc *Account) Copy() *Account {
|
func (acc *Account) Copy() *Account {
|
||||||
|
@ -31,7 +32,7 @@ func (acc *Account) String() string {
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
|
|
||||||
type PrivAccount struct {
|
type PrivAccount struct {
|
||||||
crypto.PrivKeyS
|
crypto.PrivKey
|
||||||
Account
|
Account
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,3 +50,26 @@ type AccountGetterSetter interface {
|
||||||
GetAccount(addr []byte) *Account
|
GetAccount(addr []byte) *Account
|
||||||
SetAccount(addr []byte, acc *Account)
|
SetAccount(addr []byte, acc *Account)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AccountKey(addr []byte) []byte {
|
||||||
|
return append([]byte("base/a/"), addr...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAccount(store KVStore, addr []byte) *Account {
|
||||||
|
data := store.Get(AccountKey(addr))
|
||||||
|
if len(data) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var acc *Account
|
||||||
|
err := wire.ReadBinaryBytes(data, &acc)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Error reading account %X error: %v",
|
||||||
|
data, err.Error()))
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetAccount(store KVStore, addr []byte, acc *Account) {
|
||||||
|
accBytes := wire.BinaryBytes(acc)
|
||||||
|
store.Set(AccountKey(addr), accBytes)
|
||||||
|
}
|
||||||
|
|
|
@ -3,8 +3,11 @@ package types
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Coin struct {
|
type Coin struct {
|
||||||
|
@ -17,22 +20,26 @@ func (coin Coin) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
//regex codes for extracting coins from string
|
//regex codes for extracting coins from string
|
||||||
var reDenom = regexp.MustCompile("([^\\d\\W]+)")
|
var reDenom = regexp.MustCompile("")
|
||||||
var reAmt = regexp.MustCompile("(\\d+)")
|
var reAmt = regexp.MustCompile("(\\d+)")
|
||||||
|
|
||||||
func ParseCoin(str string) (Coin, error) {
|
var reCoin = regexp.MustCompile("^([[:digit:]]+)[[:space:]]*([[:alpha:]]+)$")
|
||||||
|
|
||||||
|
func ParseCoin(str string) (Coin, error) {
|
||||||
var coin Coin
|
var coin Coin
|
||||||
|
|
||||||
if len(str) > 0 {
|
matches := reCoin.FindStringSubmatch(strings.TrimSpace(str))
|
||||||
amt, err := strconv.Atoi(reAmt.FindString(str))
|
if matches == nil {
|
||||||
if err != nil {
|
return coin, errors.Errorf("%s is invalid coin definition", str)
|
||||||
return coin, err
|
|
||||||
}
|
|
||||||
denom := reDenom.FindString(str)
|
|
||||||
coin = Coin{denom, int64(amt)}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parse the amount (should always parse properly)
|
||||||
|
amt, err := strconv.Atoi(matches[1])
|
||||||
|
if err != nil {
|
||||||
|
return coin, err
|
||||||
|
}
|
||||||
|
|
||||||
|
coin = Coin{matches[2], int64(amt)}
|
||||||
return coin, nil
|
return coin, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,18 +60,26 @@ func (coins Coins) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseCoins(str string) (Coins, error) {
|
func ParseCoins(str string) (Coins, error) {
|
||||||
|
// empty string is empty list...
|
||||||
|
if len(str) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
split := strings.Split(str, ",")
|
split := strings.Split(str, ",")
|
||||||
var coins []Coin
|
var coins Coins
|
||||||
|
|
||||||
for _, el := range split {
|
for _, el := range split {
|
||||||
if len(el) > 0 {
|
coin, err := ParseCoin(el)
|
||||||
coin, err := ParseCoin(el)
|
if err != nil {
|
||||||
if err != nil {
|
return coins, err
|
||||||
return coins, err
|
|
||||||
}
|
|
||||||
coins = append(coins, coin)
|
|
||||||
}
|
}
|
||||||
|
coins = append(coins, coin)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure they are in proper order, to avoid random failures later
|
||||||
|
coins.Sort()
|
||||||
|
if !coins.IsValid() {
|
||||||
|
return nil, errors.Errorf("ParseCoins invalid: %#v", coins)
|
||||||
}
|
}
|
||||||
|
|
||||||
return coins, nil
|
return coins, nil
|
||||||
|
@ -195,3 +210,10 @@ func (coins Coins) IsNonnegative() bool {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** Implement Sort interface ***/
|
||||||
|
|
||||||
|
func (c Coins) Len() int { return len(c) }
|
||||||
|
func (c Coins) Less(i, j int) bool { return c[i].Denom < c[j].Denom }
|
||||||
|
func (c Coins) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
|
||||||
|
func (c Coins) Sort() { sort.Sort(c) }
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"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) {
|
||||||
|
@ -56,30 +55,85 @@ func TestCoins(t *testing.T) {
|
||||||
|
|
||||||
//Test the parse coin and parse coins functionality
|
//Test the parse coin and parse coins functionality
|
||||||
func TestParse(t *testing.T) {
|
func TestParse(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
makeCoin := func(str string) Coin {
|
cases := []struct {
|
||||||
coin, err := ParseCoin(str)
|
input string
|
||||||
require.Nil(err)
|
valid bool // if false, we expect an error on parse
|
||||||
return coin
|
expected Coins // if valid is true, make sure this is returned
|
||||||
|
}{
|
||||||
|
{"", true, nil},
|
||||||
|
{"1foo", true, Coins{{"foo", 1}}},
|
||||||
|
{"10bar", true, Coins{{"bar", 10}}},
|
||||||
|
{"99bar,1foo", true, Coins{{"bar", 99}, {"foo", 1}}},
|
||||||
|
{"98 bar , 1 foo ", true, Coins{{"bar", 98}, {"foo", 1}}},
|
||||||
|
{" 55\t \t bling\n", true, Coins{{"bling", 55}}},
|
||||||
|
{"2foo, 97 bar", true, Coins{{"bar", 97}, {"foo", 2}}},
|
||||||
|
{"5 mycoin,", false, nil}, // no empty coins in a list
|
||||||
|
{"2 3foo, 97 bar", false, nil}, // 3foo is invalid coin name
|
||||||
|
{"11me coin, 12you coin", false, nil}, // no spaces in coin names
|
||||||
|
{"1.2btc", false, nil}, // amount must be integer
|
||||||
|
{"5foo-bar", false, nil}, // once more, only letters in coin name
|
||||||
}
|
}
|
||||||
|
|
||||||
makeCoins := func(str string) Coins {
|
for _, tc := range cases {
|
||||||
coin, err := ParseCoins(str)
|
res, err := ParseCoins(tc.input)
|
||||||
require.Nil(err)
|
if !tc.valid {
|
||||||
return coin
|
assert.NotNil(err, "%s: %#v", tc.input, res)
|
||||||
|
} else if assert.Nil(err, "%s: %+v", tc.input, err) {
|
||||||
|
assert.Equal(tc.expected, res)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//testing ParseCoin Function
|
}
|
||||||
assert.Equal(Coin{}, makeCoin(""), "ParseCoin makes bad empty coin")
|
|
||||||
assert.Equal(Coin{"fooCoin", 1}, makeCoin("1fooCoin"), "ParseCoin makes bad coins")
|
func TestSortCoins(t *testing.T) {
|
||||||
assert.Equal(Coin{"barCoin", 10}, makeCoin("10 barCoin"), "ParseCoin makes bad coins")
|
assert := assert.New(t)
|
||||||
|
|
||||||
//testing ParseCoins Function
|
good := Coins{
|
||||||
assert.True(Coins{{"fooCoin", 1}}.IsEqual(makeCoins("1fooCoin")),
|
Coin{"GAS", 1},
|
||||||
"ParseCoins doesn't parse a single coin")
|
Coin{"MINERAL", 1},
|
||||||
assert.True(Coins{{"barCoin", 99}, {"fooCoin", 1}}.IsEqual(makeCoins("99barCoin,1fooCoin")),
|
Coin{"TREE", 1},
|
||||||
"ParseCoins doesn't properly parse two coins")
|
}
|
||||||
assert.True(Coins{{"barCoin", 99}, {"fooCoin", 1}}.IsEqual(makeCoins("99 barCoin, 1 fooCoin")),
|
empty := Coins{
|
||||||
"ParseCoins doesn't properly parse two coins which use spaces")
|
Coin{"GOLD", 0},
|
||||||
|
}
|
||||||
|
badSort1 := Coins{
|
||||||
|
Coin{"TREE", 1},
|
||||||
|
Coin{"GAS", 1},
|
||||||
|
Coin{"MINERAL", 1},
|
||||||
|
}
|
||||||
|
badSort2 := Coins{ // both are after the first one, but the second and third are in the wrong order
|
||||||
|
Coin{"GAS", 1},
|
||||||
|
Coin{"TREE", 1},
|
||||||
|
Coin{"MINERAL", 1},
|
||||||
|
}
|
||||||
|
badAmt := Coins{
|
||||||
|
Coin{"GAS", 1},
|
||||||
|
Coin{"TREE", 0},
|
||||||
|
Coin{"MINERAL", 1},
|
||||||
|
}
|
||||||
|
dup := Coins{
|
||||||
|
Coin{"GAS", 1},
|
||||||
|
Coin{"GAS", 1},
|
||||||
|
Coin{"MINERAL", 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
coins Coins
|
||||||
|
before, after bool // valid before/after sort
|
||||||
|
}{
|
||||||
|
{good, true, true},
|
||||||
|
{empty, false, false},
|
||||||
|
{badSort1, false, true},
|
||||||
|
{badSort2, false, true},
|
||||||
|
{badAmt, false, false},
|
||||||
|
{dup, false, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
assert.Equal(tc.before, tc.coins.IsValid())
|
||||||
|
tc.coins.Sort()
|
||||||
|
assert.Equal(tc.after, tc.coins.IsValid())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"container/list"
|
"container/list"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
. "github.com/tendermint/go-common"
|
. "github.com/tendermint/tmlibs/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
type KVStore interface {
|
type KVStore interface {
|
||||||
|
|
|
@ -3,18 +3,19 @@ package types
|
||||||
// Helper functions for testing
|
// Helper functions for testing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
cmn "github.com/tendermint/go-common"
|
|
||||||
"github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Creates a PrivAccount from secret.
|
// Creates a PrivAccount from secret.
|
||||||
// The amount is not set.
|
// The amount is not set.
|
||||||
func PrivAccountFromSecret(secret string) PrivAccount {
|
func PrivAccountFromSecret(secret string) PrivAccount {
|
||||||
privKey := crypto.GenPrivKeyEd25519FromSecret([]byte(secret))
|
privKey :=
|
||||||
|
crypto.GenPrivKeyEd25519FromSecret([]byte(secret)).Wrap()
|
||||||
privAccount := PrivAccount{
|
privAccount := PrivAccount{
|
||||||
PrivKeyS: crypto.PrivKeyS{privKey},
|
PrivKey: privKey,
|
||||||
Account: Account{
|
Account: Account{
|
||||||
PubKey: crypto.PubKeyS{privKey.PubKey()},
|
PubKey: privKey.PubKey(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return privAccount
|
return privAccount
|
||||||
|
@ -30,10 +31,10 @@ func RandAccounts(num int, minAmount int64, maxAmount int64) []PrivAccount {
|
||||||
balance += cmn.RandInt64() % (maxAmount - minAmount)
|
balance += cmn.RandInt64() % (maxAmount - minAmount)
|
||||||
}
|
}
|
||||||
|
|
||||||
privKey := crypto.GenPrivKeyEd25519()
|
privKey := crypto.GenPrivKeyEd25519().Wrap()
|
||||||
pubKey := crypto.PubKeyS{privKey.PubKey()}
|
pubKey := privKey.PubKey()
|
||||||
privAccs[i] = PrivAccount{
|
privAccs[i] = PrivAccount{
|
||||||
PrivKeyS: crypto.PrivKeyS{privKey},
|
PrivKey: privKey,
|
||||||
Account: Account{
|
Account: Account{
|
||||||
PubKey: pubKey,
|
PubKey: pubKey,
|
||||||
Balance: Coins{Coin{"", balance}},
|
Balance: Coins{Coin{"", balance}},
|
||||||
|
@ -85,20 +86,20 @@ func Accs2TxOutputs(accs ...PrivAccount) []TxOutput {
|
||||||
return txs
|
return txs
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTx(seq int, accOut PrivAccount, accsIn ...PrivAccount) *SendTx {
|
func MakeSendTx(seq int, accOut PrivAccount, accsIn ...PrivAccount) *SendTx {
|
||||||
txs := &SendTx{
|
tx := &SendTx{
|
||||||
Gas: 0,
|
Gas: 0,
|
||||||
Fee: Coin{"mycoin", 1},
|
Fee: Coin{"mycoin", 1},
|
||||||
Inputs: Accs2TxInputs(seq, accsIn...),
|
Inputs: Accs2TxInputs(seq, accsIn...),
|
||||||
Outputs: Accs2TxOutputs(accOut),
|
Outputs: Accs2TxOutputs(accOut),
|
||||||
}
|
}
|
||||||
|
|
||||||
return txs
|
return tx
|
||||||
}
|
}
|
||||||
|
|
||||||
func SignTx(chainID string, tx *SendTx, accs ...PrivAccount) {
|
func SignTx(chainID string, tx *SendTx, accs ...PrivAccount) {
|
||||||
signBytes := tx.SignBytes(chainID)
|
signBytes := tx.SignBytes(chainID)
|
||||||
for i, _ := range tx.Inputs {
|
for i, _ := range tx.Inputs {
|
||||||
tx.Inputs[i].Signature = crypto.SignatureS{accs[i].Sign(signBytes)}
|
tx.Inputs[i].Signature = accs[i].Sign(signBytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
77
types/tx.go
77
types/tx.go
|
@ -5,10 +5,10 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
||||||
. "github.com/tendermint/go-common"
|
|
||||||
"github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
"github.com/tendermint/go-data"
|
|
||||||
"github.com/tendermint/go-wire"
|
"github.com/tendermint/go-wire"
|
||||||
|
"github.com/tendermint/go-wire/data"
|
||||||
|
. "github.com/tendermint/tmlibs/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -37,7 +37,7 @@ func (_ *AppTx) AssertIsTx() {}
|
||||||
|
|
||||||
var txMapper data.Mapper
|
var txMapper data.Mapper
|
||||||
|
|
||||||
// register both private key types with go-data (and thus go-wire)
|
// register both private key types with go-wire/data (and thus go-wire)
|
||||||
func init() {
|
func init() {
|
||||||
txMapper = data.NewMapper(TxS{}).
|
txMapper = data.NewMapper(TxS{}).
|
||||||
RegisterImplementation(&SendTx{}, TxNameSend, TxTypeSend).
|
RegisterImplementation(&SendTx{}, TxNameSend, TxTypeSend).
|
||||||
|
@ -46,7 +46,7 @@ func init() {
|
||||||
|
|
||||||
// TxS add json serialization to Tx
|
// TxS add json serialization to Tx
|
||||||
type TxS struct {
|
type TxS struct {
|
||||||
Tx
|
Tx `json:"unwrap"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p TxS) MarshalJSON() ([]byte, error) {
|
func (p TxS) MarshalJSON() ([]byte, error) {
|
||||||
|
@ -64,11 +64,11 @@ func (p *TxS) UnmarshalJSON(data []byte) (err error) {
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
type TxInput struct {
|
type TxInput struct {
|
||||||
Address data.Bytes `json:"address"` // Hash of the PubKey
|
Address data.Bytes `json:"address"` // Hash of the PubKey
|
||||||
Coins Coins `json:"coins"` //
|
Coins Coins `json:"coins"` //
|
||||||
Sequence int `json:"sequence"` // Must be 1 greater than the last committed TxInput
|
Sequence int `json:"sequence"` // Must be 1 greater than the last committed TxInput
|
||||||
Signature crypto.SignatureS `json:"signature"` // Depends on the PubKey type and the whole Tx
|
Signature crypto.Signature `json:"signature"` // Depends on the PubKey type and the whole Tx
|
||||||
PubKey crypto.PubKeyS `json:"pub_key"` // Is present iff Sequence == 0
|
PubKey crypto.PubKey `json:"pub_key"` // Is present iff Sequence == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (txIn TxInput) ValidateBasic() abci.Result {
|
func (txIn TxInput) ValidateBasic() abci.Result {
|
||||||
|
@ -104,13 +104,7 @@ func NewTxInput(pubKey crypto.PubKey, coins Coins, sequence int) TxInput {
|
||||||
Sequence: sequence,
|
Sequence: sequence,
|
||||||
}
|
}
|
||||||
if sequence == 1 {
|
if sequence == 1 {
|
||||||
// safely wrap if needed
|
input.PubKey = pubKey
|
||||||
// TODO: extract this as utility function?
|
|
||||||
ps, ok := pubKey.(crypto.PubKeyS)
|
|
||||||
if !ok {
|
|
||||||
ps = crypto.PubKeyS{pubKey}
|
|
||||||
}
|
|
||||||
input.PubKey = ps
|
|
||||||
}
|
}
|
||||||
return input
|
return input
|
||||||
}
|
}
|
||||||
|
@ -122,10 +116,33 @@ type TxOutput struct {
|
||||||
Coins Coins `json:"coins"` //
|
Coins Coins `json:"coins"` //
|
||||||
}
|
}
|
||||||
|
|
||||||
func (txOut TxOutput) ValidateBasic() abci.Result {
|
// An output destined for another chain may be formatted as `chainID/address`.
|
||||||
if len(txOut.Address) != 20 {
|
// ChainAndAddress returns the chainID prefix and the address.
|
||||||
return abci.ErrBaseInvalidOutput.AppendLog("Invalid address length")
|
// If there is no chainID prefix, the first returned value is nil.
|
||||||
|
func (txOut TxOutput) ChainAndAddress() ([]byte, []byte, abci.Result) {
|
||||||
|
var chainPrefix []byte
|
||||||
|
address := txOut.Address
|
||||||
|
if len(address) > 20 {
|
||||||
|
spl := bytes.Split(address, []byte("/"))
|
||||||
|
if len(spl) < 2 {
|
||||||
|
return nil, nil, abci.ErrBaseInvalidOutput.AppendLog("Invalid address format")
|
||||||
|
}
|
||||||
|
chainPrefix = spl[0]
|
||||||
|
address = bytes.Join(spl[1:], nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(address) != 20 {
|
||||||
|
return nil, nil, abci.ErrBaseInvalidOutput.AppendLog("Invalid address length")
|
||||||
|
}
|
||||||
|
return chainPrefix, address, abci.OK
|
||||||
|
}
|
||||||
|
|
||||||
|
func (txOut TxOutput) ValidateBasic() abci.Result {
|
||||||
|
_, _, r := txOut.ChainAndAddress()
|
||||||
|
if r.IsErr() {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
if !txOut.Coins.IsValid() {
|
if !txOut.Coins.IsValid() {
|
||||||
return abci.ErrBaseInvalidOutput.AppendLog(Fmt("Invalid coins %v", txOut.Coins))
|
return abci.ErrBaseInvalidOutput.AppendLog(Fmt("Invalid coins %v", txOut.Coins))
|
||||||
}
|
}
|
||||||
|
@ -151,25 +168,21 @@ type SendTx struct {
|
||||||
func (tx *SendTx) SignBytes(chainID string) []byte {
|
func (tx *SendTx) SignBytes(chainID string) []byte {
|
||||||
signBytes := wire.BinaryBytes(chainID)
|
signBytes := wire.BinaryBytes(chainID)
|
||||||
sigz := make([]crypto.Signature, len(tx.Inputs))
|
sigz := make([]crypto.Signature, len(tx.Inputs))
|
||||||
for i, input := range tx.Inputs {
|
for i := range tx.Inputs {
|
||||||
sigz[i] = input.Signature.Signature
|
sigz[i] = tx.Inputs[i].Signature
|
||||||
tx.Inputs[i].Signature.Signature = nil
|
tx.Inputs[i].Signature = crypto.Signature{}
|
||||||
}
|
}
|
||||||
signBytes = append(signBytes, wire.BinaryBytes(tx)...)
|
signBytes = append(signBytes, wire.BinaryBytes(tx)...)
|
||||||
for i := range tx.Inputs {
|
for i := range tx.Inputs {
|
||||||
tx.Inputs[i].Signature.Signature = sigz[i]
|
tx.Inputs[i].Signature = sigz[i]
|
||||||
}
|
}
|
||||||
return signBytes
|
return signBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *SendTx) SetSignature(addr []byte, sig crypto.Signature) bool {
|
func (tx *SendTx) SetSignature(addr []byte, sig crypto.Signature) bool {
|
||||||
sigs, ok := sig.(crypto.SignatureS)
|
|
||||||
if !ok {
|
|
||||||
sigs = crypto.SignatureS{sig}
|
|
||||||
}
|
|
||||||
for i, input := range tx.Inputs {
|
for i, input := range tx.Inputs {
|
||||||
if bytes.Equal(input.Address, addr) {
|
if bytes.Equal(input.Address, addr) {
|
||||||
tx.Inputs[i].Signature = sigs
|
tx.Inputs[i].Signature = sig
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,18 +206,14 @@ type AppTx struct {
|
||||||
func (tx *AppTx) SignBytes(chainID string) []byte {
|
func (tx *AppTx) SignBytes(chainID string) []byte {
|
||||||
signBytes := wire.BinaryBytes(chainID)
|
signBytes := wire.BinaryBytes(chainID)
|
||||||
sig := tx.Input.Signature
|
sig := tx.Input.Signature
|
||||||
tx.Input.Signature.Signature = nil
|
tx.Input.Signature = crypto.Signature{}
|
||||||
signBytes = append(signBytes, wire.BinaryBytes(tx)...)
|
signBytes = append(signBytes, wire.BinaryBytes(tx)...)
|
||||||
tx.Input.Signature = sig
|
tx.Input.Signature = sig
|
||||||
return signBytes
|
return signBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *AppTx) SetSignature(sig crypto.Signature) bool {
|
func (tx *AppTx) SetSignature(sig crypto.Signature) bool {
|
||||||
sigs, ok := sig.(crypto.SignatureS)
|
tx.Input.Signature = sig
|
||||||
if !ok {
|
|
||||||
sigs = crypto.SignatureS{sig}
|
|
||||||
}
|
|
||||||
tx.Input.Signature = sigs
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
crypto "github.com/tendermint/go-crypto"
|
data "github.com/tendermint/go-wire/data"
|
||||||
data "github.com/tendermint/go-data"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var chainID string = "test_chain"
|
var chainID string = "test_chain"
|
||||||
|
@ -109,7 +108,7 @@ func TestSendTxJSON(t *testing.T) {
|
||||||
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(), sig)
|
||||||
assert.Equal(tx, tx2)
|
assert.Equal(tx, tx2)
|
||||||
|
|
||||||
// let's marshal / unmarshal this with signature
|
// let's marshal / unmarshal this with signature
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package version
|
package version
|
||||||
|
|
||||||
const Maj = "0"
|
const Maj = "0"
|
||||||
const Min = "4"
|
const Min = "5"
|
||||||
const Fix = "1"
|
const Fix = "0"
|
||||||
|
|
||||||
const Version = "0.4.1"
|
const Version = "0.5.0"
|
||||||
|
|
Loading…
Reference in New Issue