Merge branch 'develop'
This commit is contained in:
commit
7b39417b99
25
CHANGELOG.md
25
CHANGELOG.md
|
@ -1,5 +1,30 @@
|
|||
# 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)
|
||||
|
||||
BUG FIXES:
|
||||
|
|
28
app/app.go
28
app/app.go
|
@ -1,13 +1,15 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
. "github.com/tendermint/go-common"
|
||||
"github.com/tendermint/go-wire"
|
||||
wire "github.com/tendermint/go-wire"
|
||||
eyes "github.com/tendermint/merkleeyes/client"
|
||||
. "github.com/tendermint/tmlibs/common"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
sm "github.com/tendermint/basecoin/state"
|
||||
"github.com/tendermint/basecoin/types"
|
||||
|
@ -24,6 +26,7 @@ type Basecoin struct {
|
|||
state *sm.State
|
||||
cacheState *sm.State
|
||||
plugins *types.Plugins
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func NewBasecoin(eyesCli *eyes.Client) *Basecoin {
|
||||
|
@ -34,9 +37,15 @@ func NewBasecoin(eyesCli *eyes.Client) *Basecoin {
|
|||
state: state,
|
||||
cacheState: nil,
|
||||
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!
|
||||
func (app *Basecoin) GetState() *sm.State {
|
||||
return app.state.CacheWrap()
|
||||
|
@ -60,7 +69,7 @@ func (app *Basecoin) SetOption(key string, value string) string {
|
|||
if plugin == nil {
|
||||
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)
|
||||
} else {
|
||||
// Set option on basecoin
|
||||
|
@ -69,13 +78,18 @@ func (app *Basecoin) SetOption(key string, value string) string {
|
|||
app.state.SetChainID(value)
|
||||
return "Success"
|
||||
case "account":
|
||||
var acc types.Account
|
||||
var acc GenesisAccount
|
||||
err := json.Unmarshal([]byte(value), &acc)
|
||||
if err != nil {
|
||||
return "Error decoding acc message: " + err.Error()
|
||||
}
|
||||
app.state.SetAccount(acc.PubKey.Address(), &acc)
|
||||
log.Notice("SetAccount", "addr", acc.PubKey.Address(), "acc", acc)
|
||||
acc.Balance.Sort()
|
||||
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"
|
||||
}
|
||||
|
@ -136,7 +150,7 @@ func (app *Basecoin) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQu
|
|||
// handle special path for account info
|
||||
if reqQuery.Path == "/account" {
|
||||
reqQuery.Path = "/key"
|
||||
reqQuery.Data = sm.AccountKey(reqQuery.Data)
|
||||
reqQuery.Data = types.AccountKey(reqQuery.Data)
|
||||
}
|
||||
|
||||
resQuery, err := app.eyesCli.QuerySync(reqQuery)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -10,8 +10,9 @@ import (
|
|||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
"github.com/tendermint/basecoin/types"
|
||||
"github.com/tendermint/go-wire"
|
||||
wire "github.com/tendermint/go-wire"
|
||||
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
|
||||
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)
|
||||
return tx
|
||||
}
|
||||
|
@ -56,6 +57,7 @@ func (at *appTest) reset() {
|
|||
|
||||
eyesCli := eyes.NewLocalClient("", 0)
|
||||
at.app = NewBasecoin(eyesCli)
|
||||
at.app.SetLogger(log.TestingLogger().With("module", "app"))
|
||||
|
||||
res := at.app.SetOption("base/chain_id", at.chainID)
|
||||
require.EqualValues(at.t, res, "Success")
|
||||
|
@ -101,9 +103,11 @@ func TestSplitKey(t *testing.T) {
|
|||
|
||||
func TestSetOption(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
eyesCli := eyes.NewLocalClient("", 0)
|
||||
app := NewBasecoin(eyesCli)
|
||||
app.SetLogger(log.TestingLogger().With("module", "app"))
|
||||
|
||||
//testing ChainID
|
||||
chainID := "testChain"
|
||||
|
@ -111,11 +115,43 @@ func TestSetOption(t *testing.T) {
|
|||
assert.EqualValues(app.GetState().GetChainID(), chainID)
|
||||
assert.EqualValues(res, "Success")
|
||||
|
||||
// make a nice account...
|
||||
accIn := types.MakeAcc("input0")
|
||||
accsInBytes, err := json.Marshal(accIn.Account)
|
||||
assert.Nil(err)
|
||||
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", "")
|
||||
assert.NotEqual(res, "Success")
|
||||
|
@ -125,6 +161,7 @@ func TestSetOption(t *testing.T) {
|
|||
|
||||
res = app.SetOption("dslfkgjdas/szfdjzs", "")
|
||||
assert.NotEqual(res, "Success")
|
||||
|
||||
}
|
||||
|
||||
// Test CheckTx and DeliverTx with insufficient and sufficient balance
|
||||
|
@ -176,7 +213,5 @@ func TestQuery(t *testing.T) {
|
|||
Path: "/account",
|
||||
Data: at.accIn.Account.PubKey.Address(),
|
||||
})
|
||||
fmt.Println(resQueryPreCommit)
|
||||
fmt.Println(resQueryPostCommit)
|
||||
assert.NotEqual(resQueryPreCommit, resQueryPostCommit, "Query should change before/after commit")
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/tendermint/basecoin/types"
|
||||
cmn "github.com/tendermint/go-common"
|
||||
//tmtypes "github.com/tendermint/tendermint/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
"github.com/tendermint/go-wire/data"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
)
|
||||
|
||||
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))
|
||||
// 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
|
||||
for _, kv := range genDoc.AppOptions.pluginOptions {
|
||||
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
|
||||
}
|
||||
|
@ -49,7 +52,7 @@ type FullGenesisDoc struct {
|
|||
}
|
||||
|
||||
type GenesisDoc struct {
|
||||
Accounts []types.Account `json:"accounts"`
|
||||
Accounts []GenesisAccount `json:"accounts"`
|
||||
PluginOptions []json.RawMessage `json:"plugin_options"`
|
||||
|
||||
pluginOptions []keyValue // unmarshaled rawmessages
|
||||
|
@ -61,11 +64,7 @@ func loadGenesis(filePath string) (*FullGenesisDoc, error) {
|
|||
return nil, errors.Wrap(err, "loading genesis file")
|
||||
}
|
||||
|
||||
// the tendermint genesis is go-wire
|
||||
// tmGenesis := new(tmtypes.GenesisDoc)
|
||||
// err = wire.ReadJSONBytes(bytes, tmGenesis)
|
||||
|
||||
// the basecoin genesis go-data :)
|
||||
// the basecoin genesis go-wire/data :)
|
||||
genDoc := new(FullGenesisDoc)
|
||||
err = json.Unmarshal(bytes, genDoc)
|
||||
if err != nil {
|
||||
|
@ -102,3 +101,40 @@ func parseGenesisList(kvz_ []json.RawMessage) (kvz []keyValue, err error) {
|
|||
}
|
||||
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/require"
|
||||
cmn "github.com/tendermint/go-common"
|
||||
"github.com/tendermint/basecoin/types"
|
||||
"github.com/tendermint/go-crypto"
|
||||
eyescli "github.com/tendermint/merkleeyes/client"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
)
|
||||
|
||||
const genesisFilepath = "./testdata/genesis.json"
|
||||
const genesisAcctFilepath = "./testdata/genesis2.json"
|
||||
|
||||
func TestLoadGenesis(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
@ -34,11 +36,15 @@ func TestLoadGenesis(t *testing.T) {
|
|||
|
||||
// make sure balance is proper
|
||||
assert.Equal(2, len(acct.Balance))
|
||||
assert.EqualValues(12345, acct.Balance[0].Amount)
|
||||
assert.EqualValues("blank", acct.Balance[0].Denom)
|
||||
assert.True(acct.Balance.IsValid())
|
||||
// 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
|
||||
apk := acct.PubKey.PubKey
|
||||
apk := acct.PubKey.Unwrap()
|
||||
require.NotNil(apk)
|
||||
epk, ok := apk.(crypto.PubKeyEd25519)
|
||||
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) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
bytes, err := cmn.ReadFile(genesisFilepath)
|
||||
require.Nil(err, "loading genesis file %+v", err)
|
||||
|
||||
// the basecoin genesis go-data :)
|
||||
// the basecoin genesis go-wire/data :)
|
||||
genDoc := new(FullGenesisDoc)
|
||||
err = json.Unmarshal(bytes, genDoc)
|
||||
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:
|
||||
environment:
|
||||
GOPATH: /home/ubuntu/.go_workspace
|
||||
REPO: $GOPATH/src/github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME
|
||||
GOPATH: "$HOME/.go_workspace"
|
||||
PROJECT_PARENT_PATH: "$GOPATH/src/github.com/$CIRCLE_PROJECT_USERNAME"
|
||||
REPO: "$PROJECT_PARENT_PATH/$CIRCLE_PROJECT_REPONAME"
|
||||
PATH: "$GOPATH/bin:$PATH"
|
||||
hosts:
|
||||
circlehost: 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:
|
||||
override:
|
||||
- go get github.com/Masterminds/glide
|
||||
- go 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:
|
||||
override:
|
||||
- "cd $REPO && glide install && go install ./cmd/basecoin"
|
||||
- ls $GOPATH/bin
|
||||
- "cd $REPO && make test"
|
||||
- "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
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/tendermint/basecoin/cmd/commands"
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -15,6 +18,7 @@ func main() {
|
|||
RootCmd.AddCommand(
|
||||
commands.InitCmd,
|
||||
commands.StartCmd,
|
||||
commands.RelayCmd,
|
||||
commands.TxCmd,
|
||||
commands.QueryCmd,
|
||||
commands.KeyCmd,
|
||||
|
@ -25,5 +29,8 @@ func main() {
|
|||
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/go-merkle"
|
||||
"github.com/tendermint/go-wire"
|
||||
"github.com/tendermint/merkleeyes/iavl"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
|
@ -196,13 +196,18 @@ func ibcPacketCreateTxCmd(cmd *cobra.Command, args []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
var payload ibc.Payload
|
||||
if err := wire.ReadBinaryBytes(payloadBytes, &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ibcTx := ibc.IBCPacketCreateTx{
|
||||
Packet: ibc.Packet{
|
||||
SrcChainID: fromChain,
|
||||
DstChainID: toChain,
|
||||
Sequence: sequence,
|
||||
Type: packetType,
|
||||
Payload: payloadBytes,
|
||||
Payload: payload,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -229,7 +234,7 @@ func ibcPacketPostTxCmd(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
|
||||
var packet ibc.Packet
|
||||
proof := new(merkle.IAVLProof)
|
||||
proof := new(iavl.IAVLProof)
|
||||
|
||||
err = wire.ReadBinaryBytes(packetBytes, &packet)
|
||||
if err != nil {
|
||||
|
|
|
@ -6,8 +6,6 @@ import (
|
|||
"path"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmn "github.com/tendermint/go-common"
|
||||
)
|
||||
|
||||
//commands
|
||||
|
@ -33,15 +31,17 @@ func setupFile(path, data string, perm os.FileMode) (int, error) {
|
|||
}
|
||||
|
||||
func initCmd(cmd *cobra.Command, args []string) error {
|
||||
rootDir := BasecoinRoot("")
|
||||
|
||||
cmn.EnsureDir(rootDir, 0777)
|
||||
// this will ensure that config.toml is there if not yet created, and create dir
|
||||
cfg, err := getTendermintConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// initalize basecoin
|
||||
genesisFile := path.Join(rootDir, "genesis.json")
|
||||
privValFile := path.Join(rootDir, "priv_validator.json")
|
||||
key1File := path.Join(rootDir, "key.json")
|
||||
key2File := path.Join(rootDir, "key2.json")
|
||||
genesisFile := cfg.GenesisFile()
|
||||
privValFile := cfg.PrivValidatorFile()
|
||||
key1File := path.Join(cfg.RootDir, "key.json")
|
||||
key2File := path.Join(cfg.RootDir, "key2.json")
|
||||
|
||||
mod1, err := setupFile(genesisFile, GenesisJSON, 0644)
|
||||
if err != nil {
|
||||
|
@ -61,9 +61,9 @@ func initCmd(cmd *cobra.Command, args []string) error {
|
|||
}
|
||||
|
||||
if (mod1 + mod2 + mod3 + mod4) > 0 {
|
||||
log.Notice("Initialized Basecoin", "genesis", genesisFile, "key", key1File)
|
||||
logger.Info("Initialized Basecoin", "genesis", genesisFile, "key", key1File)
|
||||
} else {
|
||||
log.Notice("Already initialized", "priv_validator", privValFile)
|
||||
logger.Info("Already initialized", "priv_validator", privValFile)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -76,14 +76,14 @@ var PrivValJSON = `{
|
|||
"last_signature": null,
|
||||
"last_signbytes": "",
|
||||
"last_step": 0,
|
||||
"priv_key": [
|
||||
1,
|
||||
"D07ABE82A8B15559A983B2DB5D4842B2B6E4D6AF58B080005662F424F17D68C17B90EA87E7DC0C7145C8C48C08992BE271C7234134343E8A8E8008E617DE7B30"
|
||||
],
|
||||
"pub_key": [
|
||||
1,
|
||||
"7B90EA87E7DC0C7145C8C48C08992BE271C7234134343E8A8E8008E617DE7B30"
|
||||
]
|
||||
"priv_key": {
|
||||
"type": "ed25519",
|
||||
"data": "D07ABE82A8B15559A983B2DB5D4842B2B6E4D6AF58B080005662F424F17D68C17B90EA87E7DC0C7145C8C48C08992BE271C7234134343E8A8E8008E617DE7B30"
|
||||
},
|
||||
"pub_key": {
|
||||
"type": "ed25519",
|
||||
"data": "7B90EA87E7DC0C7145C8C48C08992BE271C7234134343E8A8E8008E617DE7B30"
|
||||
}
|
||||
}`
|
||||
|
||||
var GenesisJSON = `{
|
||||
|
@ -94,10 +94,10 @@ var GenesisJSON = `{
|
|||
{
|
||||
"amount": 10,
|
||||
"name": "",
|
||||
"pub_key": [
|
||||
1,
|
||||
"7B90EA87E7DC0C7145C8C48C08992BE271C7234134343E8A8E8008E617DE7B30"
|
||||
]
|
||||
"pub_key": {
|
||||
"type": "ed25519",
|
||||
"data": "7B90EA87E7DC0C7145C8C48C08992BE271C7234134343E8A8E8008E617DE7B30"
|
||||
}
|
||||
}
|
||||
],
|
||||
"app_options": {
|
||||
|
|
|
@ -10,8 +10,10 @@ import (
|
|||
|
||||
//"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/tendermint/go-crypto"
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
)
|
||||
|
||||
//commands
|
||||
|
@ -63,8 +65,8 @@ func (a *Address) UnmarshalJSON(addrHex []byte) error {
|
|||
|
||||
type Key struct {
|
||||
Address Address `json:"address"`
|
||||
PubKey crypto.PubKeyS `json:"pub_key"`
|
||||
PrivKey crypto.PrivKeyS `json:"priv_key"`
|
||||
PubKey crypto.PubKey `json:"pub_key"`
|
||||
PrivKey crypto.PrivKey `json:"priv_key"`
|
||||
}
|
||||
|
||||
// Implements Signer
|
||||
|
@ -75,18 +77,25 @@ func (k *Key) Sign(msg []byte) crypto.Signature {
|
|||
// Generates a new validator with private key.
|
||||
func genKey() *Key {
|
||||
privKey := crypto.GenPrivKeyEd25519()
|
||||
addrBytes := privKey.PubKey().Address()
|
||||
pubKey := privKey.PubKey()
|
||||
addrBytes := pubKey.Address()
|
||||
var addr Address
|
||||
copy(addr[:], addrBytes)
|
||||
return &Key{
|
||||
Address: addr,
|
||||
PubKey: crypto.PubKeyS{privKey.PubKey()},
|
||||
PrivKey: crypto.PrivKeyS{privKey},
|
||||
PubKey: pubKey,
|
||||
PrivKey: privKey.Wrap(),
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
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/spf13/cobra"
|
||||
|
||||
"github.com/tendermint/go-merkle"
|
||||
"github.com/tendermint/go-wire"
|
||||
"github.com/tendermint/merkleeyes/iavl"
|
||||
"github.com/tendermint/tendermint/rpc/client"
|
||||
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)
|
||||
}
|
||||
|
||||
acc, err := getAcc(nodeFlag, addr)
|
||||
httpClient := client.NewHTTP(nodeFlag, "/websocket")
|
||||
acc, err := getAccWithClient(httpClient, addr)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
proof, err := merkle.ReadProof(proofBytes)
|
||||
proof, err := iavl.ReadProof(proofBytes)
|
||||
if err != nil {
|
||||
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"
|
||||
|
||||
tmcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||
tmcfg "github.com/tendermint/tendermint/config/tendermint"
|
||||
)
|
||||
|
||||
var UnsafeResetAllCmd = &cobra.Command{
|
||||
|
@ -14,8 +13,10 @@ var UnsafeResetAllCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
func unsafeResetAllCmd(cmd *cobra.Command, args []string) error {
|
||||
basecoinDir := BasecoinRoot("")
|
||||
tmConfig := tmcfg.GetConfig(basecoinDir)
|
||||
tmcmd.ResetAll(tmConfig, log)
|
||||
cfg, err := getTendermintConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmcmd.ResetAll(cfg.DBDir(), cfg.PrivValidatorFile(), logger)
|
||||
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/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/tendermint/abci/server"
|
||||
cmn "github.com/tendermint/go-common"
|
||||
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/proxy"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
|
||||
"github.com/tendermint/basecoin/app"
|
||||
)
|
||||
|
@ -49,12 +52,12 @@ func init() {
|
|||
}
|
||||
|
||||
func startCmd(cmd *cobra.Command, args []string) error {
|
||||
basecoinDir := BasecoinRoot("")
|
||||
rootDir := viper.GetString(cli.HomeFlag)
|
||||
|
||||
// Connect to MerkleEyes
|
||||
var eyesCli *eyes.Client
|
||||
if eyesFlag == "local" {
|
||||
eyesCli = eyes.NewLocalClient(path.Join(basecoinDir, "data", "merkleeyes.db"), EyesCacheSize)
|
||||
eyesCli = eyes.NewLocalClient(path.Join(rootDir, "data", "merkleeyes.db"), EyesCacheSize)
|
||||
} else {
|
||||
var err error
|
||||
eyesCli, err = eyes.NewClient(eyesFlag)
|
||||
|
@ -65,6 +68,7 @@ func startCmd(cmd *cobra.Command, args []string) error {
|
|||
|
||||
// Create Basecoin app
|
||||
basecoinApp := app.NewBasecoin(eyesCli)
|
||||
basecoinApp.SetLogger(logger.With("module", "app"))
|
||||
|
||||
// register IBC plugn
|
||||
basecoinApp.RegisterPlugin(NewIBCPlugin())
|
||||
|
@ -78,7 +82,7 @@ func startCmd(cmd *cobra.Command, args []string) error {
|
|||
// else, assume it's been loaded
|
||||
if basecoinApp.GetState().GetChainID() == "" {
|
||||
// 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 {
|
||||
err := basecoinApp.LoadGenesis(genesisFile)
|
||||
if err != nil {
|
||||
|
@ -91,23 +95,24 @@ func startCmd(cmd *cobra.Command, args []string) error {
|
|||
|
||||
chainID := basecoinApp.GetState().GetChainID()
|
||||
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
|
||||
return startBasecoinABCI(basecoinApp)
|
||||
} 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
|
||||
return startTendermint(basecoinDir, basecoinApp)
|
||||
return startTendermint(rootDir, basecoinApp)
|
||||
}
|
||||
}
|
||||
|
||||
func startBasecoinABCI(basecoinApp *app.Basecoin) error {
|
||||
|
||||
// Start the ABCI listener
|
||||
svr, err := server.NewServer(addrFlag, "socket", basecoinApp)
|
||||
if err != nil {
|
||||
return errors.Errorf("Error creating listener: %v\n", err)
|
||||
}
|
||||
svr.SetLogger(logger.With("module", "abci-server"))
|
||||
svr.Start()
|
||||
|
||||
// Wait forever
|
||||
cmn.TrapSignal(func() {
|
||||
|
@ -117,27 +122,41 @@ func startBasecoinABCI(basecoinApp *app.Basecoin) error {
|
|||
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 {
|
||||
|
||||
// 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()
|
||||
cfg, err := getTendermintConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait forever
|
||||
cmn.TrapSignal(func() {
|
||||
// Cleanup
|
||||
n.Stop()
|
||||
})
|
||||
// TODO: parse the log level from the config properly (multi modules)
|
||||
// but some tm code must be refactored for better usability
|
||||
lvl, err := log.AllowLevel(cfg.LogLevel)
|
||||
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
|
||||
}
|
||||
|
|
|
@ -3,16 +3,15 @@ package commands
|
|||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/tendermint/basecoin/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
|
||||
client "github.com/tendermint/go-rpc/client"
|
||||
wire "github.com/tendermint/go-wire"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
"github.com/tendermint/tendermint/rpc/client"
|
||||
)
|
||||
|
||||
//commands
|
||||
|
@ -60,7 +59,7 @@ func init() {
|
|||
{&fromFlag, "from", "key.json", "Path to a private key to sign the transaction"},
|
||||
{&amountFlag, "amount", "", "Coins to send in transaction of the format <amt><coin>,<amt2><coin2>,... (eg: 1btc,2gold,5silver)"},
|
||||
{&gasFlag, "gas", 0, "The amount of gas for the transaction"},
|
||||
{&feeFlag, "fee", "", "Coins for the transaction fee of the format <amt><coin>"},
|
||||
{&feeFlag, "fee", "0coin", "Coins for the transaction fee of the format <amt><coin>"},
|
||||
{&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 {
|
||||
|
||||
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
|
||||
to, err := hex.DecodeString(StripHex(toFlag))
|
||||
to, err := hex.DecodeString(StripHex(toHex))
|
||||
if err != nil {
|
||||
return errors.Errorf("To address is invalid hex: %v\n", err)
|
||||
}
|
||||
|
||||
if chainPrefix != "" {
|
||||
to = []byte(chainPrefix + "/" + string(to))
|
||||
}
|
||||
|
||||
// load the priv key
|
||||
privKey, err := LoadKey(fromFlag)
|
||||
if err != nil {
|
||||
|
@ -123,7 +139,7 @@ func sendTxCmd(cmd *cobra.Command, args []string) error {
|
|||
|
||||
// sign that puppy
|
||||
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(string(wire.JSONBytes(tx)))
|
||||
|
@ -179,7 +195,7 @@ func AppTx(name string, data []byte) error {
|
|||
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(string(wire.JSONBytes(tx)))
|
||||
|
@ -194,23 +210,17 @@ func AppTx(name string, data []byte) error {
|
|||
|
||||
// broadcast the transaction to tendermint
|
||||
func broadcastTx(tx types.Tx) ([]byte, string, error) {
|
||||
|
||||
tmResult := new(ctypes.TMResult)
|
||||
uriClient := client.NewURIClient(txNodeFlag)
|
||||
|
||||
httpClient := client.NewHTTP(txNodeFlag, "/websocket")
|
||||
// Don't you hate having to do this?
|
||||
// How many times have I lost an hour over this trick?!
|
||||
txBytes := []byte(wire.BinaryBytes(struct {
|
||||
types.Tx `json:"unwrap"`
|
||||
}{tx}))
|
||||
|
||||
_, err := uriClient.Call("broadcast_tx_commit", map[string]interface{}{"tx": txBytes}, tmResult)
|
||||
res, err := httpClient.BroadcastTxCommit(txBytes)
|
||||
if err != nil {
|
||||
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 !res.CheckTx.Code.IsOK() {
|
||||
r := res.CheckTx
|
||||
|
@ -228,12 +238,12 @@ func broadcastTx(tx types.Tx) ([]byte, string, error) {
|
|||
// if the sequence flag is set, return it;
|
||||
// else, fetch the account by querying the app and return the sequence number
|
||||
func getSeq(address []byte) (int, error) {
|
||||
|
||||
if seqFlag >= 0 {
|
||||
return seqFlag, nil
|
||||
}
|
||||
|
||||
acc, err := getAcc(txNodeFlag, address)
|
||||
httpClient := client.NewHTTP(txNodeFlag, "/websocket")
|
||||
acc, err := getAccWithClient(httpClient, address)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
|
|
@ -3,52 +3,20 @@ package commands
|
|||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"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"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
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"
|
||||
client "github.com/tendermint/tendermint/rpc/client"
|
||||
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
|
||||
//RegisterFlags, and RegisterPersistentFlags. Ex:
|
||||
// flags := []Flag2Register{
|
||||
|
@ -130,31 +98,27 @@ func StripHex(s string) string {
|
|||
return s
|
||||
}
|
||||
|
||||
func Query(tmAddr string, key []byte) (*abci.ResponseQuery, error) {
|
||||
uriClient := client.NewURIClient(tmAddr)
|
||||
tmResult := new(ctypes.TMResult)
|
||||
|
||||
params := map[string]interface{}{
|
||||
"path": "/key",
|
||||
"data": key,
|
||||
"prove": true,
|
||||
func Query(tmAddr string, key []byte) (*abci.ResultQuery, error) {
|
||||
httpClient := client.NewHTTP(tmAddr, "/websocket")
|
||||
return queryWithClient(httpClient, key)
|
||||
}
|
||||
_, err := uriClient.Call("abci_query", params, tmResult)
|
||||
|
||||
func queryWithClient(httpClient *client.HTTP, key []byte) (*abci.ResultQuery, error) {
|
||||
res, err := httpClient.ABCIQuery("/key", key, true)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("Error calling /abci_query: %v", err)
|
||||
}
|
||||
res := (*tmResult).(*ctypes.ResultABCIQuery)
|
||||
if !res.Response.Code.IsOK() {
|
||||
return nil, errors.Errorf("Query got non-zero exit code: %v. %s", res.Response.Code, res.Response.Log)
|
||||
if !res.Code.IsOK() {
|
||||
return nil, errors.Errorf("Query got non-zero exit code: %v. %s", res.Code, res.Log)
|
||||
}
|
||||
return &res.Response, nil
|
||||
return res.ResultQuery, nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
response, err := Query(tmAddr, key)
|
||||
key := types.AccountKey(address)
|
||||
response, err := queryWithClient(httpClient, key)
|
||||
if err != nil {
|
||||
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) {
|
||||
tmResult := new(ctypes.TMResult)
|
||||
uriClient := client.NewURIClient(tmAddr)
|
||||
|
||||
method := "commit"
|
||||
_, err := uriClient.Call(method, map[string]interface{}{"height": height}, tmResult)
|
||||
httpClient := client.NewHTTP(tmAddr, "/websocket")
|
||||
res, err := httpClient.Commit(height)
|
||||
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 := resCommit.Header
|
||||
commit := resCommit.Commit
|
||||
header := res.Header
|
||||
commit := res.Commit
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/tendermint/basecoin/cmd/commands"
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
var RootCmd = &cobra.Command{
|
||||
Use: "counter",
|
||||
Short: "demo plugin for basecoin",
|
||||
}
|
||||
|
||||
RootCmd.AddCommand(
|
||||
commands.InitCmd,
|
||||
commands.StartCmd,
|
||||
commands.TxCmd,
|
||||
commands.QueryCmd,
|
||||
|
@ -21,8 +24,10 @@ func main() {
|
|||
commands.VerifyCmd,
|
||||
commands.BlockCmd,
|
||||
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 = ""
|
||||
fast_sync = true
|
||||
db_backend = "leveldb"
|
||||
log_level = "notice"
|
||||
log_level = "info"
|
||||
rpc_laddr = "tcp://0.0.0.0:46657"
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
{
|
||||
"amount": 10,
|
||||
"name": "",
|
||||
"pub_key": [
|
||||
1,
|
||||
"D6EBB92440CF375054AA59BCF0C99D596DEEDFFB2543CAE1BA1908B72CF9676A"
|
||||
]
|
||||
"pub_key": {
|
||||
"type": "ed25519",
|
||||
"data":"D6EBB92440CF375054AA59BCF0C99D596DEEDFFB2543CAE1BA1908B72CF9676A"
|
||||
}
|
||||
}
|
||||
],
|
||||
"app_options": {
|
||||
|
|
|
@ -1,16 +1 @@
|
|||
{
|
||||
"address": "EBB0B4A899973C524A6BB18A161056A55F590F41",
|
||||
"last_height": 0,
|
||||
"last_round": 0,
|
||||
"last_signature": null,
|
||||
"last_signbytes": "",
|
||||
"last_step": 0,
|
||||
"priv_key": [
|
||||
1,
|
||||
"5FFDC1EA5FA2CA4A0A5503C86D2D348C5B401AD80FAA1899508F1ED00D8982E8D6EBB92440CF375054AA59BCF0C99D596DEEDFFB2543CAE1BA1908B72CF9676A"
|
||||
],
|
||||
"pub_key": [
|
||||
1,
|
||||
"D6EBB92440CF375054AA59BCF0C99D596DEEDFFB2543CAE1BA1908B72CF9676A"
|
||||
]
|
||||
}
|
||||
{"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"}}
|
|
@ -7,5 +7,5 @@ node_laddr = "tcp://0.0.0.0:46656"
|
|||
seeds = ""
|
||||
fast_sync = true
|
||||
db_backend = "leveldb"
|
||||
log_level = "notice"
|
||||
log_level = "info"
|
||||
rpc_laddr = "tcp://0.0.0.0:46657"
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
{
|
||||
"amount": 10,
|
||||
"name": "",
|
||||
"pub_key": [
|
||||
1,
|
||||
"9A76DDE4CA4EE660C073D288DBE4F8A128F23857881A95F18167682D47E7058F"
|
||||
]
|
||||
"pub_key": {
|
||||
"type": "ed25519",
|
||||
"data": "9A76DDE4CA4EE660C073D288DBE4F8A128F23857881A95F18167682D47E7058F"
|
||||
}
|
||||
}
|
||||
],
|
||||
"app_options": {
|
||||
|
|
|
@ -1,16 +1 @@
|
|||
{
|
||||
"address": "D42CFCB9C42DF9A73143EEA89255D1DF027B6240",
|
||||
"last_height": 0,
|
||||
"last_round": 0,
|
||||
"last_signature": null,
|
||||
"last_signbytes": "",
|
||||
"last_step": 0,
|
||||
"priv_key": [
|
||||
1,
|
||||
"6353FAF4ADEB03EA496A9EAE5BE56C4C6A851CB705401788184FDC9198413C2C9A76DDE4CA4EE660C073D288DBE4F8A128F23857881A95F18167682D47E7058F"
|
||||
],
|
||||
"pub_key": [
|
||||
1,
|
||||
"9A76DDE4CA4EE660C073D288DBE4F8A128F23857881A95F18167682D47E7058F"
|
||||
]
|
||||
}
|
||||
{"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"}}
|
|
@ -4,7 +4,8 @@ set -e
|
|||
cd $GOPATH/src/github.com/tendermint/basecoin/demo
|
||||
|
||||
LOG_DIR="."
|
||||
TM_VERSION="v0.9.2"
|
||||
TM_VERSION="develop"
|
||||
#TM_VERSION="v0.10.0"
|
||||
|
||||
if [[ "$CIRCLECI" == "true" ]]; then
|
||||
# set log dir
|
||||
|
@ -23,6 +24,13 @@ fi
|
|||
|
||||
set -u
|
||||
|
||||
function ifExit() {
|
||||
if [[ "$?" != 0 ]]; then
|
||||
echo "FAIL"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function removeQuotes() {
|
||||
temp="${1%\"}"
|
||||
temp="${temp#\"}"
|
||||
|
@ -52,12 +60,12 @@ function waitForNode() {
|
|||
|
||||
function waitForBlock() {
|
||||
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
|
||||
while [ "$b2" == "$b1" ]; do
|
||||
echo "Waiting for node $addr to commit a block ..."
|
||||
sleep 1
|
||||
b2=`curl -s $addr/status | jq .result[1].latest_block_height`
|
||||
b2=`curl -s $addr/status | jq .result.latest_block_height`
|
||||
done
|
||||
}
|
||||
|
||||
|
@ -83,12 +91,16 @@ echo ""
|
|||
echo "... starting chains"
|
||||
echo ""
|
||||
# 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 &
|
||||
ifExit
|
||||
|
||||
# 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 &
|
||||
ifExit
|
||||
|
||||
echo ""
|
||||
echo "... waiting for chains to start"
|
||||
|
@ -105,19 +117,26 @@ echo "... registering chain1 on chain2"
|
|||
echo ""
|
||||
# register chain1 on chain2
|
||||
basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 register --ibc_chain_id $CHAIN_ID1 --genesis $BCHOME1/genesis.json
|
||||
ifExit
|
||||
|
||||
echo ""
|
||||
echo "... creating egress packet on chain1"
|
||||
echo ""
|
||||
# create a packet on chain1 destined for chain2
|
||||
PAYLOAD="DEADBEEF" #TODO
|
||||
basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS1 packet create --ibc_from $CHAIN_ID1 --to $CHAIN_ID2 --type coin --payload $PAYLOAD --ibc_sequence 1
|
||||
# send coins from chain1 to an address on chain2
|
||||
# TODO: dont hardcode the address
|
||||
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 "... querying for packet data"
|
||||
echo ""
|
||||
# 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)
|
||||
PACKET=$(echo $QUERY_RESULT | jq .value)
|
||||
PROOF=$(echo $QUERY_RESULT | jq .proof)
|
||||
|
@ -144,6 +163,7 @@ echo "... querying for block data"
|
|||
echo ""
|
||||
# get the header and commit for the height
|
||||
HEADER_AND_COMMIT=$(basecoin block $HEIGHT)
|
||||
ifExit
|
||||
HEADER=$(echo $HEADER_AND_COMMIT | jq .hex.header)
|
||||
HEADER=$(removeQuotes $HEADER)
|
||||
COMMIT=$(echo $HEADER_AND_COMMIT | jq .hex.commit)
|
||||
|
@ -158,18 +178,21 @@ echo "... updating state of chain1 on chain2"
|
|||
echo ""
|
||||
# update the state of chain1 on chain2
|
||||
basecoin tx ibc --amount 10mycoin $CHAIN_FLAGS2 update --header 0x$HEADER --commit 0x$COMMIT
|
||||
ifExit
|
||||
|
||||
echo ""
|
||||
echo "... posting packet from chain1 on chain2"
|
||||
echo ""
|
||||
# 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
|
||||
ifExit
|
||||
|
||||
echo ""
|
||||
echo "... checking if the packet is present on chain2"
|
||||
echo ""
|
||||
# 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 "DONE!"
|
||||
|
|
|
@ -98,7 +98,7 @@ make install
|
|||
make test
|
||||
```
|
||||
|
||||
Great! Now when I run `tendermint` I have the newest of the new, the develop branch! But please not that this branch is not considered production ready and may have issues. This should only be done if you want to develop code for the future and run locally.
|
||||
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.
|
||||
|
||||
|
@ -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
|
||||
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.
|
||||
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).
|
||||
|
||||
## 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,
|
||||
and the `GasPrice` is implicit.
|
||||
|
||||
In Tendermint, 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
|
||||
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
|
||||
plugin to control its execution. There is currently no means to pass `Fee` information
|
||||
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.
|
||||
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.
|
||||
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
|
||||
|
||||
In previous tutorials we learned the [basics of the `basecoin` CLI](/docs/guides/basecoin-basics)
|
||||
and [how to implement a plugin](/docs/guides/example-plugin).
|
||||
In previous tutorials we learned the [basics of the `basecoin` CLI](/docs/guide/basecoin-basics.md)
|
||||
and [how to implement a plugin](/docs/guide/basecoin-plugins.md).
|
||||
In this tutorial, we provide more details on using the `basecoin` tool.
|
||||
|
||||
# Data Directory
|
||||
|
|
|
@ -24,8 +24,6 @@ var (
|
|||
//Called during CLI initialization
|
||||
func init() {
|
||||
|
||||
commands.DefaultHome = ".basecoin-example-plugin"
|
||||
|
||||
//Set the Plugin Flags
|
||||
ExamplePluginTxCmd.Flags().BoolVar(&validFlag, "valid", false, "Set this to make transaction valid")
|
||||
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/tendermint/basecoin/cmd/commands"
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -27,6 +30,6 @@ func main() {
|
|||
commands.UnsafeResetAllCmd,
|
||||
)
|
||||
|
||||
//Run the root command
|
||||
commands.ExecuteWithDebug(RootCmd)
|
||||
cmd := cli.PrepareMainCmd(RootCmd, "BC", os.ExpandEnv("$HOME/.basecoin-example-plugin"))
|
||||
cmd.Execute()
|
||||
}
|
||||
|
|
|
@ -1,39 +1,92 @@
|
|||
hash: c6e5febc35b5fd1003066820defb8a089db048b407239dad9faf44553fdc15e8
|
||||
updated: 2017-04-21T12:55:42.7004558-04:00
|
||||
hash: 9d06ae13959cbb2835f5ae400a4b65e4bc329a567c949aec4aeab318c271da39
|
||||
updated: 2017-05-24T15:11:32.643553723+02:00
|
||||
imports:
|
||||
- name: github.com/bgentry/speakeasy
|
||||
version: 4aabc24848ce5fd31929f7d1e4ea74d3709c14cd
|
||||
- name: github.com/btcsuite/btcd
|
||||
version: 4b348c1d33373d672edd83fc576892d0e46686d2
|
||||
version: b8df516b4b267acf2de46be593a9d948d1d2c420
|
||||
subpackages:
|
||||
- btcec
|
||||
- name: github.com/btcsuite/fastsha256
|
||||
version: 637e656429416087660c84436a2a035d69d54e2e
|
||||
- name: github.com/BurntSushi/toml
|
||||
version: b26d9c308763d68093482582cea63d69be07a0f0
|
||||
- name: github.com/ebuchman/fail-test
|
||||
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
|
||||
version: 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82
|
||||
- name: github.com/golang/protobuf
|
||||
version: 2bba0603135d7d7f5cb73b2125beeda19c09f4ef
|
||||
version: 18c9bb3261723cd5401db4d0c9fbc5c3b6c70fe8
|
||||
subpackages:
|
||||
- proto
|
||||
- ptypes/any
|
||||
- name: github.com/golang/snappy
|
||||
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
|
||||
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
|
||||
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||
- name: github.com/jmhodges/levigo
|
||||
version: c42d9e0ca023e2198120196f842701bb4c55d7b9
|
||||
- name: github.com/mattn/go-colorable
|
||||
version: ded68f7a9561c023e790de24279db7ebf473ea80
|
||||
- name: github.com/mattn/go-isatty
|
||||
version: fc9e8d8ef48496124e79ae0df75490096eccf6fe
|
||||
- name: github.com/kr/logfmt
|
||||
version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0
|
||||
- name: github.com/magiconair/properties
|
||||
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
|
||||
version: ff09b135c25aae272398c51a07235b90a75aa4f0
|
||||
- name: github.com/spf13/afero
|
||||
version: 9be650865eab0c12963d8753212f4f9c66cdcf12
|
||||
subpackages:
|
||||
- mem
|
||||
- name: github.com/spf13/cast
|
||||
version: acbeb36b902d72a7a4c18e8f3241075e7ab763e4
|
||||
- name: github.com/spf13/cobra
|
||||
version: 10f6b9d7e1631a54ad07c5c0fb71c28a1abfd3c2
|
||||
version: 3454e0e28e69c1b8effa6b5123c8e4185e20d696
|
||||
- name: github.com/spf13/jwalterweatherman
|
||||
version: 8f07c835e5cc1450c082fe3a439cf87b0cbb2d99
|
||||
- name: github.com/spf13/pflag
|
||||
version: 2300d0f8576fe575f71aaa5b9bbe4e1b0dc2eb51
|
||||
version: e57e3eeb33f795204c1ca35f56c44f83227c6e66
|
||||
- name: github.com/spf13/viper
|
||||
version: 0967fc9aceab2ce9da34061253ac10fb99bba5b2
|
||||
- name: github.com/syndtr/goleveldb
|
||||
version: 8c81ea47d4c41a385645e133e15510fc6a2a74b4
|
||||
subpackages:
|
||||
|
@ -50,7 +103,7 @@ imports:
|
|||
- leveldb/table
|
||||
- leveldb/util
|
||||
- name: github.com/tendermint/abci
|
||||
version: 56e13d87f4e3ec1ea756957d6b23caa6ebcf0998
|
||||
version: 5dabeffb35c027d7087a12149685daa68989168b
|
||||
subpackages:
|
||||
- client
|
||||
- example/dummy
|
||||
|
@ -61,72 +114,80 @@ imports:
|
|||
subpackages:
|
||||
- edwards25519
|
||||
- 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
|
||||
version: 0ca2c6fdb0706001ca4c4b9b80c9f428e8cf39da
|
||||
- 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
|
||||
version: 438b16f1f84ef002d7408ecd6fc3a3974cbc9559
|
||||
subpackages:
|
||||
- flowrate
|
||||
- name: github.com/tendermint/go-logger
|
||||
version: cefb3a45c0bf3c493a04e9bcd9b1540528be59f2
|
||||
- name: github.com/tendermint/go-merkle
|
||||
version: 714d4d04557fd068a7c2a1748241ce8428015a96
|
||||
- name: github.com/tendermint/go-p2p
|
||||
version: 17124989a93774833df33107fbf17157a7f8ef31
|
||||
subpackages:
|
||||
- upnp
|
||||
- name: github.com/tendermint/go-rpc
|
||||
version: 559613689d56eaa423b19a3a4158546beb4857de
|
||||
subpackages:
|
||||
- client
|
||||
- server
|
||||
- types
|
||||
- cmd
|
||||
- keys
|
||||
- keys/cryptostore
|
||||
- keys/server
|
||||
- keys/server/types
|
||||
- keys/storage/filestorage
|
||||
- name: github.com/tendermint/go-wire
|
||||
version: c1c9a57ab8038448ddea1714c0698f8051e5748c
|
||||
- name: github.com/tendermint/log15
|
||||
version: ae0f3d6450da9eac7074b439c8e1c3cabf0d5ce6
|
||||
version: 97beaedf0f4dbc035309157c92be3b30cc6e5d74
|
||||
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
|
||||
version: 9fb76efa5aebe773a598f97e68e75fe53d520e70
|
||||
version: c722818b460381bc5b82e38c73ff6e22a9df624d
|
||||
subpackages:
|
||||
- app
|
||||
- client
|
||||
- iavl
|
||||
- name: github.com/tendermint/tendermint
|
||||
version: 6bcd4242f1f336e2b2ef4f644fabaf56222b34d0
|
||||
version: 11b5d11e9eec170e1d3dce165f0270d5c0759d69
|
||||
subpackages:
|
||||
- blockchain
|
||||
- cmd/tendermint/commands
|
||||
- config/tendermint
|
||||
- cmd/tendermint/commands/flags
|
||||
- config
|
||||
- consensus
|
||||
- mempool
|
||||
- node
|
||||
- p2p
|
||||
- p2p/upnp
|
||||
- proxy
|
||||
- rpc/client
|
||||
- rpc/core
|
||||
- rpc/core/types
|
||||
- rpc/grpc
|
||||
- rpc/lib
|
||||
- rpc/lib/client
|
||||
- rpc/lib/server
|
||||
- rpc/lib/types
|
||||
- state
|
||||
- state/txindex
|
||||
- state/txindex/kv
|
||||
- state/txindex/null
|
||||
- types
|
||||
- version
|
||||
- name: github.com/tendermint/tmlibs
|
||||
version: 8af1c70a8be17543eb33e9bfbbcdd8371e3201cc
|
||||
subpackages:
|
||||
- autofile
|
||||
- cli
|
||||
- clist
|
||||
- common
|
||||
- db
|
||||
- events
|
||||
- flowrate
|
||||
- log
|
||||
- logger
|
||||
- merkle
|
||||
- name: golang.org/x/crypto
|
||||
version: 96846453c37f0876340a66a47f3f75b1f3a6cd2d
|
||||
version: c7af5bf2638a1164f2eb5467c39c6cffbd13a02e
|
||||
subpackages:
|
||||
- curve25519
|
||||
- nacl/box
|
||||
|
@ -137,7 +198,7 @@ imports:
|
|||
- ripemd160
|
||||
- salsa20/salsa
|
||||
- name: golang.org/x/net
|
||||
version: c8c74377599bd978aee1cf3b9b63a8634051cec2
|
||||
version: feeb485667d1fdabe727840fe00adc22431bc86e
|
||||
subpackages:
|
||||
- context
|
||||
- http2
|
||||
|
@ -147,11 +208,11 @@ imports:
|
|||
- lex/httplex
|
||||
- trace
|
||||
- name: golang.org/x/sys
|
||||
version: ea9bcade75cb975a0b9738936568ab388b845617
|
||||
version: 9c9d83fe39ed3fd2d9249fcf6b755891fff54b03
|
||||
subpackages:
|
||||
- unix
|
||||
- name: golang.org/x/text
|
||||
version: 19e3104b43db45fca0303f489a9536087b184802
|
||||
version: 470f45bf29f4147d6fbd7dfd0a02a848e49f5bf4
|
||||
subpackages:
|
||||
- secure/bidirule
|
||||
- transform
|
||||
|
@ -162,10 +223,11 @@ imports:
|
|||
subpackages:
|
||||
- googleapis/rpc/status
|
||||
- name: google.golang.org/grpc
|
||||
version: 6914ab1e338c92da4218a23d27fcd03d0ad78d46
|
||||
version: 844f573616520565fdc6fb4db242321b5456fd6d
|
||||
subpackages:
|
||||
- codes
|
||||
- credentials
|
||||
- grpclb/grpc_lb_v1
|
||||
- grpclog
|
||||
- internal
|
||||
- keepalive
|
||||
|
@ -176,6 +238,10 @@ imports:
|
|||
- status
|
||||
- tap
|
||||
- transport
|
||||
- name: gopkg.in/go-playground/validator.v9
|
||||
version: 6d8c18553ea1ac493d049edd6f102f52e618f085
|
||||
- name: gopkg.in/yaml.v2
|
||||
version: cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b
|
||||
testImports:
|
||||
- name: github.com/davecgh/go-spew
|
||||
version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9
|
||||
|
|
61
glide.yaml
61
glide.yaml
|
@ -1,24 +1,53 @@
|
|||
package: github.com/tendermint/basecoin
|
||||
import:
|
||||
- package: github.com/tendermint/go-common
|
||||
version: develop
|
||||
- package: github.com/gorilla/websocket
|
||||
- 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
|
||||
version: develop
|
||||
- package: github.com/tendermint/go-events
|
||||
version: develop
|
||||
- package: github.com/tendermint/go-logger
|
||||
version: develop
|
||||
- package: github.com/tendermint/go-data
|
||||
version: develop
|
||||
- package: github.com/tendermint/go-rpc
|
||||
version: develop
|
||||
subpackages:
|
||||
- cmd
|
||||
- keys
|
||||
- package: github.com/tendermint/go-wire
|
||||
subpackages:
|
||||
- data
|
||||
- package: github.com/tendermint/light-client
|
||||
version: develop
|
||||
subpackages:
|
||||
- commands
|
||||
- commands/proofs
|
||||
- commands/seeds
|
||||
- commands/txs
|
||||
- proofs
|
||||
- package: github.com/tendermint/merkleeyes
|
||||
version: develop
|
||||
subpackages:
|
||||
- client
|
||||
- iavl
|
||||
- package: github.com/tendermint/tendermint
|
||||
version: develop
|
||||
- package: github.com/tendermint/abci
|
||||
version: develop
|
||||
- package: github.com/gorilla/websocket
|
||||
version: v1.1.0
|
||||
subpackages:
|
||||
- config
|
||||
- node
|
||||
- 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"
|
||||
"github.com/tendermint/basecoin/app"
|
||||
"github.com/tendermint/basecoin/types"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
"github.com/tendermint/go-wire"
|
||||
eyescli "github.com/tendermint/merkleeyes/client"
|
||||
)
|
||||
|
@ -52,8 +51,7 @@ func TestCounterPlugin(t *testing.T) {
|
|||
// Sign request
|
||||
signBytes := tx.SignBytes(chainID)
|
||||
// t.Logf("Sign bytes: %X\n", signBytes)
|
||||
sig := test1PrivAcc.Sign(signBytes)
|
||||
tx.Input.Signature = crypto.SignatureS{sig}
|
||||
tx.Input.Signature = test1PrivAcc.Sign(signBytes)
|
||||
// t.Logf("Signed TX bytes: %X\n", wire.BinaryBytes(struct{ types.Tx }{tx}))
|
||||
|
||||
// Write request
|
||||
|
|
|
@ -2,15 +2,19 @@ package ibc
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
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"
|
||||
merkle "github.com/tendermint/merkleeyes/iavl"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
|
||||
"github.com/tendermint/basecoin/types"
|
||||
tm "github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
|
@ -51,8 +55,108 @@ type Packet struct {
|
|||
SrcChainID string
|
||||
DstChainID string
|
||||
Sequence uint64
|
||||
Type string
|
||||
Payload []byte
|
||||
Type string // redundant now that Type() is a method on Payload ?
|
||||
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
|
||||
|
||||
// Parse genesis
|
||||
var chainGenDoc = &tm.GenesisDoc{}
|
||||
var err error
|
||||
wire.ReadJSONPtr(&chainGenDoc, []byte(chainGen.Genesis), &err)
|
||||
chainGenDoc := new(tm.GenesisDoc)
|
||||
err := json.Unmarshal([]byte(chainGen.Genesis), chainGenDoc)
|
||||
if err != nil {
|
||||
sm.res.Code = IBCCodeEncodingError
|
||||
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"
|
||||
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(sm.store, packetKey, packet)
|
||||
|
||||
// set the sequence number
|
||||
SetSequenceNumber(sm.store, packet.SrcChainID, packet.DstChainID, packet.Sequence)
|
||||
}
|
||||
|
||||
func (sm *IBCStateMachine) runPacketPostTx(tx IBCPacketPostTx) {
|
||||
|
@ -326,7 +449,7 @@ func (sm *IBCStateMachine) runPacketPostTx(tx IBCPacketPostTx) {
|
|||
return
|
||||
}
|
||||
|
||||
// Save new Packet
|
||||
// Save new Packet (just for fun)
|
||||
save(sm.store, packetKeyIngress, packet)
|
||||
|
||||
// Load Header and make sure it exists
|
||||
|
@ -355,10 +478,24 @@ func (sm *IBCStateMachine) runPacketPostTx(tx IBCPacketPostTx) {
|
|||
ok := proof.Verify(packetKeyEgress, packetBytes, header.AppHash)
|
||||
if !ok {
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
|
@ -2,18 +2,22 @@ package ibc
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
"github.com/tendermint/basecoin/types"
|
||||
cmn "github.com/tendermint/go-common"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
"github.com/tendermint/go-merkle"
|
||||
"github.com/tendermint/go-wire"
|
||||
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"
|
||||
)
|
||||
|
||||
|
@ -30,7 +34,7 @@ func genGenesisDoc(chainID string, numVals int) (*tm.GenesisDoc, []types.PrivAcc
|
|||
name := cmn.Fmt("%v_val_%v", chainID, i)
|
||||
privAcc := types.PrivAccountFromSecret(name)
|
||||
genDoc.Validators = append(genDoc.Validators, tm.GenesisValidator{
|
||||
PubKey: privAcc.PubKey.PubKey,
|
||||
PubKey: privAcc.PubKey,
|
||||
Amount: 1,
|
||||
Name: name,
|
||||
})
|
||||
|
@ -64,23 +68,65 @@ func (pas PrivAccountsByAddress) Swap(i, j int) {
|
|||
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
func TestIBCPlugin(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
var testGenesisDoc = `{
|
||||
"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)
|
||||
store := types.NewKVCache(eyesClient)
|
||||
store.SetLogging() // Log all activity
|
||||
|
||||
ibcPlugin := New()
|
||||
ctx := types.CallContext{
|
||||
CallerAddress: nil,
|
||||
CallerAccount: nil,
|
||||
Coins: types.Coins{},
|
||||
}
|
||||
ctx := types.NewCallContext(nil, nil, types.Coins{})
|
||||
|
||||
chainID_1 := "test_chain"
|
||||
genDoc_1, privAccs_1 := genGenesisDoc(chainID_1, 4)
|
||||
genDocJSON_1 := wire.JSONBytesPretty(genDoc_1)
|
||||
genDoc_1, _ := genGenesisDoc(chainID_1, 4)
|
||||
genDocJSON_1, err := json.Marshal(genDoc_1)
|
||||
require.Nil(err)
|
||||
|
||||
// Register a malformed chain
|
||||
res := ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCRegisterChainTx{
|
||||
|
@ -89,20 +135,10 @@ func TestIBCPlugin(t *testing.T) {
|
|||
Genesis: "<THIS IS NOT JSON>",
|
||||
},
|
||||
}}))
|
||||
assert.Equal(IBCCodeEncodingError, res.Code)
|
||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||
store.ClearLogLines()
|
||||
assertAndLog(t, store, res, IBCCodeEncodingError)
|
||||
|
||||
// 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()
|
||||
registerChain(t, ibcPlugin, store, ctx, "test_chain", string(genDocJSON_1))
|
||||
|
||||
// Duplicate request fails
|
||||
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCRegisterChainTx{
|
||||
|
@ -111,74 +147,82 @@ func TestIBCPlugin(t *testing.T) {
|
|||
Genesis: string(genDocJSON_1),
|
||||
},
|
||||
}}))
|
||||
assert.Equal(IBCCodeChainAlreadyExists, res.Code, res.Log)
|
||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||
store.ClearLogLines()
|
||||
assertAndLog(t, store, res, IBCCodeChainAlreadyExists)
|
||||
}
|
||||
|
||||
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)
|
||||
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{
|
||||
packet := NewPacket("test_chain", "dst_chain", 0, DataPayload([]byte("hello world")))
|
||||
res := ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCPacketCreateTx{
|
||||
Packet: packet,
|
||||
}}))
|
||||
assert.Equal(abci.CodeType_OK, res.Code, res.Log)
|
||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||
store.ClearLogLines()
|
||||
assertAndLog(t, store, res, abci.CodeType_OK)
|
||||
|
||||
// Post a duplicate packet
|
||||
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCPacketCreateTx{
|
||||
Packet: packet,
|
||||
}}))
|
||||
assert.Equal(IBCCodePacketAlreadyExists, res.Code, res.Log)
|
||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||
store.ClearLogLines()
|
||||
assertAndLog(t, store, res, IBCCodePacketAlreadyExists)
|
||||
}
|
||||
|
||||
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.
|
||||
store.Sync()
|
||||
resCommit := eyesClient.CommitSync()
|
||||
appHash := resCommit.Data
|
||||
header := tm.Header{
|
||||
ChainID: "test_chain",
|
||||
Height: 999,
|
||||
AppHash: appHash,
|
||||
ValidatorsHash: []byte("must_exist"), // TODO make optional
|
||||
}
|
||||
header := newHeader("test_chain", 999, appHash, []byte("must_exist"))
|
||||
|
||||
// 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
|
||||
}
|
||||
commit := constructCommit(privAccs_1, header)
|
||||
|
||||
// Update a chain
|
||||
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCUpdateChainTx{
|
||||
Header: header,
|
||||
Commit: commit,
|
||||
}}))
|
||||
assert.Equal(abci.CodeType_OK, res.Code, res.Log)
|
||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||
store.ClearLogLines()
|
||||
assertAndLog(t, store, res, abci.CodeType_OK)
|
||||
|
||||
// Get proof for the packet
|
||||
packetKey := toKey(_IBC, _EGRESS,
|
||||
|
@ -192,7 +236,7 @@ func TestIBCPlugin(t *testing.T) {
|
|||
Prove: true,
|
||||
})
|
||||
assert.Nil(err)
|
||||
var proof *merkle.IAVLProof
|
||||
var proof *iavl.IAVLProof
|
||||
err = wire.ReadBinaryBytes(resQuery.Proof, &proof)
|
||||
assert.Nil(err)
|
||||
|
||||
|
@ -203,169 +247,74 @@ func TestIBCPlugin(t *testing.T) {
|
|||
Packet: packet,
|
||||
Proof: proof,
|
||||
}}))
|
||||
assert.Equal(abci.CodeType_OK, res.Code, res.Log)
|
||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||
store.ClearLogLines()
|
||||
assertAndLog(t, store, res, abci.CodeType_OK)
|
||||
}
|
||||
|
||||
func TestIBCPluginBadCommit(t *testing.T) {
|
||||
func TestIBCPluginPayloadCoins(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.CallContext{
|
||||
CallerAddress: nil,
|
||||
CallerAccount: nil,
|
||||
Coins: types.Coins{},
|
||||
coins := types.Coins{
|
||||
types.Coin{
|
||||
Denom: "mycoin",
|
||||
Amount: 100,
|
||||
},
|
||||
}
|
||||
ctx := types.NewCallContext(nil, nil, coins)
|
||||
|
||||
chainID_1 := "test_chain"
|
||||
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
|
||||
res := ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCRegisterChainTx{
|
||||
BlockchainGenesis{
|
||||
ChainID: "test_chain",
|
||||
Genesis: string(genDocJSON_1),
|
||||
},
|
||||
// Register a chain
|
||||
registerChain(t, ibcPlugin, store, ctx, "test_chain", string(genDocJSON_1))
|
||||
|
||||
// send coins to this addr on the other chain
|
||||
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)
|
||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||
store.ClearLogLines()
|
||||
assertAndLog(t, store, res, abci.CodeType_InsufficientFunds)
|
||||
|
||||
// Construct a Header
|
||||
header := tm.Header{
|
||||
ChainID: "test_chain",
|
||||
Height: 999,
|
||||
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"),
|
||||
}
|
||||
// Send a small enough number of coins
|
||||
packet = NewPacket("test_chain", "dst_chain", 0, CoinsPayload{
|
||||
Address: destinationAddr,
|
||||
Coins: coinsGood,
|
||||
})
|
||||
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCPacketCreateTx{
|
||||
Packet: packet,
|
||||
}}))
|
||||
assert.Equal(abci.CodeType_OK, res.Code, res.Log)
|
||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||
store.ClearLogLines()
|
||||
assertAndLog(t, store, res, abci.CodeType_OK)
|
||||
|
||||
// Construct a Header that includes the above packet.
|
||||
store.Sync()
|
||||
resCommit := eyesClient.CommitSync()
|
||||
appHash := resCommit.Data
|
||||
header := tm.Header{
|
||||
ChainID: "test_chain",
|
||||
Height: 999,
|
||||
AppHash: appHash,
|
||||
ValidatorsHash: []byte("must_exist"), // TODO make optional
|
||||
}
|
||||
header := newHeader("test_chain", 999, appHash, []byte("must_exist"))
|
||||
|
||||
// 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
|
||||
}
|
||||
commit := constructCommit(privAccs_1, header)
|
||||
|
||||
// Update a chain
|
||||
res = ibcPlugin.RunTx(store, ctx, wire.BinaryBytes(struct{ IBCTx }{IBCUpdateChainTx{
|
||||
Header: header,
|
||||
Commit: commit,
|
||||
}}))
|
||||
assert.Equal(abci.CodeType_OK, res.Code, res.Log)
|
||||
t.Log(">>", strings.Join(store.GetLogLines(), "\n"))
|
||||
store.ClearLogLines()
|
||||
assertAndLog(t, store, res, abci.CodeType_OK)
|
||||
|
||||
// Get proof for the packet
|
||||
packetKey := toKey(_IBC, _EGRESS,
|
||||
|
@ -379,7 +328,120 @@ func TestIBCPluginBadProof(t *testing.T) {
|
|||
Prove: true,
|
||||
})
|
||||
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)
|
||||
assert.Nil(err)
|
||||
|
||||
|
@ -393,7 +455,58 @@ func TestIBCPluginBadProof(t *testing.T) {
|
|||
Packet: packet,
|
||||
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"))
|
||||
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"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
cmn "github.com/tendermint/go-common"
|
||||
"github.com/tendermint/go-rpc/client"
|
||||
"github.com/tendermint/go-rpc/types"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
"github.com/tendermint/tendermint/rpc/lib/client"
|
||||
"github.com/tendermint/tendermint/rpc/lib/types"
|
||||
"github.com/tendermint/go-wire"
|
||||
_ "github.com/tendermint/tendermint/rpc/core/types" // Register RPCResponse > Result types
|
||||
)
|
||||
|
|
|
@ -2,14 +2,15 @@ package state
|
|||
|
||||
import (
|
||||
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"
|
||||
cmn "github.com/tendermint/go-common"
|
||||
"github.com/tendermint/go-events"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
|
||||
chainID := state.GetChainID()
|
||||
|
||||
// Exec tx
|
||||
|
@ -95,11 +96,11 @@ func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc e
|
|||
signBytes := tx.SignBytes(chainID)
|
||||
res = validateInputAdvanced(inAcc, signBytes, tx.Input)
|
||||
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()")
|
||||
}
|
||||
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}))
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
if res.IsOK() {
|
||||
cache.CacheSync()
|
||||
log.Info("Successful execution")
|
||||
state.logger.Info("Successful execution")
|
||||
// Fire events
|
||||
/*
|
||||
if evc != nil {
|
||||
|
@ -144,7 +145,7 @@ func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc e
|
|||
}
|
||||
*/
|
||||
} else {
|
||||
log.Info("AppTx failed", "error", res)
|
||||
state.logger.Info("AppTx failed", "error", res)
|
||||
// Just return the coins and return.
|
||||
inAccCopy.Balance = inAccCopy.Balance.Plus(coins)
|
||||
// But take the gas
|
||||
|
@ -190,17 +191,23 @@ func getOrMakeOutputs(state types.AccountGetter, accounts map[string]*types.Acco
|
|||
}
|
||||
|
||||
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
|
||||
if _, ok := accounts[string(out.Address)]; ok {
|
||||
if _, ok := accounts[string(outAddress)]; ok {
|
||||
return nil, abci.ErrBaseDuplicateAddress
|
||||
}
|
||||
acc := state.GetAccount(out.Address)
|
||||
acc := state.GetAccount(outAddress)
|
||||
// output account may be nil (new)
|
||||
if acc == nil {
|
||||
// zero value is valid, empty account
|
||||
acc = &types.Account{}
|
||||
}
|
||||
accounts[string(out.Address)] = acc
|
||||
accounts[string(outAddress)] = acc
|
||||
}
|
||||
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))
|
||||
}
|
||||
// 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.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 {
|
||||
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 {
|
||||
cmn.PanicSanity("adjustByOutputs() expects account in accounts")
|
||||
}
|
||||
acc.Balance = acc.Balance.Plus(out.Coins)
|
||||
if !isCheckTx {
|
||||
state.SetAccount(out.Address, acc)
|
||||
state.SetAccount(outAddress, acc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,9 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
"github.com/tendermint/basecoin/plugins/ibc"
|
||||
"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...)
|
||||
}
|
||||
|
||||
// make tx from accsIn to et.accOut
|
||||
func (et *execTest) getTx(seq int, accOut types.PrivAccount, accsIn ...types.PrivAccount) *types.SendTx {
|
||||
return types.GetTx(seq, accOut, accsIn...)
|
||||
}
|
||||
|
||||
// returns the final balance and expected balance for input and output accounts
|
||||
func (et *execTest) exec(tx *types.SendTx, checkTx bool) (res abci.Result, inGot, inExp, outGot, outExp types.Coins) {
|
||||
initBalIn := et.state.GetAccount(et.accIn.Account.PubKey.Address()).Balance
|
||||
|
@ -63,6 +61,7 @@ func (et *execTest) reset() {
|
|||
|
||||
et.store = types.NewMemKVStore()
|
||||
et.state = NewState(et.store)
|
||||
et.state.SetLogger(log.TestingLogger())
|
||||
et.state.SetChainID(et.chainID)
|
||||
|
||||
// NOTE we dont run acc2State here
|
||||
|
@ -83,19 +82,19 @@ func TestGetInputs(t *testing.T) {
|
|||
|
||||
//test getInputs for registered, non-registered account
|
||||
et.reset()
|
||||
txs := types.Accs2TxInputs(1, et.accIn)
|
||||
acc, res = getInputs(et.state, txs)
|
||||
inputs := types.Accs2TxInputs(1, et.accIn)
|
||||
acc, res = getInputs(et.state, inputs)
|
||||
assert.True(res.IsErr(), "getInputs: expected error when using getInput with non-registered Input")
|
||||
|
||||
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")
|
||||
|
||||
//test sending duplicate accounts
|
||||
et.reset()
|
||||
et.acc2State(et.accIn, et.accIn, et.accIn)
|
||||
txs = types.Accs2TxInputs(1, et.accIn, et.accIn, et.accIn)
|
||||
acc, res = getInputs(et.state, txs)
|
||||
inputs = types.Accs2TxInputs(1, et.accIn, et.accIn, et.accIn)
|
||||
acc, res = getInputs(et.state, inputs)
|
||||
assert.True(res.IsErr(), "getInputs: expected error when sending duplicate accounts")
|
||||
}
|
||||
|
||||
|
@ -110,24 +109,24 @@ func TestGetOrMakeOutputs(t *testing.T) {
|
|||
|
||||
//test sending duplicate accounts
|
||||
et.reset()
|
||||
txs := types.Accs2TxOutputs(et.accIn, et.accIn, et.accIn)
|
||||
_, res = getOrMakeOutputs(et.state, nil, txs)
|
||||
outputs := types.Accs2TxOutputs(et.accIn, et.accIn, et.accIn)
|
||||
_, res = getOrMakeOutputs(et.state, nil, outputs)
|
||||
assert.True(res.IsErr(), "getOrMakeOutputs: expected error when sending duplicate accounts")
|
||||
|
||||
//test sending to existing/new account
|
||||
et.reset()
|
||||
txs1 := types.Accs2TxOutputs(et.accIn)
|
||||
txs2 := types.Accs2TxOutputs(et.accOut)
|
||||
outputs1 := types.Accs2TxOutputs(et.accIn)
|
||||
outputs2 := types.Accs2TxOutputs(et.accOut)
|
||||
|
||||
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")
|
||||
|
||||
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")
|
||||
|
||||
//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")
|
||||
|
||||
}
|
||||
|
@ -137,12 +136,12 @@ func TestValidateInputsBasic(t *testing.T) {
|
|||
et := newExecTest()
|
||||
|
||||
//validate input basic
|
||||
txs := types.Accs2TxInputs(1, et.accIn)
|
||||
res := validateInputsBasic(txs)
|
||||
inputs := types.Accs2TxInputs(1, et.accIn)
|
||||
res := validateInputsBasic(inputs)
|
||||
assert.True(res.IsOK(), "validateInputsBasic: expected no error on good tx input. Error: %v", res.Error())
|
||||
|
||||
txs[0].Coins[0].Amount = 0
|
||||
res = validateInputsBasic(txs)
|
||||
inputs[0].Coins[0].Amount = 0
|
||||
res = validateInputsBasic(inputs)
|
||||
assert.True(res.IsErr(), "validateInputsBasic: expected error on bad tx input")
|
||||
|
||||
}
|
||||
|
@ -157,28 +156,28 @@ func TestValidateInputsAdvanced(t *testing.T) {
|
|||
accIn3 := types.MakeAcc("fooz")
|
||||
|
||||
//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)
|
||||
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())
|
||||
signBytes := txs.SignBytes(et.chainID)
|
||||
signBytes := tx.SignBytes(et.chainID)
|
||||
|
||||
//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")
|
||||
|
||||
//test good case sgined
|
||||
et.signTx(txs, accIn1, accIn2, accIn3, et.accOut)
|
||||
totalCoins, res = validateInputsAdvanced(accMap, signBytes, txs.Inputs)
|
||||
et.signTx(tx, accIn1, accIn2, accIn3, et.accOut)
|
||||
totalCoins, res = validateInputsAdvanced(accMap, signBytes, tx.Inputs)
|
||||
assert.True(res.IsOK(), "validateInputsAdvanced: expected no error on good tx input. Error: %v", res.Error())
|
||||
|
||||
txsTotalCoins := txs.Inputs[0].Coins.
|
||||
Plus(txs.Inputs[1].Coins).
|
||||
Plus(txs.Inputs[2].Coins)
|
||||
txTotalCoins := tx.Inputs[0].Coins.
|
||||
Plus(tx.Inputs[1].Coins).
|
||||
Plus(tx.Inputs[2].Coins)
|
||||
|
||||
assert.True(totalCoins.IsEqual(txsTotalCoins),
|
||||
"ValidateInputsAdvanced: transaction total coins are not equal: got %v, expected %v", txsTotalCoins, totalCoins)
|
||||
assert.True(totalCoins.IsEqual(txTotalCoins),
|
||||
"ValidateInputsAdvanced: transaction total coins are not equal: got %v, expected %v", txTotalCoins, totalCoins)
|
||||
}
|
||||
|
||||
func TestValidateInputAdvanced(t *testing.T) {
|
||||
|
@ -186,31 +185,31 @@ func TestValidateInputAdvanced(t *testing.T) {
|
|||
et := newExecTest()
|
||||
|
||||
//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)
|
||||
signBytes := txs.SignBytes(et.chainID)
|
||||
signBytes := tx.SignBytes(et.chainID)
|
||||
|
||||
//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")
|
||||
|
||||
//good signed case
|
||||
et.signTx(txs, et.accIn, et.accOut)
|
||||
res = validateInputAdvanced(&et.accIn.Account, signBytes, txs.Inputs[0])
|
||||
et.signTx(tx, et.accIn, et.accOut)
|
||||
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())
|
||||
|
||||
//bad sequence case
|
||||
et.accIn.Sequence = 1
|
||||
et.signTx(txs, et.accIn, et.accOut)
|
||||
res = validateInputAdvanced(&et.accIn.Account, signBytes, txs.Inputs[0])
|
||||
et.signTx(tx, et.accIn, et.accOut)
|
||||
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")
|
||||
et.accIn.Sequence = 0 //restore sequence
|
||||
|
||||
//bad balance case
|
||||
et.accIn.Balance = types.Coins{{"mycoin", 2}}
|
||||
et.signTx(txs, et.accIn, et.accOut)
|
||||
res = validateInputAdvanced(&et.accIn.Account, signBytes, txs.Inputs[0])
|
||||
et.signTx(tx, et.accIn, et.accOut)
|
||||
res = validateInputAdvanced(&et.accIn.Account, signBytes, tx.Inputs[0])
|
||||
assert.Equal(abci.CodeType_BaseInsufficientFunds, res.Code,
|
||||
"validateInputAdvanced: expected error on tx input with insufficient funds %v", et.accIn.Sequence)
|
||||
}
|
||||
|
@ -220,12 +219,12 @@ func TestValidateOutputsAdvanced(t *testing.T) {
|
|||
et := newExecTest()
|
||||
|
||||
//validateOutputsBasic
|
||||
txs := types.Accs2TxOutputs(et.accIn)
|
||||
res := validateOutputsBasic(txs)
|
||||
tx := types.Accs2TxOutputs(et.accIn)
|
||||
res := validateOutputsBasic(tx)
|
||||
assert.True(res.IsOK(), "validateOutputsBasic: expected no error on good tx output. Error: %v", res.Error())
|
||||
|
||||
txs[0].Coins[0].Amount = 0
|
||||
res = validateOutputsBasic(txs)
|
||||
tx[0].Coins[0].Amount = 0
|
||||
res = validateOutputsBasic(tx)
|
||||
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()
|
||||
|
||||
//SumOutput
|
||||
txs := types.Accs2TxOutputs(et.accIn, et.accOut)
|
||||
total := sumOutputs(txs)
|
||||
assert.True(total.IsEqual(txs[0].Coins.Plus(txs[1].Coins)), "sumOutputs: total coins are not equal")
|
||||
tx := types.Accs2TxOutputs(et.accIn, et.accOut)
|
||||
total := sumOutputs(tx)
|
||||
assert.True(total.IsEqual(tx[0].Coins.Plus(tx[1].Coins)), "sumOutputs: total coins are not equal")
|
||||
}
|
||||
|
||||
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)
|
||||
et := newExecTest()
|
||||
|
||||
//ExecTx
|
||||
txs := et.getTx(1, et.accOut, et.accIn)
|
||||
tx := types.MakeSendTx(1, et.accOut, et.accIn)
|
||||
et.acc2State(et.accIn)
|
||||
et.acc2State(et.accOut)
|
||||
et.signTx(txs, et.accIn)
|
||||
et.signTx(tx, et.accIn)
|
||||
|
||||
//Bad Balance
|
||||
et.accIn.Balance = types.Coins{{"mycoin", 2}}
|
||||
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)
|
||||
|
||||
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.False(balIn.IsEqual(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.acc2State(et.accIn)
|
||||
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)
|
||||
|
||||
//Regular DeliverTx
|
||||
et.reset()
|
||||
et.acc2State(et.accIn)
|
||||
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(balIn.IsEqual(balInExp),
|
||||
"ExecTx/good DeliverTx: unexpected change in input balance, got: %v, expected: %v", balIn, balInExp)
|
||||
assert.True(balOut.IsEqual(balOutExp),
|
||||
"ExecTx/good DeliverTx: unexpected change in output balance, got: %v, expected: %v", balOut, balOutExp)
|
||||
}
|
||||
|
||||
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 (
|
||||
abci "github.com/tendermint/abci/types"
|
||||
"github.com/tendermint/basecoin/types"
|
||||
. "github.com/tendermint/go-common"
|
||||
"github.com/tendermint/go-wire"
|
||||
eyes "github.com/tendermint/merkleeyes/client"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
)
|
||||
|
||||
// CONTRACT: State should be quick to copy.
|
||||
|
@ -15,6 +14,7 @@ type State struct {
|
|||
store types.KVStore
|
||||
readCache map[string][]byte // optional, for caching writes to store
|
||||
writeCache *types.KVCache // optional, for caching writes w/o writing to store
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func NewState(store types.KVStore) *State {
|
||||
|
@ -23,9 +23,14 @@ func NewState(store types.KVStore) *State {
|
|||
store: store,
|
||||
readCache: make(map[string][]byte),
|
||||
writeCache: nil,
|
||||
logger: log.NewNopLogger(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *State) SetLogger(l log.Logger) {
|
||||
s.logger = l
|
||||
}
|
||||
|
||||
func (s *State) SetChainID(chainID string) {
|
||||
s.chainID = 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 {
|
||||
return GetAccount(s, addr)
|
||||
return types.GetAccount(s, addr)
|
||||
}
|
||||
|
||||
func (s *State) SetAccount(addr []byte, acc *types.Account) {
|
||||
SetAccount(s, addr, acc)
|
||||
types.SetAccount(s, addr, acc)
|
||||
}
|
||||
|
||||
func (s *State) CacheWrap() *State {
|
||||
|
@ -71,6 +76,7 @@ func (s *State) CacheWrap() *State {
|
|||
store: cache,
|
||||
readCache: nil,
|
||||
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"
|
||||
eyes "github.com/tendermint/merkleeyes/client"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -16,6 +17,7 @@ func TestState(t *testing.T) {
|
|||
//States and Stores for tests
|
||||
store := types.NewMemKVStore()
|
||||
state := NewState(store)
|
||||
state.SetLogger(log.TestingLogger())
|
||||
cache := state.CacheWrap()
|
||||
eyesCli := eyes.NewLocalClient("", 0)
|
||||
|
||||
|
@ -29,12 +31,14 @@ func TestState(t *testing.T) {
|
|||
reset := func() {
|
||||
store = types.NewMemKVStore()
|
||||
state = NewState(store)
|
||||
state.SetLogger(log.TestingLogger())
|
||||
cache = state.CacheWrap()
|
||||
}
|
||||
|
||||
//set the state to using the eyesCli instead of MemKVStore
|
||||
useEyesCli := func() {
|
||||
state = NewState(eyesCli)
|
||||
state.SetLogger(log.TestingLogger())
|
||||
cache = state.CacheWrap()
|
||||
}
|
||||
|
||||
|
|
|
@ -6,12 +6,11 @@ import (
|
|||
|
||||
"github.com/gorilla/websocket"
|
||||
"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"
|
||||
_ "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() {
|
||||
|
@ -67,16 +66,18 @@ func main() {
|
|||
// Sign request
|
||||
signBytes := tx.SignBytes(chainID)
|
||||
sig := root.Sign(signBytes)
|
||||
tx.Inputs[0].Signature = crypto.SignatureS{sig}
|
||||
tx.Inputs[0].Signature = sig
|
||||
//fmt.Println("tx:", tx)
|
||||
|
||||
// Write request
|
||||
txBytes := wire.BinaryBytes(struct{ types.Tx }{tx})
|
||||
request := rpctypes.NewRPCRequest("fakeid", "broadcast_tx_sync", map[string]interface{}{"tx": txBytes})
|
||||
//request := rpctypes.NewRPCRequest("fakeid", "broadcast_tx_sync", map[string]interface{}{"tx": txBytes})
|
||||
request, 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)
|
||||
//fmt.Print(".")
|
||||
err := ws.WriteMessage(websocket.TextMessage, reqBytes)
|
||||
err = ws.WriteMessage(websocket.TextMessage, reqBytes)
|
||||
if err != nil {
|
||||
cmn.Exit("writing websocket request: " + err.Error())
|
||||
}
|
||||
|
@ -118,15 +119,18 @@ func main() {
|
|||
// Sign request
|
||||
signBytes := tx.SignBytes(chainID)
|
||||
sig := privAccountA.Sign(signBytes)
|
||||
tx.Inputs[0].Signature = crypto.SignatureS{sig}
|
||||
tx.Inputs[0].Signature = sig
|
||||
//fmt.Println("tx:", tx)
|
||||
|
||||
// Write request
|
||||
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)
|
||||
//fmt.Print(".")
|
||||
err := ws.WriteMessage(websocket.TextMessage, reqBytes)
|
||||
err = ws.WriteMessage(websocket.TextMessage, reqBytes)
|
||||
if err != nil {
|
||||
cmn.Exit("writing websocket request: " + err.Error())
|
||||
}
|
||||
|
|
|
@ -8,16 +8,17 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
"github.com/tendermint/basecoin/app"
|
||||
"github.com/tendermint/basecoin/types"
|
||||
cmn "github.com/tendermint/go-common"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
"github.com/tendermint/go-wire"
|
||||
wire "github.com/tendermint/go-wire"
|
||||
eyescli "github.com/tendermint/merkleeyes/client"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
)
|
||||
|
||||
func TestSendTx(t *testing.T) {
|
||||
eyesCli := eyescli.NewLocalClient("", 0)
|
||||
chainID := "test_chain_id"
|
||||
bcApp := app.NewBasecoin(eyesCli)
|
||||
bcApp.SetLogger(log.TestingLogger().With("module", "app"))
|
||||
bcApp.SetOption("base/chain_id", chainID)
|
||||
// t.Log(bcApp.Info())
|
||||
|
||||
|
@ -50,7 +51,7 @@ func TestSendTx(t *testing.T) {
|
|||
signBytes := tx.SignBytes(chainID)
|
||||
// t.Log("Sign bytes: %X\n", 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}))
|
||||
|
||||
// Write request
|
||||
|
@ -102,7 +103,7 @@ func TestSequence(t *testing.T) {
|
|||
// Sign request
|
||||
signBytes := tx.SignBytes(chainID)
|
||||
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)
|
||||
|
||||
// Write request
|
||||
|
@ -146,7 +147,7 @@ func TestSequence(t *testing.T) {
|
|||
// Sign request
|
||||
signBytes := tx.SignBytes(chainID)
|
||||
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)
|
||||
|
||||
// Write request
|
||||
|
|
|
@ -4,10 +4,11 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/tendermint/go-crypto"
|
||||
"github.com/tendermint/go-wire"
|
||||
)
|
||||
|
||||
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"`
|
||||
Balance Coins `json:"coins"`
|
||||
}
|
||||
|
@ -31,7 +32,7 @@ func (acc *Account) String() string {
|
|||
//----------------------------------------
|
||||
|
||||
type PrivAccount struct {
|
||||
crypto.PrivKeyS
|
||||
crypto.PrivKey
|
||||
Account
|
||||
}
|
||||
|
||||
|
@ -49,3 +50,26 @@ type AccountGetterSetter interface {
|
|||
GetAccount(addr []byte) *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 (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Coin struct {
|
||||
|
@ -17,22 +20,26 @@ func (coin Coin) String() string {
|
|||
}
|
||||
|
||||
//regex codes for extracting coins from string
|
||||
var reDenom = regexp.MustCompile("([^\\d\\W]+)")
|
||||
var reDenom = regexp.MustCompile("")
|
||||
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
|
||||
|
||||
if len(str) > 0 {
|
||||
amt, err := strconv.Atoi(reAmt.FindString(str))
|
||||
matches := reCoin.FindStringSubmatch(strings.TrimSpace(str))
|
||||
if matches == nil {
|
||||
return coin, errors.Errorf("%s is invalid coin definition", str)
|
||||
}
|
||||
|
||||
// parse the amount (should always parse properly)
|
||||
amt, err := strconv.Atoi(matches[1])
|
||||
if err != nil {
|
||||
return coin, err
|
||||
}
|
||||
denom := reDenom.FindString(str)
|
||||
coin = Coin{denom, int64(amt)}
|
||||
}
|
||||
|
||||
coin = Coin{matches[2], int64(amt)}
|
||||
return coin, nil
|
||||
}
|
||||
|
||||
|
@ -53,18 +60,26 @@ func (coins Coins) String() string {
|
|||
}
|
||||
|
||||
func ParseCoins(str string) (Coins, error) {
|
||||
// empty string is empty list...
|
||||
if len(str) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
split := strings.Split(str, ",")
|
||||
var coins []Coin
|
||||
var coins Coins
|
||||
|
||||
for _, el := range split {
|
||||
if len(el) > 0 {
|
||||
coin, err := ParseCoin(el)
|
||||
if err != nil {
|
||||
return coins, err
|
||||
}
|
||||
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
|
||||
|
@ -195,3 +210,10 @@ func (coins Coins) IsNonnegative() bool {
|
|||
}
|
||||
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"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCoins(t *testing.T) {
|
||||
|
@ -56,30 +55,85 @@ func TestCoins(t *testing.T) {
|
|||
|
||||
//Test the parse coin and parse coins functionality
|
||||
func TestParse(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
assert := assert.New(t)
|
||||
|
||||
makeCoin := func(str string) Coin {
|
||||
coin, err := ParseCoin(str)
|
||||
require.Nil(err)
|
||||
return coin
|
||||
cases := []struct {
|
||||
input string
|
||||
valid bool // if false, we expect an error on parse
|
||||
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 {
|
||||
coin, err := ParseCoins(str)
|
||||
require.Nil(err)
|
||||
return coin
|
||||
for _, tc := range cases {
|
||||
res, err := ParseCoins(tc.input)
|
||||
if !tc.valid {
|
||||
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")
|
||||
assert.Equal(Coin{"barCoin", 10}, makeCoin("10 barCoin"), "ParseCoin makes bad coins")
|
||||
|
||||
//testing ParseCoins Function
|
||||
assert.True(Coins{{"fooCoin", 1}}.IsEqual(makeCoins("1fooCoin")),
|
||||
"ParseCoins doesn't parse a single coin")
|
||||
assert.True(Coins{{"barCoin", 99}, {"fooCoin", 1}}.IsEqual(makeCoins("99barCoin,1fooCoin")),
|
||||
"ParseCoins doesn't properly parse two coins")
|
||||
assert.True(Coins{{"barCoin", 99}, {"fooCoin", 1}}.IsEqual(makeCoins("99 barCoin, 1 fooCoin")),
|
||||
"ParseCoins doesn't properly parse two coins which use spaces")
|
||||
}
|
||||
|
||||
func TestSortCoins(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
good := Coins{
|
||||
Coin{"GAS", 1},
|
||||
Coin{"MINERAL", 1},
|
||||
Coin{"TREE", 1},
|
||||
}
|
||||
empty := Coins{
|
||||
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"
|
||||
"fmt"
|
||||
|
||||
. "github.com/tendermint/go-common"
|
||||
. "github.com/tendermint/tmlibs/common"
|
||||
)
|
||||
|
||||
type KVStore interface {
|
||||
|
|
|
@ -3,18 +3,19 @@ package types
|
|||
// Helper functions for testing
|
||||
|
||||
import (
|
||||
cmn "github.com/tendermint/go-common"
|
||||
"github.com/tendermint/go-crypto"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
)
|
||||
|
||||
// Creates a PrivAccount from secret.
|
||||
// The amount is not set.
|
||||
func PrivAccountFromSecret(secret string) PrivAccount {
|
||||
privKey := crypto.GenPrivKeyEd25519FromSecret([]byte(secret))
|
||||
privKey :=
|
||||
crypto.GenPrivKeyEd25519FromSecret([]byte(secret)).Wrap()
|
||||
privAccount := PrivAccount{
|
||||
PrivKeyS: crypto.PrivKeyS{privKey},
|
||||
PrivKey: privKey,
|
||||
Account: Account{
|
||||
PubKey: crypto.PubKeyS{privKey.PubKey()},
|
||||
PubKey: privKey.PubKey(),
|
||||
},
|
||||
}
|
||||
return privAccount
|
||||
|
@ -30,10 +31,10 @@ func RandAccounts(num int, minAmount int64, maxAmount int64) []PrivAccount {
|
|||
balance += cmn.RandInt64() % (maxAmount - minAmount)
|
||||
}
|
||||
|
||||
privKey := crypto.GenPrivKeyEd25519()
|
||||
pubKey := crypto.PubKeyS{privKey.PubKey()}
|
||||
privKey := crypto.GenPrivKeyEd25519().Wrap()
|
||||
pubKey := privKey.PubKey()
|
||||
privAccs[i] = PrivAccount{
|
||||
PrivKeyS: crypto.PrivKeyS{privKey},
|
||||
PrivKey: privKey,
|
||||
Account: Account{
|
||||
PubKey: pubKey,
|
||||
Balance: Coins{Coin{"", balance}},
|
||||
|
@ -85,20 +86,20 @@ func Accs2TxOutputs(accs ...PrivAccount) []TxOutput {
|
|||
return txs
|
||||
}
|
||||
|
||||
func GetTx(seq int, accOut PrivAccount, accsIn ...PrivAccount) *SendTx {
|
||||
txs := &SendTx{
|
||||
func MakeSendTx(seq int, accOut PrivAccount, accsIn ...PrivAccount) *SendTx {
|
||||
tx := &SendTx{
|
||||
Gas: 0,
|
||||
Fee: Coin{"mycoin", 1},
|
||||
Inputs: Accs2TxInputs(seq, accsIn...),
|
||||
Outputs: Accs2TxOutputs(accOut),
|
||||
}
|
||||
|
||||
return txs
|
||||
return tx
|
||||
}
|
||||
|
||||
func SignTx(chainID string, tx *SendTx, accs ...PrivAccount) {
|
||||
signBytes := tx.SignBytes(chainID)
|
||||
for i, _ := range tx.Inputs {
|
||||
tx.Inputs[i].Signature = crypto.SignatureS{accs[i].Sign(signBytes)}
|
||||
tx.Inputs[i].Signature = accs[i].Sign(signBytes)
|
||||
}
|
||||
}
|
||||
|
|
71
types/tx.go
71
types/tx.go
|
@ -5,10 +5,10 @@ import (
|
|||
"encoding/json"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
. "github.com/tendermint/go-common"
|
||||
"github.com/tendermint/go-crypto"
|
||||
"github.com/tendermint/go-data"
|
||||
"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
|
||||
|
||||
// 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() {
|
||||
txMapper = data.NewMapper(TxS{}).
|
||||
RegisterImplementation(&SendTx{}, TxNameSend, TxTypeSend).
|
||||
|
@ -46,7 +46,7 @@ func init() {
|
|||
|
||||
// TxS add json serialization to Tx
|
||||
type TxS struct {
|
||||
Tx
|
||||
Tx `json:"unwrap"`
|
||||
}
|
||||
|
||||
func (p TxS) MarshalJSON() ([]byte, error) {
|
||||
|
@ -67,8 +67,8 @@ type TxInput struct {
|
|||
Address data.Bytes `json:"address"` // Hash of the PubKey
|
||||
Coins Coins `json:"coins"` //
|
||||
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
|
||||
PubKey crypto.PubKeyS `json:"pub_key"` // Is present iff Sequence == 0
|
||||
Signature crypto.Signature `json:"signature"` // Depends on the PubKey type and the whole Tx
|
||||
PubKey crypto.PubKey `json:"pub_key"` // Is present iff Sequence == 0
|
||||
}
|
||||
|
||||
func (txIn TxInput) ValidateBasic() abci.Result {
|
||||
|
@ -104,13 +104,7 @@ func NewTxInput(pubKey crypto.PubKey, coins Coins, sequence int) TxInput {
|
|||
Sequence: sequence,
|
||||
}
|
||||
if sequence == 1 {
|
||||
// safely wrap if needed
|
||||
// TODO: extract this as utility function?
|
||||
ps, ok := pubKey.(crypto.PubKeyS)
|
||||
if !ok {
|
||||
ps = crypto.PubKeyS{pubKey}
|
||||
}
|
||||
input.PubKey = ps
|
||||
input.PubKey = pubKey
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
@ -122,10 +116,33 @@ type TxOutput struct {
|
|||
Coins Coins `json:"coins"` //
|
||||
}
|
||||
|
||||
func (txOut TxOutput) ValidateBasic() abci.Result {
|
||||
if len(txOut.Address) != 20 {
|
||||
return abci.ErrBaseInvalidOutput.AppendLog("Invalid address length")
|
||||
// An output destined for another chain may be formatted as `chainID/address`.
|
||||
// ChainAndAddress returns the chainID prefix and the address.
|
||||
// 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() {
|
||||
return abci.ErrBaseInvalidOutput.AppendLog(Fmt("Invalid coins %v", txOut.Coins))
|
||||
}
|
||||
|
@ -151,25 +168,21 @@ type SendTx struct {
|
|||
func (tx *SendTx) SignBytes(chainID string) []byte {
|
||||
signBytes := wire.BinaryBytes(chainID)
|
||||
sigz := make([]crypto.Signature, len(tx.Inputs))
|
||||
for i, input := range tx.Inputs {
|
||||
sigz[i] = input.Signature.Signature
|
||||
tx.Inputs[i].Signature.Signature = nil
|
||||
for i := range tx.Inputs {
|
||||
sigz[i] = tx.Inputs[i].Signature
|
||||
tx.Inputs[i].Signature = crypto.Signature{}
|
||||
}
|
||||
signBytes = append(signBytes, wire.BinaryBytes(tx)...)
|
||||
for i := range tx.Inputs {
|
||||
tx.Inputs[i].Signature.Signature = sigz[i]
|
||||
tx.Inputs[i].Signature = sigz[i]
|
||||
}
|
||||
return signBytes
|
||||
}
|
||||
|
||||
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 {
|
||||
if bytes.Equal(input.Address, addr) {
|
||||
tx.Inputs[i].Signature = sigs
|
||||
tx.Inputs[i].Signature = sig
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -193,18 +206,14 @@ type AppTx struct {
|
|||
func (tx *AppTx) SignBytes(chainID string) []byte {
|
||||
signBytes := wire.BinaryBytes(chainID)
|
||||
sig := tx.Input.Signature
|
||||
tx.Input.Signature.Signature = nil
|
||||
tx.Input.Signature = crypto.Signature{}
|
||||
signBytes = append(signBytes, wire.BinaryBytes(tx)...)
|
||||
tx.Input.Signature = sig
|
||||
return signBytes
|
||||
}
|
||||
|
||||
func (tx *AppTx) SetSignature(sig crypto.Signature) bool {
|
||||
sigs, ok := sig.(crypto.SignatureS)
|
||||
if !ok {
|
||||
sigs = crypto.SignatureS{sig}
|
||||
}
|
||||
tx.Input.Signature = sigs
|
||||
tx.Input.Signature = sig
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,7 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
data "github.com/tendermint/go-data"
|
||||
data "github.com/tendermint/go-wire/data"
|
||||
)
|
||||
|
||||
var chainID string = "test_chain"
|
||||
|
@ -109,7 +108,7 @@ func TestSendTxJSON(t *testing.T) {
|
|||
sig := test1PrivAcc.Sign(signBytes)
|
||||
// we handle both raw sig and wrapped sig the same
|
||||
tx.SetSignature(test1PrivAcc.PubKey.Address(), sig)
|
||||
tx2.SetSignature(test1PrivAcc.PubKey.Address(), crypto.SignatureS{sig})
|
||||
tx2.SetSignature(test1PrivAcc.PubKey.Address(), sig)
|
||||
assert.Equal(tx, tx2)
|
||||
|
||||
// let's marshal / unmarshal this with signature
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package version
|
||||
|
||||
const Maj = "0"
|
||||
const Min = "4"
|
||||
const Fix = "1"
|
||||
const Min = "5"
|
||||
const Fix = "0"
|
||||
|
||||
const Version = "0.4.1"
|
||||
const Version = "0.5.0"
|
||||
|
|
Loading…
Reference in New Issue