From dfa9bc811734d8e1ff8a4863ec7c52a8a0ea5d22 Mon Sep 17 00:00:00 2001 From: zramsay Date: Mon, 19 Feb 2018 18:02:04 +0000 Subject: [PATCH 01/70] baseapp: start TestInfo (#471) --- baseapp/baseapp_test.go | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 8eac7ad1028e..0467233b920e 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -48,22 +48,35 @@ func TestMountStores(t *testing.T) { err = app.LoadLatestVersion(capKey2) assert.Nil(t, err) } - +// Test that we can make commits and then reload old versions. +// Test that LoadLatestVersion actually does. func TestLoadVersion(t *testing.T) { // TODO - // Test that we can make commits and then reload old versions. - // Test that LoadLatestVersion actually does. } +// Test that txs can be unmarshalled and read and that +// correct error codes are returned when not func TestTxDecoder(t *testing.T) { // TODO - // Test that txs can be unmarshalled and read and that - // correct error codes are returned when not } +// Test that Info returns the latest committed state. func TestInfo(t *testing.T) { + + app := newBaseApp(t.Name()) + + // ----- test an empty response ------- + reqInfo := abci.RequestInfo{} + res := app.Info(reqInfo) + + // should be empty + assert.Equal(t, "", res.Version) + assert.Equal(t, int64(0), res.LastBlockHeight) + assert.Equal(t, []uint8(nil), res.LastBlockAppHash) + + // ----- test a proper response ------- // TODO - // Test that Info returns the latest committed state. + } func TestInitChainer(t *testing.T) { @@ -101,14 +114,14 @@ func TestInitChainer(t *testing.T) { assert.Equal(t, value, res.Value) } -// Test that successive CheckTx can see eachothers effects +// Test that successive CheckTx can see each others' effects // on the store within a block, and that the CheckTx state // gets reset to the latest Committed state during Commit func TestCheckTx(t *testing.T) { // TODO } -// Test that successive DeliverTx can see eachothers effects +// Test that successive DeliverTx can see each others' effects // on the store, both within and across blocks. func TestDeliverTx(t *testing.T) { app := newBaseApp(t.Name()) From e1e886a0fc6336e8e44a13c12f8736bf2a0d2f7a Mon Sep 17 00:00:00 2001 From: zramsay Date: Mon, 19 Feb 2018 22:17:06 +0000 Subject: [PATCH 02/70] s/dummy&Dummy/kvstore&KVStore/g --- Makefile | 3 --- docs/guide.md | 2 +- docs/sdk/overview.rst | 4 ++-- examples/{dummy => kvstore}/main.go | 10 +++++----- examples/{dummy => kvstore}/tx.go | 24 ++++++++++++------------ 5 files changed, 20 insertions(+), 23 deletions(-) rename examples/{dummy => kvstore}/main.go (84%) rename examples/{dummy => kvstore}/tx.go (64%) diff --git a/Makefile b/Makefile index c5e0357ccdd3..ea802ef2faee 100644 --- a/Makefile +++ b/Makefile @@ -58,9 +58,6 @@ godocs: ######################################## ### Testing -TUTORIALS=$(shell find docs/guide -name "*md" -type f) - -#test: test_unit test_cli test_tutorial test: test_unit # test_cli test_unit: diff --git a/docs/guide.md b/docs/guide.md index ae2829b451ed..8741ada865c3 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -282,7 +282,7 @@ func NewHandler(am sdk.AccountMapper) sdk.Handler { ### vs encoding/json ### vs protobuf -## Dummy example +## KVStore example ## Basecoin example diff --git a/docs/sdk/overview.rst b/docs/sdk/overview.rst index d7639d49431c..25ed2b132b8e 100644 --- a/docs/sdk/overview.rst +++ b/docs/sdk/overview.rst @@ -421,8 +421,8 @@ vs encoding/json vs protobuf ~~~~~~~~~~~ -Dummy example -------------- +KVStore example +--------------- Basecoin example ---------------- diff --git a/examples/dummy/main.go b/examples/kvstore/main.go similarity index 84% rename from examples/dummy/main.go rename to examples/kvstore/main.go index 6fd7aa547889..5ffe590f1a0e 100644 --- a/examples/dummy/main.go +++ b/examples/kvstore/main.go @@ -27,7 +27,7 @@ func main() { var capKeyMainStore = sdk.NewKVStoreKey("main") // Create BaseApp. - var baseApp = bam.NewBaseApp("dummy", logger, db) + var baseApp = bam.NewBaseApp("kvstore", logger, db) // Set mounts for BaseApp's MultiStore. baseApp.MountStore(capKeyMainStore, sdk.StoreTypeIAVL) @@ -36,7 +36,7 @@ func main() { baseApp.SetTxDecoder(decodeTx) // Set a handler Route. - baseApp.Router().AddRoute("dummy", DummyHandler(capKeyMainStore)) + baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore)) // Load latest version. if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil { @@ -60,11 +60,11 @@ func main() { return } -func DummyHandler(storeKey sdk.StoreKey) sdk.Handler { +func KVStoreHandler(storeKey sdk.StoreKey) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { - dTx, ok := msg.(dummyTx) + dTx, ok := msg.(kvstoreTx) if !ok { - panic("DummyHandler should only receive dummyTx") + panic("KVStoreHandler should only receive kvstoreTx") } // tx is already unmarshalled diff --git a/examples/dummy/tx.go b/examples/kvstore/tx.go similarity index 64% rename from examples/dummy/tx.go rename to examples/kvstore/tx.go index 7bea103e7aa5..b324081e838f 100644 --- a/examples/dummy/tx.go +++ b/examples/kvstore/tx.go @@ -8,13 +8,13 @@ import ( ) // An sdk.Tx which is its own sdk.Msg. -type dummyTx struct { +type kvstoreTx struct { key []byte value []byte bytes []byte } -func (tx dummyTx) Get(key interface{}) (value interface{}) { +func (tx kvstoreTx) Get(key interface{}) (value interface{}) { switch k := key.(type) { case string: switch k { @@ -27,32 +27,32 @@ func (tx dummyTx) Get(key interface{}) (value interface{}) { return nil } -func (tx dummyTx) Type() string { - return "dummy" +func (tx kvstoreTx) Type() string { + return "kvstore" } -func (tx dummyTx) GetMsg() sdk.Msg { +func (tx kvstoreTx) GetMsg() sdk.Msg { return tx } -func (tx dummyTx) GetSignBytes() []byte { +func (tx kvstoreTx) GetSignBytes() []byte { return tx.bytes } // Should the app be calling this? Or only handlers? -func (tx dummyTx) ValidateBasic() sdk.Error { +func (tx kvstoreTx) ValidateBasic() sdk.Error { return nil } -func (tx dummyTx) GetSigners() []crypto.Address { +func (tx kvstoreTx) GetSigners() []crypto.Address { return nil } -func (tx dummyTx) GetSignatures() []sdk.StdSignature { +func (tx kvstoreTx) GetSignatures() []sdk.StdSignature { return nil } -func (tx dummyTx) GetFeePayer() crypto.Address { +func (tx kvstoreTx) GetFeePayer() crypto.Address { return nil } @@ -64,10 +64,10 @@ func decodeTx(txBytes []byte) (sdk.Tx, sdk.Error) { split := bytes.Split(txBytes, []byte("=")) if len(split) == 1 { k := split[0] - tx = dummyTx{k, k, txBytes} + tx = kvstoreTx{k, k, txBytes} } else if len(split) == 2 { k, v := split[0], split[1] - tx = dummyTx{k, v, txBytes} + tx = kvstoreTx{k, v, txBytes} } else { return nil, sdk.ErrTxParse("too many =") } From 37600d2cd0dff55a4802226dc220edf2064dd83d Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Tue, 20 Feb 2018 10:10:36 +0000 Subject: [PATCH 03/70] basecoin get_tools --- examples/basecoin/Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/basecoin/Makefile b/examples/basecoin/Makefile index c617692a5c6b..42244a90f739 100644 --- a/examples/basecoin/Makefile +++ b/examples/basecoin/Makefile @@ -1,7 +1,10 @@ PACKAGES=$(shell go list ./... | grep -v '/vendor/') BUILD_FLAGS = -ldflags "-X github.com/cosmos/cosmos-sdk/examples/basecoin/version.GitCommit=`git rev-parse --short HEAD`" -all: get_vendor_deps build test +all: get_tools get_vendor_deps build test + +get_tools: + go get github.com/Masterminds/glide build: go build $(BUILD_FLAGS) -o build/basecoin ./cmd/... @@ -16,4 +19,4 @@ test: benchmark: @go test -bench=. $(PACKAGES) -.PHONY: build get_vendor_deps test benchmark +.PHONY: get_tools build get_vendor_deps test benchmark From 9ec5f37a0261add94513690ff8decb5ab63fa680 Mon Sep 17 00:00:00 2001 From: Adrian Brink Date: Tue, 20 Feb 2018 13:00:48 +0100 Subject: [PATCH 04/70] Fix installation process for basecoind from top-level folder The make build command only works if you install the dependencies first. Previously you had to cd into examples/basecoin and run get_vendor_deps and then cd into cosmos-sdk and then run build. With this change a user can just run build in the top-level folder and the dependencies are installed automatically. --- Makefile | 1 + examples/basecoin/Makefile | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index c5e0357ccdd3..1f640bacbcce 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ gaia: build: @rm -rf examples/basecoin/vendor/ + cd examples/basecoin && $(MAKE) get_vendor_deps go build $(BUILD_FLAGS) -o build/basecoind ./examples/basecoin/cmd/basecoind/... dist: diff --git a/examples/basecoin/Makefile b/examples/basecoin/Makefile index 42244a90f739..0196b9590cdd 100644 --- a/examples/basecoin/Makefile +++ b/examples/basecoin/Makefile @@ -1,10 +1,10 @@ PACKAGES=$(shell go list ./... | grep -v '/vendor/') BUILD_FLAGS = -ldflags "-X github.com/cosmos/cosmos-sdk/examples/basecoin/version.GitCommit=`git rev-parse --short HEAD`" -all: get_tools get_vendor_deps build test +all: get_tools get_vendor_deps build test get_tools: - go get github.com/Masterminds/glide + go get github.com/tendermint/glide build: go build $(BUILD_FLAGS) -o build/basecoin ./cmd/... @@ -19,4 +19,4 @@ test: benchmark: @go test -bench=. $(PACKAGES) -.PHONY: get_tools build get_vendor_deps test benchmark +.PHONY: get_tools build get_vendor_deps test benchmark From c1b9721f896a1a9c9377a6dba3ce6a82b91a4af2 Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Tue, 20 Feb 2018 21:13:27 +0000 Subject: [PATCH 05/70] coverage recipe --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index c5e0357ccdd3..3526bf46b43a 100644 --- a/Makefile +++ b/Makefile @@ -63,6 +63,11 @@ TUTORIALS=$(shell find docs/guide -name "*md" -type f) #test: test_unit test_cli test_tutorial test: test_unit # test_cli +# Must be run in each package seperately for the visualization +# Added here for easy reference +# coverage: +# go test -coverprofile=c.out && go tool cover -html=c.out + test_unit: @rm -rf examples/basecoin/vendor/ @go test $(PACKAGES) From 2b25f5948da2f9d4111359af5df9c6c44b27b958 Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Tue, 20 Feb 2018 22:10:01 +0000 Subject: [PATCH 06/70] glide.lock --- examples/basecoin/glide.lock | 151 +++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 examples/basecoin/glide.lock diff --git a/examples/basecoin/glide.lock b/examples/basecoin/glide.lock new file mode 100644 index 000000000000..09836fa8b1af --- /dev/null +++ b/examples/basecoin/glide.lock @@ -0,0 +1,151 @@ +hash: 74ba16fcb7dac2ceca406dcc6c6ed00ab1cd09af4ca6f3f1d35452254e149fb4 +updated: 2018-02-20T22:02:29.601415268Z +imports: +- name: github.com/btcsuite/btcd + version: 50de9da05b50eb15658bb350f6ea24368a111ab7 + subpackages: + - btcec +- name: github.com/cosmos/cosmos-sdk + version: 6197eb831e7ffb2f603d59f4602663bb9b9a642e + subpackages: + - baseapp + - store + - types + - x/auth + - x/bank +- name: github.com/davecgh/go-spew + version: 346938d642f2ec3594ed81d874461961cd0faa76 + subpackages: + - spew +- name: github.com/go-kit/kit + version: 4dc7be5d2d12881735283bcab7352178e190fc71 + subpackages: + - log + - log/level + - log/term +- name: github.com/go-logfmt/logfmt + version: 390ab7935ee28ec6b286364bba9b4dd6410cb3d5 +- name: github.com/go-stack/stack + version: 259ab82a6cad3992b4e21ff5cac294ccb06474bc +- name: github.com/gogo/protobuf + version: 1adfc126b41513cc696b209667c8656ea7aac67c + subpackages: + - gogoproto + - jsonpb + - proto + - protoc-gen-gogo/descriptor + - sortkeys + - types +- name: github.com/golang/protobuf + version: 925541529c1fa6821df4e44ce2723319eb2be768 + subpackages: + - proto + - ptypes + - ptypes/any + - ptypes/duration + - ptypes/timestamp +- name: github.com/golang/snappy + version: 553a641470496b2327abcac10b36396bd98e45c9 +- name: github.com/jmhodges/levigo + version: c42d9e0ca023e2198120196f842701bb4c55d7b9 +- name: github.com/kr/logfmt + version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0 +- name: github.com/pkg/errors + version: 645ef00459ed84a119197bfb8d8205042c6df63d +- name: github.com/syndtr/goleveldb + version: 34011bf325bce385408353a30b101fe5e923eb6e + subpackages: + - leveldb + - leveldb/cache + - leveldb/comparer + - leveldb/errors + - leveldb/filter + - leveldb/iterator + - leveldb/journal + - leveldb/memdb + - leveldb/opt + - leveldb/storage + - leveldb/table + - leveldb/util +- name: github.com/tendermint/abci + version: fe7e26eecffd1dfbed3644893ff5177ad729fe94 + subpackages: + - server + - types +- name: github.com/tendermint/ed25519 + version: d8387025d2b9d158cf4efb07e7ebf814bcce2057 + subpackages: + - edwards25519 + - extra25519 +- name: github.com/tendermint/go-crypto + version: 4fc3055dbd17aa1203d0abc64b9293f378da22ec +- name: github.com/tendermint/go-wire + version: dec83f641903b22f039da3974607859715d0377e +- name: github.com/tendermint/iavl + version: 1a59ec0c82dc940c25339dd7c834df5cb76a95cb +- name: github.com/tendermint/tmlibs + version: c858b3ba78316fdd9096a11409a7e7a493e7d974 + subpackages: + - common + - db + - log + - merkle +- name: golang.org/x/crypto + version: 1875d0a70c90e57f11972aefd42276df65e895b9 + subpackages: + - nacl/secretbox + - openpgp/armor + - openpgp/errors + - poly1305 + - ripemd160 + - salsa20/salsa +- name: golang.org/x/net + version: 2fb46b16b8dda405028c50f7c7f0f9dd1fa6bfb1 + subpackages: + - context + - http2 + - http2/hpack + - idna + - internal/timeseries + - lex/httplex + - trace +- name: golang.org/x/text + version: e19ae1496984b1c655b8044a65c0300a3c878dd3 + subpackages: + - secure/bidirule + - transform + - unicode/bidi + - unicode/norm +- name: google.golang.org/genproto + version: 4eb30f4778eed4c258ba66527a0d4f9ec8a36c45 + subpackages: + - googleapis/rpc/status +- name: google.golang.org/grpc + version: 401e0e00e4bb830a10496d64cd95e068c5bf50de + subpackages: + - balancer + - codes + - connectivity + - credentials + - grpclb/grpc_lb_v1/messages + - grpclog + - internal + - keepalive + - metadata + - naming + - peer + - resolver + - stats + - status + - tap + - transport +testImports: +- name: github.com/pmezard/go-difflib + version: 792786c7400a136282c1664665ae0a8db921c6c2 + subpackages: + - difflib +- name: github.com/stretchr/testify + version: 12b6f73e6084dad08a7c6e575284b177ecafbc71 + subpackages: + - assert + - require From b9780ff9aee90903781ee1e7e5e0f746c06447f9 Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Tue, 20 Feb 2018 23:57:24 +0000 Subject: [PATCH 07/70] x/bank: boost test coverage --- x/bank/tx_test.go | 76 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/x/bank/tx_test.go b/x/bank/tx_test.go index 4ce8b1fe9d28..bf78c7f2820e 100644 --- a/x/bank/tx_test.go +++ b/x/bank/tx_test.go @@ -1,6 +1,7 @@ package bank import ( + //"fmt" "testing" "github.com/stretchr/testify/assert" @@ -10,6 +11,27 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +func TestSendMsgType(t *testing.T) { + // Construct a SendMsg + var msg = SendMsg{ + Inputs: []Input{ + { + Address: crypto.Address([]byte("input")), + Coins: sdk.Coins{{"atom", 10}}, + Sequence: 1, + }, + }, + Outputs: []Output{ + { + Address: crypto.Address([]byte("output")), + Coins: sdk.Coins{{"atom", 10}}, + }, + }, + } + + assert.Equal(t, msg.Type(), "bank") +} + func TestInputValidation(t *testing.T) { addr1 := crypto.Address([]byte{1, 2}) addr2 := crypto.Address([]byte{7, 8}) @@ -165,6 +187,60 @@ func TestSendMsgValidation(t *testing.T) { } } +func TestSendMsgString(t *testing.T) { + // Construct a SendMsg + var msg = SendMsg{ + Inputs: []Input{ + { + Address: crypto.Address([]byte("input")), + Coins: sdk.Coins{{"atom", 10}}, + Sequence: 1, + }, + }, + Outputs: []Output{ + { + Address: crypto.Address([]byte("output")), + Coins: sdk.Coins{{"atom", 10}}, + }, + }, + } + res := msg.String() + assert.Equal(t, res, "SendMsg{[Input{696E707574,10atom}]->[Output{364637353734373037353734,10atom}]}") +} + +// ---------------------------------------- +// IssueMsg Tests + +func TestIssueMsgString(t *testing.T) { + // Construct a IssueMsg + var msg = IssueMsg{ + Banker: crypto.Address([]byte("input")), + Outputs: []Output{ + { + Address: crypto.Address([]byte("loan-from-bank")), + Coins: sdk.Coins{{"atom", 10}}, + }, + }, + } + res := msg.String() + assert.Equal(t, res, "IssueMsg{696E707574#[Output{36433646363136453244363637323646364432443632363136453642,10atom}]}") +} + +func TestIssueMsgType(t *testing.T) { + // Construct an IssueMsg + var msg = IssueMsg{ + Banker: crypto.Address([]byte("input")), + Outputs: []Output{ + { + Address: crypto.Address([]byte("loan-from-bank")), + Coins: sdk.Coins{{"atom", 10}}, + }, + }, + } + + assert.Equal(t, msg.Type(), "bank") +} + /* // TODO where does this test belong ? func TestSendMsgSigners(t *testing.T) { From 0e0b6ae504c4dbdc93a4dc2c0db929392a3c2fd0 Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Wed, 21 Feb 2018 01:15:57 +0000 Subject: [PATCH 08/70] x/bank: moar tests --- x/bank/tx_test.go | 145 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 127 insertions(+), 18 deletions(-) diff --git a/x/bank/tx_test.go b/x/bank/tx_test.go index bf78c7f2820e..6cff82da8c7a 100644 --- a/x/bank/tx_test.go +++ b/x/bank/tx_test.go @@ -1,7 +1,7 @@ package bank import ( - //"fmt" + "fmt" "testing" "github.com/stretchr/testify/assert" @@ -11,6 +11,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +func TestNewSendMsg(t *testing.T) {} + func TestSendMsgType(t *testing.T) { // Construct a SendMsg var msg = SendMsg{ @@ -29,6 +31,7 @@ func TestSendMsgType(t *testing.T) { }, } + // TODO some failures for bad result assert.Equal(t, msg.Type(), "bank") } @@ -205,44 +208,71 @@ func TestSendMsgString(t *testing.T) { }, } res := msg.String() + // TODO some failures for bad results assert.Equal(t, res, "SendMsg{[Input{696E707574,10atom}]->[Output{364637353734373037353734,10atom}]}") } -// ---------------------------------------- -// IssueMsg Tests - -func TestIssueMsgString(t *testing.T) { - // Construct a IssueMsg - var msg = IssueMsg{ - Banker: crypto.Address([]byte("input")), +func TestSendMsgGet(t *testing.T) { + var msg = SendMsg{ + Inputs: []Input{ + { + Address: crypto.Address([]byte("input")), + Coins: sdk.Coins{{"atom", 10}}, + Sequence: 1, + }, + }, Outputs: []Output{ { - Address: crypto.Address([]byte("loan-from-bank")), + Address: crypto.Address([]byte("output")), Coins: sdk.Coins{{"atom", 10}}, }, }, } - res := msg.String() - assert.Equal(t, res, "IssueMsg{696E707574#[Output{36433646363136453244363637323646364432443632363136453642,10atom}]}") + res := msg.Get(nil) + assert.Nil(t, res) } -func TestIssueMsgType(t *testing.T) { - // Construct an IssueMsg - var msg = IssueMsg{ - Banker: crypto.Address([]byte("input")), +func TestSendMsgGetSignBytes(t *testing.T) { + var msg = SendMsg{ + Inputs: []Input{ + { + Address: crypto.Address([]byte("input")), + Coins: sdk.Coins{{"atom", 10}}, + Sequence: 1, + }, + }, Outputs: []Output{ { - Address: crypto.Address([]byte("loan-from-bank")), + Address: crypto.Address([]byte("output")), Coins: sdk.Coins{{"atom", 10}}, }, }, } + res := msg.GetSignBytes() + // TODO bad results + assert.Equal(t, string(res), `{"inputs":[{"address":"696E707574","coins":[{"denom":"atom","amount":10}],"sequence":1}],"outputs":[{"address":"6F7574707574","coins":[{"denom":"atom","amount":10}]}]}`) +} - assert.Equal(t, msg.Type(), "bank") +func TestSendMsgGetSigners(t *testing.T) { + var msg = SendMsg{ + Inputs: []Input{ + { + Address: crypto.Address([]byte("input1")), + }, + { + Address: crypto.Address([]byte("input2")), + }, + { + Address: crypto.Address([]byte("input3")), + }, + }, + } + res := msg.GetSigners() + assert.Equal(t, fmt.Sprintf("%v", res), "[696E70757431 696E70757432 696E70757433]") } /* -// TODO where does this test belong ? +// what to do w/ this test? func TestSendMsgSigners(t *testing.T) { signers := []crypto.Address{ {1, 2, 3}, @@ -260,3 +290,82 @@ func TestSendMsgSigners(t *testing.T) { assert.Equal(t, signers, tx.Signers()) } */ + +// ---------------------------------------- +// IssueMsg Tests + +func TestNewIssueMsg(t *testing.T) { + // TODO +} + +func TestIssueMsgType(t *testing.T) { + // Construct an IssueMsg + var msg = IssueMsg{ + Banker: crypto.Address([]byte("input")), + Outputs: []Output{ + { + Address: crypto.Address([]byte("loan-from-bank")), + Coins: sdk.Coins{{"atom", 10}}, + }, + }, + } + + // TODO some failures for bad result + assert.Equal(t, msg.Type(), "bank") +} + +func TestIssueMsgValidation(t *testing.T) { + // TODO +} + +func TestIssueMsgString(t *testing.T) { + // Construct a IssueMsg + var msg = IssueMsg{ + Banker: crypto.Address([]byte("input")), + Outputs: []Output{ + { + Address: crypto.Address([]byte("loan-from-bank")), + Coins: sdk.Coins{{"atom", 10}}, + }, + }, + } + res := msg.String() + assert.Equal(t, res, "IssueMsg{696E707574#[Output{36433646363136453244363637323646364432443632363136453642,10atom}]}") +} + +func TestIssueMsgGet(t *testing.T) { + var msg = IssueMsg{ + Banker: crypto.Address([]byte("input")), + Outputs: []Output{ + { + Address: crypto.Address([]byte("loan-from-bank")), + Coins: sdk.Coins{{"atom", 10}}, + }, + }, + } + res := msg.Get(nil) + assert.Nil(t, res) +} + +func TestIssueMsgGetSignBytes(t *testing.T) { + var msg = IssueMsg{ + Banker: crypto.Address([]byte("input")), + Outputs: []Output{ + { + Address: crypto.Address([]byte("loan-from-bank")), + Coins: sdk.Coins{{"atom", 10}}, + }, + }, + } + res := msg.GetSignBytes() + // TODO bad results + assert.Equal(t, string(res), `{"banker":"696E707574","outputs":[{"address":"6C6F616E2D66726F6D2D62616E6B","coins":[{"denom":"atom","amount":10}]}]}`) +} + +func TestIssueMsgGetSigners(t *testing.T) { + var msg = IssueMsg{ + Banker: crypto.Address([]byte("onlyone")), + } + res := msg.GetSigners() + assert.Equal(t, fmt.Sprintf("%v", res), "[6F6E6C796F6E65]") +} From d69d3d8149b8e1029c190245e25d9ae99adfa387 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 20 Feb 2018 23:14:58 -0500 Subject: [PATCH 09/70] dev version --- version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version/version.go b/version/version.go index e7aa7488066e..96944d14cc55 100644 --- a/version/version.go +++ b/version/version.go @@ -9,7 +9,7 @@ const Maj = "0" const Min = "10" const Fix = "0" -const Version = "0.10.0" +const Version = "0.10.0-dev" // GitCommit set by build flags var GitCommit = "" From 7f4bcff3cf170e70a4c579fcb91118e26cf483a3 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Wed, 31 Jan 2018 19:15:32 -0800 Subject: [PATCH 10/70] Add GetCommitKVStore and CommitKVStore --- CHANGELOG.md | 7 +++++++ store/rootmultistore.go | 5 +++++ store/types.go | 5 +++-- types/store.go | 9 +++++++++ 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a213f90b4127..b8f6ccdfe8d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.11.0 (TBD) + +BREAKING CHANGES + +* [examples] dummy -> kvstore +* [types] CommitMultiStore interface has new `GetCommitKVStore(key StoreKey) CommitKVStore` method + ## 0.10.0 (February 20, 2017) BREAKING CHANGES diff --git a/store/rootmultistore.go b/store/rootmultistore.go index 5b1307c10405..a39c35813ffb 100644 --- a/store/rootmultistore.go +++ b/store/rootmultistore.go @@ -67,6 +67,11 @@ func (rs *rootMultiStore) GetCommitStore(key StoreKey) CommitStore { return rs.stores[key] } +// Implements CommitMultiStore. +func (rs *rootMultiStore) GetCommitKVStore(key StoreKey) CommitKVStore { + return rs.stores[key].(CommitKVStore) +} + // Implements CommitMultiStore. func (rs *rootMultiStore) LoadLatestVersion() error { ver := getLatestVersion(rs.db) diff --git a/store/types.go b/store/types.go index ec842d9cf813..fa27e94dad01 100644 --- a/store/types.go +++ b/store/types.go @@ -6,14 +6,15 @@ import ( // Import cosmos-sdk/types/store.go for convenience. type Store = types.Store +type Committer = types.Committer +type CommitStore = types.CommitStore type MultiStore = types.MultiStore type CacheMultiStore = types.CacheMultiStore -type CommitStore = types.CommitStore -type Committer = types.Committer type CommitMultiStore = types.CommitMultiStore type KVStore = types.KVStore type Iterator = types.Iterator type CacheKVStore = types.CacheKVStore +type CommitKVStore = types.CommitKVStore type CacheWrapper = types.CacheWrapper type CacheWrap = types.CacheWrap type CommitID = types.CommitID diff --git a/types/store.go b/types/store.go index b552f8cb0259..ef2e9154b69a 100644 --- a/types/store.go +++ b/types/store.go @@ -68,6 +68,9 @@ type CommitMultiStore interface { // Panics on a nil key. GetCommitStore(key StoreKey) CommitStore + // Panics on a nil key. + GetCommitKVStore(key StoreKey) CommitKVStore + // Load the latest persisted version. Called once after all // calls to Mount*Store() are complete. LoadLatestVersion() error @@ -129,6 +132,12 @@ type CacheKVStore interface { Write() } +// Stores of MultiStore must implement CommitStore. +type CommitKVStore interface { + Committer + KVStore +} + //---------------------------------------- // CacheWrap From e4ba2f83a2171b37266a68053a4155f87a1f6157 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 21 Feb 2018 09:53:14 -0500 Subject: [PATCH 11/70] makefile: dont use sub vendor deps --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 1e21c90a04be..ea802ef2faee 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,6 @@ gaia: build: @rm -rf examples/basecoin/vendor/ - cd examples/basecoin && $(MAKE) get_vendor_deps go build $(BUILD_FLAGS) -o build/basecoind ./examples/basecoin/cmd/basecoind/... dist: From 5c2758b8df862f2cb48fff890f8d00a4c507ccba Mon Sep 17 00:00:00 2001 From: Adrian Brink Date: Wed, 21 Feb 2018 20:16:22 +0100 Subject: [PATCH 12/70] Clarify AnteHandler and MsgHandler --- docs/apps.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/apps.md b/docs/apps.md index 13a356b38c88..76434791c2dd 100644 --- a/docs/apps.md +++ b/docs/apps.md @@ -17,10 +17,14 @@ stores they're given the key for. The BaseApp ensures all stores are properly lo One mounted store is considered the "main" - it holds the latest block header, from which we can find and load the most recent state. -BaseApp distinguishes between two handler types - the `AnteHandler` and the `Handler`. -The former is a stateful validity check (eg. checking nonce and sufficient balance), -the later the full state transition function. Only AnteHandler runs during CheckTx, -while both run in DeliverTx. +BaseApp distinguishes between two handler types - the `AnteHandler` and the `MsgHandler`. +The former is a global validity check (checking nonces, sigs and sufficient balances to pay fees, +e.g. things that apply to all transaction from all modules), the later is the full state transition function. +During CheckTx the state transition function is only applied to the checkTxState and should return +before any expensive state transitions are run (this is up to each developer). It also needs to return the estimated +gas cost. +During DeliverTx the state transition function is applied to the blockchain state and the transactions +need to be fully executed. BaseApp is responsible for managing the context passed into handlers - it makes the block header available and provides the right stores for CheckTx and DeliverTx. From ec4711afd872376043458e9e9455c2d7060f005d Mon Sep 17 00:00:00 2001 From: Adrian Brink Date: Sun, 25 Feb 2018 21:59:11 +0100 Subject: [PATCH 13/70] Don't abort after AnteHandler if CheckTx Currently we are aborting running a transaction after the AnteHandler in case it is a checkTx. This means that the handler never gets the chance to check the validity of a transaction. Furthermore the AnteHandler should not handle CheckTx logic. The AnteHandler should handle global validation, whereas each Handler should handle module validation. --- baseapp/baseapp.go | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index cf86e3f7ac42..530f325d0260 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -40,9 +40,8 @@ type BaseApp struct { // Volatile // .msCheck and .ctxCheck are set on initialization and reset on Commit. // .msDeliver and .ctxDeliver are (re-)set on BeginBlock. - // .valUpdates accumulate in DeliverTx and reset in BeginBlock. + // .valUpdates accumulate in DeliverTx and are reset in BeginBlock. // QUESTION: should we put valUpdates in the ctxDeliver? - msCheck sdk.CacheMultiStore // CheckTx state, a cache-wrap of `.cms` msDeliver sdk.CacheMultiStore // DeliverTx state, a cache-wrap of `.cms` ctxCheck sdk.Context // CheckTx context @@ -51,6 +50,7 @@ type BaseApp struct { } var _ abci.Application = &BaseApp{} +var _ abci.Application = (*BaseApp)(nil) // Create and name new BaseApp func NewBaseApp(name string, logger log.Logger, db dbm.DB) *BaseApp { @@ -94,11 +94,9 @@ func (app *BaseApp) SetEndBlocker(endBlocker sdk.EndBlocker) { app.endBlocker = endBlocker } func (app *BaseApp) SetAnteHandler(ah sdk.AnteHandler) { - // deducts fee from payer, verifies signatures and nonces, sets Signers to ctx. app.anteHandler = ah } -// nolint - Get functions func (app *BaseApp) Router() Router { return app.router } // load latest application version @@ -172,7 +170,6 @@ func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context { // Implements ABCI func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo { - lastCommitID := app.cms.LastCommitID() return abci.ResponseInfo{ @@ -234,7 +231,6 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg // Implements ABCI func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) { - // Decode the Tx. var result sdk.Result var tx, err = app.txDecoder(txBytes) @@ -259,7 +255,6 @@ func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) { // Implements ABCI func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { - // Decode the Tx. var result sdk.Result var tx, err = app.txDecoder(txBytes) @@ -300,7 +295,6 @@ func (app *BaseApp) Deliver(tx sdk.Tx) (result sdk.Result) { // txBytes may be nil in some cases, eg. in tests. // Also, in the future we may support "internal" transactions. func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk.Result) { - // Handle any panics. defer func() { if r := recover(); r != nil { @@ -329,20 +323,27 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk ctx = app.ctxDeliver.WithTxBytes(txBytes) } - // TODO: override default ante handler w/ custom ante handler. - // Run the ante handler. newCtx, result, abort := app.anteHandler(ctx, tx) - if isCheckTx || abort { + if abort { return result } if !newCtx.IsZero() { ctx = newCtx } - // CacheWrap app.msDeliver in case it fails. - msCache := app.msDeliver.CacheMultiStore() - ctx = ctx.WithMultiStore(msCache) + // Get the correct cache + var msCache sdk.CacheMultiStore + if isCheckTx == true { + // CacheWrap app.msCheck in case it fails. + msCache = app.msCheck.CacheMultiStore() + ctx = ctx.WithMultiStore(msCache) + } else { + // CacheWrap app.msDeliver in case it fails. + msCache = app.msDeliver.CacheMultiStore() + ctx = ctx.WithMultiStore(msCache) + + } // Match and run route. msgType := msg.Type() From d79473d1abac9782aed820ef1f20b61112090c39 Mon Sep 17 00:00:00 2001 From: Adrian Brink Date: Mon, 26 Feb 2018 11:59:48 +0100 Subject: [PATCH 14/70] Go through all tests --- baseapp/baseapp_test.go | 2 ++ x/bank/tx_test.go | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 0467233b920e..d228d2761ed2 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -48,6 +48,7 @@ func TestMountStores(t *testing.T) { err = app.LoadLatestVersion(capKey2) assert.Nil(t, err) } + // Test that we can make commits and then reload old versions. // Test that LoadLatestVersion actually does. func TestLoadVersion(t *testing.T) { @@ -71,6 +72,7 @@ func TestInfo(t *testing.T) { // should be empty assert.Equal(t, "", res.Version) + assert.Equal(t, t.Name(), res.GetData()) assert.Equal(t, int64(0), res.LastBlockHeight) assert.Equal(t, []uint8(nil), res.LastBlockAppHash) diff --git a/x/bank/tx_test.go b/x/bank/tx_test.go index 6cff82da8c7a..2bc0458b2594 100644 --- a/x/bank/tx_test.go +++ b/x/bank/tx_test.go @@ -122,7 +122,6 @@ func TestOutputValidation(t *testing.T) { } func TestSendMsgValidation(t *testing.T) { - addr1 := crypto.Address([]byte{1, 2}) addr2 := crypto.Address([]byte{7, 8}) atom123 := sdk.Coins{{"atom", 123}} From 0cafd75351c9b00070c6c71fa3483bafd770d8da Mon Sep 17 00:00:00 2001 From: Fabian Date: Tue, 27 Feb 2018 13:08:23 +0100 Subject: [PATCH 15/70] Create lcd-rest-api.yaml --- docs/sdk/lcd-rest-api.yaml | 741 +++++++++++++++++++++++++++++++++++++ 1 file changed, 741 insertions(+) create mode 100644 docs/sdk/lcd-rest-api.yaml diff --git a/docs/sdk/lcd-rest-api.yaml b/docs/sdk/lcd-rest-api.yaml new file mode 100644 index 000000000000..08264d56460d --- /dev/null +++ b/docs/sdk/lcd-rest-api.yaml @@ -0,0 +1,741 @@ +openapi: 3.0.0 +servers: + - url: 'http://localhost:8998' +info: + version: "1.0.0-oas3" + title: Light client daemon to interface with Cosmos baseserver via REST + description: Specification for the LCD provided by `gaia rest-server` + +paths: + /version: + get: + summary: Version of the light client daemon + description: Get the version of the LCD running locally to compare against expected + responses: + 200: + description: Plaintext version i.e. "v0.5.0" + /node_info: + description: Only the node info. Block information can be queried via /block/latest + get: + summary: The propertied of the connected node + responses: + 200: + description: Node status + content: + application/json: + schema: + type: object + properties: + pub_key: + $ref: '#/components/schemas/PubKey' + moniker: + type: string + example: 159.89.198.221 + network: + type: string + example: gaia-2 + remote_addr: + type: string + listen_addr: + type: string + example: 192.168.56.1:46656 + version: + description: Tendermint version + type: string + example: 0.15.0 + other: + description: more information on versions + type: array + /keys: + get: + summary: List of accounts stored locally + responses: + 200: + description: Array of accounts + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Account' + post: + summary: Create a new account locally + responses: + 200: + description: OK + requestBody: + content: + application/json: + schema: + type: object + required: + - name + - password + - seed + properties: + name: + type: string + password: + type: string + seed: + type: string + description: The account to create. + /keys/seed: + get: + summary: Create a new seed to create a new account with + responses: + 200: + description: 12 word Seed + content: + application/json: + schema: + type: string + /keys/{name}: + parameters: + - in: path + name: name + description: Account name + required: true + schema: + type: string + get: + summary: Get a certain locally stored account + responses: + 200: + description: Locally stored account + content: + application/json: + schema: + $ref: "#/components/schemas/Account" + 404: + description: Account is not available + put: + summary: Update the password for this account + requestBody: + content: + application/json: + schema: + type: object + required: + - password + properties: + password: + type: string + responses: + 200: + description: Updated password + 401: + description: Password is wrong + 404: + description: Account is not available + delete: + summary: Remove an account + requestBody: + content: + application/json: + schema: + type: object + required: + - password + properties: + password: + type: string + responses: + 200: + description: Removed account + 401: + description: Password is wrong + 404: + description: Account is not available + /accounts/send: + post: + summary: Send coins (build -> sign -> send) + security: + - sign: [] + requestBody: + content: + application/json: + schema: + type: object + properties: + fees: + $ref: "#/components/schemas/Coins" + outputs: + type: array + items: + type: object + properties: + pub_key: + $ref: "#/components/schemas/PubKey" + amount: + type: array + items: + $ref: "#/components/schemas/Coins" + responses: + 202: + description: Tx was send and will probably be added to the next block + 400: + description: The Tx was malformated + + /accounts/{address}: + parameters: + - in: path + name: address + description: Account address + required: true + schema: + $ref: "#/components/schemas/Address" + get: + summary: Get the account balances + responses: + 200: + description: Account balances + content: + application/json: + schema: + $ref: "#/components/schemas/Balance" + 204: + description: There is no data for the requested account. This is not a 404 as the account might exist, just does not hold data. + /accounts/{address}/send: + parameters: + - in: path + name: address + description: Account address + required: true + schema: + $ref: "#/components/schemas/Address" + post: + summary: Send coins (build -> sign -> send) + security: + - sign: [] + requestBody: + content: + application/json: + schema: + type: object + properties: + fees: + $ref: "#/components/schemas/Coins" + amount: + type: array + items: + $ref: "#/components/schemas/Coins" + responses: + 202: + description: Tx was send and will probably be added to the next block + 400: + description: The Tx was malformated + /accounts/{address}/nonce: + parameters: + - in: path + name: address + description: Account address + required: true + schema: + $ref: "#/components/schemas/Address" + get: + summary: Get the nonce for a certain account + responses: + 200: + description: Plaintext nonce i.e. "4" defaults to "0" + /blocks/latest: + get: + summary: Get the latest block + responses: + 200: + description: The latest block + content: + application/json: + schema: + $ref: "#/components/schemas/Block" + /blocks/{height}: + parameters: + - in: path + name: height + description: Block height + required: true + schema: + type: number + get: + summary: Get a block at a certain height + responses: + 200: + description: The block at a specific height + content: + application/json: + schema: + $ref: "#/components/schemas/Block" + 404: + description: Block at height is not available + /validatorsets/latest: + get: + summary: Get the latest validator set + responses: + 200: + description: The validator set at the latest block height + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Delegate" + /validatorsets/{height}: + parameters: + - in: path + name: height + description: Block height + required: true + schema: + type: number + get: + summary: Get a validator set a certain height + responses: + 200: + description: The validator set at a specific block height + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Delegate" + 404: + description: Block at height not available + /txs: + parameters: + - in: query + name: tag + schema: + type: string + example: "coin.sender=EE5F3404034C524501629B56E0DDC38FAD651F04" + required: true + - in: query + name: page + description: Pagination page + schema: + type: number + default: 0 + - in: query + name: size + description: Pagination size + schema: + type: number + default: 50 + get: + summary: Query Tx + responses: + 200: + description: All Tx matching the provided tags + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Tx" + 404: + description: Pagination is out of bounds + /txs/sign: + post: + summary: Sign a Tx + description: Sign a Tx providing locally stored account and according password + security: + - sign: [] + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/TxBuild" + responses: + 200: + description: The signed Tx + content: + application/json: + schema: + $ref: "#/components/schemas/TxSigned" + 401: + description: Account name and/or password where wrong + /txs/broadcast: + post: + summary: Send signed Tx + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/TxSigned" + responses: + 202: + description: Tx was send and will probably be added to the next block + 400: + description: The Tx was malformated + /txs/{hash}: + parameters: + - in: path + name: hash + description: Tx hash + required: true + schema: + $ref: "#/components/schemas/Hash" + get: + summary: Get a Tx by hash + responses: + 200: + description: Tx with the provided hash + content: + application/json: + schema: + $ref: "#/components/schemas/Tx" + 404: + description: Tx not available for provided hash + /delegates: + parameters: + - in: query + name: delegator + description: Query for all delegates a delegator has stake with + schema: + $ref: "#/components/schemas/Address" + get: + summary: Get a list of canidates/delegates/validators (optionally filtered by delegator) + responses: + 200: + description: List of delegates, filtered by provided delegator address + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Delegate" + /delegates/bond: + post: + summary: Bond atoms (build -> sign -> send) + security: + - sign: [] + requestBody: + content: + application/json: + schema: + type: array + items: + type: object + properties: + amount: + $ref: "#/components/schemas/Coins" + pub_key: + $ref: "#/components/schemas/PubKey" + responses: + 202: + description: Tx was send and will probably be added to the next block + 400: + description: The Tx was malformated + /delegates/unbond: + post: + summary: Unbond atoms (build -> sign -> send) + security: + - sign: [] + requestBody: + content: + application/json: + schema: + type: array + items: + type: object + properties: + amount: + $ref: "#/components/schemas/Coins" + pub_key: + $ref: "#/components/schemas/PubKey" + responses: + 202: + description: Tx was send and will probably be added to the next block + 400: + description: The Tx was malformated + /delegates/{pubkey}: + parameters: + - in: path + name: pubkey + description: Pubkey of a delegate + required: true + schema: + type: string + example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958 + get: + summary: Get a certain canidate/delegate/validator + responses: + 200: + description: Delegate for specified pub_key + content: + application/json: + schema: + $ref: "#/components/schemas/Delegate" + 404: + description: No delegate found for provided pub_key + /delegates/{pubkey}/bond: + parameters: + - in: path + name: pubkey + description: Pubkey of a delegate + required: true + schema: + type: string + example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958 + post: + summary: Bond atoms (build -> sign -> send) + security: + - sign: [] + requestBody: + content: + application/json: + schema: + type: object + properties: + amount: + $ref: "#/components/schemas/Coins" + responses: + 202: + description: Tx was send and will probably be added to the next block + 400: + description: The Tx was malformated + /delegates/{pubkey}/unbond: + parameters: + - in: path + name: pubkey + description: Pubkey of a delegate + required: true + schema: + type: string + example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958 + post: + summary: Unbond atoms (build -> sign -> send) + security: + - sign: [] + requestBody: + content: + application/json: + schema: + type: object + properties: + amount: + $ref: "#/components/schemas/Coins" + responses: + 202: + description: Tx was send and will probably be added to the next block + 400: + description: The Tx was malformated + +components: + schemas: + Address: + type: string + example: DF096FDE8D380FA5B2AD20DB2962C82DDEA1ED9B + Coins: + type: object + properties: + denom: + type: string + example: fermion + amount: + type: number + example: 50 + Hash: + type: string + example: EE5F3404034C524501629B56E0DDC38FAD651F04 + Tx: + type: object + properties: + type: + type: string + enum: + - stake/delegate + data: + type: object + TxChain: + type: object + properties: + type: + type: string + default: chain/tx + data: + type: object + properties: + chain_id: + type: string + example: gaia-2 + expires_at: + type: number + example: 0 + tx: + type: object + properties: + type: + type: string + default: nonce + data: + type: object + properties: + sequence: + type: number + example: 0 + signers: + type: array + items: + type: object + properties: + chain: + type: string + example: '' + app: + type: string + default: sigs + addr: + $ref: "#/components/schemas/Address" + tx: + $ref: "#/components/schemas/Tx" + TxBuild: + type: object + properties: + type: + type: string + default: sigs/one + data: + type: object + properties: + tx: + $ref: "#/components/schemas/Tx" + signature: + type: object + properties: + Sig: + type: string + default: '' + Pubkey: + type: string + default: '' + TxSigned: + type: object + properties: + type: + type: string + default: sigs/one + data: + type: object + properties: + tx: + $ref: "#/components/schemas/Tx" + signature: + type: object + properties: + Sig: + type: string + example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958 + Pubkey: + $ref: "#/components/schemas/PubKey" + PubKey: + type: object + properties: + type: + type: string + enum: + - ed25519 + data: + type: string + example: 81B11E717789600CC192B26F452A983DF13B985EE75ABD9DD9E68D7BA007A958 + Account: + type: object + properties: + name: + type: string + example: Main Account + address: + $ref: "#/components/schemas/Address" + pub_key: + $ref: "#/components/schemas/PubKey" + Balance: + type: object + properties: + height: + type: number + example: 123456 + coins: + type: array + items: + $ref: "#/components/schemas/Coins" + credit: + type: array + BlockID: + type: object + properties: + hash: + $ref: "#/components/schemas/Hash" + parts: + type: object + properties: + total: + type: number + example: 0 + hash: + $ref: "#/components/schemas/Hash" + Block: + type: object + properties: + header: + type: object + properties: + chain_id: + type: string + example: gaia-2 + height: + type: number + example: 1 + time: + type: string + example: '2017-12-30T05:53:09.287+01:00' + num_txs: + type: number + example: 0 + last_block_id: + $ref: "#/components/schemas/BlockID" + total_txs: + type: number + example: 35 + last_commit_hash: + $ref: "#/components/schemas/Hash" + data_hash: + $ref: "#/components/schemas/Hash" + validators_hash: + $ref: "#/components/schemas/Hash" + consensus_hash: + $ref: "#/components/schemas/Hash" + app_hash: + $ref: "#/components/schemas/Hash" + last_results_hash: + $ref: "#/components/schemas/Hash" + evidence_hash: + $ref: "#/components/schemas/Hash" + txs: + type: array + items: + $ref: "#/components/schemas/Tx" + evidence: + type: array + last_commit: + type: object + properties: + blockID: + $ref: "#/components/schemas/BlockID" + precommits: + type: array + Delegate: + type: object + properties: + pub_key: + $ref: "#/components/schemas/PubKey" + power: + type: number + example: 1000 + name: + type: string + example: "159.89.3.34" + + + securitySchemes: + sign: + type: http + scheme: basic From babbf0635fc4a4232d5e6b1b784d3ae27f99ab5a Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 27 Feb 2018 17:44:20 +0100 Subject: [PATCH 16/70] Remove errant basecoin vendor mess --- Makefile | 2 +- examples/basecoin/Makefile | 11 ++--------- examples/basecoin/glide.yaml | 4 ---- 3 files changed, 3 insertions(+), 14 deletions(-) delete mode 100644 examples/basecoin/glide.yaml diff --git a/Makefile b/Makefile index 1e21c90a04be..b89183b72835 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,6 @@ gaia: build: @rm -rf examples/basecoin/vendor/ - cd examples/basecoin && $(MAKE) get_vendor_deps go build $(BUILD_FLAGS) -o build/basecoind ./examples/basecoin/cmd/basecoind/... dist: @@ -66,6 +65,7 @@ test_unit: @go test $(PACKAGES) test_cover: + @rm -rf examples/basecoin/vendor/ @bash tests/test_cover.sh benchmark: diff --git a/examples/basecoin/Makefile b/examples/basecoin/Makefile index 0196b9590cdd..dac7dd213d79 100644 --- a/examples/basecoin/Makefile +++ b/examples/basecoin/Makefile @@ -1,22 +1,15 @@ PACKAGES=$(shell go list ./... | grep -v '/vendor/') BUILD_FLAGS = -ldflags "-X github.com/cosmos/cosmos-sdk/examples/basecoin/version.GitCommit=`git rev-parse --short HEAD`" -all: get_tools get_vendor_deps build test - -get_tools: - go get github.com/tendermint/glide +all: build test build: go build $(BUILD_FLAGS) -o build/basecoin ./cmd/... -get_vendor_deps: - @rm -rf vendor/ - @glide install - test: @go test $(PACKAGES) benchmark: @go test -bench=. $(PACKAGES) -.PHONY: get_tools build get_vendor_deps test benchmark +.PHONY: all build test benchmark diff --git a/examples/basecoin/glide.yaml b/examples/basecoin/glide.yaml deleted file mode 100644 index e8eae21cd6a8..000000000000 --- a/examples/basecoin/glide.yaml +++ /dev/null @@ -1,4 +0,0 @@ -package: github.com/cosmos/cosmos-sdk/examples/basecoin -import: -- package: github.com/cosmos/cosmos-sdk - version: develop From 7f44470ad53297059a2cf1811c3a8e007abaa4b3 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 27 Feb 2018 22:23:56 -0500 Subject: [PATCH 17/70] baseapp: save header on commit. fixes #507 --- baseapp/baseapp.go | 26 ++++++++---- baseapp/baseapp_test.go | 90 ++++++++++++++++++++++++++++++++--------- 2 files changed, 89 insertions(+), 27 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index cf86e3f7ac42..a04a50cdf73f 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -16,7 +16,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -var mainHeaderKey = []byte("header") +// Key to store the header in the DB itself. +// Use the db directly instead of a store to avoid +// conflicts with handlers writing to the store +// and to avoid affecting the Merkle root. +var dbHeaderKey = []byte("header") // The ABCI application type BaseApp struct { @@ -126,19 +130,20 @@ func (app *BaseApp) LastBlockHeight() int64 { // initializes the remaining logic from app.cms func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error { var lastCommitID = app.cms.LastCommitID() - var main = app.cms.GetKVStore(mainKey) var header abci.Header // main store should exist. + // TODO: we don't actually need the main store here + main := app.cms.GetKVStore(mainKey) if main == nil { return errors.New("BaseApp expects MultiStore with 'main' KVStore") } - // if we've committed before, we expect main:// + // if we've committed before, we expect to exist in the db if !lastCommitID.IsZero() { - headerBytes := main.Get(mainHeaderKey) + headerBytes := app.db.Get(dbHeaderKey) if len(headerBytes) == 0 { - errStr := fmt.Sprintf("Version > 0 but missing key %s", mainHeaderKey) + errStr := fmt.Sprintf("Version > 0 but missing key %s", dbHeaderKey) return errors.New(errStr) } err := proto.Unmarshal(headerBytes, &header) @@ -147,7 +152,7 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error { } lastVersion := lastCommitID.Version if header.Height != lastVersion { - errStr := fmt.Sprintf("Expected main://%s.Height %v but got %v", mainHeaderKey, lastVersion, header.Height) + errStr := fmt.Sprintf("Expected main://%s.Height %v but got %v", dbHeaderKey, lastVersion, header.Height) return errors.New(errStr) } } @@ -369,6 +374,14 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc // Implements ABCI func (app *BaseApp) Commit() (res abci.ResponseCommit) { + // Write the latest Header to the store + header := app.ctxDeliver.BlockHeader() + headerBytes, err := proto.Marshal(&header) + if err != nil { + panic(err) + } + app.db.Set(dbHeaderKey, headerBytes) + // Write the Deliver state and commit the MultiStore app.msDeliver.Write() commitID := app.cms.Commit() @@ -379,7 +392,6 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) { // Reset the Check state // NOTE: safe because Tendermint holds a lock on the mempool for Commit. // Use the header from this latest block. - header := app.ctxDeliver.BlockHeader() app.msCheck = app.cms.CacheMultiStore() app.ctxCheck = app.NewContext(true, header) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index d228d2761ed2..3add7a8b21d1 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -42,19 +42,82 @@ func TestMountStores(t *testing.T) { app.MountStoresIAVL(capKey1, capKey2) - // both stores are mounted + // stores are mounted err := app.LoadLatestVersion(capKey1) assert.Nil(t, err) - err = app.LoadLatestVersion(capKey2) - assert.Nil(t, err) + + // check both stores + store1 := app.cms.GetCommitKVStore(capKey1) + assert.NotNil(t, store1) + store2 := app.cms.GetCommitKVStore(capKey2) + assert.NotNil(t, store2) } // Test that we can make commits and then reload old versions. // Test that LoadLatestVersion actually does. func TestLoadVersion(t *testing.T) { - // TODO + logger := defaultLogger() + db := dbm.NewMemDB() + name := t.Name() + app := NewBaseApp(name, logger, db) + + // make a cap key and mount the store + capKey := sdk.NewKVStoreKey("main") + app.MountStoresIAVL(capKey) + err := app.LoadLatestVersion(capKey) // needed to make stores non-nil + assert.Nil(t, err) + + emptyCommitID := sdk.CommitID{} + + lastHeight := app.LastBlockHeight() + lastID := app.LastCommitID() + assert.Equal(t, int64(0), lastHeight) + assert.Equal(t, emptyCommitID, lastID) + + // execute some blocks + header := abci.Header{Height: 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) + res := app.Commit() + commitID := sdk.CommitID{1, res.Data} + + // reload + app = NewBaseApp(name, logger, db) + app.MountStoresIAVL(capKey) + err = app.LoadLatestVersion(capKey) // needed to make stores non-nil + assert.Nil(t, err) + + lastHeight = app.LastBlockHeight() + lastID = app.LastCommitID() + assert.Equal(t, int64(1), lastHeight) + assert.Equal(t, commitID, lastID) } +// Test that the app hash is static +// TODO: https://github.com/cosmos/cosmos-sdk/issues/520 +/*func TestStaticAppHash(t *testing.T) { + app := newBaseApp(t.Name()) + + // make a cap key and mount the store + capKey := sdk.NewKVStoreKey("main") + app.MountStoresIAVL(capKey) + err := app.LoadLatestVersion(capKey) // needed to make stores non-nil + assert.Nil(t, err) + + // execute some blocks + header := abci.Header{Height: 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) + res := app.Commit() + commitID1 := sdk.CommitID{1, res.Data} + + header = abci.Header{Height: 2} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) + res = app.Commit() + commitID2 := sdk.CommitID{2, res.Data} + + assert.Equal(t, commitID1.Hash, commitID2.Hash) +} +*/ + // Test that txs can be unmarshalled and read and that // correct error codes are returned when not func TestTxDecoder(t *testing.T) { @@ -246,7 +309,8 @@ func TestValidatorChange(t *testing.T) { // Create app. app := newBaseApp(t.Name()) - storeKeys := createMounts(app.cms) + capKey := sdk.NewKVStoreKey("key") + app.MountStoresIAVL(capKey) app.SetTxDecoder(func(txBytes []byte) (sdk.Tx, sdk.Error) { var ttx testUpdatePowerTx fromJSON(txBytes, &ttx) @@ -260,7 +324,7 @@ func TestValidatorChange(t *testing.T) { }) // Load latest state, which should be empty. - err := app.LoadLatestVersion(storeKeys["main"]) + err := app.LoadLatestVersion(capKey) assert.Nil(t, err) assert.Equal(t, app.LastBlockHeight(), int64(0)) @@ -369,17 +433,3 @@ func fromJSON(bz []byte, ptr interface{}) { panic(err) } } - -// Mounts stores to CommitMultiStore and returns a map of keys. -func createMounts(ms sdk.CommitMultiStore) map[string]sdk.StoreKey { - dbMain := dbm.NewMemDB() - dbXtra := dbm.NewMemDB() - keyMain := sdk.NewKVStoreKey("main") - keyXtra := sdk.NewKVStoreKey("xtra") - ms.MountStoreWithDB(keyMain, sdk.StoreTypeIAVL, dbMain) - ms.MountStoreWithDB(keyXtra, sdk.StoreTypeIAVL, dbXtra) - return map[string]sdk.StoreKey{ - "main": keyMain, - "xtra": keyXtra, - } -} From 588acc272e1c8120eda73e9f385b4720b26ca253 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 27 Feb 2018 23:07:54 -0500 Subject: [PATCH 18/70] baseapp: dont save header for now. use DeliverTx state in InitChain (fixes #474) --- baseapp/baseapp.go | 83 ++++++++++++++++++------------- baseapp/baseapp_test.go | 1 + examples/basecoin/app/app_test.go | 2 + 3 files changed, 52 insertions(+), 34 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index a04a50cdf73f..308aa6edaf90 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -4,7 +4,6 @@ import ( "fmt" "runtime/debug" - "github.com/golang/protobuf/proto" "github.com/pkg/errors" abci "github.com/tendermint/abci/types" @@ -129,8 +128,6 @@ func (app *BaseApp) LastBlockHeight() int64 { // initializes the remaining logic from app.cms func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error { - var lastCommitID = app.cms.LastCommitID() - var header abci.Header // main store should exist. // TODO: we don't actually need the main store here @@ -139,23 +136,35 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error { return errors.New("BaseApp expects MultiStore with 'main' KVStore") } + // XXX: Do we really need the header? What does it have that we want + // here that's not already in the CommitID ? If an app wants to have it, + // they can do so in their BeginBlocker. If we force it in baseapp, + // then either we force the AppHash to change with every block (since the header + // will be in the merkle store) or we can't write the state and the header to the + // db atomically without doing some surgery on the store interfaces ... + // if we've committed before, we expect to exist in the db - if !lastCommitID.IsZero() { - headerBytes := app.db.Get(dbHeaderKey) - if len(headerBytes) == 0 { - errStr := fmt.Sprintf("Version > 0 but missing key %s", dbHeaderKey) - return errors.New(errStr) - } - err := proto.Unmarshal(headerBytes, &header) - if err != nil { - return errors.Wrap(err, "Failed to parse Header") - } - lastVersion := lastCommitID.Version - if header.Height != lastVersion { - errStr := fmt.Sprintf("Expected main://%s.Height %v but got %v", dbHeaderKey, lastVersion, header.Height) - return errors.New(errStr) + /* + var lastCommitID = app.cms.LastCommitID() + var header abci.Header + + if !lastCommitID.IsZero() { + headerBytes := app.db.Get(dbHeaderKey) + if len(headerBytes) == 0 { + errStr := fmt.Sprintf("Version > 0 but missing key %s", dbHeaderKey) + return errors.New(errStr) + } + err := proto.Unmarshal(headerBytes, &header) + if err != nil { + return errors.Wrap(err, "Failed to parse Header") + } + lastVersion := lastCommitID.Version + if header.Height != lastVersion { + errStr := fmt.Sprintf("Expected db://%s.Height %v but got %v", dbHeaderKey, lastVersion, header.Height) + return errors.New(errStr) + } } - } + */ // initialize Check state app.msCheck = app.cms.CacheMultiStore() @@ -201,16 +210,14 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC return } - // make a context for the initialization. - // NOTE: we're writing to the cms directly, without a CacheWrap - ctx := sdk.NewContext(app.cms, abci.Header{}, false, nil) + // Initialize the deliver state + app.msDeliver = app.cms.CacheMultiStore() + app.ctxDeliver = app.NewContext(false, abci.Header{}) - res = app.initChainer(ctx, req) - // TODO: handle error https://github.com/cosmos/cosmos-sdk/issues/468 + app.initChainer(app.ctxDeliver, req) // no error - // XXX this commits everything and bumps the version. - // https://github.com/cosmos/cosmos-sdk/issues/442#issuecomment-366470148 - app.cms.Commit() + // NOTE: we don't commit, but BeginBlock for block 1 + // starts from this state return } @@ -228,8 +235,14 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { // Implements ABCI func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) { - app.msDeliver = app.cms.CacheMultiStore() - app.ctxDeliver = app.NewContext(false, req.Header) + // Initialize the DeliverTx state. + // If this is the first block, it was already + // initialized in InitChain. If we're testing, + // then we may not have run InitChain and these could be nil + if req.Header.Height > 1 || app.msDeliver == nil { + app.msDeliver = app.cms.CacheMultiStore() + app.ctxDeliver = app.NewContext(false, req.Header) + } app.valUpdates = nil if app.beginBlocker != nil { res = app.beginBlocker(app.ctxDeliver, req) @@ -374,13 +387,15 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc // Implements ABCI func (app *BaseApp) Commit() (res abci.ResponseCommit) { - // Write the latest Header to the store header := app.ctxDeliver.BlockHeader() - headerBytes, err := proto.Marshal(&header) - if err != nil { - panic(err) - } - app.db.Set(dbHeaderKey, headerBytes) + /* + // Write the latest Header to the store + headerBytes, err := proto.Marshal(&header) + if err != nil { + panic(err) + } + app.db.SetSync(dbHeaderKey, headerBytes) + */ // Write the Deliver state and commit the MultiStore app.msDeliver.Write() diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 3add7a8b21d1..1155ca24b40f 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -175,6 +175,7 @@ func TestInitChainer(t *testing.T) { // set initChainer and try again - should see the value app.SetInitChainer(initChainer) app.InitChain(abci.RequestInitChain{}) + app.Commit() res = app.Query(query) assert.Equal(t, value, res.Value) } diff --git a/examples/basecoin/app/app_test.go b/examples/basecoin/app/app_test.go index b3677301f1cb..20985fa32960 100644 --- a/examples/basecoin/app/app_test.go +++ b/examples/basecoin/app/app_test.go @@ -86,6 +86,7 @@ func TestGenesis(t *testing.T) { vals := []abci.Validator{} bapp.InitChain(abci.RequestInitChain{vals, stateBytes}) + bapp.Commit() // A checkTx context ctx := bapp.BaseApp.NewContext(true, abci.Header{}) @@ -127,6 +128,7 @@ func TestSendMsgWithAccounts(t *testing.T) { // Initialize the chain vals := []abci.Validator{} bapp.InitChain(abci.RequestInitChain{vals, stateBytes}) + bapp.Commit() // A checkTx context (true) ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{}) From 2cefcf9e6c26a42f3b48105c71647ebd8579207b Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 27 Feb 2018 23:46:27 -0500 Subject: [PATCH 19/70] baseapp: introduce checkState and deliverState to wrap their ms and ctx --- baseapp/baseapp.go | 99 ++++++++++++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 39 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 0d6a73dfa748..f1bd112b8434 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -41,18 +41,16 @@ type BaseApp struct { //-------------------- // Volatile - // .msCheck and .ctxCheck are set on initialization and reset on Commit. - // .msDeliver and .ctxDeliver are (re-)set on BeginBlock. + // checkState is set on initialization and reset on Commit. + // deliverState is set in InitChain and BeginBlock and cleared on Commit. + // See methods setCheckState and setDeliverState. // .valUpdates accumulate in DeliverTx and are reset in BeginBlock. - // QUESTION: should we put valUpdates in the ctxDeliver? - msCheck sdk.CacheMultiStore // CheckTx state, a cache-wrap of `.cms` - msDeliver sdk.CacheMultiStore // DeliverTx state, a cache-wrap of `.cms` - ctxCheck sdk.Context // CheckTx context - ctxDeliver sdk.Context // DeliverTx context - valUpdates []abci.Validator // cached validator changes from DeliverTx + // QUESTION: should we put valUpdates in the deliverState.ctx? + checkState *state // for CheckTx + deliverState *state // for DeliverTx + valUpdates []abci.Validator // cached validator changes from DeliverTx } -var _ abci.Application = &BaseApp{} var _ abci.Application = (*BaseApp)(nil) // Create and name new BaseApp @@ -165,8 +163,7 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error { */ // initialize Check state - app.msCheck = app.cms.CacheMultiStore() - app.ctxCheck = app.NewContext(true, abci.Header{}) + app.setCheckState(abci.Header{}) return nil } @@ -174,9 +171,34 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error { // NewContext returns a new Context with the correct store, the given header, and nil txBytes. func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context { if isCheckTx { - return sdk.NewContext(app.msCheck, header, true, nil) + return sdk.NewContext(app.checkState.ms, header, true, nil) + } + return sdk.NewContext(app.deliverState.ms, header, false, nil) +} + +type state struct { + ms sdk.CacheMultiStore + ctx sdk.Context +} + +func (st *state) CacheMultiStore() sdk.CacheMultiStore { + return st.ms.CacheMultiStore() +} + +func (app *BaseApp) setCheckState(header abci.Header) { + ms := app.cms.CacheMultiStore() + app.checkState = &state{ + ms: ms, + ctx: sdk.NewContext(ms, header, true, nil), + } +} + +func (app *BaseApp) setDeliverState(header abci.Header) { + ms := app.cms.CacheMultiStore() + app.deliverState = &state{ + ms: ms, + ctx: sdk.NewContext(ms, header, false, nil), } - return sdk.NewContext(app.msDeliver, header, false, nil) } //---------------------------------------- @@ -207,14 +229,12 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC return } - // Initialize the deliver state - app.msDeliver = app.cms.CacheMultiStore() - app.ctxDeliver = app.NewContext(false, abci.Header{}) - - app.initChainer(app.ctxDeliver, req) // no error + // Initialize the deliver state and run initChain + app.setDeliverState(abci.Header{}) + app.initChainer(app.deliverState.ctx, req) // no error // NOTE: we don't commit, but BeginBlock for block 1 - // starts from this state + // starts from this deliverState return } @@ -233,16 +253,15 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { // Implements ABCI func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) { // Initialize the DeliverTx state. - // If this is the first block, it was already - // initialized in InitChain. If we're testing, - // then we may not have run InitChain and these could be nil - if req.Header.Height > 1 || app.msDeliver == nil { - app.msDeliver = app.cms.CacheMultiStore() - app.ctxDeliver = app.NewContext(false, req.Header) + // If this is the first block, it should already + // be initialized in InitChain. It may also be nil + // if this is a test and InitChain was never called. + if app.deliverState == nil { + app.setDeliverState(req.Header) } app.valUpdates = nil if app.beginBlocker != nil { - res = app.beginBlocker(app.ctxDeliver, req) + res = app.beginBlocker(app.deliverState.ctx, req) } return } @@ -336,9 +355,9 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk // Get the context var ctx sdk.Context if isCheckTx { - ctx = app.ctxCheck.WithTxBytes(txBytes) + ctx = app.checkState.ctx.WithTxBytes(txBytes) } else { - ctx = app.ctxDeliver.WithTxBytes(txBytes) + ctx = app.deliverState.ctx.WithTxBytes(txBytes) } // Run the ante handler. @@ -353,12 +372,12 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk // Get the correct cache var msCache sdk.CacheMultiStore if isCheckTx == true { - // CacheWrap app.msCheck in case it fails. - msCache = app.msCheck.CacheMultiStore() + // CacheWrap app.checkState.ms in case it fails. + msCache = app.checkState.CacheMultiStore() ctx = ctx.WithMultiStore(msCache) } else { - // CacheWrap app.msDeliver in case it fails. - msCache = app.msDeliver.CacheMultiStore() + // CacheWrap app.deliverState.ms in case it fails. + msCache = app.deliverState.CacheMultiStore() ctx = ctx.WithMultiStore(msCache) } @@ -368,7 +387,7 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk handler := app.router.Route(msgType) result = handler(ctx, msg) - // If result was successful, write to app.msDeliver or app.msCheck. + // If result was successful, write to app.checkState.ms or app.deliverState.ms if result.IsOK() { msCache.Write() } @@ -379,7 +398,7 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk // Implements ABCI func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) { if app.endBlocker != nil { - res = app.endBlocker(app.ctxDeliver, req) + res = app.endBlocker(app.deliverState.ctx, req) } else { res.ValidatorUpdates = app.valUpdates } @@ -388,7 +407,7 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc // Implements ABCI func (app *BaseApp) Commit() (res abci.ResponseCommit) { - header := app.ctxDeliver.BlockHeader() + header := app.deliverState.ctx.BlockHeader() /* // Write the latest Header to the store headerBytes, err := proto.Marshal(&header) @@ -399,17 +418,19 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) { */ // Write the Deliver state and commit the MultiStore - app.msDeliver.Write() + app.deliverState.ms.Write() commitID := app.cms.Commit() app.logger.Debug("Commit synced", "commit", commitID, ) - // Reset the Check state + // Reset the Check state to the latest committed // NOTE: safe because Tendermint holds a lock on the mempool for Commit. // Use the header from this latest block. - app.msCheck = app.cms.CacheMultiStore() - app.ctxCheck = app.NewContext(true, header) + app.setCheckState(header) + + // Emtpy the Deliver state + app.deliverState = nil return abci.ResponseCommit{ Data: commitID.Hash, From 55730270e5b852f9e6d8b0fed63b6169fe4b4363 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 28 Feb 2018 00:04:18 -0500 Subject: [PATCH 20/70] basecoin: remove glide --- examples/basecoin/README.md | 15 +--- examples/basecoin/glide.lock | 151 ----------------------------------- 2 files changed, 2 insertions(+), 164 deletions(-) delete mode 100644 examples/basecoin/glide.lock diff --git a/examples/basecoin/README.md b/examples/basecoin/README.md index a1b8185865aa..ed667535deb5 100644 --- a/examples/basecoin/README.md +++ b/examples/basecoin/README.md @@ -2,18 +2,7 @@ This is the "Basecoin" example application built on the Cosmos-SDK. This "Basecoin" is not affiliated with [Coinbase](http://www.getbasecoin.com/), nor the [stable coin](http://www.getbasecoin.com/). -You need a recent version of `glide` to install Basecoin's dependencies. - -```bash -> make get_tools -``` - -Then, you can build the cmd binaries (NOTE: a work in progress!), or run the tests. - -``` -> make get_vendor_deps -> make build -> make test -``` +Assuming you've run `make get_tools && make get_vendor_deps` from the root of this repository, +run `make build` here to build the `basecoind` and `basecli` binaries. If you want to create a new application, start by copying the Basecoin app. diff --git a/examples/basecoin/glide.lock b/examples/basecoin/glide.lock deleted file mode 100644 index 09836fa8b1af..000000000000 --- a/examples/basecoin/glide.lock +++ /dev/null @@ -1,151 +0,0 @@ -hash: 74ba16fcb7dac2ceca406dcc6c6ed00ab1cd09af4ca6f3f1d35452254e149fb4 -updated: 2018-02-20T22:02:29.601415268Z -imports: -- name: github.com/btcsuite/btcd - version: 50de9da05b50eb15658bb350f6ea24368a111ab7 - subpackages: - - btcec -- name: github.com/cosmos/cosmos-sdk - version: 6197eb831e7ffb2f603d59f4602663bb9b9a642e - subpackages: - - baseapp - - store - - types - - x/auth - - x/bank -- name: github.com/davecgh/go-spew - version: 346938d642f2ec3594ed81d874461961cd0faa76 - subpackages: - - spew -- name: github.com/go-kit/kit - version: 4dc7be5d2d12881735283bcab7352178e190fc71 - subpackages: - - log - - log/level - - log/term -- name: github.com/go-logfmt/logfmt - version: 390ab7935ee28ec6b286364bba9b4dd6410cb3d5 -- name: github.com/go-stack/stack - version: 259ab82a6cad3992b4e21ff5cac294ccb06474bc -- name: github.com/gogo/protobuf - version: 1adfc126b41513cc696b209667c8656ea7aac67c - subpackages: - - gogoproto - - jsonpb - - proto - - protoc-gen-gogo/descriptor - - sortkeys - - types -- name: github.com/golang/protobuf - version: 925541529c1fa6821df4e44ce2723319eb2be768 - subpackages: - - proto - - ptypes - - ptypes/any - - ptypes/duration - - ptypes/timestamp -- name: github.com/golang/snappy - version: 553a641470496b2327abcac10b36396bd98e45c9 -- name: github.com/jmhodges/levigo - version: c42d9e0ca023e2198120196f842701bb4c55d7b9 -- name: github.com/kr/logfmt - version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0 -- name: github.com/pkg/errors - version: 645ef00459ed84a119197bfb8d8205042c6df63d -- name: github.com/syndtr/goleveldb - version: 34011bf325bce385408353a30b101fe5e923eb6e - subpackages: - - leveldb - - leveldb/cache - - leveldb/comparer - - leveldb/errors - - leveldb/filter - - leveldb/iterator - - leveldb/journal - - leveldb/memdb - - leveldb/opt - - leveldb/storage - - leveldb/table - - leveldb/util -- name: github.com/tendermint/abci - version: fe7e26eecffd1dfbed3644893ff5177ad729fe94 - subpackages: - - server - - types -- name: github.com/tendermint/ed25519 - version: d8387025d2b9d158cf4efb07e7ebf814bcce2057 - subpackages: - - edwards25519 - - extra25519 -- name: github.com/tendermint/go-crypto - version: 4fc3055dbd17aa1203d0abc64b9293f378da22ec -- name: github.com/tendermint/go-wire - version: dec83f641903b22f039da3974607859715d0377e -- name: github.com/tendermint/iavl - version: 1a59ec0c82dc940c25339dd7c834df5cb76a95cb -- name: github.com/tendermint/tmlibs - version: c858b3ba78316fdd9096a11409a7e7a493e7d974 - subpackages: - - common - - db - - log - - merkle -- name: golang.org/x/crypto - version: 1875d0a70c90e57f11972aefd42276df65e895b9 - subpackages: - - nacl/secretbox - - openpgp/armor - - openpgp/errors - - poly1305 - - ripemd160 - - salsa20/salsa -- name: golang.org/x/net - version: 2fb46b16b8dda405028c50f7c7f0f9dd1fa6bfb1 - subpackages: - - context - - http2 - - http2/hpack - - idna - - internal/timeseries - - lex/httplex - - trace -- name: golang.org/x/text - version: e19ae1496984b1c655b8044a65c0300a3c878dd3 - subpackages: - - secure/bidirule - - transform - - unicode/bidi - - unicode/norm -- name: google.golang.org/genproto - version: 4eb30f4778eed4c258ba66527a0d4f9ec8a36c45 - subpackages: - - googleapis/rpc/status -- name: google.golang.org/grpc - version: 401e0e00e4bb830a10496d64cd95e068c5bf50de - subpackages: - - balancer - - codes - - connectivity - - credentials - - grpclb/grpc_lb_v1/messages - - grpclog - - internal - - keepalive - - metadata - - naming - - peer - - resolver - - stats - - status - - tap - - transport -testImports: -- name: github.com/pmezard/go-difflib - version: 792786c7400a136282c1664665ae0a8db921c6c2 - subpackages: - - difflib -- name: github.com/stretchr/testify - version: 12b6f73e6084dad08a7c6e575284b177ecafbc71 - subpackages: - - assert - - require From 40c3465ec513f99dc58514789b98e36f7581250e Mon Sep 17 00:00:00 2001 From: Adrian Brink Date: Mon, 26 Feb 2018 14:02:03 +0100 Subject: [PATCH 21/70] Add multiple routes simultaneously closes #329 --- baseapp/router.go | 6 ++++-- examples/basecoin/app/app.go | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/baseapp/router.go b/baseapp/router.go index b6b56792bd0e..a9d754716465 100644 --- a/baseapp/router.go +++ b/baseapp/router.go @@ -8,7 +8,7 @@ import ( // Router provides handlers for each transaction type. type Router interface { - AddRoute(r string, h sdk.Handler) + AddRoute(r string, h sdk.Handler) (rtr Router) Route(path string) (h sdk.Handler) } @@ -34,11 +34,13 @@ func NewRouter() *router { var isAlpha = regexp.MustCompile(`^[a-zA-Z]+$`).MatchString // AddRoute - TODO add description -func (rtr *router) AddRoute(r string, h sdk.Handler) { +func (rtr *router) AddRoute(r string, h sdk.Handler) Router { if !isAlpha(r) { panic("route expressions can only contain alphanumeric characters") } rtr.routes = append(rtr.routes, route{r, h}) + + return rtr } // Route - TODO add description diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index 58c25dda8092..b26a9bdd45c2 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -53,8 +53,9 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { // add handlers coinKeeper := bank.NewCoinKeeper(app.accountMapper) - app.Router().AddRoute("bank", bank.NewHandler(coinKeeper)) - app.Router().AddRoute("sketchy", sketchy.NewHandler()) + app.Router(). + AddRoute("bank", bank.NewHandler(coinKeeper)). + AddRoute("sketchy", sketchy.NewHandler()) // initialize BaseApp app.SetTxDecoder(app.txDecoder) From d1fc3d6801745f5ce2f633d47bd80ebfa80bddfe Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 21 Feb 2018 10:45:31 +0100 Subject: [PATCH 22/70] Add overview --- examples/gaia/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 examples/gaia/README.md diff --git a/examples/gaia/README.md b/examples/gaia/README.md new file mode 100644 index 000000000000..485af236f051 --- /dev/null +++ b/examples/gaia/README.md @@ -0,0 +1,3 @@ +Gaiad is the abci application, which can be run stand-alone, or in-process with tendermint. + +Gaiacli is a client application, which connects to tendermint rpc, and sends transactions and queries the state. It uses light-client proofs to guarantee the results even if it doesn't have 100% trust in the node it connects to. From a99d913982d0e1f6c9bc33a4b9ca150657d86bc8 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 14 Feb 2018 18:57:48 +0100 Subject: [PATCH 23/70] Extract version command to common location --- examples/gaia/gaiacli/main.go | 4 ++- examples/gaia/gaiad/main.go | 3 ++- examples/gaia/gaiad/version.go | 26 ------------------- .../gaiacli/version.go => version/command.go | 10 +++---- 4 files changed, 9 insertions(+), 34 deletions(-) delete mode 100644 examples/gaia/gaiad/version.go rename examples/gaia/gaiacli/version.go => version/command.go (67%) diff --git a/examples/gaia/gaiacli/main.go b/examples/gaia/gaiacli/main.go index 6321e25ee721..dce125acbb0d 100644 --- a/examples/gaia/gaiacli/main.go +++ b/examples/gaia/gaiacli/main.go @@ -7,6 +7,8 @@ import ( "github.com/spf13/cobra" "github.com/tendermint/tmlibs/cli" + + "github.com/cosmos/cosmos-sdk/version" ) const ( @@ -66,7 +68,7 @@ func main() { serveCommand(), KeyCommands(), lineBreak, - VersionCmd, + version.VersionCmd, ) // prepare and add flags diff --git a/examples/gaia/gaiad/main.go b/examples/gaia/gaiad/main.go index ca9bb20dba30..544cd03e2b18 100644 --- a/examples/gaia/gaiad/main.go +++ b/examples/gaia/gaiad/main.go @@ -9,6 +9,7 @@ import ( "github.com/tendermint/tmlibs/cli" "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/version" ) const ( @@ -35,7 +36,7 @@ func main() { AddNodeCommands(gaiadCmd, node) gaiadCmd.AddCommand( - VersionCmd, + version.VersionCmd, ) // prepare and add flags diff --git a/examples/gaia/gaiad/version.go b/examples/gaia/gaiad/version.go deleted file mode 100644 index 59fd799c07f0..000000000000 --- a/examples/gaia/gaiad/version.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/spf13/cobra" - - "github.com/cosmos/cosmos-sdk/version" -) - -var ( - // VersionCmd prints out the current sdk version - VersionCmd = &cobra.Command{ - Use: "version", - Short: "Print the app version", - Run: doVersionCmd, - } -) - -func doVersionCmd(cmd *cobra.Command, args []string) { - v := version.Version - if version.GitCommit != "" { - v = v + " " + version.GitCommit - } - fmt.Println(v) -} diff --git a/examples/gaia/gaiacli/version.go b/version/command.go similarity index 67% rename from examples/gaia/gaiacli/version.go rename to version/command.go index 59fd799c07f0..9986f605d736 100644 --- a/examples/gaia/gaiacli/version.go +++ b/version/command.go @@ -1,11 +1,9 @@ -package main +package version import ( "fmt" "github.com/spf13/cobra" - - "github.com/cosmos/cosmos-sdk/version" ) var ( @@ -18,9 +16,9 @@ var ( ) func doVersionCmd(cmd *cobra.Command, args []string) { - v := version.Version - if version.GitCommit != "" { - v = v + " " + version.GitCommit + v := Version + if GitCommit != "" { + v = v + " " + GitCommit } fmt.Println(v) } From 438d18e059500f478ef266f3fa31f7caee3c4109 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 14 Feb 2018 20:04:34 +0100 Subject: [PATCH 24/70] Pulled most of gaiad into server, with rough implementation --- baseapp/baseapp.go | 6 +- examples/gaia/gaiad/main.go | 22 +++++--- examples/gaia/gaiad/node.go | 47 ---------------- server/reset.go | 31 +++++++++++ server/start.go | 107 ++++++++++++++++++++++++++++++++++++ 5 files changed, 155 insertions(+), 58 deletions(-) delete mode 100644 examples/gaia/gaiad/node.go create mode 100644 server/reset.go create mode 100644 server/start.go diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index f1bd112b8434..e5eb59538c70 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -24,7 +24,7 @@ var dbHeaderKey = []byte("header") // The ABCI application type BaseApp struct { // initialized on creation - logger log.Logger + Logger log.Logger name string // application name from abci.Info db dbm.DB // common DB backend cms sdk.CommitMultiStore // Main (uncached) state @@ -56,7 +56,7 @@ var _ abci.Application = (*BaseApp)(nil) // Create and name new BaseApp func NewBaseApp(name string, logger log.Logger, db dbm.DB) *BaseApp { return &BaseApp{ - logger: logger, + Logger: logger, name: name, db: db, cms: store.NewCommitMultiStore(db), @@ -420,7 +420,7 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) { // Write the Deliver state and commit the MultiStore app.deliverState.ms.Write() commitID := app.cms.Commit() - app.logger.Debug("Commit synced", + app.Logger.Debug("Commit synced", "commit", commitID, ) diff --git a/examples/gaia/gaiad/main.go b/examples/gaia/gaiad/main.go index 544cd03e2b18..63cfd45ddbe0 100644 --- a/examples/gaia/gaiad/main.go +++ b/examples/gaia/gaiad/main.go @@ -9,15 +9,10 @@ import ( "github.com/tendermint/tmlibs/cli" "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/version" ) -const ( - flagTo = "to" - flagAmount = "amount" - flagFee = "fee" -) - // gaiadCmd is the entry point for this binary var ( gaiadCmd = &cobra.Command{ @@ -26,16 +21,27 @@ var ( } ) +// TODO: move into server +var ( + initNodeCmd = &cobra.Command{ + Use: "init ", + Short: "Initialize full node", + RunE: todoNotImplemented, + } +) + func todoNotImplemented(_ *cobra.Command, _ []string) error { return errors.New("TODO: Command not yet implemented") } func main() { // TODO: set this to something real - var node baseapp.BaseApp + var app *baseapp.BaseApp - AddNodeCommands(gaiadCmd, node) gaiadCmd.AddCommand( + initNodeCmd, + server.StartNodeCmd(app), + server.UnsafeResetAllCmd(app.Logger), version.VersionCmd, ) diff --git a/examples/gaia/gaiad/node.go b/examples/gaia/gaiad/node.go deleted file mode 100644 index 8d362a6a7807..000000000000 --- a/examples/gaia/gaiad/node.go +++ /dev/null @@ -1,47 +0,0 @@ -package main - -import ( - "github.com/spf13/cobra" - - "github.com/cosmos/cosmos-sdk/baseapp" -) - -const ( - flagWithTendermint = "with-tendermint" -) - -var ( - initNodeCmd = &cobra.Command{ - Use: "init ", - Short: "Initialize full node", - RunE: todoNotImplemented, - } - - resetNodeCmd = &cobra.Command{ - Use: "unsafe_reset_all", - Short: "Reset full node data (danger, must resync)", - RunE: todoNotImplemented, - } -) - -// AddNodeCommands registers all commands to interact -// with a local full-node as subcommands of the argument. -// -// Accept an application it should start -func AddNodeCommands(cmd *cobra.Command, node baseapp.BaseApp) { - cmd.AddCommand( - initNodeCmd, - startNodeCmd(node), - resetNodeCmd, - ) -} - -func startNodeCmd(node baseapp.BaseApp) *cobra.Command { - cmd := &cobra.Command{ - Use: "start", - Short: "Run the full node", - RunE: todoNotImplemented, - } - cmd.Flags().Bool(flagWithTendermint, true, "run abci app embedded in-process with tendermint") - return cmd -} diff --git a/server/reset.go b/server/reset.go new file mode 100644 index 000000000000..5c70bbdace14 --- /dev/null +++ b/server/reset.go @@ -0,0 +1,31 @@ +package server + +import ( + "github.com/spf13/cobra" + + tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" + "github.com/tendermint/tmlibs/log" +) + +// UnsafeResetAllCmd - extension of the tendermint command, resets initialization +func UnsafeResetAllCmd(logger log.Logger) *cobra.Command { + cmd := resetAll{logger} + return &cobra.Command{ + Use: "unsafe_reset_all", + Short: "Reset all blockchain data", + RunE: cmd.run, + } +} + +type resetAll struct { + logger log.Logger +} + +func (r resetAll) run(cmd *cobra.Command, args []string) error { + cfg, err := tcmd.ParseConfig() + if err != nil { + return err + } + tcmd.ResetAll(cfg.DBDir(), cfg.PrivValidatorFile(), r.logger) + return nil +} diff --git a/server/start.go b/server/start.go new file mode 100644 index 000000000000..137a0b6cc695 --- /dev/null +++ b/server/start.go @@ -0,0 +1,107 @@ +package server + +import ( + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/tendermint/abci/server" + + tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" + "github.com/tendermint/tendermint/node" + "github.com/tendermint/tendermint/proxy" + "github.com/tendermint/tendermint/types" + cmn "github.com/tendermint/tmlibs/common" + + "github.com/cosmos/cosmos-sdk/baseapp" +) + +const ( + flagWithTendermint = "with-tendermint" + flagAddress = "address" +) + +// StartNodeCmd runs the service passed in, either +// stand-alone, or in-process with tendermint +func StartNodeCmd(app *baseapp.BaseApp) *cobra.Command { + start := startNodeCmd{ + app: app, + } + cmd := &cobra.Command{ + Use: "start", + Short: "Run the full node", + RunE: start.run, + } + // basic flags for abci app + cmd.Flags().Bool(flagWithTendermint, true, "run abci app embedded in-process with tendermint") + cmd.Flags().String(flagAddress, "tcp://0.0.0.0:46658", "Listen address") + + // AddNodeFlags adds support for all + // tendermint-specific command line options + tcmd.AddNodeFlags(cmd) + return cmd +} + +type startNodeCmd struct { + // do this in main: + // rootDir := viper.GetString(cli.HomeFlag) + // node.Logger = .... + app *baseapp.BaseApp +} + +func (s startNodeCmd) run(cmd *cobra.Command, args []string) error { + logger := s.app.Logger + if !viper.GetBool(flagWithTendermint) { + logger.Info("Starting ABCI without Tendermint") + return s.startStandAlone() + } + logger.Info("Starting ABCI with Tendermint") + return s.startInProcess() +} + +func (s startNodeCmd) startStandAlone() error { + logger := s.app.Logger + // Start the ABCI listener + addr := viper.GetString(flagAddress) + svr, err := server.NewServer(addr, "socket", s.app) + 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() { + // Cleanup + svr.Stop() + }) + return nil +} + +func (s startNodeCmd) startInProcess() error { + logger := s.app.Logger + cfg, err := tcmd.ParseConfig() + if err != nil { + return err + } + + // Create & start tendermint node + n, err := node.NewNode(cfg, + types.LoadOrGenPrivValidatorFS(cfg.PrivValidatorFile()), + proxy.NewLocalClientCreator(s.app), + node.DefaultGenesisDocProviderFunc(cfg), + node.DefaultDBProvider, + logger.With("module", "node")) + if err != nil { + return err + } + + err = n.Start() + if err != nil { + return err + } + + // Trap signal, run forever. + n.RunForever() + return nil +} From 09e07bb44aa88343af831e4212a870c714fb9824 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 14 Feb 2018 21:53:06 +0100 Subject: [PATCH 25/70] Extracted initialization logic into server, default option generation in gaia --- examples/gaia/gaiad/main.go | 38 ++++++---- server/init.go | 145 ++++++++++++++++++++++++++++++++++++ server/start.go | 14 ++-- 3 files changed, 177 insertions(+), 20 deletions(-) create mode 100644 server/init.go diff --git a/examples/gaia/gaiad/main.go b/examples/gaia/gaiad/main.go index 63cfd45ddbe0..2f81828dbfe2 100644 --- a/examples/gaia/gaiad/main.go +++ b/examples/gaia/gaiad/main.go @@ -1,7 +1,8 @@ package main import ( - "errors" + "encoding/json" + "fmt" "os" "github.com/spf13/cobra" @@ -21,17 +22,28 @@ var ( } ) -// TODO: move into server -var ( - initNodeCmd = &cobra.Command{ - Use: "init ", - Short: "Initialize full node", - RunE: todoNotImplemented, +// defaultOptions sets up the app_options for the +// default genesis file +func defaultOptions(args []string) (json.RawMessage, error) { + addr, secret, err := server.GenerateCoinKey() + if err != nil { + return nil, err } -) - -func todoNotImplemented(_ *cobra.Command, _ []string) error { - return errors.New("TODO: Command not yet implemented") + fmt.Println("Secret phrase to access coins:") + fmt.Println(secret) + + opts := fmt.Sprintf(`{ + "accounts": [{ + "address": "%s", + "coins": [ + { + "denom": "mycoin", + "amount": 9007199254740992 + } + ] + }] + }`, addr) + return json.RawMessage(opts), nil } func main() { @@ -39,8 +51,8 @@ func main() { var app *baseapp.BaseApp gaiadCmd.AddCommand( - initNodeCmd, - server.StartNodeCmd(app), + server.InitCmd(defaultOptions), + server.StartCmd(app), server.UnsafeResetAllCmd(app.Logger), version.VersionCmd, ) diff --git a/server/init.go b/server/init.go new file mode 100644 index 000000000000..374e3cb3fe1d --- /dev/null +++ b/server/init.go @@ -0,0 +1,145 @@ +package server + +import ( + "encoding/json" + "fmt" + "io/ioutil" + + "github.com/spf13/cobra" + + crypto "github.com/tendermint/go-crypto" + "github.com/tendermint/go-crypto/keys" + "github.com/tendermint/go-crypto/keys/words" + tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" + tmtypes "github.com/tendermint/tendermint/types" +) + +// InitCmd will initialize all files for tendermint, +// along with proper app_options. +// The application can pass in a function to generate +// proper options. And may want to use GenerateCoinKey +// to create default account(s). +func InitCmd(gen GenOptions) *cobra.Command { + cmd := initCmd{ + gen: gen, + } + return &cobra.Command{ + Use: "init", + Short: "Initialize genesis files", + RunE: cmd.run, + } +} + +// GenOptions can parse command-line and flag to +// generate default app_options for the genesis file. +// This is application-specific +type GenOptions func(args []string) (json.RawMessage, error) + +// GenerateCoinKey returns the address of a public key, +// along with the secret phrase to recover the private key. +// You can give coins to this address and return the recovery +// phrase to the user to access them. +func GenerateCoinKey() (crypto.Address, string, error) { + // construct an in-memory key store + codec, err := words.LoadCodec("english") + if err != nil { + return nil, "", err + } + keybase := keys.New( + dbm.NewMemDB(), + codec, + ) + + // generate a private key, with recovery phrase + info, secret, err := keybase.Create("name", "pass", keys.AlgoEd25519) + if err != nil { + return nil, "", err + } + addr := info.PubKey.Address() + return addr, secret, nil +} + +type initCmd struct { + gen GenOptions +} + +func (c initCmd) run(cmd *cobra.Command, args []string) error { + // Run the basic tendermint initialization, + // set up a default genesis with no app_options + cfg, err := tcmd.ParseConfig() + if err != nil { + return err + } + tcmd.InitFilesCmd.Run(cmd, args) + + // no app_options, leave like tendermint + if c.gen == nil { + return nil + } + + // Now, we want to add the custom app_options + options, err := c.gen(args) + if err != nil { + return err + } + + // And add them to the genesis file + genFile := cfg.GenesisFile() + return addGenesisOptions(genFile, options) +} + +func addGenesisOptions(filename string, options json.RawMessage) error { + bz, err := ioutil.ReadFile(filename) + if err != nil { + return err + } + + var doc tmtypes.GenesisDoc + err = json.Unmarshal(bz, &doc) + if err != nil { + return err + } + + doc.AppOptions = options + out, err := json.MarshalIndent(doc, "", " ") + if err != nil { + return err + } + + return ioutil.WriteFile(filename, out, 0600) +} + +// GetGenesisJSON returns a new tendermint genesis with Basecoin app_options +// that grant a large amount of "mycoin" to a single address +// TODO: A better UX for generating genesis files +func GetGenesisJSON(pubkey, chainID, denom, addr string, options string) string { + return fmt.Sprintf(`{ + "app_hash": "", + "chain_id": "%s", + "genesis_time": "0001-01-01T00:00:00.000Z", + "validators": [ + { + "power": 10, + "name": "", + "pub_key": { + "type": "ed25519", + "data": "%s" + } + } + ], + "app_options": { + "accounts": [{ + "address": "%s", + "coins": [ + { + "denom": "%s", + "amount": 9007199254740992 + } + ] + }], + "plugin_options": [ + "coin/issuer", {"app": "sigs", "addr": "%s"}%s + ] + } +}`, chainID, pubkey, addr, denom, addr, options) +} diff --git a/server/start.go b/server/start.go index 137a0b6cc695..d369ff0167a4 100644 --- a/server/start.go +++ b/server/start.go @@ -21,10 +21,10 @@ const ( flagAddress = "address" ) -// StartNodeCmd runs the service passed in, either +// StartCmd runs the service passed in, either // stand-alone, or in-process with tendermint -func StartNodeCmd(app *baseapp.BaseApp) *cobra.Command { - start := startNodeCmd{ +func StartCmd(app *baseapp.BaseApp) *cobra.Command { + start := startCmd{ app: app, } cmd := &cobra.Command{ @@ -42,14 +42,14 @@ func StartNodeCmd(app *baseapp.BaseApp) *cobra.Command { return cmd } -type startNodeCmd struct { +type startCmd struct { // do this in main: // rootDir := viper.GetString(cli.HomeFlag) // node.Logger = .... app *baseapp.BaseApp } -func (s startNodeCmd) run(cmd *cobra.Command, args []string) error { +func (s startCmd) run(cmd *cobra.Command, args []string) error { logger := s.app.Logger if !viper.GetBool(flagWithTendermint) { logger.Info("Starting ABCI without Tendermint") @@ -59,7 +59,7 @@ func (s startNodeCmd) run(cmd *cobra.Command, args []string) error { return s.startInProcess() } -func (s startNodeCmd) startStandAlone() error { +func (s startCmd) startStandAlone() error { logger := s.app.Logger // Start the ABCI listener addr := viper.GetString(flagAddress) @@ -78,7 +78,7 @@ func (s startNodeCmd) startStandAlone() error { return nil } -func (s startNodeCmd) startInProcess() error { +func (s startCmd) startInProcess() error { logger := s.app.Logger cfg, err := tcmd.ParseConfig() if err != nil { From c0f9a6f1cbee81daf88bbc1c27ae8b1c348d43c5 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 21 Feb 2018 10:41:35 +0100 Subject: [PATCH 26/70] Update dependencies to import tendermint --- glide.lock | 70 +++++++++++++++++++++++++++++++++++++++++++++++--- glide.yaml | 8 ++++++ server/init.go | 1 + 3 files changed, 75 insertions(+), 4 deletions(-) diff --git a/glide.lock b/glide.lock index 51b7db0b86f1..84aac00a8b51 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 2b4ad3bf1489a7cb5e62c6cb4c1fa976d4ae21993743e4968418c4e81925fb99 -updated: 2018-02-19T17:13:04.368106064Z +hash: 89fd2ad769c283b6b29b02b84dd6459a40a47de5dfdfb15d7ced95d53ee9ec18 +updated: 2018-02-21T10:37:20.800957+01:00 imports: - name: github.com/btcsuite/btcd version: 50de9da05b50eb15658bb350f6ea24368a111ab7 @@ -9,6 +9,8 @@ imports: version: 346938d642f2ec3594ed81d874461961cd0faa76 subpackages: - spew +- name: github.com/ebuchman/fail-test + version: 95f809107225be108efcf10a3509e4ea6ceef3c4 - name: github.com/fsnotify/fsnotify version: c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9 - name: github.com/go-kit/kit @@ -40,6 +42,8 @@ imports: - ptypes/timestamp - name: github.com/golang/snappy version: 553a641470496b2327abcac10b36396bd98e45c9 +- name: github.com/gorilla/websocket + version: ea4d1f681babbce9545c9c5f3d5194a789c89f5b - name: github.com/hashicorp/hcl version: 23c074d0eceb2b8a5bfdbb271ab780cde70f05a8 subpackages: @@ -51,6 +55,8 @@ imports: - json/parser - json/scanner - json/token +- name: github.com/howeyc/crc16 + version: 96a97a1abb579c7ff1a8ffa77f2e72d1c314b57f - name: github.com/inconshreveable/mousetrap version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 - name: github.com/jmhodges/levigo @@ -65,6 +71,8 @@ imports: version: acdc4509485b587f5e675510c4f2c63e90ff68a8 - name: github.com/pkg/errors version: 645ef00459ed84a119197bfb8d8205042c6df63d +- name: github.com/rcrowley/go-metrics + version: 1f30fe9094a513ce4c700b9a54458bbb0c96996c - name: github.com/rigelrozanski/common version: f691f115798593d783b9999b1263c2f4ffecc439 - name: github.com/spf13/afero @@ -97,8 +105,11 @@ imports: - leveldb/table - leveldb/util - name: github.com/tendermint/abci - version: c960c5275617ef141c92c3d7fc65a396c97662df + version: 68592f4d8ee34e97db94b7a7976b1309efdb7eb9 subpackages: + - client + - example/code + - example/dummy - server - types - name: github.com/tendermint/ed25519 @@ -108,21 +119,72 @@ imports: - extra25519 - name: github.com/tendermint/go-crypto version: 4fc3055dbd17aa1203d0abc64b9293f378da22ec + subpackages: + - keys + - keys/bcrypt + - keys/words + - keys/words/wordlist - name: github.com/tendermint/go-wire version: 5d7845f24b843c914cf571dad2ca13c91cf70f0d - name: github.com/tendermint/iavl version: 1a59ec0c82dc940c25339dd7c834df5cb76a95cb +- name: github.com/tendermint/tendermint + version: edeebb7d202467179d1cf6372fd014d36bebf961 + subpackages: + - blockchain + - cmd/tendermint/commands + - config + - consensus + - consensus/types + - evidence + - lite + - lite/client + - lite/errors + - lite/files + - lite/proxy + - mempool + - node + - p2p + - p2p/conn + - p2p/pex + - p2p/trust + - 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 + - wire - name: github.com/tendermint/tmlibs - version: a0f652dc2e131be86fc8d9e4e2beec9831a8a6ec + version: 1b9b5652a199ab0be2e781393fb275b66377309d subpackages: + - autofile - cli + - cli/flags + - clist - common - db + - flowrate - log - merkle + - pubsub + - pubsub/query - name: golang.org/x/crypto version: 1875d0a70c90e57f11972aefd42276df65e895b9 subpackages: + - blowfish + - curve25519 + - nacl/box - nacl/secretbox - openpgp/armor - openpgp/errors diff --git a/glide.yaml b/glide.yaml index 276513b2cebc..b6f950f2e344 100644 --- a/glide.yaml +++ b/glide.yaml @@ -25,6 +25,14 @@ import: - db - log - merkle +- package: github.com/tendermint/tendermint + version: breaking/wire-sdk2 + subpackages: + - cmd/tendermint/commands + - config + - lite + - rpc/client + - types - package: golang.org/x/crypto subpackages: - ripemd160 diff --git a/server/init.go b/server/init.go index 374e3cb3fe1d..73725093846e 100644 --- a/server/init.go +++ b/server/init.go @@ -12,6 +12,7 @@ import ( "github.com/tendermint/go-crypto/keys/words" tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" tmtypes "github.com/tendermint/tendermint/types" + dbm "github.com/tendermint/tmlibs/db" ) // InitCmd will initialize all files for tendermint, From 4e91a0db899f0ee60c6e87308edb3130520cc419 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 21 Feb 2018 10:57:51 +0100 Subject: [PATCH 27/70] Add cli to basecoind, fix compatability issues --- examples/basecoin/cmd/basecoind/main.go | 57 ++++++++++++++++++++++--- examples/gaia/gaiad/main.go | 2 +- server/start.go | 23 +++++----- 3 files changed, 62 insertions(+), 20 deletions(-) diff --git a/examples/basecoin/cmd/basecoind/main.go b/examples/basecoin/cmd/basecoind/main.go index f14867f407af..170fb15e27ea 100644 --- a/examples/basecoin/cmd/basecoind/main.go +++ b/examples/basecoin/cmd/basecoind/main.go @@ -1,27 +1,70 @@ package main import ( + "encoding/json" "fmt" "os" + "github.com/spf13/cobra" + + "github.com/tendermint/tmlibs/cli" dbm "github.com/tendermint/tmlibs/db" "github.com/tendermint/tmlibs/log" - "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/examples/basecoin/app" + "github.com/cosmos/cosmos-sdk/server" + "github.com/cosmos/cosmos-sdk/version" ) -func main() { - fmt.Println("This is temporary, for unblocking our build process.") - return +// gaiadCmd is the entry point for this binary +var ( + gaiadCmd = &cobra.Command{ + Use: "gaiad", + Short: "Gaia Daemon (server)", + } +) + +// defaultOptions sets up the app_options for the +// default genesis file +func defaultOptions(args []string) (json.RawMessage, error) { + addr, secret, err := server.GenerateCoinKey() + if err != nil { + return nil, err + } + fmt.Println("Secret phrase to access coins:") + fmt.Println(secret) + + opts := fmt.Sprintf(`{ + "accounts": [{ + "address": "%s", + "coins": [ + { + "denom": "mycoin", + "amount": 9007199254740992 + } + ] + }] + }`, addr) + return json.RawMessage(opts), nil +} - // TODO CREATE CLI +func main() { logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "main") - db, err := dbm.NewGoLevelDB("basecoind", "data") + db, err := dbm.NewGoLevelDB("/tmp/basecoind", "data") if err != nil { fmt.Println(err) os.Exit(1) } bapp := app.NewBasecoinApp(logger, db) - baseapp.RunForever(bapp) + + gaiadCmd.AddCommand( + server.InitCmd(defaultOptions), + server.StartCmd(bapp, bapp.Logger), + server.UnsafeResetAllCmd(bapp.Logger), + version.VersionCmd, + ) + + // prepare and add flags + executor := cli.PrepareBaseCmd(gaiadCmd, "GA", os.ExpandEnv("$HOME/.gaiad")) + executor.Execute() } diff --git a/examples/gaia/gaiad/main.go b/examples/gaia/gaiad/main.go index 2f81828dbfe2..d0e1262a1253 100644 --- a/examples/gaia/gaiad/main.go +++ b/examples/gaia/gaiad/main.go @@ -52,7 +52,7 @@ func main() { gaiadCmd.AddCommand( server.InitCmd(defaultOptions), - server.StartCmd(app), + server.StartCmd(app, app.Logger), server.UnsafeResetAllCmd(app.Logger), version.VersionCmd, ) diff --git a/server/start.go b/server/start.go index d369ff0167a4..909f04dc2f3f 100644 --- a/server/start.go +++ b/server/start.go @@ -6,14 +6,14 @@ import ( "github.com/spf13/viper" "github.com/tendermint/abci/server" + abci "github.com/tendermint/abci/types" tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/types" cmn "github.com/tendermint/tmlibs/common" - - "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/tendermint/tmlibs/log" ) const ( @@ -23,9 +23,10 @@ const ( // StartCmd runs the service passed in, either // stand-alone, or in-process with tendermint -func StartCmd(app *baseapp.BaseApp) *cobra.Command { +func StartCmd(app abci.Application, logger log.Logger) *cobra.Command { start := startCmd{ - app: app, + app: app, + logger: logger, } cmd := &cobra.Command{ Use: "start", @@ -46,28 +47,27 @@ type startCmd struct { // do this in main: // rootDir := viper.GetString(cli.HomeFlag) // node.Logger = .... - app *baseapp.BaseApp + app abci.Application + logger log.Logger } func (s startCmd) run(cmd *cobra.Command, args []string) error { - logger := s.app.Logger if !viper.GetBool(flagWithTendermint) { - logger.Info("Starting ABCI without Tendermint") + s.logger.Info("Starting ABCI without Tendermint") return s.startStandAlone() } - logger.Info("Starting ABCI with Tendermint") + s.logger.Info("Starting ABCI with Tendermint") return s.startInProcess() } func (s startCmd) startStandAlone() error { - logger := s.app.Logger // Start the ABCI listener addr := viper.GetString(flagAddress) svr, err := server.NewServer(addr, "socket", s.app) if err != nil { return errors.Errorf("Error creating listener: %v\n", err) } - svr.SetLogger(logger.With("module", "abci-server")) + svr.SetLogger(s.logger.With("module", "abci-server")) svr.Start() // Wait forever @@ -79,7 +79,6 @@ func (s startCmd) startStandAlone() error { } func (s startCmd) startInProcess() error { - logger := s.app.Logger cfg, err := tcmd.ParseConfig() if err != nil { return err @@ -91,7 +90,7 @@ func (s startCmd) startInProcess() error { proxy.NewLocalClientCreator(s.app), node.DefaultGenesisDocProviderFunc(cfg), node.DefaultDBProvider, - logger.With("module", "node")) + s.logger.With("module", "node")) if err != nil { return err } From 94ddda6a1f18222f923a3ebc1bdb913a7b71efab Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 21 Feb 2018 11:38:48 +0100 Subject: [PATCH 28/70] Copy init code from tendermint so it runs properly --- examples/basecoin/cmd/basecoind/main.go | 10 ++-- examples/gaia/gaiad/main.go | 4 +- server/init.go | 64 ++++++++++++++++++++++--- 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/examples/basecoin/cmd/basecoind/main.go b/examples/basecoin/cmd/basecoind/main.go index 170fb15e27ea..786c0f3f1ed3 100644 --- a/examples/basecoin/cmd/basecoind/main.go +++ b/examples/basecoin/cmd/basecoind/main.go @@ -49,8 +49,12 @@ func defaultOptions(args []string) (json.RawMessage, error) { } func main() { + // TODO: this should somehow be updated on cli flags? + // But we need to create the app first... hmmm..... + rootDir := os.ExpandEnv("$HOME/.basecoind") + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "main") - db, err := dbm.NewGoLevelDB("/tmp/basecoind", "data") + db, err := dbm.NewGoLevelDB("basecoin", rootDir) if err != nil { fmt.Println(err) os.Exit(1) @@ -58,13 +62,13 @@ func main() { bapp := app.NewBasecoinApp(logger, db) gaiadCmd.AddCommand( - server.InitCmd(defaultOptions), + server.InitCmd(defaultOptions, bapp.Logger), server.StartCmd(bapp, bapp.Logger), server.UnsafeResetAllCmd(bapp.Logger), version.VersionCmd, ) // prepare and add flags - executor := cli.PrepareBaseCmd(gaiadCmd, "GA", os.ExpandEnv("$HOME/.gaiad")) + executor := cli.PrepareBaseCmd(gaiadCmd, "BC", rootDir) executor.Execute() } diff --git a/examples/gaia/gaiad/main.go b/examples/gaia/gaiad/main.go index d0e1262a1253..fb5c0b20f5bd 100644 --- a/examples/gaia/gaiad/main.go +++ b/examples/gaia/gaiad/main.go @@ -48,10 +48,10 @@ func defaultOptions(args []string) (json.RawMessage, error) { func main() { // TODO: set this to something real - var app *baseapp.BaseApp + app := new(baseapp.BaseApp) gaiadCmd.AddCommand( - server.InitCmd(defaultOptions), + server.InitCmd(defaultOptions, app.Logger), server.StartCmd(app, app.Logger), server.UnsafeResetAllCmd(app.Logger), version.VersionCmd, diff --git a/server/init.go b/server/init.go index 73725093846e..7e6eaf78ccab 100644 --- a/server/init.go +++ b/server/init.go @@ -10,9 +10,13 @@ import ( crypto "github.com/tendermint/go-crypto" "github.com/tendermint/go-crypto/keys" "github.com/tendermint/go-crypto/keys/words" + cmn "github.com/tendermint/tmlibs/common" + dbm "github.com/tendermint/tmlibs/db" + "github.com/tendermint/tmlibs/log" + tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" + cfg "github.com/tendermint/tendermint/config" tmtypes "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tmlibs/db" ) // InitCmd will initialize all files for tendermint, @@ -20,9 +24,10 @@ import ( // The application can pass in a function to generate // proper options. And may want to use GenerateCoinKey // to create default account(s). -func InitCmd(gen GenOptions) *cobra.Command { +func InitCmd(gen GenOptions, logger log.Logger) *cobra.Command { cmd := initCmd{ - gen: gen, + gen: gen, + logger: logger, } return &cobra.Command{ Use: "init", @@ -56,22 +61,31 @@ func GenerateCoinKey() (crypto.Address, string, error) { if err != nil { return nil, "", err } + + // debug + bz, err := json.Marshal(info.PubKey) + fmt.Printf("PubKey: %s\n", string(bz)) + addr := info.PubKey.Address() return addr, secret, nil } type initCmd struct { - gen GenOptions + gen GenOptions + logger log.Logger } func (c initCmd) run(cmd *cobra.Command, args []string) error { // Run the basic tendermint initialization, // set up a default genesis with no app_options - cfg, err := tcmd.ParseConfig() + config, err := tcmd.ParseConfig() + if err != nil { + return err + } + err = c.initTendermintFiles(config) if err != nil { return err } - tcmd.InitFilesCmd.Run(cmd, args) // no app_options, leave like tendermint if c.gen == nil { @@ -85,10 +99,46 @@ func (c initCmd) run(cmd *cobra.Command, args []string) error { } // And add them to the genesis file - genFile := cfg.GenesisFile() + genFile := config.GenesisFile() return addGenesisOptions(genFile, options) } +// This was copied from tendermint/cmd/tendermint/commands/init.go +// so we could pass in the config and the logger. +func (c initCmd) initTendermintFiles(config *cfg.Config) error { + // private validator + privValFile := config.PrivValidatorFile() + var privValidator *tmtypes.PrivValidatorFS + if cmn.FileExists(privValFile) { + privValidator = tmtypes.LoadPrivValidatorFS(privValFile) + c.logger.Info("Found private validator", "path", privValFile) + } else { + privValidator = tmtypes.GenPrivValidatorFS(privValFile) + privValidator.Save() + c.logger.Info("Genetated private validator", "path", privValFile) + } + + // genesis file + genFile := config.GenesisFile() + if cmn.FileExists(genFile) { + c.logger.Info("Found genesis file", "path", genFile) + } else { + genDoc := tmtypes.GenesisDoc{ + ChainID: cmn.Fmt("test-chain-%v", cmn.RandStr(6)), + } + genDoc.Validators = []tmtypes.GenesisValidator{{ + PubKey: privValidator.GetPubKey(), + Power: 10, + }} + + if err := genDoc.SaveAs(genFile); err != nil { + return err + } + c.logger.Info("Genetated genesis file", "path", genFile) + } + return nil +} + func addGenesisOptions(filename string, options json.RawMessage) error { bz, err := ioutil.ReadFile(filename) if err != nil { From 603c35857428c2724620b47a1e283567d951fad0 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 21 Feb 2018 12:49:56 +0100 Subject: [PATCH 29/70] Fix up server app_options initialization --- server/init.go | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/server/init.go b/server/init.go index 7e6eaf78ccab..9006149c6e00 100644 --- a/server/init.go +++ b/server/init.go @@ -139,19 +139,24 @@ func (c initCmd) initTendermintFiles(config *cfg.Config) error { return nil } +// GenesisDoc involves some tendermint-specific structures we don't +// want to parse, so we just grab it into a raw object format, +// so we can add one line. +type GenesisDoc map[string]json.RawMessage + func addGenesisOptions(filename string, options json.RawMessage) error { bz, err := ioutil.ReadFile(filename) if err != nil { return err } - var doc tmtypes.GenesisDoc + var doc GenesisDoc err = json.Unmarshal(bz, &doc) if err != nil { return err } - doc.AppOptions = options + doc["app_state"] = options out, err := json.MarshalIndent(doc, "", " ") if err != nil { return err @@ -165,20 +170,6 @@ func addGenesisOptions(filename string, options json.RawMessage) error { // TODO: A better UX for generating genesis files func GetGenesisJSON(pubkey, chainID, denom, addr string, options string) string { return fmt.Sprintf(`{ - "app_hash": "", - "chain_id": "%s", - "genesis_time": "0001-01-01T00:00:00.000Z", - "validators": [ - { - "power": 10, - "name": "", - "pub_key": { - "type": "ed25519", - "data": "%s" - } - } - ], - "app_options": { "accounts": [{ "address": "%s", "coins": [ @@ -191,6 +182,5 @@ func GetGenesisJSON(pubkey, chainID, denom, addr string, options string) string "plugin_options": [ "coin/issuer", {"app": "sigs", "addr": "%s"}%s ] - } }`, chainID, pubkey, addr, denom, addr, options) } From f93947f3ab780bdefbc89fb0580ec626f07163cc Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 21 Feb 2018 13:03:14 +0100 Subject: [PATCH 30/70] Update tendermint, can parse genesis in start --- baseapp/baseapp.go | 9 +++++++++ glide.lock | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index e5eb59538c70..70b1d299f3cb 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -236,6 +236,15 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC // NOTE: we don't commit, but BeginBlock for block 1 // starts from this deliverState + // TODO: d00d, tendermint sends nil, until that is fixed, we gotta + // read in the genesis file ourselves here + res = app.initChainer(ctx, req) + // TODO: handle error https://github.com/cosmos/cosmos-sdk/issues/468 + + // XXX this commits everything and bumps the version. + // https://github.com/cosmos/cosmos-sdk/issues/442#issuecomment-366470148 + app.cms.Commit() + return } diff --git a/glide.lock b/glide.lock index 84aac00a8b51..d5db865da33c 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: 89fd2ad769c283b6b29b02b84dd6459a40a47de5dfdfb15d7ced95d53ee9ec18 -updated: 2018-02-21T10:37:20.800957+01:00 +updated: 2018-02-21T12:53:23.608467+01:00 imports: - name: github.com/btcsuite/btcd version: 50de9da05b50eb15658bb350f6ea24368a111ab7 @@ -129,7 +129,7 @@ imports: - name: github.com/tendermint/iavl version: 1a59ec0c82dc940c25339dd7c834df5cb76a95cb - name: github.com/tendermint/tendermint - version: edeebb7d202467179d1cf6372fd014d36bebf961 + version: b24792244d6d03be5a566fd48572743faf3c02e0 subpackages: - blockchain - cmd/tendermint/commands From 1f31fbeea89275ea5b75ba65bb3de2ad240b715f Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 21 Feb 2018 16:04:54 +0100 Subject: [PATCH 31/70] Start writing test scaffold to test server --- mock/app.go | 120 +++++++++++++++++++++++++++++++++++++++++++ mock/helpers.go | 27 ++++++++++ mock/tx.go | 76 +++++++++++++++++++++++++++ server/init.go | 4 -- server/init_test.go | 7 +++ server/start_test.go | 15 ++++++ 6 files changed, 245 insertions(+), 4 deletions(-) create mode 100644 mock/app.go create mode 100644 mock/helpers.go create mode 100644 mock/tx.go create mode 100644 server/init_test.go create mode 100644 server/start_test.go diff --git a/mock/app.go b/mock/app.go new file mode 100644 index 000000000000..554879f0425f --- /dev/null +++ b/mock/app.go @@ -0,0 +1,120 @@ +package mock + +import ( + "encoding/json" + "fmt" + + abci "github.com/tendermint/abci/types" + dbm "github.com/tendermint/tmlibs/db" + "github.com/tendermint/tmlibs/log" + + bam "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// NewApp creates a simple mock kvstore app for testing. +// It should work similar to a real app. +// Make sure rootDir is empty before running the test, +// in order to guarantee consistent results +func NewApp(logger log.Logger, rootDir string) (abci.Application, error) { + db, err := dbm.NewGoLevelDB("mock", rootDir) + if err != nil { + return nil, err + } + + // Capabilities key to access the main KVStore. + capKeyMainStore := sdk.NewKVStoreKey("main") + + // Create BaseApp. + baseApp := bam.NewBaseApp("kvstore", logger, db) + + // Set mounts for BaseApp's MultiStore. + baseApp.MountStoresIAVL(capKeyMainStore) + + // Set Tx decoder + baseApp.SetTxDecoder(decodeTx) + + baseApp.SetInitChainer(InitChainer(capKeyMainStore)) + + // Set a handler Route. + baseApp.Router().AddRoute("kvstore", KVStoreHandler(capKeyMainStore)) + + // Load latest version. + if err := baseApp.LoadLatestVersion(capKeyMainStore); err != nil { + return nil, err + } + + return baseApp, nil +} + +// KVStoreHandler is a simple handler that takes kvstoreTx and writes +// them to the db +func KVStoreHandler(storeKey sdk.StoreKey) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + dTx, ok := msg.(kvstoreTx) + if !ok { + panic("KVStoreHandler should only receive kvstoreTx") + } + + // tx is already unmarshalled + key := dTx.key + value := dTx.value + + store := ctx.KVStore(storeKey) + store.Set(key, value) + + return sdk.Result{ + Code: 0, + Log: fmt.Sprintf("set %s=%s", key, value), + } + } +} + +type KV struct { + Key string `json:"key"` + Value string `json:"value"` +} + +type GenesisJSON struct { + Values []KV `json:"values"` +} + +// InitChainer returns a function that can initialize the chain +// with key/value pairs +func InitChainer(key sdk.StoreKey) func(sdk.Context, abci.RequestInitChain) abci.ResponseInitChain { + return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { + stateJSON := req.AppStateBytes + + genesisState := new(GenesisJSON) + err := json.Unmarshal(stateJSON, genesisState) + if err != nil { + panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468 + // return sdk.ErrGenesisParse("").TraceCause(err, "") + } + + for _, val := range genesisState.Values { + store := ctx.KVStore(key) + store.Set([]byte(val.Key), []byte(val.Value)) + } + return abci.ResponseInitChain{} + } +} + +// GenInitOptions can be passed into InitCmd, +// returns a static string of a few key-values that can be parsed +// by InitChainer +func GenInitOptions(args []string) (json.RawMessage, error) { + opts := []byte(`{ + "values": [ + { + "key": "hello", + "value": "goodbye" + }, + { + "key": "foo", + "value": "bar" + } + ] +}`) + return opts, nil +} diff --git a/mock/helpers.go b/mock/helpers.go new file mode 100644 index 000000000000..4b6f70ba75b2 --- /dev/null +++ b/mock/helpers.go @@ -0,0 +1,27 @@ +package mock + +import ( + "io/ioutil" + "os" + + abci "github.com/tendermint/abci/types" + "github.com/tendermint/tmlibs/log" +) + +// SetupApp returns an application as well as a clean-up function +// to be used to quickly setup a test case with an app +func SetupApp() (abci.Application, func(), error) { + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)). + With("module", "mock") + rootDir, err := ioutil.TempDir("mock-sdk", "") + if err != nil { + return nil, nil, err + } + + cleanup := func() { + os.RemoveAll(rootDir) + } + + app, err := NewApp(logger, rootDir) + return app, cleanup, err +} diff --git a/mock/tx.go b/mock/tx.go new file mode 100644 index 000000000000..077c090e6034 --- /dev/null +++ b/mock/tx.go @@ -0,0 +1,76 @@ +package mock + +import ( + "bytes" + + sdk "github.com/cosmos/cosmos-sdk/types" + crypto "github.com/tendermint/go-crypto" +) + +// An sdk.Tx which is its own sdk.Msg. +type kvstoreTx struct { + key []byte + value []byte + bytes []byte +} + +func (tx kvstoreTx) Get(key interface{}) (value interface{}) { + switch k := key.(type) { + case string: + switch k { + case "key": + return tx.key + case "value": + return tx.value + } + } + return nil +} + +func (tx kvstoreTx) Type() string { + return "kvstore" +} + +func (tx kvstoreTx) GetMsg() sdk.Msg { + return tx +} + +func (tx kvstoreTx) GetSignBytes() []byte { + return tx.bytes +} + +// Should the app be calling this? Or only handlers? +func (tx kvstoreTx) ValidateBasic() sdk.Error { + return nil +} + +func (tx kvstoreTx) GetSigners() []crypto.Address { + return nil +} + +func (tx kvstoreTx) GetSignatures() []sdk.StdSignature { + return nil +} + +func (tx kvstoreTx) GetFeePayer() crypto.Address { + return nil +} + +// takes raw transaction bytes and decodes them into an sdk.Tx. An sdk.Tx has +// all the signatures and can be used to authenticate. +func decodeTx(txBytes []byte) (sdk.Tx, sdk.Error) { + var tx sdk.Tx + + split := bytes.Split(txBytes, []byte("=")) + if len(split) == 1 { + k := split[0] + tx = kvstoreTx{k, k, txBytes} + } else if len(split) == 2 { + k, v := split[0], split[1] + tx = kvstoreTx{k, v, txBytes} + } else { + return nil, sdk.ErrTxParse("too many =") + } + + return tx, nil +} diff --git a/server/init.go b/server/init.go index 9006149c6e00..b8071b0e563c 100644 --- a/server/init.go +++ b/server/init.go @@ -62,10 +62,6 @@ func GenerateCoinKey() (crypto.Address, string, error) { return nil, "", err } - // debug - bz, err := json.Marshal(info.PubKey) - fmt.Printf("PubKey: %s\n", string(bz)) - addr := info.PubKey.Address() return addr, secret, nil } diff --git a/server/init_test.go b/server/init_test.go new file mode 100644 index 000000000000..3d7e5671d55c --- /dev/null +++ b/server/init_test.go @@ -0,0 +1,7 @@ +package server + +import "testing" + +func TestInit(t *testing.T) { + +} diff --git a/server/start_test.go b/server/start_test.go new file mode 100644 index 000000000000..10c012ecf6d9 --- /dev/null +++ b/server/start_test.go @@ -0,0 +1,15 @@ +package server + +import ( + "testing" +) + +func TestStart(t *testing.T) { + // app, cleanup, err := mock.SetupApp() + // if cleanup != nil { + // defer cleanup() + // } + + // require.NoError(t, err) + // // TODO: init and start +} From 3ea9634291bcd36fcb1746dd77df2f6560f3bdc3 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 21 Feb 2018 16:14:49 +0100 Subject: [PATCH 32/70] Test init/query mock app --- mock/app_test.go | 36 ++++++++++++++++++++++++++++++++++++ mock/helpers.go | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 mock/app_test.go diff --git a/mock/app_test.go b/mock/app_test.go new file mode 100644 index 000000000000..78236282757d --- /dev/null +++ b/mock/app_test.go @@ -0,0 +1,36 @@ +package mock + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + abci "github.com/tendermint/abci/types" +) + +// TestInitApp makes sure we can initialize this thing without an error +func TestInitApp(t *testing.T) { + // set up an app + app, closer, err := SetupApp() + // closer may need to be run, even when error in later stage + if closer != nil { + defer closer() + } + require.NoError(t, err) + + // initialize it future-way + opts, err := GenInitOptions(nil) + require.NoError(t, err) + req := abci.RequestInitChain{AppStateBytes: opts} + app.InitChain(req) + + // make sure we can query these values + query := abci.RequestQuery{ + Path: "/main/key", + Data: []byte("foo"), + } + qres := app.Query(query) + require.Equal(t, uint32(0), qres.Code, qres.Log) + assert.Equal(t, []byte("bar"), qres.Value) +} diff --git a/mock/helpers.go b/mock/helpers.go index 4b6f70ba75b2..4e30eaa30e69 100644 --- a/mock/helpers.go +++ b/mock/helpers.go @@ -13,7 +13,7 @@ import ( func SetupApp() (abci.Application, func(), error) { logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)). With("module", "mock") - rootDir, err := ioutil.TempDir("mock-sdk", "") + rootDir, err := ioutil.TempDir("", "mock-sdk") if err != nil { return nil, nil, err } From cdd33aa04b5ebf1cdbb4c1479bd97ae39f18c5d1 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 21 Feb 2018 16:28:18 +0100 Subject: [PATCH 33/70] mock app tests now work --- mock/app_test.go | 38 ++++++++++++++++++++++++++++++++++++++ mock/tx.go | 12 ++++++++++++ 2 files changed, 50 insertions(+) diff --git a/mock/app_test.go b/mock/app_test.go index 78236282757d..f21abd594fdf 100644 --- a/mock/app_test.go +++ b/mock/app_test.go @@ -33,4 +33,42 @@ func TestInitApp(t *testing.T) { qres := app.Query(query) require.Equal(t, uint32(0), qres.Code, qres.Log) assert.Equal(t, []byte("bar"), qres.Value) + +} + +// TextDeliverTx ensures we can write a tx +func TestDeliverTx(t *testing.T) { + // set up an app + app, closer, err := SetupApp() + // closer may need to be run, even when error in later stage + if closer != nil { + defer closer() + } + require.NoError(t, err) + + key := "my-special-key" + value := "top-secret-data!!" + tx := NewTx(key, value) + txBytes := tx.GetSignBytes() + + header := abci.Header{ + AppHash: []byte("apphash"), + Height: 1, + } + app.BeginBlock(abci.RequestBeginBlock{Header: header}) + dres := app.DeliverTx(txBytes) + require.Equal(t, uint32(0), dres.Code, dres.Log) + app.EndBlock(abci.RequestEndBlock{}) + cres := app.Commit() + require.NotEmpty(t, cres.Data) + + // make sure we can query these values + query := abci.RequestQuery{ + Path: "/main/key", + Data: []byte(key), + } + qres := app.Query(query) + require.Equal(t, uint32(0), qres.Code, qres.Log) + assert.Equal(t, []byte(value), qres.Value) + } diff --git a/mock/tx.go b/mock/tx.go index 077c090e6034..970549d2cdee 100644 --- a/mock/tx.go +++ b/mock/tx.go @@ -2,6 +2,7 @@ package mock import ( "bytes" + "fmt" sdk "github.com/cosmos/cosmos-sdk/types" crypto "github.com/tendermint/go-crypto" @@ -14,6 +15,17 @@ type kvstoreTx struct { bytes []byte } +var _ sdk.Tx = kvstoreTx{} + +func NewTx(key, value string) kvstoreTx { + bytes := fmt.Sprintf("%s=%s", key, value) + return kvstoreTx{ + key: []byte(key), + value: []byte(value), + bytes: []byte(bytes), + } +} + func (tx kvstoreTx) Get(key interface{}) (value interface{}) { switch k := key.(type) { case string: From d694dbe7c18a449aa38c757e60eb0899b3536fb1 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 21 Feb 2018 16:41:56 +0100 Subject: [PATCH 34/70] Add first server command tests --- server/init.go | 4 ++-- server/init_test.go | 31 ++++++++++++++++++++++++++++++- server/start_test.go | 31 ++++++++++++++++++++++++++----- 3 files changed, 58 insertions(+), 8 deletions(-) diff --git a/server/init.go b/server/init.go index b8071b0e563c..a56ad5c37d7b 100644 --- a/server/init.go +++ b/server/init.go @@ -111,7 +111,7 @@ func (c initCmd) initTendermintFiles(config *cfg.Config) error { } else { privValidator = tmtypes.GenPrivValidatorFS(privValFile) privValidator.Save() - c.logger.Info("Genetated private validator", "path", privValFile) + c.logger.Info("Generated private validator", "path", privValFile) } // genesis file @@ -130,7 +130,7 @@ func (c initCmd) initTendermintFiles(config *cfg.Config) error { if err := genDoc.SaveAs(genFile); err != nil { return err } - c.logger.Info("Genetated genesis file", "path", genFile) + c.logger.Info("Generated genesis file", "path", genFile) } return nil } diff --git a/server/init_test.go b/server/init_test.go index 3d7e5671d55c..0af1ecc11823 100644 --- a/server/init_test.go +++ b/server/init_test.go @@ -1,7 +1,36 @@ package server -import "testing" +import ( + "io/ioutil" + "os" + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/require" + + "github.com/tendermint/tmlibs/log" + + "github.com/cosmos/cosmos-sdk/mock" +) + +// setupViper creates a homedir to run inside, +// and returns a cleanup function to defer +func setupViper() func() { + rootDir, err := ioutil.TempDir("", "mock-sdk-cmd") + if err != nil { + panic(err) // fuck it! + } + viper.Set("home", rootDir) + return func() { + os.RemoveAll(rootDir) + } +} func TestInit(t *testing.T) { + defer setupViper()() + logger := log.NewNopLogger() + cmd := InitCmd(mock.GenInitOptions, logger) + err := cmd.RunE(nil, nil) + require.NoError(t, err) } diff --git a/server/start_test.go b/server/start_test.go index 10c012ecf6d9..9756e5932458 100644 --- a/server/start_test.go +++ b/server/start_test.go @@ -1,15 +1,36 @@ package server import ( + "os" "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/require" + + "github.com/cosmos/cosmos-sdk/mock" + "github.com/tendermint/tmlibs/log" ) func TestStart(t *testing.T) { - // app, cleanup, err := mock.SetupApp() - // if cleanup != nil { - // defer cleanup() - // } + defer setupViper()() + + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)). + With("module", "mock-cmd") + // logger := log.NewNopLogger() + initCmd := InitCmd(mock.GenInitOptions, logger) + err := initCmd.RunE(nil, nil) + require.NoError(t, err) + + // try to start up + // this should hang forever on success.... how to close??? + + rootDir := viper.GetString("home") + app, err := mock.NewApp(logger, rootDir) + require.NoError(t, err) + _ = StartCmd(app, logger) + // startCmd := StartCmd(app, logger) + // // TODO: test with tendermint + // err = startCmd.RunE(nil, nil) // require.NoError(t, err) - // // TODO: init and start } From 34772f8b6e88ea7798c8f78697744998ddd354f4 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 21 Feb 2018 17:38:16 +0100 Subject: [PATCH 35/70] Unit test initialization bug --- server/start_test.go | 62 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/server/start_test.go b/server/start_test.go index 9756e5932458..de66b8203c98 100644 --- a/server/start_test.go +++ b/server/start_test.go @@ -1,9 +1,12 @@ package server import ( + "fmt" "os" "testing" + "time" + "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/stretchr/testify/require" @@ -11,7 +14,29 @@ import ( "github.com/tendermint/tmlibs/log" ) -func TestStart(t *testing.T) { +func TestStartStandAlone(t *testing.T) { + defer setupViper()() + + logger := log.NewNopLogger() + initCmd := InitCmd(mock.GenInitOptions, logger) + err := initCmd.RunE(nil, nil) + require.NoError(t, err) + + rootDir := viper.GetString("home") + app, err := mock.NewApp(logger, rootDir) + require.NoError(t, err) + + // set up app and start up + viper.Set(flagWithTendermint, false) + viper.Set(flagAddress, "localhost:11122") + startCmd := StartCmd(app, logger) + timeout := time.Duration(3) * time.Second + + err = runOrTimeout(startCmd, timeout) + require.NoError(t, err) +} + +func TestStartWithTendermint(t *testing.T) { defer setupViper()() logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)). @@ -21,16 +46,35 @@ func TestStart(t *testing.T) { err := initCmd.RunE(nil, nil) require.NoError(t, err) - // try to start up - // this should hang forever on success.... how to close??? - rootDir := viper.GetString("home") app, err := mock.NewApp(logger, rootDir) require.NoError(t, err) - _ = StartCmd(app, logger) - // startCmd := StartCmd(app, logger) - // // TODO: test with tendermint - // err = startCmd.RunE(nil, nil) - // require.NoError(t, err) + // set up app and start up + viper.Set(flagWithTendermint, true) + startCmd := StartCmd(app, logger) + timeout := time.Duration(3) * time.Second + + err = runOrTimeout(startCmd, timeout) + require.NoError(t, err) +} + +func runOrTimeout(cmd *cobra.Command, timeout time.Duration) error { + done := make(chan error) + go func(out chan<- error) { + // this should NOT exit + err := cmd.RunE(nil, nil) + if err != nil { + out <- err + } + out <- fmt.Errorf("start died for unknown reasons") + }(done) + timer := time.NewTimer(timeout) + + select { + case err := <-done: + return err + case <-timer.C: + return nil + } } From 7848ee23db37a829099db1dfb2f52287341eaa40 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 21 Feb 2018 18:56:04 +0100 Subject: [PATCH 36/70] Addressed pr comments --- examples/basecoin/cmd/basecoind/main.go | 28 +++++++++++++----------- mock/app.go | 2 +- mock/helpers.go | 2 +- server/start.go | 29 ++++++++++++++++++------- server/start_test.go | 12 ++-------- 5 files changed, 40 insertions(+), 33 deletions(-) diff --git a/examples/basecoin/cmd/basecoind/main.go b/examples/basecoin/cmd/basecoind/main.go index 786c0f3f1ed3..fdb2b5e9d459 100644 --- a/examples/basecoin/cmd/basecoind/main.go +++ b/examples/basecoin/cmd/basecoind/main.go @@ -7,6 +7,7 @@ import ( "github.com/spf13/cobra" + abci "github.com/tendermint/abci/types" "github.com/tendermint/tmlibs/cli" dbm "github.com/tendermint/tmlibs/db" "github.com/tendermint/tmlibs/log" @@ -16,9 +17,9 @@ import ( "github.com/cosmos/cosmos-sdk/version" ) -// gaiadCmd is the entry point for this binary +// basecoindCmd is the entry point for this binary var ( - gaiadCmd = &cobra.Command{ + basecoindCmd = &cobra.Command{ Use: "gaiad", Short: "Gaia Daemon (server)", } @@ -48,27 +49,28 @@ func defaultOptions(args []string) (json.RawMessage, error) { return json.RawMessage(opts), nil } -func main() { - // TODO: this should somehow be updated on cli flags? - // But we need to create the app first... hmmm..... - rootDir := os.ExpandEnv("$HOME/.basecoind") - - logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "main") +func generateApp(rootDir string, logger log.Logger) abci.Application { db, err := dbm.NewGoLevelDB("basecoin", rootDir) if err != nil { fmt.Println(err) os.Exit(1) } bapp := app.NewBasecoinApp(logger, db) + return bapp +} - gaiadCmd.AddCommand( - server.InitCmd(defaultOptions, bapp.Logger), - server.StartCmd(bapp, bapp.Logger), - server.UnsafeResetAllCmd(bapp.Logger), +func main() { + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "main") + + basecoindCmd.AddCommand( + server.InitCmd(defaultOptions, logger), + server.StartCmd(generateApp, logger), + server.UnsafeResetAllCmd(logger), version.VersionCmd, ) // prepare and add flags - executor := cli.PrepareBaseCmd(gaiadCmd, "BC", rootDir) + rootDir := os.ExpandEnv("$HOME/.basecoind") + executor := cli.PrepareBaseCmd(basecoindCmd, "BC", rootDir) executor.Execute() } diff --git a/mock/app.go b/mock/app.go index 554879f0425f..0168e4c0425c 100644 --- a/mock/app.go +++ b/mock/app.go @@ -16,7 +16,7 @@ import ( // It should work similar to a real app. // Make sure rootDir is empty before running the test, // in order to guarantee consistent results -func NewApp(logger log.Logger, rootDir string) (abci.Application, error) { +func NewApp(rootDir string, logger log.Logger) (abci.Application, error) { db, err := dbm.NewGoLevelDB("mock", rootDir) if err != nil { return nil, err diff --git a/mock/helpers.go b/mock/helpers.go index 4e30eaa30e69..601fee897dfd 100644 --- a/mock/helpers.go +++ b/mock/helpers.go @@ -22,6 +22,6 @@ func SetupApp() (abci.Application, func(), error) { os.RemoveAll(rootDir) } - app, err := NewApp(logger, rootDir) + app, err := NewApp(rootDir, logger) return app, cleanup, err } diff --git a/server/start.go b/server/start.go index 909f04dc2f3f..1424c81532da 100644 --- a/server/start.go +++ b/server/start.go @@ -21,9 +21,13 @@ const ( flagAddress = "address" ) +// appGenerator lets us lazily initialize app, using home dir +// and other flags (?) to start +type appGenerator func(string, log.Logger) (abci.Application, error) + // StartCmd runs the service passed in, either // stand-alone, or in-process with tendermint -func StartCmd(app abci.Application, logger log.Logger) *cobra.Command { +func StartCmd(app appGenerator, logger log.Logger) *cobra.Command { start := startCmd{ app: app, logger: logger, @@ -44,10 +48,7 @@ func StartCmd(app abci.Application, logger log.Logger) *cobra.Command { } type startCmd struct { - // do this in main: - // rootDir := viper.GetString(cli.HomeFlag) - // node.Logger = .... - app abci.Application + app appGenerator logger log.Logger } @@ -61,9 +62,15 @@ func (s startCmd) run(cmd *cobra.Command, args []string) error { } func (s startCmd) startStandAlone() error { - // Start the ABCI listener + // Generate the app in the proper dir addr := viper.GetString(flagAddress) - svr, err := server.NewServer(addr, "socket", s.app) + home := viper.GetString("home") + app, err := s.app(home, s.logger) + if err != nil { + return err + } + + svr, err := server.NewServer(addr, "socket", app) if err != nil { return errors.Errorf("Error creating listener: %v\n", err) } @@ -84,10 +91,16 @@ func (s startCmd) startInProcess() error { return err } + home := cfg.RootDir + app, err := s.app(home, s.logger) + if err != nil { + return err + } + // Create & start tendermint node n, err := node.NewNode(cfg, types.LoadOrGenPrivValidatorFS(cfg.PrivValidatorFile()), - proxy.NewLocalClientCreator(s.app), + proxy.NewLocalClientCreator(app), node.DefaultGenesisDocProviderFunc(cfg), node.DefaultDBProvider, s.logger.With("module", "node")) diff --git a/server/start_test.go b/server/start_test.go index de66b8203c98..5b7ab3e76c40 100644 --- a/server/start_test.go +++ b/server/start_test.go @@ -22,14 +22,10 @@ func TestStartStandAlone(t *testing.T) { err := initCmd.RunE(nil, nil) require.NoError(t, err) - rootDir := viper.GetString("home") - app, err := mock.NewApp(logger, rootDir) - require.NoError(t, err) - // set up app and start up viper.Set(flagWithTendermint, false) viper.Set(flagAddress, "localhost:11122") - startCmd := StartCmd(app, logger) + startCmd := StartCmd(mock.NewApp, logger) timeout := time.Duration(3) * time.Second err = runOrTimeout(startCmd, timeout) @@ -46,13 +42,9 @@ func TestStartWithTendermint(t *testing.T) { err := initCmd.RunE(nil, nil) require.NoError(t, err) - rootDir := viper.GetString("home") - app, err := mock.NewApp(logger, rootDir) - require.NoError(t, err) - // set up app and start up viper.Set(flagWithTendermint, true) - startCmd := StartCmd(app, logger) + startCmd := StartCmd(mock.NewApp, logger) timeout := time.Duration(3) * time.Second err = runOrTimeout(startCmd, timeout) From e8676921f726c55212e6fcc9dedc469c15df5734 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 21 Feb 2018 19:52:42 +0100 Subject: [PATCH 37/70] Basecoind start works with upgraded tendermint --- baseapp/baseapp.go | 2 -- examples/basecoin/cmd/basecoind/main.go | 10 +++++----- examples/gaia/gaiad/main.go | 16 ++++++++++++---- glide.lock | 8 ++++---- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 70b1d299f3cb..1b1b4da87fd1 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -236,8 +236,6 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC // NOTE: we don't commit, but BeginBlock for block 1 // starts from this deliverState - // TODO: d00d, tendermint sends nil, until that is fixed, we gotta - // read in the genesis file ourselves here res = app.initChainer(ctx, req) // TODO: handle error https://github.com/cosmos/cosmos-sdk/issues/468 diff --git a/examples/basecoin/cmd/basecoind/main.go b/examples/basecoin/cmd/basecoind/main.go index fdb2b5e9d459..44ea00fbbab3 100644 --- a/examples/basecoin/cmd/basecoind/main.go +++ b/examples/basecoin/cmd/basecoind/main.go @@ -49,18 +49,18 @@ func defaultOptions(args []string) (json.RawMessage, error) { return json.RawMessage(opts), nil } -func generateApp(rootDir string, logger log.Logger) abci.Application { +func generateApp(rootDir string, logger log.Logger) (abci.Application, error) { db, err := dbm.NewGoLevelDB("basecoin", rootDir) if err != nil { - fmt.Println(err) - os.Exit(1) + return nil, err } bapp := app.NewBasecoinApp(logger, db) - return bapp + return bapp, nil } func main() { - logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "main") + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)). + With("module", "main") basecoindCmd.AddCommand( server.InitCmd(defaultOptions, logger), diff --git a/examples/gaia/gaiad/main.go b/examples/gaia/gaiad/main.go index fb5c0b20f5bd..0c4c49eec7d0 100644 --- a/examples/gaia/gaiad/main.go +++ b/examples/gaia/gaiad/main.go @@ -7,7 +7,9 @@ import ( "github.com/spf13/cobra" + abci "github.com/tendermint/abci/types" "github.com/tendermint/tmlibs/cli" + "github.com/tendermint/tmlibs/log" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/server" @@ -46,14 +48,20 @@ func defaultOptions(args []string) (json.RawMessage, error) { return json.RawMessage(opts), nil } -func main() { +func generateApp(rootDir string, logger log.Logger) (abci.Application, error) { // TODO: set this to something real app := new(baseapp.BaseApp) + return app, nil +} + +func main() { + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)). + With("module", "main") gaiadCmd.AddCommand( - server.InitCmd(defaultOptions, app.Logger), - server.StartCmd(app, app.Logger), - server.UnsafeResetAllCmd(app.Logger), + server.InitCmd(defaultOptions, logger), + server.StartCmd(generateApp, logger), + server.UnsafeResetAllCmd(logger), version.VersionCmd, ) diff --git a/glide.lock b/glide.lock index d5db865da33c..e7ef79fa70e9 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: 89fd2ad769c283b6b29b02b84dd6459a40a47de5dfdfb15d7ced95d53ee9ec18 -updated: 2018-02-21T12:53:23.608467+01:00 +updated: 2018-02-21T19:39:16.798141851+01:00 imports: - name: github.com/btcsuite/btcd version: 50de9da05b50eb15658bb350f6ea24368a111ab7 @@ -105,7 +105,7 @@ imports: - leveldb/table - leveldb/util - name: github.com/tendermint/abci - version: 68592f4d8ee34e97db94b7a7976b1309efdb7eb9 + version: c960c5275617ef141c92c3d7fc65a396c97662df subpackages: - client - example/code @@ -129,7 +129,7 @@ imports: - name: github.com/tendermint/iavl version: 1a59ec0c82dc940c25339dd7c834df5cb76a95cb - name: github.com/tendermint/tendermint - version: b24792244d6d03be5a566fd48572743faf3c02e0 + version: 4a63409d5c72a36eee49c962b060fd0063958bda subpackages: - blockchain - cmd/tendermint/commands @@ -166,7 +166,7 @@ imports: - version - wire - name: github.com/tendermint/tmlibs - version: 1b9b5652a199ab0be2e781393fb275b66377309d + version: a0f652dc2e131be86fc8d9e4e2beec9831a8a6ec subpackages: - autofile - cli From 66e6677281280757175c4bca6802e083d96a747b Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 22 Feb 2018 15:33:34 +0100 Subject: [PATCH 38/70] Copy over gaiacli skeleton to basecli --- Makefile | 3 +- examples/basecoin/cmd/basecli/client.go | 131 ++++++++++++++++++++++ examples/basecoin/cmd/basecli/commands.go | 23 ++++ examples/basecoin/cmd/basecli/key.go | 77 +++++++++++++ examples/basecoin/cmd/basecli/main.go | 59 ++++++++++ 5 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 examples/basecoin/cmd/basecli/client.go create mode 100644 examples/basecoin/cmd/basecli/commands.go create mode 100644 examples/basecoin/cmd/basecli/key.go create mode 100644 examples/basecoin/cmd/basecli/main.go diff --git a/Makefile b/Makefile index 6c86730d0b2f..395af306ac35 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,8 @@ gaia: build: @rm -rf examples/basecoin/vendor/ - go build $(BUILD_FLAGS) -o build/basecoind ./examples/basecoin/cmd/basecoind/... + go build $(BUILD_FLAGS) -o build/basecoind ./examples/basecoin/cmd/basecoind + go build $(BUILD_FLAGS) -o build/basecli ./examples/basecoin/cmd/basecli dist: @bash publish/dist.sh diff --git a/examples/basecoin/cmd/basecli/client.go b/examples/basecoin/cmd/basecli/client.go new file mode 100644 index 000000000000..682a571e6ee9 --- /dev/null +++ b/examples/basecoin/cmd/basecli/client.go @@ -0,0 +1,131 @@ +package main + +import "github.com/spf13/cobra" + +const ( + // these are needed for every init + flagChainID = "chain-id" + flagNode = "node" + + // one of the following should be provided to verify the connection + flagGenesis = "genesis" + flagCommit = "commit" + flagValHash = "validator-set" + + flagSelect = "select" + flagTags = "tag" + flagAny = "any" + + flagBind = "bind" + flagCORS = "cors" + flagTrustNode = "trust-node" + + // this is for signing + flagName = "name" +) + +var ( + statusCmd = &cobra.Command{ + Use: "status", + Short: "Query remote node for status", + RunE: todoNotImplemented, + } +) + +// AddClientCommands returns a sub-tree of all basic client commands +// +// Call AddGetCommand and AddPostCommand to add custom txs and queries +func AddClientCommands(cmd *cobra.Command) { + cmd.AddCommand( + initClientCommand(), + statusCmd, + blockCommand(), + validatorCommand(), + lineBreak, + txSearchCommand(), + txCommand(), + lineBreak, + ) +} + +// GetCommands adds common flags to query commands +func GetCommands(cmds ...*cobra.Command) []*cobra.Command { + for _, c := range cmds { + c.Flags().Bool(flagTrustNode, false, "Don't verify proofs for responses") + } + return cmds +} + +// PostCommands adds common flags for commands to post tx +func PostCommands(cmds ...*cobra.Command) []*cobra.Command { + for _, c := range cmds { + c.Flags().String(flagName, "", "Name of private key with which to sign") + c.Flags().String(flagPassword, "", "Password to use the named private key") + } + return cmds +} + +func initClientCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "init", + Short: "Initialize light client", + RunE: todoNotImplemented, + } + cmd.Flags().StringP(flagChainID, "c", "", "ID of chain we connect to") + cmd.Flags().StringP(flagNode, "n", "tcp://localhost:46657", "Node to connect to") + cmd.Flags().String(flagGenesis, "", "Genesis file to verify header validity") + cmd.Flags().String(flagCommit, "", "File with trusted and signed header") + cmd.Flags().String(flagValHash, "", "Hash of trusted validator set (hex-encoded)") + return cmd +} + +func blockCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "block ", + Short: "Get verified data for a the block at given height", + RunE: todoNotImplemented, + } + cmd.Flags().StringSlice(flagSelect, []string{"header", "tx"}, "Fields to return (header|txs|results)") + return cmd +} + +func validatorCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "validatorset ", + Short: "Get the full validator set at given height", + RunE: todoNotImplemented, + } + return cmd +} + +func serveCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "serve", + Short: "Start LCD (light-client daemon), a local REST server", + RunE: todoNotImplemented, + } + // TODO: handle unix sockets also? + cmd.Flags().StringP(flagBind, "b", "localhost:1317", "Interface and port that server binds to") + cmd.Flags().String(flagCORS, "", "Set to domains that can make CORS requests (* for all)") + return cmd +} + +func txSearchCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "txs", + Short: "Search for all transactions that match the given tags", + RunE: todoNotImplemented, + } + cmd.Flags().StringSlice(flagTags, nil, "Tags that must match (may provide multiple)") + cmd.Flags().Bool(flagAny, false, "Return transactions that match ANY tag, rather than ALL") + return cmd +} + +func txCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "tx ", + Short: "Matches this txhash over all committed blocks", + RunE: todoNotImplemented, + } + return cmd +} diff --git a/examples/basecoin/cmd/basecli/commands.go b/examples/basecoin/cmd/basecli/commands.go new file mode 100644 index 000000000000..2af60010d220 --- /dev/null +++ b/examples/basecoin/cmd/basecli/commands.go @@ -0,0 +1,23 @@ +package main + +import ( + "github.com/spf13/cobra" +) + +const ( + flagTo = "to" + flagAmount = "amount" + flagFee = "fee" +) + +func postSendCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "send", + Short: "Create and sign a send tx", + RunE: todoNotImplemented, + } + cmd.Flags().String(flagTo, "", "Address to send coins") + cmd.Flags().String(flagAmount, "", "Amount of coins to send") + cmd.Flags().String(flagFee, "", "Fee to pay along with transaction") + return cmd +} diff --git a/examples/basecoin/cmd/basecli/key.go b/examples/basecoin/cmd/basecli/key.go new file mode 100644 index 000000000000..b3ffa323aed6 --- /dev/null +++ b/examples/basecoin/cmd/basecli/key.go @@ -0,0 +1,77 @@ +package main + +import "github.com/spf13/cobra" + +const ( + flagPassword = "password" + flagNewPassword = "new-password" + flagType = "type" + flagSeed = "seed" + flagDryRun = "dry-run" +) + +var ( + listKeysCmd = &cobra.Command{ + Use: "list", + Short: "List all locally availably keys", + RunE: todoNotImplemented, + } + + showKeysCmd = &cobra.Command{ + Use: "show ", + Short: "Show key info for the given name", + RunE: todoNotImplemented, + } +) + +// KeyCommands registers a sub-tree of commands to interact with +// local private key storage. +func KeyCommands() *cobra.Command { + cmd := &cobra.Command{ + Use: "keys", + Short: "Add or view local private keys", + } + cmd.AddCommand( + addKeyCommand(), + listKeysCmd, + showKeysCmd, + lineBreak, + deleteKeyCommand(), + updateKeyCommand(), + ) + return cmd +} + +func addKeyCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "add ", + Short: "Create a new key, or import from seed", + RunE: todoNotImplemented, + } + cmd.Flags().StringP(flagPassword, "p", "", "Password to encrypt private key") + cmd.Flags().StringP(flagType, "t", "ed25519", "Type of private key (ed25519|secp256k1|ledger)") + cmd.Flags().StringP(flagSeed, "s", "", "Provide seed phrase to recover existing key instead of creating") + cmd.Flags().Bool(flagDryRun, false, "Perform action, but don't add key to local keystore") + return cmd +} + +func updateKeyCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "update ", + Short: "Change the password used to protect private key", + RunE: todoNotImplemented, + } + cmd.Flags().StringP(flagPassword, "p", "", "Current password to decrypt key") + cmd.Flags().String(flagNewPassword, "", "New password to use to protect key") + return cmd +} + +func deleteKeyCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "delete ", + Short: "Delete the given key", + RunE: todoNotImplemented, + } + cmd.Flags().StringP(flagPassword, "p", "", "Password of existing key to delete") + return cmd +} diff --git a/examples/basecoin/cmd/basecli/main.go b/examples/basecoin/cmd/basecli/main.go new file mode 100644 index 000000000000..1989d1b38d0a --- /dev/null +++ b/examples/basecoin/cmd/basecli/main.go @@ -0,0 +1,59 @@ +package main + +import ( + "errors" + "os" + + "github.com/spf13/cobra" + + "github.com/tendermint/tmlibs/cli" + + "github.com/cosmos/cosmos-sdk/version" +) + +// gaiacliCmd is the entry point for this binary +var ( + basecliCmd = &cobra.Command{ + Use: "basecli", + Short: "Basecoin light-client", + } + + lineBreak = &cobra.Command{Run: func(*cobra.Command, []string) {}} + + getAccountCmd = &cobra.Command{ + Use: "account
", + Short: "Query account balance", + RunE: todoNotImplemented, + } +) + +func todoNotImplemented(_ *cobra.Command, _ []string) error { + return errors.New("TODO: Command not yet implemented") +} + +func main() { + // disable sorting + cobra.EnableCommandSorting = false + + // generic client commands + AddClientCommands(basecliCmd) + // query commands (custom to binary) + basecliCmd.AddCommand( + GetCommands(getAccountCmd)...) + // post tx commands (custom to binary) + basecliCmd.AddCommand( + PostCommands(postSendCommand())...) + + // add proxy, version and key info + basecliCmd.AddCommand( + lineBreak, + serveCommand(), + KeyCommands(), + lineBreak, + version.VersionCmd, + ) + + // prepare and add flags + executor := cli.PrepareBaseCmd(basecliCmd, "GA", os.ExpandEnv("$HOME/.basecli")) + executor.Execute() +} From ee31db32638e1c49075a2195d81d368218bb82ec Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 22 Feb 2018 16:17:19 +0100 Subject: [PATCH 39/70] Very rough import of old keys logic --- client/keys.go | 23 +++++++ client/keys/README.md | 117 +++++++++++++++++++++++++++++++++++ client/keys/delete.go | 54 ++++++++++++++++ client/keys/get.go | 47 ++++++++++++++ client/keys/list.go | 39 ++++++++++++ client/keys/new.go | 98 +++++++++++++++++++++++++++++ client/keys/recover.go | 66 ++++++++++++++++++++ client/keys/root.go | 39 ++++++++++++ client/keys/update.go | 57 +++++++++++++++++ client/keys/utils.go | 137 +++++++++++++++++++++++++++++++++++++++++ client/keys/wire.go | 17 +++++ glide.lock | 12 ++-- glide.yaml | 4 ++ 13 files changed, 706 insertions(+), 4 deletions(-) create mode 100644 client/keys.go create mode 100644 client/keys/README.md create mode 100644 client/keys/delete.go create mode 100644 client/keys/get.go create mode 100644 client/keys/list.go create mode 100644 client/keys/new.go create mode 100644 client/keys/recover.go create mode 100644 client/keys/root.go create mode 100644 client/keys/update.go create mode 100644 client/keys/utils.go create mode 100644 client/keys/wire.go diff --git a/client/keys.go b/client/keys.go new file mode 100644 index 000000000000..73c2226e5e7d --- /dev/null +++ b/client/keys.go @@ -0,0 +1,23 @@ +package client + +import ( + "github.com/tendermint/go-crypto/keys" + "github.com/tendermint/go-crypto/keys/words" + dbm "github.com/tendermint/tmlibs/db" +) + +// KeyDBName is the directory under root where we store the keys +const KeyDBName = "keys" + +// GetKeyManager initializes a key manager based on the configuration +func GetKeyManager(rootDir string) (keys.Keybase, error) { + db, err := dbm.NewGoLevelDB(KeyDBName, rootDir) + if err != nil { + return nil, err + } + keybase := keys.New( + db, + words.MustLoadCodec("english"), + ) + return keybase, nil +} diff --git a/client/keys/README.md b/client/keys/README.md new file mode 100644 index 000000000000..8bf9ca73b628 --- /dev/null +++ b/client/keys/README.md @@ -0,0 +1,117 @@ +# Keys CLI + +This is as much an example how to expose cobra/viper, as for a cli itself +(I think this code is overkill for what go-keys needs). But please look at +the commands, and give feedback and changes. + +`RootCmd` calls some initialization functions (`cobra.OnInitialize` and `RootCmd.PersistentPreRunE`) which serve to connect environmental variables and cobra flags, as well as load the config file. It also validates the flags registered on root and creates the cryptomanager, which will be used by all subcommands. + +## Help info + +``` +# keys help + +Keys allows you to manage your local keystore for tendermint. + +These keys may be in any format supported by go-crypto and can be +used by light-clients, full nodes, or any other application that +needs to sign with a private key. + +Usage: + keys [command] + +Available Commands: + get Get details of one key + list List all keys + new Create a new public/private key pair + serve Run the key manager as an http server + update Change the password for a private key + +Flags: + --keydir string Directory to store private keys (subdir of root) (default "keys") + -o, --output string Output format (text|json) (default "text") + -r, --root string root directory for config and data (default "/Users/ethan/.tlc") + +Use "keys [command] --help" for more information about a command. +``` + +## Getting the config file + +The first step is to load in root, by checking the following in order: + +* -r, --root command line flag +* TM_ROOT environmental variable +* default ($HOME/.tlc evaluated at runtime) + +Once the `rootDir` is established, the script looks for a config file named `keys.{json,toml,yaml,hcl}` in that directory and parses it. These values will provide defaults for flags of the same name. + +There is an example config file for testing out locally, which writes keys to `./.mykeys`. You can + +## Getting/Setting variables + +When we want to get the value of a user-defined variable (eg. `output`), we can call `viper.GetString("output")`, which will do the following checks, until it finds a match: + +* Is `--output` command line flag present? +* Is `TM_OUTPUT` environmental variable set? +* Was a config file found and does it have an `output` variable? +* Is there a default set on the command line flag? + +If no variable is set and there was no default, we get back "". + +This setup allows us to have powerful command line flags, but use env variables or config files (local or 12-factor style) to avoid passing these arguments every time. + +## Nesting structures + +Sometimes we don't just need key-value pairs, but actually a multi-level config file, like + +``` +[mail] +from = "no-reply@example.com" +server = "mail.example.com" +port = 567 +password = "XXXXXX" +``` + +This CLI is too simple to warant such a structure, but I think eg. tendermint could benefit from such an approach. Here are some pointers: + +* [Accessing nested keys from config files](https://github.com/spf13/viper#accessing-nested-keys) +* [Overriding nested values with envvars](https://www.netlify.com/blog/2016/09/06/creating-a-microservice-boilerplate-in-go/#nested-config-values) - the mentioned outstanding PR is already merged into master! +* Overriding nested values with cli flags? (use `--log_config.level=info` ??) + +I'd love to see an example of this fully worked out in a more complex CLI. + +## Have your cake and eat it too + +It's easy to render data different ways. Some better for viewing, some better for importing to other programs. You can just add some global (persistent) flags to control the output formatting, and everyone gets what they want. + +``` +# keys list -e hex +All keys: +betty d0789984492b1674e276b590d56b7ae077f81adc +john b77f4720b220d1411a649b6c7f1151eb6b1c226a + +# keys list -e btc +All keys: +betty 3uTF4r29CbtnzsNHZoPSYsE4BDwH +john 3ZGp2Md35iw4XVtRvZDUaAEkCUZP + +# keys list -e b64 -o json +[ + { + "name": "betty", + "address": "0HiZhEkrFnTidrWQ1Wt64Hf4Gtw=", + "pubkey": { + "type": "secp256k1", + "data": "F83WvhT0KwttSoqQqd_0_r2ztUUaQix5EXdO8AZyREoV31Og780NW59HsqTAb2O4hZ-w-j0Z-4b2IjfdqqfhVQ==" + } + }, + { + "name": "john", + "address": "t39HILIg0UEaZJtsfxFR62scImo=", + "pubkey": { + "type": "ed25519", + "data": "t1LFmbg_8UTwj-n1wkqmnTp6NfaOivokEhlYySlGYCY=" + } + } +] +``` diff --git a/client/keys/delete.go b/client/keys/delete.go new file mode 100644 index 000000000000..91f39e6a118f --- /dev/null +++ b/client/keys/delete.go @@ -0,0 +1,54 @@ +// Copyright © 2017 Ethan Frey +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package keys + +import ( + "fmt" + + "github.com/pkg/errors" + + "github.com/spf13/cobra" +) + +// deleteCmd represents the delete command +var deleteCmd = &cobra.Command{ + Use: "delete [name]", + Short: "DANGER: Delete a private key from your system", + RunE: runDeleteCmd, +} + +func runDeleteCmd(cmd *cobra.Command, args []string) error { + if len(args) != 1 || len(args[0]) == 0 { + return errors.New("You must provide a name for the key") + } + name := args[0] + + oldpass, err := getPassword("DANGER - enter password to permanently delete key:") + if err != nil { + return err + } + + kb, err := GetKeyBase() + if err != nil { + return err + } + + err = kb.Delete(name, oldpass) + if err != nil { + return err + } + fmt.Println("Password deleted forever (uh oh!)") + return nil +} diff --git a/client/keys/get.go b/client/keys/get.go new file mode 100644 index 000000000000..e247473b2e6a --- /dev/null +++ b/client/keys/get.go @@ -0,0 +1,47 @@ +// Copyright © 2017 Ethan Frey +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package keys + +import ( + "github.com/pkg/errors" + + "github.com/spf13/cobra" +) + +// getCmd represents the get command +var getCmd = &cobra.Command{ + Use: "get [name]", + Short: "Get details of one key", + Long: `Return public details of one local key.`, + RunE: runGetCmd, +} + +func runGetCmd(cmd *cobra.Command, args []string) error { + if len(args) != 1 || len(args[0]) == 0 { + return errors.New("You must provide a name for the key") + } + name := args[0] + + kb, err := GetKeyBase() + if err != nil { + return err + } + + info, err := kb.Get(name) + if err == nil { + printInfo(info) + } + return err +} diff --git a/client/keys/list.go b/client/keys/list.go new file mode 100644 index 000000000000..8654deca682c --- /dev/null +++ b/client/keys/list.go @@ -0,0 +1,39 @@ +// Copyright © 2017 Ethan Frey +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package keys + +import "github.com/spf13/cobra" + +// listCmd represents the list command +var listCmd = &cobra.Command{ + Use: "list", + Short: "List all keys", + Long: `Return a list of all public keys stored by this key manager +along with their associated name and address.`, + RunE: runListCmd, +} + +func runListCmd(cmd *cobra.Command, args []string) error { + kb, err := GetKeyBase() + if err != nil { + return err + } + + infos, err := kb.List() + if err == nil { + printInfos(infos) + } + return err +} diff --git a/client/keys/new.go b/client/keys/new.go new file mode 100644 index 000000000000..8884f60a07dc --- /dev/null +++ b/client/keys/new.go @@ -0,0 +1,98 @@ +// Copyright © 2017 Ethan Frey +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package keys + +import ( + "fmt" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/tendermint/go-crypto/keys" + "github.com/tendermint/tmlibs/cli" +) + +const ( + flagType = "type" + flagNoBackup = "no-backup" +) + +// newCmd represents the new command +var newCmd = &cobra.Command{ + Use: "new [name]", + Short: "Create a new public/private key pair", + Long: `Add a public/private key pair to the key store. +The password must be entered in the terminal and not +passed as a command line argument for security.`, + RunE: runNewCmd, +} + +func init() { + newCmd.Flags().StringP(flagType, "t", "ed25519", "Type of key (ed25519|secp256k1|ledger") + newCmd.Flags().Bool(flagNoBackup, false, "Don't print out seed phrase (if others are watching the terminal)") +} + +func runNewCmd(cmd *cobra.Command, args []string) error { + if len(args) != 1 || len(args[0]) == 0 { + return errors.New("You must provide a name for the key") + } + name := args[0] + algo := keys.CryptoAlgo(viper.GetString(flagType)) + + pass, err := getCheckPassword("Enter a passphrase:", "Repeat the passphrase:") + if err != nil { + return err + } + + kb, err := GetKeyBase() + if err != nil { + return err + } + + info, seed, err := kb.Create(name, pass, algo) + if err == nil { + printCreate(info, seed) + } + return err +} + +type NewOutput struct { + Key keys.Info `json:"key"` + Seed string `json:"seed"` +} + +func printCreate(info keys.Info, seed string) { + switch viper.Get(cli.OutputFlag) { + case "text": + printInfo(info) + // print seed unless requested not to. + if !viper.GetBool(flagNoBackup) { + fmt.Println("**Important** write this seed phrase in a safe place.") + fmt.Println("It is the only way to recover your account if you ever forget your password.\n") + fmt.Println(seed) + } + case "json": + out := NewOutput{Key: info} + if !viper.GetBool(flagNoBackup) { + out.Seed = seed + } + json, err := MarshalJSON(out) + if err != nil { + panic(err) // really shouldn't happen... + } + fmt.Println(string(json)) + } +} diff --git a/client/keys/recover.go b/client/keys/recover.go new file mode 100644 index 000000000000..cd78eab8e6cb --- /dev/null +++ b/client/keys/recover.go @@ -0,0 +1,66 @@ +// Copyright © 2017 Ethan Frey +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package keys + +import ( + "github.com/pkg/errors" + + "github.com/spf13/cobra" +) + +// recoverCmd represents the recover command +var recoverCmd = &cobra.Command{ + Use: "recover [name]", + Short: "Recover a private key from a seed phrase", + Long: `Recover a private key from a seed phrase. + +I really hope you wrote this down when you created the new key. +The seed is only displayed on creation, never again. + +You can also use this to copy a key between multiple testnets, +simply by "recovering" the key in the other nets you want to copy +to. Of course, it has no coins on the other nets, just the same address.`, + RunE: runRecoverCmd, +} + +func runRecoverCmd(cmd *cobra.Command, args []string) error { + if len(args) != 1 || len(args[0]) == 0 { + return errors.New("You must provide a name for the key") + } + name := args[0] + + pass, err := getPassword("Enter the new passphrase:") + if err != nil { + return err + } + + // not really a password... huh? + seed, err := getSeed("Enter your recovery seed phrase:") + if err != nil { + return err + } + + kb, err := GetKeyBase() + if err != nil { + return err + } + + info, err := kb.Recover(name, pass, seed) + if err != nil { + return err + } + printInfo(info) + return nil +} diff --git a/client/keys/root.go b/client/keys/root.go new file mode 100644 index 000000000000..1336272b35c3 --- /dev/null +++ b/client/keys/root.go @@ -0,0 +1,39 @@ +// Copyright © 2017 Ethan Frey +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package keys + +import ( + "github.com/spf13/cobra" +) + +// RootCmd represents the base command when called without any subcommands +var RootCmd = &cobra.Command{ + Use: "keys", + Short: "Key manager for tendermint clients", + Long: `Keys allows you to manage your local keystore for tendermint. + +These keys may be in any format supported by go-crypto and can be +used by light-clients, full nodes, or any other application that +needs to sign with a private key.`, +} + +func init() { + RootCmd.AddCommand(getCmd) + RootCmd.AddCommand(listCmd) + RootCmd.AddCommand(newCmd) + RootCmd.AddCommand(updateCmd) + RootCmd.AddCommand(deleteCmd) + RootCmd.AddCommand(recoverCmd) +} diff --git a/client/keys/update.go b/client/keys/update.go new file mode 100644 index 000000000000..de8d06a525b4 --- /dev/null +++ b/client/keys/update.go @@ -0,0 +1,57 @@ +// Copyright © 2017 Ethan Frey +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package keys + +import ( + "fmt" + + "github.com/pkg/errors" + + "github.com/spf13/cobra" +) + +// updateCmd represents the update command +var updateCmd = &cobra.Command{ + Use: "update [name]", + Short: "Change the password for a private key", + RunE: runUpdateCmd, +} + +func runUpdateCmd(cmd *cobra.Command, args []string) error { + if len(args) != 1 || len(args[0]) == 0 { + return errors.New("You must provide a name for the key") + } + name := args[0] + + oldpass, err := getPassword("Enter the current passphrase:") + if err != nil { + return err + } + newpass, err := getCheckPassword("Enter the new passphrase:", "Repeat the new passphrase:") + if err != nil { + return err + } + + kb, err := GetKeyBase() + if err != nil { + return err + } + err = kb.Update(name, oldpass, newpass) + if err != nil { + return err + } + fmt.Println("Password successfully updated!") + return nil +} diff --git a/client/keys/utils.go b/client/keys/utils.go new file mode 100644 index 000000000000..7e34dcd8eb9c --- /dev/null +++ b/client/keys/utils.go @@ -0,0 +1,137 @@ +package keys + +import ( + "bufio" + "fmt" + "os" + "strings" + + "github.com/bgentry/speakeasy" + isatty "github.com/mattn/go-isatty" + "github.com/pkg/errors" + "github.com/spf13/viper" + + keys "github.com/tendermint/go-crypto/keys" + "github.com/tendermint/tmlibs/cli" + + "github.com/cosmos/cosmos-sdk/client" +) + +// MinPassLength is the minimum acceptable password length +const MinPassLength = 8 + +var ( + // keybase is used to make GetKeyBase a singleton + keybase keys.Keybase +) + +// GetKeyBase initializes a keybase based on the configuration +func GetKeyBase() (keys.Keybase, error) { + if keybase == nil { + rootDir := viper.GetString(cli.HomeFlag) + keyman, err := client.GetKeyManager(rootDir) + if err != nil { + return nil, err + } + keybase = keyman + } + return keybase, nil +} + +// if we read from non-tty, we just need to init the buffer reader once, +// in case we try to read multiple passwords (eg. update) +var buf *bufio.Reader + +func inputIsTty() bool { + return isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) +} + +func stdinPassword() (string, error) { + if buf == nil { + buf = bufio.NewReader(os.Stdin) + } + pass, err := buf.ReadString('\n') + if err != nil { + return "", err + } + return strings.TrimSpace(pass), nil +} + +func getPassword(prompt string) (pass string, err error) { + if inputIsTty() { + pass, err = speakeasy.Ask(prompt) + } else { + pass, err = stdinPassword() + } + if err != nil { + return "", err + } + if len(pass) < MinPassLength { + return "", errors.Errorf("Password must be at least %d characters", MinPassLength) + } + return pass, nil +} + +func getSeed(prompt string) (seed string, err error) { + if inputIsTty() { + fmt.Println(prompt) + } + seed, err = stdinPassword() + seed = strings.TrimSpace(seed) + return +} + +func getCheckPassword(prompt, prompt2 string) (string, error) { + // simple read on no-tty + if !inputIsTty() { + return getPassword(prompt) + } + + // TODO: own function??? + pass, err := getPassword(prompt) + if err != nil { + return "", err + } + pass2, err := getPassword(prompt2) + if err != nil { + return "", err + } + if pass != pass2 { + return "", errors.New("Passphrases don't match") + } + return pass, nil +} + +func printInfo(info keys.Info) { + switch viper.Get(cli.OutputFlag) { + case "text": + addr := info.PubKey.Address().String() + sep := "\t\t" + if len(info.Name) > 7 { + sep = "\t" + } + fmt.Printf("%s%s%s\n", info.Name, sep, addr) + case "json": + json, err := MarshalJSON(info) + if err != nil { + panic(err) // really shouldn't happen... + } + fmt.Println(string(json)) + } +} + +func printInfos(infos []keys.Info) { + switch viper.Get(cli.OutputFlag) { + case "text": + fmt.Println("All keys:") + for _, i := range infos { + printInfo(i) + } + case "json": + json, err := MarshalJSON(infos) + if err != nil { + panic(err) // really shouldn't happen... + } + fmt.Println(string(json)) + } +} diff --git a/client/keys/wire.go b/client/keys/wire.go new file mode 100644 index 000000000000..5f7c15344e67 --- /dev/null +++ b/client/keys/wire.go @@ -0,0 +1,17 @@ +package keys + +import ( + crypto "github.com/tendermint/go-crypto" + wire "github.com/tendermint/go-wire" +) + +var cdc *wire.Codec + +func init() { + cdc = wire.NewCodec() + crypto.RegisterWire(cdc) +} + +func MarshalJSON(o interface{}) ([]byte, error) { + return cdc.MarshalJSON(o) +} diff --git a/glide.lock b/glide.lock index e7ef79fa70e9..43b61b13f4d1 100644 --- a/glide.lock +++ b/glide.lock @@ -1,6 +1,8 @@ -hash: 89fd2ad769c283b6b29b02b84dd6459a40a47de5dfdfb15d7ced95d53ee9ec18 -updated: 2018-02-21T19:39:16.798141851+01:00 +hash: fa45c8a4f5512ed730f793b93d4876bdc604a1333a5a1f938c98a0f7dd55f22e +updated: 2018-02-22T16:18:43.639619321+01:00 imports: +- name: github.com/bgentry/speakeasy + version: 4aabc24848ce5fd31929f7d1e4ea74d3709c14cd - name: github.com/btcsuite/btcd version: 50de9da05b50eb15658bb350f6ea24368a111ab7 subpackages: @@ -65,6 +67,8 @@ imports: version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0 - name: github.com/magiconair/properties version: 49d762b9817ba1c2e9d0c69183c2b4a8b8f1d934 +- name: github.com/mattn/go-isatty + version: 0360b2af4f38e8d38c7fce2a9f4e702702d73a39 - name: github.com/mitchellh/mapstructure version: b4575eea38cca1123ec2dc90c26529b5c5acfcff - name: github.com/pelletier/go-toml @@ -105,7 +109,7 @@ imports: - leveldb/table - leveldb/util - name: github.com/tendermint/abci - version: c960c5275617ef141c92c3d7fc65a396c97662df + version: 68592f4d8ee34e97db94b7a7976b1309efdb7eb9 subpackages: - client - example/code @@ -166,7 +170,7 @@ imports: - version - wire - name: github.com/tendermint/tmlibs - version: a0f652dc2e131be86fc8d9e4e2beec9831a8a6ec + version: 1b9b5652a199ab0be2e781393fb275b66377309d subpackages: - autofile - cli diff --git a/glide.yaml b/glide.yaml index b6f950f2e344..086cd0c25cfc 100644 --- a/glide.yaml +++ b/glide.yaml @@ -4,6 +4,10 @@ import: version: ^1.0.0 subpackages: - proto +- package: github.com/bgentry/speakeasy + version: ^0.1.0 +- package: github.com/mattn/go-isatty + version: ~0.0.3 - package: github.com/pkg/errors version: ^0.8.0 - package: github.com/rigelrozanski/common From 7361269eb669146e0d5bfdf660006344e91212e1 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 22 Feb 2018 16:49:32 +0100 Subject: [PATCH 40/70] Updated keys add --- client/keys.go | 14 ++++- client/keys/add.go | 128 +++++++++++++++++++++++++++++++++++++++++++ client/keys/new.go | 98 --------------------------------- client/keys/utils.go | 4 +- 4 files changed, 142 insertions(+), 102 deletions(-) create mode 100644 client/keys/add.go delete mode 100644 client/keys/new.go diff --git a/client/keys.go b/client/keys.go index 73c2226e5e7d..f64ba405f6e3 100644 --- a/client/keys.go +++ b/client/keys.go @@ -9,8 +9,8 @@ import ( // KeyDBName is the directory under root where we store the keys const KeyDBName = "keys" -// GetKeyManager initializes a key manager based on the configuration -func GetKeyManager(rootDir string) (keys.Keybase, error) { +// GetKeyBase initializes a keybase based on the configuration +func GetKeyBase(rootDir string) (keys.Keybase, error) { db, err := dbm.NewGoLevelDB(KeyDBName, rootDir) if err != nil { return nil, err @@ -21,3 +21,13 @@ func GetKeyManager(rootDir string) (keys.Keybase, error) { ) return keybase, nil } + +// MockKeyBase generates an in-memory keybase that will be discarded +// useful for --dry-run to generate a seed phrase without +// storing the key +func MockKeyBase() keys.Keybase { + return keys.New( + dbm.NewMemDB(), + words.MustLoadCodec("english"), + ) +} diff --git a/client/keys/add.go b/client/keys/add.go new file mode 100644 index 000000000000..f6f1698b7b2b --- /dev/null +++ b/client/keys/add.go @@ -0,0 +1,128 @@ +// Copyright © 2017 Ethan Frey +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package keys + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/tendermint/go-crypto/keys" + "github.com/tendermint/tmlibs/cli" +) + +const ( + flagType = "type" + flagRecover = "recover" + flagNoBackup = "no-backup" + flagDryRun = "dry-run" +) + +func addKeyCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "add ", + Short: "Create a new key, or import from seed", + Long: `Add a public/private key pair to the key store. +If you select --seed/-s you can recover a key from the seed +phrase, otherwise, a new key will be generated.`, + RunE: runAddCmd, + } + cmd.Flags().StringP(flagType, "t", "ed25519", "Type of private key (ed25519|secp256k1|ledger)") + cmd.Flags().Bool(flagRecover, false, "Provide seed phrase to recover existing key instead of creating") + cmd.Flags().Bool(flagNoBackup, false, "Don't print out seed phrase (if others are watching the terminal)") + cmd.Flags().Bool(flagDryRun, false, "Perform action, but don't add key to local keystore") + return cmd +} + +func runAddCmd(cmd *cobra.Command, args []string) error { + var kb keys.Keybase + var err error + var name, pass string + + if viper.GetBool(flagDryRun) { + // we throw this away, so don't enforce args, + // we want to get a new random seed phrase quickly + kb = client.MockKeyBase() + pass = "throwing-this-key-away" + name = "inmemorykey" + } else { + if len(args) != 1 || len(args[0]) == 0 { + return errors.New("You must provide a name for the key") + } + name = args[0] + kb, err = GetKeyBase() + if err != nil { + return err + } + pass, err = getCheckPassword("Enter a passphrase for your key:", "Repeat the passphrase:") + if err != nil { + return err + } + } + + if viper.GetBool(flagRecover) { + seed, err := getPassword("Enter your recovery seed phrase:") + if err != nil { + return err + } + info, err := kb.Recover(name, pass, seed) + if err != nil { + return err + } + // print out results without the seed phrase + viper.Set(flagNoBackup, true) + printCreate(info, "") + } else { + algo := keys.CryptoAlgo(viper.GetString(flagType)) + info, seed, err := kb.Create(name, pass, algo) + if err != nil { + return err + } + printCreate(info, seed) + } + return nil +} + +// addOutput lets us json format the data +type addOutput struct { + Key keys.Info `json:"key"` + Seed string `json:"seed"` +} + +func printCreate(info keys.Info, seed string) { + switch viper.Get(cli.OutputFlag) { + case "text": + printInfo(info) + // print seed unless requested not to. + if !viper.GetBool(flagNoBackup) { + fmt.Println("**Important** write this seed phrase in a safe place.") + fmt.Println("It is the only way to recover your account if you ever forget your password.\n") + fmt.Println(seed) + } + case "json": + out := addOutput{Key: info} + if !viper.GetBool(flagNoBackup) { + out.Seed = seed + } + json, err := MarshalJSON(out) + if err != nil { + panic(err) // really shouldn't happen... + } + fmt.Println(string(json)) + } +} diff --git a/client/keys/new.go b/client/keys/new.go deleted file mode 100644 index 8884f60a07dc..000000000000 --- a/client/keys/new.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright © 2017 Ethan Frey -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package keys - -import ( - "fmt" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/tendermint/go-crypto/keys" - "github.com/tendermint/tmlibs/cli" -) - -const ( - flagType = "type" - flagNoBackup = "no-backup" -) - -// newCmd represents the new command -var newCmd = &cobra.Command{ - Use: "new [name]", - Short: "Create a new public/private key pair", - Long: `Add a public/private key pair to the key store. -The password must be entered in the terminal and not -passed as a command line argument for security.`, - RunE: runNewCmd, -} - -func init() { - newCmd.Flags().StringP(flagType, "t", "ed25519", "Type of key (ed25519|secp256k1|ledger") - newCmd.Flags().Bool(flagNoBackup, false, "Don't print out seed phrase (if others are watching the terminal)") -} - -func runNewCmd(cmd *cobra.Command, args []string) error { - if len(args) != 1 || len(args[0]) == 0 { - return errors.New("You must provide a name for the key") - } - name := args[0] - algo := keys.CryptoAlgo(viper.GetString(flagType)) - - pass, err := getCheckPassword("Enter a passphrase:", "Repeat the passphrase:") - if err != nil { - return err - } - - kb, err := GetKeyBase() - if err != nil { - return err - } - - info, seed, err := kb.Create(name, pass, algo) - if err == nil { - printCreate(info, seed) - } - return err -} - -type NewOutput struct { - Key keys.Info `json:"key"` - Seed string `json:"seed"` -} - -func printCreate(info keys.Info, seed string) { - switch viper.Get(cli.OutputFlag) { - case "text": - printInfo(info) - // print seed unless requested not to. - if !viper.GetBool(flagNoBackup) { - fmt.Println("**Important** write this seed phrase in a safe place.") - fmt.Println("It is the only way to recover your account if you ever forget your password.\n") - fmt.Println(seed) - } - case "json": - out := NewOutput{Key: info} - if !viper.GetBool(flagNoBackup) { - out.Seed = seed - } - json, err := MarshalJSON(out) - if err != nil { - panic(err) // really shouldn't happen... - } - fmt.Println(string(json)) - } -} diff --git a/client/keys/utils.go b/client/keys/utils.go index 7e34dcd8eb9c..346aa17bcff5 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -29,11 +29,11 @@ var ( func GetKeyBase() (keys.Keybase, error) { if keybase == nil { rootDir := viper.GetString(cli.HomeFlag) - keyman, err := client.GetKeyManager(rootDir) + kb, err := client.GetKeyBase(rootDir) if err != nil { return nil, err } - keybase = keyman + keybase = kb } return keybase, nil } From b0c65f804570154e1310f21269d7253f58135348 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 22 Feb 2018 17:20:25 +0100 Subject: [PATCH 41/70] All keys logic works with new basecli --- client/keys/add.go | 5 +- client/keys/delete.go | 12 +++-- client/keys/list.go | 4 +- client/keys/recover.go | 66 --------------------------- client/keys/root.go | 37 ++++++++------- client/keys/{get.go => show.go} | 11 ++--- client/keys/update.go | 12 +++-- examples/basecoin/cmd/basecli/main.go | 5 +- 8 files changed, 49 insertions(+), 103 deletions(-) delete mode 100644 client/keys/recover.go rename client/keys/{get.go => show.go} (83%) diff --git a/client/keys/add.go b/client/keys/add.go index f6f1698b7b2b..88d998323fff 100644 --- a/client/keys/add.go +++ b/client/keys/add.go @@ -105,7 +105,8 @@ type addOutput struct { } func printCreate(info keys.Info, seed string) { - switch viper.Get(cli.OutputFlag) { + output := viper.Get(cli.OutputFlag) + switch output { case "text": printInfo(info) // print seed unless requested not to. @@ -124,5 +125,7 @@ func printCreate(info keys.Info, seed string) { panic(err) // really shouldn't happen... } fmt.Println(string(json)) + default: + panic(fmt.Sprintf("I can't speak: %s", output)) } } diff --git a/client/keys/delete.go b/client/keys/delete.go index 91f39e6a118f..ce5a03dd429a 100644 --- a/client/keys/delete.go +++ b/client/keys/delete.go @@ -22,11 +22,13 @@ import ( "github.com/spf13/cobra" ) -// deleteCmd represents the delete command -var deleteCmd = &cobra.Command{ - Use: "delete [name]", - Short: "DANGER: Delete a private key from your system", - RunE: runDeleteCmd, +func deleteKeyCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "delete ", + Short: "Delete the given key", + RunE: runDeleteCmd, + } + return cmd } func runDeleteCmd(cmd *cobra.Command, args []string) error { diff --git a/client/keys/list.go b/client/keys/list.go index 8654deca682c..8e11d36afc05 100644 --- a/client/keys/list.go +++ b/client/keys/list.go @@ -16,8 +16,8 @@ package keys import "github.com/spf13/cobra" -// listCmd represents the list command -var listCmd = &cobra.Command{ +// listKeysCmd represents the list command +var listKeysCmd = &cobra.Command{ Use: "list", Short: "List all keys", Long: `Return a list of all public keys stored by this key manager diff --git a/client/keys/recover.go b/client/keys/recover.go deleted file mode 100644 index cd78eab8e6cb..000000000000 --- a/client/keys/recover.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright © 2017 Ethan Frey -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package keys - -import ( - "github.com/pkg/errors" - - "github.com/spf13/cobra" -) - -// recoverCmd represents the recover command -var recoverCmd = &cobra.Command{ - Use: "recover [name]", - Short: "Recover a private key from a seed phrase", - Long: `Recover a private key from a seed phrase. - -I really hope you wrote this down when you created the new key. -The seed is only displayed on creation, never again. - -You can also use this to copy a key between multiple testnets, -simply by "recovering" the key in the other nets you want to copy -to. Of course, it has no coins on the other nets, just the same address.`, - RunE: runRecoverCmd, -} - -func runRecoverCmd(cmd *cobra.Command, args []string) error { - if len(args) != 1 || len(args[0]) == 0 { - return errors.New("You must provide a name for the key") - } - name := args[0] - - pass, err := getPassword("Enter the new passphrase:") - if err != nil { - return err - } - - // not really a password... huh? - seed, err := getSeed("Enter your recovery seed phrase:") - if err != nil { - return err - } - - kb, err := GetKeyBase() - if err != nil { - return err - } - - info, err := kb.Recover(name, pass, seed) - if err != nil { - return err - } - printInfo(info) - return nil -} diff --git a/client/keys/root.go b/client/keys/root.go index 1336272b35c3..0dcf05040286 100644 --- a/client/keys/root.go +++ b/client/keys/root.go @@ -18,22 +18,27 @@ import ( "github.com/spf13/cobra" ) -// RootCmd represents the base command when called without any subcommands -var RootCmd = &cobra.Command{ - Use: "keys", - Short: "Key manager for tendermint clients", - Long: `Keys allows you to manage your local keystore for tendermint. +var lineBreak = &cobra.Command{Run: func(*cobra.Command, []string) {}} -These keys may be in any format supported by go-crypto and can be -used by light-clients, full nodes, or any other application that -needs to sign with a private key.`, -} +// Commands registers a sub-tree of commands to interact with +// local private key storage. +func Commands() *cobra.Command { + cmd := &cobra.Command{ + Use: "keys", + Short: "Add or view local private keys", + Long: `Keys allows you to manage your local keystore for tendermint. -func init() { - RootCmd.AddCommand(getCmd) - RootCmd.AddCommand(listCmd) - RootCmd.AddCommand(newCmd) - RootCmd.AddCommand(updateCmd) - RootCmd.AddCommand(deleteCmd) - RootCmd.AddCommand(recoverCmd) + These keys may be in any format supported by go-crypto and can be + used by light-clients, full nodes, or any other application that + needs to sign with a private key.`, + } + cmd.AddCommand( + addKeyCommand(), + listKeysCmd, + showKeysCmd, + lineBreak, + deleteKeyCommand(), + updateKeyCommand(), + ) + return cmd } diff --git a/client/keys/get.go b/client/keys/show.go similarity index 83% rename from client/keys/get.go rename to client/keys/show.go index e247473b2e6a..e569f265ded5 100644 --- a/client/keys/get.go +++ b/client/keys/show.go @@ -20,15 +20,14 @@ import ( "github.com/spf13/cobra" ) -// getCmd represents the get command -var getCmd = &cobra.Command{ - Use: "get [name]", - Short: "Get details of one key", +var showKeysCmd = &cobra.Command{ + Use: "show ", + Short: "Show key info for the given name", Long: `Return public details of one local key.`, - RunE: runGetCmd, + RunE: runShowCmd, } -func runGetCmd(cmd *cobra.Command, args []string) error { +func runShowCmd(cmd *cobra.Command, args []string) error { if len(args) != 1 || len(args[0]) == 0 { return errors.New("You must provide a name for the key") } diff --git a/client/keys/update.go b/client/keys/update.go index de8d06a525b4..59010a6d3e48 100644 --- a/client/keys/update.go +++ b/client/keys/update.go @@ -22,11 +22,13 @@ import ( "github.com/spf13/cobra" ) -// updateCmd represents the update command -var updateCmd = &cobra.Command{ - Use: "update [name]", - Short: "Change the password for a private key", - RunE: runUpdateCmd, +func updateKeyCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "update ", + Short: "Change the password used to protect private key", + RunE: runUpdateCmd, + } + return cmd } func runUpdateCmd(cmd *cobra.Command, args []string) error { diff --git a/examples/basecoin/cmd/basecli/main.go b/examples/basecoin/cmd/basecli/main.go index 1989d1b38d0a..3a785124a685 100644 --- a/examples/basecoin/cmd/basecli/main.go +++ b/examples/basecoin/cmd/basecli/main.go @@ -8,6 +8,7 @@ import ( "github.com/tendermint/tmlibs/cli" + "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/version" ) @@ -48,12 +49,12 @@ func main() { basecliCmd.AddCommand( lineBreak, serveCommand(), - KeyCommands(), + keys.Commands(), lineBreak, version.VersionCmd, ) // prepare and add flags - executor := cli.PrepareBaseCmd(basecliCmd, "GA", os.ExpandEnv("$HOME/.basecli")) + executor := cli.PrepareMainCmd(basecliCmd, "BC", os.ExpandEnv("$HOME/.basecli")) executor.Execute() } From 58f2cbfa384596d6372e5b3b0eeea15c91dd9b9d Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 22 Feb 2018 17:21:55 +0100 Subject: [PATCH 42/70] Removed keys stub --- examples/basecoin/cmd/basecli/client.go | 1 - examples/basecoin/cmd/basecli/key.go | 77 ------------------------- 2 files changed, 78 deletions(-) delete mode 100644 examples/basecoin/cmd/basecli/key.go diff --git a/examples/basecoin/cmd/basecli/client.go b/examples/basecoin/cmd/basecli/client.go index 682a571e6ee9..484ac73f74fb 100644 --- a/examples/basecoin/cmd/basecli/client.go +++ b/examples/basecoin/cmd/basecli/client.go @@ -60,7 +60,6 @@ func GetCommands(cmds ...*cobra.Command) []*cobra.Command { func PostCommands(cmds ...*cobra.Command) []*cobra.Command { for _, c := range cmds { c.Flags().String(flagName, "", "Name of private key with which to sign") - c.Flags().String(flagPassword, "", "Password to use the named private key") } return cmds } diff --git a/examples/basecoin/cmd/basecli/key.go b/examples/basecoin/cmd/basecli/key.go deleted file mode 100644 index b3ffa323aed6..000000000000 --- a/examples/basecoin/cmd/basecli/key.go +++ /dev/null @@ -1,77 +0,0 @@ -package main - -import "github.com/spf13/cobra" - -const ( - flagPassword = "password" - flagNewPassword = "new-password" - flagType = "type" - flagSeed = "seed" - flagDryRun = "dry-run" -) - -var ( - listKeysCmd = &cobra.Command{ - Use: "list", - Short: "List all locally availably keys", - RunE: todoNotImplemented, - } - - showKeysCmd = &cobra.Command{ - Use: "show ", - Short: "Show key info for the given name", - RunE: todoNotImplemented, - } -) - -// KeyCommands registers a sub-tree of commands to interact with -// local private key storage. -func KeyCommands() *cobra.Command { - cmd := &cobra.Command{ - Use: "keys", - Short: "Add or view local private keys", - } - cmd.AddCommand( - addKeyCommand(), - listKeysCmd, - showKeysCmd, - lineBreak, - deleteKeyCommand(), - updateKeyCommand(), - ) - return cmd -} - -func addKeyCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "add ", - Short: "Create a new key, or import from seed", - RunE: todoNotImplemented, - } - cmd.Flags().StringP(flagPassword, "p", "", "Password to encrypt private key") - cmd.Flags().StringP(flagType, "t", "ed25519", "Type of private key (ed25519|secp256k1|ledger)") - cmd.Flags().StringP(flagSeed, "s", "", "Provide seed phrase to recover existing key instead of creating") - cmd.Flags().Bool(flagDryRun, false, "Perform action, but don't add key to local keystore") - return cmd -} - -func updateKeyCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "update ", - Short: "Change the password used to protect private key", - RunE: todoNotImplemented, - } - cmd.Flags().StringP(flagPassword, "p", "", "Current password to decrypt key") - cmd.Flags().String(flagNewPassword, "", "New password to use to protect key") - return cmd -} - -func deleteKeyCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "delete ", - Short: "Delete the given key", - RunE: todoNotImplemented, - } - cmd.Flags().StringP(flagPassword, "p", "", "Password of existing key to delete") - return cmd -} From 8c93a6455b6f9c52afea1ba1b35ce9886230d5d2 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 22 Feb 2018 18:15:01 +0100 Subject: [PATCH 43/70] Implement query account without proofs --- client/node.go | 8 +++ examples/basecoin/cmd/basecli/client.go | 9 ++- examples/basecoin/cmd/basecli/commands.go | 77 +++++++++++++++++++++++ examples/basecoin/cmd/basecli/main.go | 12 +--- 4 files changed, 96 insertions(+), 10 deletions(-) create mode 100644 client/node.go diff --git a/client/node.go b/client/node.go new file mode 100644 index 000000000000..5328f49d84d9 --- /dev/null +++ b/client/node.go @@ -0,0 +1,8 @@ +package client + +import rpcclient "github.com/tendermint/tendermint/rpc/client" + +// GetNode prepares a simple rpc.Client from the flags +func GetNode(uri string) rpcclient.Client { + return rpcclient.NewHTTP(uri, "/websocket") +} diff --git a/examples/basecoin/cmd/basecli/client.go b/examples/basecoin/cmd/basecli/client.go index 484ac73f74fb..736ce4b4e280 100644 --- a/examples/basecoin/cmd/basecli/client.go +++ b/examples/basecoin/cmd/basecli/client.go @@ -12,6 +12,7 @@ const ( flagCommit = "commit" flagValHash = "validator-set" + flagHeight = "height" flagSelect = "select" flagTags = "tag" flagAny = "any" @@ -51,7 +52,11 @@ func AddClientCommands(cmd *cobra.Command) { // GetCommands adds common flags to query commands func GetCommands(cmds ...*cobra.Command) []*cobra.Command { for _, c := range cmds { - c.Flags().Bool(flagTrustNode, false, "Don't verify proofs for responses") + // TODO: make this default false when we support proofs + c.Flags().Bool(flagTrustNode, true, "Don't verify proofs for responses") + c.Flags().String(flagChainID, "", "Chain ID of tendermint node") + c.Flags().String(flagNode, "", ": to tendermint rpc interface for this chain") + c.Flags().Int64(flagHeight, 0, "block height to query, omit to get most recent provable block") } return cmds } @@ -60,6 +65,8 @@ func GetCommands(cmds ...*cobra.Command) []*cobra.Command { func PostCommands(cmds ...*cobra.Command) []*cobra.Command { for _, c := range cmds { c.Flags().String(flagName, "", "Name of private key with which to sign") + c.Flags().String(flagChainID, "", "Chain ID of tendermint node") + c.Flags().String(flagNode, "", ": to tendermint rpc interface for this chain") } return cmds } diff --git a/examples/basecoin/cmd/basecli/commands.go b/examples/basecoin/cmd/basecli/commands.go index 2af60010d220..a1c2dd5d8286 100644 --- a/examples/basecoin/cmd/basecli/commands.go +++ b/examples/basecoin/cmd/basecli/commands.go @@ -1,7 +1,20 @@ package main import ( + "encoding/hex" + "encoding/json" + "fmt" + + "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/viper" + + crypto "github.com/tendermint/go-crypto" + rpcclient "github.com/tendermint/tendermint/rpc/client" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/examples/basecoin/app" + "github.com/cosmos/cosmos-sdk/examples/basecoin/types" ) const ( @@ -21,3 +34,67 @@ func postSendCommand() *cobra.Command { cmd.Flags().String(flagFee, "", "Fee to pay along with transaction") return cmd } + +func getAccountCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "account
", + Short: "Query account balance", + RunE: getAccount, + } + return cmd +} + +func getAccount(cmd *cobra.Command, args []string) error { + if len(args) != 1 || len(args[0]) == 0 { + return errors.New("You must provide an account name") + } + + // find the key to look up the account + addr := args[0] + bz, err := hex.DecodeString(addr) + if err != nil { + return err + } + key := crypto.Address(bz) + + // TODO: make the store name a variable in getAccountCmd? + path := "/main/key" + + uri := viper.GetString(flagNode) + if uri == "" { + return errors.New("Must define which node to query with --node") + } + node := client.GetNode(uri) + + opts := rpcclient.ABCIQueryOptions{ + Height: viper.GetInt64(flagHeight), + // Trusted: viper.GetBool(flagTrustNode), + Trusted: true, + } + result, err := node.ABCIQueryWithOptions(path, key, opts) + if err != nil { + return err + } + resp := result.Response + if resp.Code != uint32(0) { + return errors.Errorf("Query failed: (%d) %s", resp.Code, resp.Log) + } + + // parse out the value + acct := new(types.AppAccount) + cdc := app.MakeTxCodec() + err = cdc.UnmarshalBinary(resp.Value, acct) + if err != nil { + return err + } + + // TODO: better + // fmt.Printf("Account: %#v\n", acct) + output, err := json.MarshalIndent(acct, "", " ") + // output, err := json.MarshalIndent(acct.BaseAccount.Coins, "", " ") + if err != nil { + return err + } + fmt.Println(string(output)) + return nil +} diff --git a/examples/basecoin/cmd/basecli/main.go b/examples/basecoin/cmd/basecli/main.go index 3a785124a685..5ba5450157b7 100644 --- a/examples/basecoin/cmd/basecli/main.go +++ b/examples/basecoin/cmd/basecli/main.go @@ -20,12 +20,6 @@ var ( } lineBreak = &cobra.Command{Run: func(*cobra.Command, []string) {}} - - getAccountCmd = &cobra.Command{ - Use: "account
", - Short: "Query account balance", - RunE: todoNotImplemented, - } ) func todoNotImplemented(_ *cobra.Command, _ []string) error { @@ -38,10 +32,10 @@ func main() { // generic client commands AddClientCommands(basecliCmd) - // query commands (custom to binary) + + // query/post commands (custom to binary) basecliCmd.AddCommand( - GetCommands(getAccountCmd)...) - // post tx commands (custom to binary) + GetCommands(getAccountCmd())...) basecliCmd.AddCommand( PostCommands(postSendCommand())...) From f481900b236ea1b0d4380a048030ea79f41e0c40 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 22 Feb 2018 18:51:08 +0100 Subject: [PATCH 44/70] Hacked together sendtx - basecoin parse logic fails --- examples/basecoin/app/app.go | 1 + .../cmd/basecli/{commands.go => account.go} | 18 --- examples/basecoin/cmd/basecli/sendtx.go | 150 ++++++++++++++++++ 3 files changed, 151 insertions(+), 18 deletions(-) rename examples/basecoin/cmd/basecli/{commands.go => account.go} (81%) create mode 100644 examples/basecoin/cmd/basecli/sendtx.go diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index b26a9bdd45c2..45226a4a4580 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -73,6 +73,7 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { // custom tx codec func MakeTxCodec() *wire.Codec { cdc := wire.NewCodec() + cdc.RegisterInterface((*sdk.Msg)(nil), nil) crypto.RegisterWire(cdc) // Register crypto.[PubKey,PrivKey,Signature] types. bank.RegisterWire(cdc) // Register bank.[SendMsg,IssueMsg] types. return cdc diff --git a/examples/basecoin/cmd/basecli/commands.go b/examples/basecoin/cmd/basecli/account.go similarity index 81% rename from examples/basecoin/cmd/basecli/commands.go rename to examples/basecoin/cmd/basecli/account.go index a1c2dd5d8286..26c832904497 100644 --- a/examples/basecoin/cmd/basecli/commands.go +++ b/examples/basecoin/cmd/basecli/account.go @@ -17,24 +17,6 @@ import ( "github.com/cosmos/cosmos-sdk/examples/basecoin/types" ) -const ( - flagTo = "to" - flagAmount = "amount" - flagFee = "fee" -) - -func postSendCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "send", - Short: "Create and sign a send tx", - RunE: todoNotImplemented, - } - cmd.Flags().String(flagTo, "", "Address to send coins") - cmd.Flags().String(flagAmount, "", "Amount of coins to send") - cmd.Flags().String(flagFee, "", "Fee to pay along with transaction") - return cmd -} - func getAccountCmd() *cobra.Command { cmd := &cobra.Command{ Use: "account
", diff --git a/examples/basecoin/cmd/basecli/sendtx.go b/examples/basecoin/cmd/basecli/sendtx.go new file mode 100644 index 000000000000..add8c27ebc68 --- /dev/null +++ b/examples/basecoin/cmd/basecli/sendtx.go @@ -0,0 +1,150 @@ +package main + +import ( + "encoding/hex" + "fmt" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/examples/basecoin/app" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank" + crypto "github.com/tendermint/go-crypto" + "github.com/tendermint/tmlibs/cli" +) + +const ( + flagTo = "to" + flagAmount = "amount" + flagFee = "fee" + flagSequence = "seq" +) + +func postSendCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "send", + Short: "Create and sign a send tx", + RunE: sendTx, + } + cmd.Flags().String(flagTo, "", "Address to send coins") + cmd.Flags().String(flagAmount, "", "Amount of coins to send") + cmd.Flags().String(flagFee, "", "Fee to pay along with transaction") + cmd.Flags().Int64(flagSequence, 0, "Sequence number to sign the tx") + return cmd +} + +func sendTx(cmd *cobra.Command, args []string) error { + txBytes, err := buildTx() + if err != nil { + return err + } + + uri := viper.GetString(flagNode) + if uri == "" { + return errors.New("Must define which node to query with --node") + } + node := client.GetNode(uri) + + result, err := node.BroadcastTxCommit(txBytes) + if err != nil { + return err + } + + if result.CheckTx.Code != uint32(0) { + return errors.Errorf("CheckTx failed: (%d) %s", + result.CheckTx.Code, + result.CheckTx.Log) + } + if result.DeliverTx.Code != uint32(0) { + return errors.Errorf("CheckTx failed: (%d) %s", + result.DeliverTx.Code, + result.DeliverTx.Log) + } + + fmt.Printf("Result: %#v\n", result) + + // // parse out the value + // acct := new(types.AppAccount) + // cdc := app.MakeTxCodec() + // err = cdc.UnmarshalBinary(resp.Value, acct) + // if err != nil { + // return err + // } + + // // TODO: better + // // fmt.Printf("Account: %#v\n", acct) + // output, err := json.MarshalIndent(acct, "", " ") + // // output, err := json.MarshalIndent(acct.BaseAccount.Coins, "", " ") + // if err != nil { + // return err + // } + // fmt.Println(string(output)) + return nil +} + +func buildTx() ([]byte, error) { + rootDir := viper.GetString(cli.HomeFlag) + keybase, err := client.GetKeyBase(rootDir) + if err != nil { + return nil, err + } + + name := viper.GetString(flagName) + info, err := keybase.Get(name) + if err != nil { + return nil, errors.WithMessage(err, "No key for name") + } + from := info.PubKey.Address() + + msg, err := buildMsg(from) + if err != nil { + return nil, err + } + + // sign and build + bz := msg.GetSignBytes() + passphrase := "1234567890" // XXX: adults only + sig, pubkey, err := keybase.Sign(name, passphrase, bz) + if err != nil { + return nil, err + } + sigs := []sdk.StdSignature{{ + PubKey: pubkey, + Signature: sig, + Sequence: viper.GetInt64(flagSequence), + }} + + // marshal bytes + tx := sdk.NewStdTx(msg, sigs) + cdc := app.MakeTxCodec() + txBytes, err := cdc.MarshalBinary(tx) + if err != nil { + return nil, err + } + return txBytes, nil +} + +func buildMsg(from crypto.Address) (sdk.Msg, error) { + // parse coins + amount := viper.GetString(flagAmount) + coins, err := sdk.ParseCoins(amount) + if err != nil { + return nil, err + } + + // parse destination address + dest := viper.GetString(flagTo) + bz, err := hex.DecodeString(dest) + if err != nil { + return nil, err + } + to := crypto.Address(bz) + + input := bank.NewInput(from, coins) + output := bank.NewOutput(to, coins) + msg := bank.NewSendMsg([]bank.Input{input}, []bank.Output{output}) + return msg, nil +} From 67e896dcaf87b175aea3348d40cc83c7408036f4 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 22 Feb 2018 19:10:50 +0100 Subject: [PATCH 45/70] Clean up sendtx example --- examples/basecoin/app/app_test.go | 15 +++++++++++---- examples/basecoin/cmd/basecli/sendtx.go | 19 +------------------ 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/examples/basecoin/app/app_test.go b/examples/basecoin/app/app_test.go index 20985fa32960..49eb580fd413 100644 --- a/examples/basecoin/app/app_test.go +++ b/examples/basecoin/app/app_test.go @@ -53,14 +53,21 @@ func TestSendMsg(t *testing.T) { Signature: sig, }}) + // just marshal/unmarshal! + cdc := MakeTxCodec() + txBytes, err := cdc.MarshalBinary(tx) + require.NoError(t, err) + // Run a Check - res := bapp.Check(tx) - assert.Equal(t, sdk.CodeUnrecognizedAddress, res.Code, res.Log) + cres := bapp.CheckTx(txBytes) + assert.Equal(t, sdk.CodeUnrecognizedAddress, + sdk.CodeType(cres.Code), cres.Log) // Simulate a Block bapp.BeginBlock(abci.RequestBeginBlock{}) - res = bapp.Deliver(tx) - assert.Equal(t, sdk.CodeUnrecognizedAddress, res.Code, res.Log) + dres := bapp.DeliverTx(txBytes) + assert.Equal(t, sdk.CodeUnrecognizedAddress, + sdk.CodeType(dres.Code), dres.Log) } func TestGenesis(t *testing.T) { diff --git a/examples/basecoin/cmd/basecli/sendtx.go b/examples/basecoin/cmd/basecli/sendtx.go index add8c27ebc68..b81f898d5efd 100644 --- a/examples/basecoin/cmd/basecli/sendtx.go +++ b/examples/basecoin/cmd/basecli/sendtx.go @@ -64,24 +64,7 @@ func sendTx(cmd *cobra.Command, args []string) error { result.DeliverTx.Log) } - fmt.Printf("Result: %#v\n", result) - - // // parse out the value - // acct := new(types.AppAccount) - // cdc := app.MakeTxCodec() - // err = cdc.UnmarshalBinary(resp.Value, acct) - // if err != nil { - // return err - // } - - // // TODO: better - // // fmt.Printf("Account: %#v\n", acct) - // output, err := json.MarshalIndent(acct, "", " ") - // // output, err := json.MarshalIndent(acct.BaseAccount.Coins, "", " ") - // if err != nil { - // return err - // } - // fmt.Println(string(output)) + fmt.Printf("Committed at block %d. Hash: %s\n", result.Height, result.Hash.String()) return nil } From 00304dd094d8f9fd5ae46a27dd7718c6d53f3149 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 23 Feb 2018 10:40:10 +0100 Subject: [PATCH 46/70] Prompt for password on sendtx --- client/input.go | 87 ++++++++++++++++++++++++ client/keys/add.go | 4 +- client/keys/delete.go | 3 +- client/keys/update.go | 5 +- client/keys/utils.go | 73 -------------------- examples/basecoin/cmd/basecli/account.go | 3 +- examples/basecoin/cmd/basecli/sendtx.go | 6 +- examples/basecoin/types/account.go | 2 +- 8 files changed, 101 insertions(+), 82 deletions(-) create mode 100644 client/input.go diff --git a/client/input.go b/client/input.go new file mode 100644 index 000000000000..b370b0d88f6f --- /dev/null +++ b/client/input.go @@ -0,0 +1,87 @@ +package client + +import ( + "bufio" + "fmt" + "os" + "strings" + + "github.com/bgentry/speakeasy" + "github.com/pkg/errors" + isatty "github.com/tendermint/vendor-bak/github.com/mattn/go-isatty" +) + +// MinPassLength is the minimum acceptable password length +const MinPassLength = 8 + +// if we read from non-tty, we just need to init the buffer reader once, +// in case we try to read multiple passwords (eg. update) +var buf *bufio.Reader + +// GetPassword will prompt for a password one-time (to sign a tx) +// It enforces the password length +func GetPassword(prompt string) (pass string, err error) { + if inputIsTty() { + pass, err = speakeasy.Ask(prompt) + } else { + pass, err = stdinPassword() + } + if err != nil { + return "", err + } + if len(pass) < MinPassLength { + return "", errors.Errorf("Password must be at least %d characters", MinPassLength) + } + return pass, nil +} + +// GetSeed will request a seed phrase from stdin and trims off +// leading/trailing spaces +func GetSeed(prompt string) (seed string, err error) { + if inputIsTty() { + fmt.Println(prompt) + } + seed, err = stdinPassword() + seed = strings.TrimSpace(seed) + return +} + +// GetCheckPassword will prompt for a password twice to verify they +// match (for creating a new password). +// It enforces the password length. Only parses password once if +// input is piped in. +func GetCheckPassword(prompt, prompt2 string) (string, error) { + // simple read on no-tty + if !inputIsTty() { + return GetPassword(prompt) + } + + // TODO: own function??? + pass, err := GetPassword(prompt) + if err != nil { + return "", err + } + pass2, err := GetPassword(prompt2) + if err != nil { + return "", err + } + if pass != pass2 { + return "", errors.New("Passphrases don't match") + } + return pass, nil +} + +func inputIsTty() bool { + return isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) +} + +func stdinPassword() (string, error) { + if buf == nil { + buf = bufio.NewReader(os.Stdin) + } + pass, err := buf.ReadString('\n') + if err != nil { + return "", err + } + return strings.TrimSpace(pass), nil +} diff --git a/client/keys/add.go b/client/keys/add.go index 88d998323fff..07b1deba1c96 100644 --- a/client/keys/add.go +++ b/client/keys/add.go @@ -69,14 +69,14 @@ func runAddCmd(cmd *cobra.Command, args []string) error { if err != nil { return err } - pass, err = getCheckPassword("Enter a passphrase for your key:", "Repeat the passphrase:") + pass, err = client.GetCheckPassword("Enter a passphrase for your key:", "Repeat the passphrase:") if err != nil { return err } } if viper.GetBool(flagRecover) { - seed, err := getPassword("Enter your recovery seed phrase:") + seed, err := client.GetSeed("Enter your recovery seed phrase:") if err != nil { return err } diff --git a/client/keys/delete.go b/client/keys/delete.go index ce5a03dd429a..58050087a5b5 100644 --- a/client/keys/delete.go +++ b/client/keys/delete.go @@ -17,6 +17,7 @@ package keys import ( "fmt" + "github.com/cosmos/cosmos-sdk/client" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -37,7 +38,7 @@ func runDeleteCmd(cmd *cobra.Command, args []string) error { } name := args[0] - oldpass, err := getPassword("DANGER - enter password to permanently delete key:") + oldpass, err := client.GetPassword("DANGER - enter password to permanently delete key:") if err != nil { return err } diff --git a/client/keys/update.go b/client/keys/update.go index 59010a6d3e48..a7197dd96813 100644 --- a/client/keys/update.go +++ b/client/keys/update.go @@ -17,6 +17,7 @@ package keys import ( "fmt" + "github.com/cosmos/cosmos-sdk/client" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -37,11 +38,11 @@ func runUpdateCmd(cmd *cobra.Command, args []string) error { } name := args[0] - oldpass, err := getPassword("Enter the current passphrase:") + oldpass, err := client.GetPassword("Enter the current passphrase:") if err != nil { return err } - newpass, err := getCheckPassword("Enter the new passphrase:", "Repeat the new passphrase:") + newpass, err := client.GetCheckPassword("Enter the new passphrase:", "Repeat the new passphrase:") if err != nil { return err } diff --git a/client/keys/utils.go b/client/keys/utils.go index 346aa17bcff5..5c156cb74842 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -1,14 +1,8 @@ package keys import ( - "bufio" "fmt" - "os" - "strings" - "github.com/bgentry/speakeasy" - isatty "github.com/mattn/go-isatty" - "github.com/pkg/errors" "github.com/spf13/viper" keys "github.com/tendermint/go-crypto/keys" @@ -17,9 +11,6 @@ import ( "github.com/cosmos/cosmos-sdk/client" ) -// MinPassLength is the minimum acceptable password length -const MinPassLength = 8 - var ( // keybase is used to make GetKeyBase a singleton keybase keys.Keybase @@ -38,70 +29,6 @@ func GetKeyBase() (keys.Keybase, error) { return keybase, nil } -// if we read from non-tty, we just need to init the buffer reader once, -// in case we try to read multiple passwords (eg. update) -var buf *bufio.Reader - -func inputIsTty() bool { - return isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) -} - -func stdinPassword() (string, error) { - if buf == nil { - buf = bufio.NewReader(os.Stdin) - } - pass, err := buf.ReadString('\n') - if err != nil { - return "", err - } - return strings.TrimSpace(pass), nil -} - -func getPassword(prompt string) (pass string, err error) { - if inputIsTty() { - pass, err = speakeasy.Ask(prompt) - } else { - pass, err = stdinPassword() - } - if err != nil { - return "", err - } - if len(pass) < MinPassLength { - return "", errors.Errorf("Password must be at least %d characters", MinPassLength) - } - return pass, nil -} - -func getSeed(prompt string) (seed string, err error) { - if inputIsTty() { - fmt.Println(prompt) - } - seed, err = stdinPassword() - seed = strings.TrimSpace(seed) - return -} - -func getCheckPassword(prompt, prompt2 string) (string, error) { - // simple read on no-tty - if !inputIsTty() { - return getPassword(prompt) - } - - // TODO: own function??? - pass, err := getPassword(prompt) - if err != nil { - return "", err - } - pass2, err := getPassword(prompt2) - if err != nil { - return "", err - } - if pass != pass2 { - return "", errors.New("Passphrases don't match") - } - return pass, nil -} - func printInfo(info keys.Info) { switch viper.Get(cli.OutputFlag) { case "text": diff --git a/examples/basecoin/cmd/basecli/account.go b/examples/basecoin/cmd/basecli/account.go index 26c832904497..118e37b24f65 100644 --- a/examples/basecoin/cmd/basecli/account.go +++ b/examples/basecoin/cmd/basecli/account.go @@ -70,8 +70,7 @@ func getAccount(cmd *cobra.Command, args []string) error { return err } - // TODO: better - // fmt.Printf("Account: %#v\n", acct) + // print out whole account or just coins? output, err := json.MarshalIndent(acct, "", " ") // output, err := json.MarshalIndent(acct.BaseAccount.Coins, "", " ") if err != nil { diff --git a/examples/basecoin/cmd/basecli/sendtx.go b/examples/basecoin/cmd/basecli/sendtx.go index b81f898d5efd..8eff14b6a186 100644 --- a/examples/basecoin/cmd/basecli/sendtx.go +++ b/examples/basecoin/cmd/basecli/sendtx.go @@ -89,7 +89,11 @@ func buildTx() ([]byte, error) { // sign and build bz := msg.GetSignBytes() - passphrase := "1234567890" // XXX: adults only + prompt := fmt.Sprintf("Password to sign with '%s':", name) + passphrase, err := client.GetPassword(prompt) + if err != nil { + return nil, err + } sig, pubkey, err := keybase.Sign(name, passphrase, bz) if err != nil { return nil, err diff --git a/examples/basecoin/types/account.go b/examples/basecoin/types/account.go index 3177f991220b..c9b1d8a239f8 100644 --- a/examples/basecoin/types/account.go +++ b/examples/basecoin/types/account.go @@ -15,7 +15,7 @@ var _ sdk.Account = (*AppAccount)(nil) // auth.AccountStore uses the flexible go-wire library. type AppAccount struct { auth.BaseAccount - Name string + Name string `json:"name"` } // nolint From 356baf61c1f94b24000109235081560bdae6a813 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 23 Feb 2018 10:49:30 +0100 Subject: [PATCH 47/70] Move sendtx and query account commands into x/bank --- client/flags.go | 33 ++++++++++++++ examples/basecoin/cmd/basecli/client.go | 45 ++++--------------- examples/basecoin/cmd/basecli/main.go | 6 ++- .../basecli => x/bank/commands}/account.go | 13 +++--- .../cmd/basecli => x/bank/commands}/sendtx.go | 9 ++-- 5 files changed, 58 insertions(+), 48 deletions(-) create mode 100644 client/flags.go rename {examples/basecoin/cmd/basecli => x/bank/commands}/account.go (85%) rename {examples/basecoin/cmd/basecli => x/bank/commands}/sendtx.go (93%) diff --git a/client/flags.go b/client/flags.go new file mode 100644 index 000000000000..e89c49cf8928 --- /dev/null +++ b/client/flags.go @@ -0,0 +1,33 @@ +package client + +import "github.com/spf13/cobra" + +const ( + FlagChainID = "chain-id" + FlagNode = "node" + FlagHeight = "height" + FlagTrustNode = "trust-node" + FlagName = "name" +) + +// GetCommands adds common flags to query commands +func GetCommands(cmds ...*cobra.Command) []*cobra.Command { + for _, c := range cmds { + // TODO: make this default false when we support proofs + c.Flags().Bool(FlagTrustNode, true, "Don't verify proofs for responses") + c.Flags().String(FlagChainID, "", "Chain ID of tendermint node") + c.Flags().String(FlagNode, "", ": to tendermint rpc interface for this chain") + c.Flags().Int64(FlagHeight, 0, "block height to query, omit to get most recent provable block") + } + return cmds +} + +// PostCommands adds common flags for commands to post tx +func PostCommands(cmds ...*cobra.Command) []*cobra.Command { + for _, c := range cmds { + c.Flags().String(FlagName, "", "Name of private key with which to sign") + c.Flags().String(FlagChainID, "", "Chain ID of tendermint node") + c.Flags().String(FlagNode, "", ": to tendermint rpc interface for this chain") + } + return cmds +} diff --git a/examples/basecoin/cmd/basecli/client.go b/examples/basecoin/cmd/basecli/client.go index 736ce4b4e280..26ee87375d4e 100644 --- a/examples/basecoin/cmd/basecli/client.go +++ b/examples/basecoin/cmd/basecli/client.go @@ -1,28 +1,23 @@ package main -import "github.com/spf13/cobra" +import ( + "github.com/spf13/cobra" -const ( - // these are needed for every init - flagChainID = "chain-id" - flagNode = "node" + "github.com/cosmos/cosmos-sdk/client" +) +const ( // one of the following should be provided to verify the connection flagGenesis = "genesis" flagCommit = "commit" flagValHash = "validator-set" - flagHeight = "height" flagSelect = "select" flagTags = "tag" flagAny = "any" - flagBind = "bind" - flagCORS = "cors" - flagTrustNode = "trust-node" - - // this is for signing - flagName = "name" + flagBind = "bind" + flagCORS = "cors" ) var ( @@ -49,36 +44,14 @@ func AddClientCommands(cmd *cobra.Command) { ) } -// GetCommands adds common flags to query commands -func GetCommands(cmds ...*cobra.Command) []*cobra.Command { - for _, c := range cmds { - // TODO: make this default false when we support proofs - c.Flags().Bool(flagTrustNode, true, "Don't verify proofs for responses") - c.Flags().String(flagChainID, "", "Chain ID of tendermint node") - c.Flags().String(flagNode, "", ": to tendermint rpc interface for this chain") - c.Flags().Int64(flagHeight, 0, "block height to query, omit to get most recent provable block") - } - return cmds -} - -// PostCommands adds common flags for commands to post tx -func PostCommands(cmds ...*cobra.Command) []*cobra.Command { - for _, c := range cmds { - c.Flags().String(flagName, "", "Name of private key with which to sign") - c.Flags().String(flagChainID, "", "Chain ID of tendermint node") - c.Flags().String(flagNode, "", ": to tendermint rpc interface for this chain") - } - return cmds -} - func initClientCommand() *cobra.Command { cmd := &cobra.Command{ Use: "init", Short: "Initialize light client", RunE: todoNotImplemented, } - cmd.Flags().StringP(flagChainID, "c", "", "ID of chain we connect to") - cmd.Flags().StringP(flagNode, "n", "tcp://localhost:46657", "Node to connect to") + cmd.Flags().StringP(client.FlagChainID, "c", "", "ID of chain we connect to") + cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to") cmd.Flags().String(flagGenesis, "", "Genesis file to verify header validity") cmd.Flags().String(flagCommit, "", "File with trusted and signed header") cmd.Flags().String(flagValHash, "", "Hash of trusted validator set (hex-encoded)") diff --git a/examples/basecoin/cmd/basecli/main.go b/examples/basecoin/cmd/basecli/main.go index 5ba5450157b7..f12dc8c65f7b 100644 --- a/examples/basecoin/cmd/basecli/main.go +++ b/examples/basecoin/cmd/basecli/main.go @@ -8,8 +8,10 @@ import ( "github.com/tendermint/tmlibs/cli" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/version" + bankcmd "github.com/cosmos/cosmos-sdk/x/bank/commands" ) // gaiacliCmd is the entry point for this binary @@ -35,9 +37,9 @@ func main() { // query/post commands (custom to binary) basecliCmd.AddCommand( - GetCommands(getAccountCmd())...) + client.GetCommands(bankcmd.GetAccountCmd())...) basecliCmd.AddCommand( - PostCommands(postSendCommand())...) + client.PostCommands(bankcmd.SendTxCommand())...) // add proxy, version and key info basecliCmd.AddCommand( diff --git a/examples/basecoin/cmd/basecli/account.go b/x/bank/commands/account.go similarity index 85% rename from examples/basecoin/cmd/basecli/account.go rename to x/bank/commands/account.go index 118e37b24f65..c805d44c21bb 100644 --- a/examples/basecoin/cmd/basecli/account.go +++ b/x/bank/commands/account.go @@ -1,4 +1,4 @@ -package main +package commands import ( "encoding/hex" @@ -17,7 +17,9 @@ import ( "github.com/cosmos/cosmos-sdk/examples/basecoin/types" ) -func getAccountCmd() *cobra.Command { +// GetAccountCmd returns a query account that will display the +// state of the account at a given address +func GetAccountCmd() *cobra.Command { cmd := &cobra.Command{ Use: "account
", Short: "Query account balance", @@ -42,16 +44,15 @@ func getAccount(cmd *cobra.Command, args []string) error { // TODO: make the store name a variable in getAccountCmd? path := "/main/key" - uri := viper.GetString(flagNode) + uri := viper.GetString(client.FlagNode) if uri == "" { return errors.New("Must define which node to query with --node") } node := client.GetNode(uri) opts := rpcclient.ABCIQueryOptions{ - Height: viper.GetInt64(flagHeight), - // Trusted: viper.GetBool(flagTrustNode), - Trusted: true, + Height: viper.GetInt64(client.FlagHeight), + Trusted: viper.GetBool(client.FlagTrustNode), } result, err := node.ABCIQueryWithOptions(path, key, opts) if err != nil { diff --git a/examples/basecoin/cmd/basecli/sendtx.go b/x/bank/commands/sendtx.go similarity index 93% rename from examples/basecoin/cmd/basecli/sendtx.go rename to x/bank/commands/sendtx.go index 8eff14b6a186..011d2877d8b9 100644 --- a/examples/basecoin/cmd/basecli/sendtx.go +++ b/x/bank/commands/sendtx.go @@ -1,4 +1,4 @@ -package main +package commands import ( "encoding/hex" @@ -23,7 +23,8 @@ const ( flagSequence = "seq" ) -func postSendCommand() *cobra.Command { +// SendTxCommand will create a send tx and sign it with the given key +func SendTxCommand() *cobra.Command { cmd := &cobra.Command{ Use: "send", Short: "Create and sign a send tx", @@ -42,7 +43,7 @@ func sendTx(cmd *cobra.Command, args []string) error { return err } - uri := viper.GetString(flagNode) + uri := viper.GetString(client.FlagNode) if uri == "" { return errors.New("Must define which node to query with --node") } @@ -75,7 +76,7 @@ func buildTx() ([]byte, error) { return nil, err } - name := viper.GetString(flagName) + name := viper.GetString(client.FlagName) info, err := keybase.Get(name) if err != nil { return nil, errors.WithMessage(err, "No key for name") From c7ca6ec038b3e8a6c79b4b0eafaf58656c2d3866 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 23 Feb 2018 11:00:33 +0100 Subject: [PATCH 48/70] Fixed import --- client/input.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/input.go b/client/input.go index b370b0d88f6f..8670f42bf2c2 100644 --- a/client/input.go +++ b/client/input.go @@ -7,8 +7,8 @@ import ( "strings" "github.com/bgentry/speakeasy" + isatty "github.com/mattn/go-isatty" "github.com/pkg/errors" - isatty "github.com/tendermint/vendor-bak/github.com/mattn/go-isatty" ) // MinPassLength is the minimum acceptable password length From 7779eec990a690502c3007e2480607994e13064a Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 23 Feb 2018 11:07:53 +0100 Subject: [PATCH 49/70] Make store name for query account configurable --- examples/basecoin/cmd/basecli/main.go | 8 ++++++-- x/bank/commands/account.go | 19 +++++++++++-------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/examples/basecoin/cmd/basecli/main.go b/examples/basecoin/cmd/basecli/main.go index f12dc8c65f7b..8dbdad04d9d6 100644 --- a/examples/basecoin/cmd/basecli/main.go +++ b/examples/basecoin/cmd/basecli/main.go @@ -37,9 +37,13 @@ func main() { // query/post commands (custom to binary) basecliCmd.AddCommand( - client.GetCommands(bankcmd.GetAccountCmd())...) + client.GetCommands( + bankcmd.GetAccountCmd("main"), + )...) basecliCmd.AddCommand( - client.PostCommands(bankcmd.SendTxCommand())...) + client.PostCommands( + bankcmd.SendTxCommand(), + )...) // add proxy, version and key info basecliCmd.AddCommand( diff --git a/x/bank/commands/account.go b/x/bank/commands/account.go index c805d44c21bb..87f4066d9d1e 100644 --- a/x/bank/commands/account.go +++ b/x/bank/commands/account.go @@ -19,16 +19,21 @@ import ( // GetAccountCmd returns a query account that will display the // state of the account at a given address -func GetAccountCmd() *cobra.Command { - cmd := &cobra.Command{ +func GetAccountCmd(storeName string) *cobra.Command { + cmd := acctCmd{storeName} + + return &cobra.Command{ Use: "account
", Short: "Query account balance", - RunE: getAccount, + RunE: cmd.get, } - return cmd } -func getAccount(cmd *cobra.Command, args []string) error { +type acctCmd struct { + storeName string +} + +func (a acctCmd) get(cmd *cobra.Command, args []string) error { if len(args) != 1 || len(args[0]) == 0 { return errors.New("You must provide an account name") } @@ -40,9 +45,7 @@ func getAccount(cmd *cobra.Command, args []string) error { return err } key := crypto.Address(bz) - - // TODO: make the store name a variable in getAccountCmd? - path := "/main/key" + path := fmt.Sprintf("/%s/key", a.storeName) uri := viper.GetString(client.FlagNode) if uri == "" { From bae7cec3fa97e5672884263676d0eb46ab50df8c Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 23 Feb 2018 11:25:25 +0100 Subject: [PATCH 50/70] Move all subcommands out of main into proper folders --- client/flags.go | 4 ++ client/keys/root.go | 5 +- client/lcd/root.go | 36 ++++++++++++ .../basecli/client.go => client/rpc/root.go | 55 +++---------------- client/tx/root.go | 45 +++++++++++++++ examples/basecoin/cmd/basecli/main.go | 20 ++++--- 6 files changed, 108 insertions(+), 57 deletions(-) create mode 100644 client/lcd/root.go rename examples/basecoin/cmd/basecli/client.go => client/rpc/root.go (54%) create mode 100644 client/tx/root.go diff --git a/client/flags.go b/client/flags.go index e89c49cf8928..db35ad06a49b 100644 --- a/client/flags.go +++ b/client/flags.go @@ -10,6 +10,10 @@ const ( FlagName = "name" ) +// LineBreak can be included in a command list to provide a blank line +// to help with readability +var LineBreak = &cobra.Command{Run: func(*cobra.Command, []string) {}} + // GetCommands adds common flags to query commands func GetCommands(cmds ...*cobra.Command) []*cobra.Command { for _, c := range cmds { diff --git a/client/keys/root.go b/client/keys/root.go index 0dcf05040286..d067d15159da 100644 --- a/client/keys/root.go +++ b/client/keys/root.go @@ -15,11 +15,10 @@ package keys import ( + "github.com/cosmos/cosmos-sdk/client" "github.com/spf13/cobra" ) -var lineBreak = &cobra.Command{Run: func(*cobra.Command, []string) {}} - // Commands registers a sub-tree of commands to interact with // local private key storage. func Commands() *cobra.Command { @@ -36,7 +35,7 @@ func Commands() *cobra.Command { addKeyCommand(), listKeysCmd, showKeysCmd, - lineBreak, + client.LineBreak, deleteKeyCommand(), updateKeyCommand(), ) diff --git a/client/lcd/root.go b/client/lcd/root.go new file mode 100644 index 000000000000..9187af9a1a8d --- /dev/null +++ b/client/lcd/root.go @@ -0,0 +1,36 @@ +package lcd + +import ( + "errors" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" +) + +const ( + flagBind = "bind" + flagCORS = "cors" +) + +// XXX: remove this when not needed +func todoNotImplemented(_ *cobra.Command, _ []string) error { + return errors.New("TODO: Command not yet implemented") +} + +// ServeCommand will generate a long-running rest server +// (aka Light Client Daemon) that exposes functionality similar +// to the cli, but over rest +func ServeCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "serve", + Short: "Start LCD (light-client daemon), a local REST server", + RunE: todoNotImplemented, + } + // TODO: handle unix sockets also? + cmd.Flags().StringP(flagBind, "b", "localhost:1317", "Interface and port that server binds to") + cmd.Flags().String(flagCORS, "", "Set to domains that can make CORS requests (* for all)") + cmd.Flags().StringP(client.FlagChainID, "c", "", "ID of chain we connect to") + cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to") + return cmd +} diff --git a/examples/basecoin/cmd/basecli/client.go b/client/rpc/root.go similarity index 54% rename from examples/basecoin/cmd/basecli/client.go rename to client/rpc/root.go index 26ee87375d4e..a65a9959ec19 100644 --- a/examples/basecoin/cmd/basecli/client.go +++ b/client/rpc/root.go @@ -1,11 +1,17 @@ -package main +package rpc import ( + "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" ) +// XXX: remove this when not needed +func todoNotImplemented(_ *cobra.Command, _ []string) error { + return errors.New("TODO: Command not yet implemented") +} + const ( // one of the following should be provided to verify the connection flagGenesis = "genesis" @@ -13,11 +19,6 @@ const ( flagValHash = "validator-set" flagSelect = "select" - flagTags = "tag" - flagAny = "any" - - flagBind = "bind" - flagCORS = "cors" ) var ( @@ -28,19 +29,13 @@ var ( } ) -// AddClientCommands returns a sub-tree of all basic client commands -// -// Call AddGetCommand and AddPostCommand to add custom txs and queries -func AddClientCommands(cmd *cobra.Command) { +// AddCommands adds a number of rpc-related subcommands +func AddCommands(cmd *cobra.Command) { cmd.AddCommand( initClientCommand(), statusCmd, blockCommand(), validatorCommand(), - lineBreak, - txSearchCommand(), - txCommand(), - lineBreak, ) } @@ -76,35 +71,3 @@ func validatorCommand() *cobra.Command { } return cmd } - -func serveCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "serve", - Short: "Start LCD (light-client daemon), a local REST server", - RunE: todoNotImplemented, - } - // TODO: handle unix sockets also? - cmd.Flags().StringP(flagBind, "b", "localhost:1317", "Interface and port that server binds to") - cmd.Flags().String(flagCORS, "", "Set to domains that can make CORS requests (* for all)") - return cmd -} - -func txSearchCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "txs", - Short: "Search for all transactions that match the given tags", - RunE: todoNotImplemented, - } - cmd.Flags().StringSlice(flagTags, nil, "Tags that must match (may provide multiple)") - cmd.Flags().Bool(flagAny, false, "Return transactions that match ANY tag, rather than ALL") - return cmd -} - -func txCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "tx ", - Short: "Matches this txhash over all committed blocks", - RunE: todoNotImplemented, - } - return cmd -} diff --git a/client/tx/root.go b/client/tx/root.go new file mode 100644 index 000000000000..1d2f33d9e11a --- /dev/null +++ b/client/tx/root.go @@ -0,0 +1,45 @@ +package tx + +import ( + "errors" + + "github.com/spf13/cobra" +) + +const ( + flagTags = "tag" + flagAny = "any" +) + +// XXX: remove this when not needed +func todoNotImplemented(_ *cobra.Command, _ []string) error { + return errors.New("TODO: Command not yet implemented") +} + +// AddCommands adds a number of tx-query related subcommands +func AddCommands(cmd *cobra.Command) { + cmd.AddCommand( + txSearchCommand(), + txCommand(), + ) +} + +func txSearchCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "txs", + Short: "Search for all transactions that match the given tags", + RunE: todoNotImplemented, + } + cmd.Flags().StringSlice(flagTags, nil, "Tags that must match (may provide multiple)") + cmd.Flags().Bool(flagAny, false, "Return transactions that match ANY tag, rather than ALL") + return cmd +} + +func txCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "tx ", + Short: "Matches this txhash over all committed blocks", + RunE: todoNotImplemented, + } + return cmd +} diff --git a/examples/basecoin/cmd/basecli/main.go b/examples/basecoin/cmd/basecli/main.go index 8dbdad04d9d6..6cabc2ceffe0 100644 --- a/examples/basecoin/cmd/basecli/main.go +++ b/examples/basecoin/cmd/basecli/main.go @@ -10,6 +10,9 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/client/lcd" + "github.com/cosmos/cosmos-sdk/client/rpc" + "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/version" bankcmd "github.com/cosmos/cosmos-sdk/x/bank/commands" ) @@ -20,8 +23,6 @@ var ( Use: "basecli", Short: "Basecoin light-client", } - - lineBreak = &cobra.Command{Run: func(*cobra.Command, []string) {}} ) func todoNotImplemented(_ *cobra.Command, _ []string) error { @@ -32,10 +33,13 @@ func main() { // disable sorting cobra.EnableCommandSorting = false - // generic client commands - AddClientCommands(basecliCmd) + // add standard rpc, and tx commands + rpc.AddCommands(basecliCmd) + basecliCmd.AddCommand(client.LineBreak) + tx.AddCommands(basecliCmd) + basecliCmd.AddCommand(client.LineBreak) - // query/post commands (custom to binary) + // add query/post commands (custom to binary) basecliCmd.AddCommand( client.GetCommands( bankcmd.GetAccountCmd("main"), @@ -47,10 +51,10 @@ func main() { // add proxy, version and key info basecliCmd.AddCommand( - lineBreak, - serveCommand(), + client.LineBreak, + lcd.ServeCommand(), keys.Commands(), - lineBreak, + client.LineBreak, version.VersionCmd, ) From 734b1073ba56774e9635a1e01e86cc92414ec4ef Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 23 Feb 2018 13:53:49 +0100 Subject: [PATCH 51/70] Added tx subcommands and automate manual testing --- client/flags.go | 4 +- client/tx/root.go | 32 ------------ client/tx/search.go | 81 ++++++++++++++++++++++++++++++ client/tx/tx.go | 102 ++++++++++++++++++++++++++++++++++++++ tests/check_basecli.sh | 58 ++++++++++++++++++++++ x/bank/commands/sendtx.go | 2 +- x/bank/handler.go | 1 + 7 files changed, 245 insertions(+), 35 deletions(-) create mode 100644 client/tx/search.go create mode 100644 client/tx/tx.go create mode 100755 tests/check_basecli.sh diff --git a/client/flags.go b/client/flags.go index db35ad06a49b..1f55c2921769 100644 --- a/client/flags.go +++ b/client/flags.go @@ -20,7 +20,7 @@ func GetCommands(cmds ...*cobra.Command) []*cobra.Command { // TODO: make this default false when we support proofs c.Flags().Bool(FlagTrustNode, true, "Don't verify proofs for responses") c.Flags().String(FlagChainID, "", "Chain ID of tendermint node") - c.Flags().String(FlagNode, "", ": to tendermint rpc interface for this chain") + c.Flags().String(FlagNode, "tcp://localhost:46657", ": to tendermint rpc interface for this chain") c.Flags().Int64(FlagHeight, 0, "block height to query, omit to get most recent provable block") } return cmds @@ -31,7 +31,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { for _, c := range cmds { c.Flags().String(FlagName, "", "Name of private key with which to sign") c.Flags().String(FlagChainID, "", "Chain ID of tendermint node") - c.Flags().String(FlagNode, "", ": to tendermint rpc interface for this chain") + c.Flags().String(FlagNode, "tcp://localhost:46657", ": to tendermint rpc interface for this chain") } return cmds } diff --git a/client/tx/root.go b/client/tx/root.go index 1d2f33d9e11a..e6c6786bebb8 100644 --- a/client/tx/root.go +++ b/client/tx/root.go @@ -1,21 +1,9 @@ package tx import ( - "errors" - "github.com/spf13/cobra" ) -const ( - flagTags = "tag" - flagAny = "any" -) - -// XXX: remove this when not needed -func todoNotImplemented(_ *cobra.Command, _ []string) error { - return errors.New("TODO: Command not yet implemented") -} - // AddCommands adds a number of tx-query related subcommands func AddCommands(cmd *cobra.Command) { cmd.AddCommand( @@ -23,23 +11,3 @@ func AddCommands(cmd *cobra.Command) { txCommand(), ) } - -func txSearchCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "txs", - Short: "Search for all transactions that match the given tags", - RunE: todoNotImplemented, - } - cmd.Flags().StringSlice(flagTags, nil, "Tags that must match (may provide multiple)") - cmd.Flags().Bool(flagAny, false, "Return transactions that match ANY tag, rather than ALL") - return cmd -} - -func txCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "tx ", - Short: "Matches this txhash over all committed blocks", - RunE: todoNotImplemented, - } - return cmd -} diff --git a/client/tx/search.go b/client/tx/search.go new file mode 100644 index 000000000000..51256470a308 --- /dev/null +++ b/client/tx/search.go @@ -0,0 +1,81 @@ +package tx + +import ( + "errors" + "fmt" + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/examples/basecoin/app" + ctypes "github.com/tendermint/tendermint/rpc/core/types" +) + +const ( + flagTags = "tag" + flagAny = "any" +) + +func txSearchCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "txs", + Short: "Search for all transactions that match the given tags", + RunE: searchTx, + } + cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to") + // TODO: change this to false when we can + cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses") + cmd.Flags().StringSlice(flagTags, nil, "Tags that must match (may provide multiple)") + cmd.Flags().Bool(flagAny, false, "Return transactions that match ANY tag, rather than ALL") + return cmd +} + +func searchTx(cmd *cobra.Command, args []string) error { + tags := viper.GetStringSlice(flagTags) + if len(tags) == 0 { + return errors.New("Must declare at least one tag to search") + } + // XXX: implement ANY + query := strings.Join(tags, " AND ") + + // get the node + uri := viper.GetString(client.FlagNode) + if uri == "" { + return errors.New("Must define which node to query with --node") + } + node := client.GetNode(uri) + + prove := !viper.GetBool(client.FlagTrustNode) + res, err := node.TxSearch(query, prove) + if err != nil { + return err + } + + info, err := formatTxResults(res) + if err != nil { + return err + } + + cdc := app.MakeTxCodec() + output, err := cdc.MarshalJSON(info) + if err != nil { + return err + } + fmt.Println(string(output)) + + return nil +} + +func formatTxResults(res []*ctypes.ResultTx) ([]txInfo, error) { + var err error + out := make([]txInfo, len(res)) + for i := range res { + out[i], err = formatTxResult(res[i]) + if err != nil { + return nil, err + } + } + return out, nil +} diff --git a/client/tx/tx.go b/client/tx/tx.go new file mode 100644 index 000000000000..5ea3d1995a4b --- /dev/null +++ b/client/tx/tx.go @@ -0,0 +1,102 @@ +package tx + +import ( + "encoding/hex" + "fmt" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/examples/basecoin/app" // XXX: not good + sdk "github.com/cosmos/cosmos-sdk/types" + abci "github.com/tendermint/abci/types" + ctypes "github.com/tendermint/tendermint/rpc/core/types" +) + +func txCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "tx ", + Short: "Matches this txhash over all committed blocks", + RunE: queryTx, + } + cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to") + // TODO: change this to false when we can + cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses") + return cmd +} + +func queryTx(cmd *cobra.Command, args []string) error { + if len(args) != 1 || len(args[0]) == 0 { + return errors.New("You must provide a tx hash") + } + + // find the key to look up the account + hexStr := args[0] + hash, err := hex.DecodeString(hexStr) + if err != nil { + return err + } + + // get the node + uri := viper.GetString(client.FlagNode) + if uri == "" { + return errors.New("Must define which node to query with --node") + } + node := client.GetNode(uri) + prove := !viper.GetBool(client.FlagTrustNode) + + res, err := node.Tx(hash, prove) + if err != nil { + return err + } + info, err := formatTxResult(res) + if err != nil { + return err + } + + cdc := app.MakeTxCodec() + output, err := cdc.MarshalJSON(info) + if err != nil { + return err + } + fmt.Println(string(output)) + + return nil +} + +func formatTxResult(res *ctypes.ResultTx) (txInfo, error) { + height := res.Height + result := res.TxResult + // TODO: verify the proof if requested + + tx, err := parseTx(res.Tx) + if err != nil { + return txInfo{}, err + } + + info := txInfo{ + Height: height, + Tx: tx, + Result: result, + } + return info, nil +} + +// txInfo is used to prepare info to display +type txInfo struct { + Height int64 `json:"height"` + Tx sdk.Tx `json:"tx"` + Result abci.ResponseDeliverTx `json:"result"` +} + +func parseTx(txBytes []byte) (sdk.Tx, error) { + var tx sdk.StdTx + cdc := app.MakeTxCodec() + err := cdc.UnmarshalBinary(txBytes, &tx) + if err != nil { + return nil, err + } + return tx, nil +} diff --git a/tests/check_basecli.sh b/tests/check_basecli.sh new file mode 100755 index 000000000000..e21cb897c420 --- /dev/null +++ b/tests/check_basecli.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +# Note: Bucky, I know you want to kill bash tests. +# Please show me how to do an alternative to this. +# I would rather get code running before I leave than +# fight trying to invent some new test harness that +# no one else will understand. +# +# Thus, I leave this as an exercise to the reader to +# port into a non-bash version. And I don't do it proper... +# just automate my manual tests + +# WARNING!!! +rm -rf ~/.basecoind ~/.basecli +cd $GOPATH/src/github.com/cosmos/cosmos-sdk +# make get_vendor_deps +make build + +# init stuff +SEED=`./build/basecoind init | tail -1` +PASS='some-silly-123' +(echo $PASS; echo $SEED) | ./build/basecli keys add demo --recover +ADDR=`./build/basecli keys show demo | cut -f3` +echo "Recovered seed for demo:" $ADDR + +# start up server +./build/basecoind start > ~/.basecoind/basecoind.log 2>&1 & +sleep 5 +PID_SERVER=$! + +# query original state +TO='ABCAFE00DEADBEEF00CAFE00DEADBEEF00CAFE00' +echo; echo "My account:" $ADDR +./build/basecli account $ADDR +echo; echo "Empty account:" $TO +./build/basecli account $TO + +# send some money +TX=`echo $PASS | ./build/basecli send --to=$TO --amount=1000mycoin --name=demo --seq=0` +echo; echo "SendTx"; echo $TX +HASH=`echo $TX | cut -d' ' -f6` +echo "tx hash:" $HASH + +# let some blocks come up.... +sleep 2 + +# balances change +echo; echo "My account went down" +./build/basecli account $ADDR +echo; echo "Empty account got some cash" +./build/basecli account $TO + +# query original tx +echo; echo "View tx" +./build/basecli tx $HASH + +# shutdown +kill $PID_SERVER diff --git a/x/bank/commands/sendtx.go b/x/bank/commands/sendtx.go index 011d2877d8b9..1cdc77cb01a1 100644 --- a/x/bank/commands/sendtx.go +++ b/x/bank/commands/sendtx.go @@ -9,7 +9,7 @@ import ( "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/examples/basecoin/app" + "github.com/cosmos/cosmos-sdk/examples/basecoin/app" // XXX: not good sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/bank" crypto "github.com/tendermint/go-crypto" diff --git a/x/bank/handler.go b/x/bank/handler.go index e035818cdb92..b5d2a675724e 100644 --- a/x/bank/handler.go +++ b/x/bank/handler.go @@ -39,6 +39,7 @@ func handleSendMsg(ctx sdk.Context, ck CoinKeeper, msg SendMsg) sdk.Result { } } + // TODO: add some tags so we can search it! return sdk.Result{} // TODO } From c083678cae85a46bf7e5c1d25fa14ea761bb8dfb Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 23 Feb 2018 14:17:07 +0100 Subject: [PATCH 52/70] cleaned up basecli tx so it really works --- client/tx/tx.go | 11 ++++------- tests/check_basecli.sh | 5 ++++- types/signature.go | 6 +++--- types/tx_msg.go | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/client/tx/tx.go b/client/tx/tx.go index 5ea3d1995a4b..98d72b84d76d 100644 --- a/client/tx/tx.go +++ b/client/tx/tx.go @@ -2,6 +2,7 @@ package tx import ( "encoding/hex" + "encoding/json" "fmt" "github.com/pkg/errors" @@ -56,8 +57,7 @@ func queryTx(cmd *cobra.Command, args []string) error { return err } - cdc := app.MakeTxCodec() - output, err := cdc.MarshalJSON(info) + output, err := json.MarshalIndent(info, "", " ") if err != nil { return err } @@ -67,19 +67,16 @@ func queryTx(cmd *cobra.Command, args []string) error { } func formatTxResult(res *ctypes.ResultTx) (txInfo, error) { - height := res.Height - result := res.TxResult // TODO: verify the proof if requested - tx, err := parseTx(res.Tx) if err != nil { return txInfo{}, err } info := txInfo{ - Height: height, + Height: res.Height, Tx: tx, - Result: result, + Result: res.TxResult, } return info, nil } diff --git a/tests/check_basecli.sh b/tests/check_basecli.sh index e21cb897c420..b470ea218acd 100755 --- a/tests/check_basecli.sh +++ b/tests/check_basecli.sh @@ -54,5 +54,8 @@ echo; echo "Empty account got some cash" echo; echo "View tx" ./build/basecli tx $HASH -# shutdown +# shutdown, but add a sleep if you want to manually run some cli scripts +# against this server before it goes away +# sleep 120 kill $PID_SERVER + diff --git a/types/signature.go b/types/signature.go index 7fecc5fe96a6..5bca2f6069ee 100644 --- a/types/signature.go +++ b/types/signature.go @@ -4,7 +4,7 @@ import crypto "github.com/tendermint/go-crypto" // Standard Signature type StdSignature struct { - crypto.PubKey // optional - crypto.Signature - Sequence int64 + crypto.PubKey `json:"pub_key"` // optional + crypto.Signature `json:"signature"` + Sequence int64 `json:"sequence"` } diff --git a/types/tx_msg.go b/types/tx_msg.go index 8763075b8bd0..1a7f63f938a0 100644 --- a/types/tx_msg.go +++ b/types/tx_msg.go @@ -52,8 +52,8 @@ var _ Tx = (*StdTx)(nil) // StdTx is a standard way to wrap a Msg with Signatures. // NOTE: the first signature is the FeePayer (Signatures must not be nil). type StdTx struct { - Msg - Signatures []StdSignature + Msg `json:"msg"` + Signatures []StdSignature `json:"signatures"` } func NewStdTx(msg Msg, sigs []StdSignature) StdTx { From 8392cf93acefad1b74b3fb49b1cf77528fcd4669 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 23 Feb 2018 19:23:24 +0100 Subject: [PATCH 53/70] Implement RPC subcommands Turned out the tendermint rpc was broken in the refactor and had to fix that first... --- client/rpc/block.go | 63 ++++++++++++++++++++++++++++++++++++++++ client/rpc/root.go | 39 ++++--------------------- client/rpc/status.go | 38 ++++++++++++++++++++++++ client/rpc/validators.go | 55 +++++++++++++++++++++++++++++++++++ glide.lock | 6 ++-- tests/check_basecli.sh | 8 +++++ 6 files changed, 172 insertions(+), 37 deletions(-) create mode 100644 client/rpc/block.go create mode 100644 client/rpc/status.go create mode 100644 client/rpc/validators.go diff --git a/client/rpc/block.go b/client/rpc/block.go new file mode 100644 index 000000000000..bc127ddb2179 --- /dev/null +++ b/client/rpc/block.go @@ -0,0 +1,63 @@ +package rpc + +import ( + "encoding/json" + "fmt" + "strconv" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +const ( + flagSelect = "select" +) + +func blockCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "block [height]", + Short: "Get verified data for a the block at given height", + RunE: getBlock, + } + cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to") + // TODO: change this to false when we can + cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses") + cmd.Flags().StringSlice(flagSelect, []string{"header", "tx"}, "Fields to return (header|txs|results)") + return cmd +} + +func getBlock(cmd *cobra.Command, args []string) error { + var height *int64 + // optional height + if len(args) > 0 { + h, err := strconv.Atoi(args[0]) + if err != nil { + return err + } + if h > 0 { + tmp := int64(h) + height = &tmp + } + } + + // get the node + uri := viper.GetString(client.FlagNode) + node := client.GetNode(uri) + + // TODO: actually honor the --select flag! + // header -> BlockchainInfo + // header, tx -> Block + // results -> BlockResults + res, err := node.Block(height) + if err != nil { + return err + } + + output, err := json.MarshalIndent(res, " ", "") + if err != nil { + return err + } + fmt.Println(string(output)) + return nil +} diff --git a/client/rpc/root.go b/client/rpc/root.go index a65a9959ec19..8acf8ddad125 100644 --- a/client/rpc/root.go +++ b/client/rpc/root.go @@ -7,33 +7,23 @@ import ( "github.com/cosmos/cosmos-sdk/client" ) -// XXX: remove this when not needed -func todoNotImplemented(_ *cobra.Command, _ []string) error { - return errors.New("TODO: Command not yet implemented") -} - const ( // one of the following should be provided to verify the connection flagGenesis = "genesis" flagCommit = "commit" flagValHash = "validator-set" - - flagSelect = "select" ) -var ( - statusCmd = &cobra.Command{ - Use: "status", - Short: "Query remote node for status", - RunE: todoNotImplemented, - } -) +// XXX: remove this when not needed +func todoNotImplemented(_ *cobra.Command, _ []string) error { + return errors.New("TODO: Command not yet implemented") +} // AddCommands adds a number of rpc-related subcommands func AddCommands(cmd *cobra.Command) { cmd.AddCommand( initClientCommand(), - statusCmd, + statusCommand(), blockCommand(), validatorCommand(), ) @@ -52,22 +42,3 @@ func initClientCommand() *cobra.Command { cmd.Flags().String(flagValHash, "", "Hash of trusted validator set (hex-encoded)") return cmd } - -func blockCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "block ", - Short: "Get verified data for a the block at given height", - RunE: todoNotImplemented, - } - cmd.Flags().StringSlice(flagSelect, []string{"header", "tx"}, "Fields to return (header|txs|results)") - return cmd -} - -func validatorCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "validatorset ", - Short: "Get the full validator set at given height", - RunE: todoNotImplemented, - } - return cmd -} diff --git a/client/rpc/status.go b/client/rpc/status.go new file mode 100644 index 000000000000..61e922f4c664 --- /dev/null +++ b/client/rpc/status.go @@ -0,0 +1,38 @@ +package rpc + +import ( + "encoding/json" + "fmt" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/cosmos/cosmos-sdk/client" +) + +func statusCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "status", + Short: "Query remote node for status", + RunE: checkStatus, + } + cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to") + return cmd +} + +func checkStatus(cmd *cobra.Command, args []string) error { + // get the node + uri := viper.GetString(client.FlagNode) + node := client.GetNode(uri) + res, err := node.Status() + if err != nil { + return err + } + + output, err := json.MarshalIndent(res, " ", "") + if err != nil { + return err + } + fmt.Println(string(output)) + return nil +} diff --git a/client/rpc/validators.go b/client/rpc/validators.go new file mode 100644 index 000000000000..143bbe17e66d --- /dev/null +++ b/client/rpc/validators.go @@ -0,0 +1,55 @@ +package rpc + +import ( + "encoding/json" + "fmt" + "strconv" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/cosmos/cosmos-sdk/client" +) + +func validatorCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "validatorset ", + Short: "Get the full validator set at given height", + RunE: getValidators, + } + cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to") + // TODO: change this to false when we can + cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses") + return cmd +} + +func getValidators(cmd *cobra.Command, args []string) error { + var height *int64 + // optional height + if len(args) > 0 { + h, err := strconv.Atoi(args[0]) + if err != nil { + return err + } + if h > 0 { + tmp := int64(h) + height = &tmp + } + } + + // get the node + uri := viper.GetString(client.FlagNode) + node := client.GetNode(uri) + + res, err := node.Validators(height) + if err != nil { + return err + } + + output, err := json.MarshalIndent(res, " ", "") + if err != nil { + return err + } + fmt.Println(string(output)) + return nil +} diff --git a/glide.lock b/glide.lock index 43b61b13f4d1..57384c8673f9 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: fa45c8a4f5512ed730f793b93d4876bdc604a1333a5a1f938c98a0f7dd55f22e -updated: 2018-02-22T16:18:43.639619321+01:00 +updated: 2018-02-23T14:49:05.743112+01:00 imports: - name: github.com/bgentry/speakeasy version: 4aabc24848ce5fd31929f7d1e4ea74d3709c14cd @@ -123,7 +123,7 @@ imports: - extra25519 - name: github.com/tendermint/go-crypto version: 4fc3055dbd17aa1203d0abc64b9293f378da22ec - subpackages: + subpackages - keys - keys/bcrypt - keys/words @@ -133,7 +133,7 @@ imports: - name: github.com/tendermint/iavl version: 1a59ec0c82dc940c25339dd7c834df5cb76a95cb - name: github.com/tendermint/tendermint - version: 4a63409d5c72a36eee49c962b060fd0063958bda + version: 6947e118f54e4df24f5e2c79bcdd66199e54d885 subpackages: - blockchain - cmd/tendermint/commands diff --git a/tests/check_basecli.sh b/tests/check_basecli.sh index b470ea218acd..ec2c458cb6a8 100755 --- a/tests/check_basecli.sh +++ b/tests/check_basecli.sh @@ -42,7 +42,9 @@ HASH=`echo $TX | cut -d' ' -f6` echo "tx hash:" $HASH # let some blocks come up.... +./build/basecli status | jq .latest_block_height sleep 2 +./build/basecli status | jq .latest_block_height # balances change echo; echo "My account went down" @@ -54,6 +56,12 @@ echo; echo "Empty account got some cash" echo; echo "View tx" ./build/basecli tx $HASH +# wait a bit then dump out some blockchain state +sleep 10 +./build/basecli status --trace +./build/basecli block --trace +./build/basecli validatorset --trace + # shutdown, but add a sleep if you want to manually run some cli scripts # against this server before it goes away # sleep 120 From 43ea57907926ce6174a6e205f9756c3c36633882 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 23 Feb 2018 19:33:50 +0100 Subject: [PATCH 54/70] Tools uses glide, not gopkg.in for versioning --- tools/Makefile | 6 +++--- tools/glide.lock | 14 +++++++------- tools/glide.yaml | 9 ++++++++- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/tools/Makefile b/tools/Makefile index 39611f305009..6786575cdc88 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -40,10 +40,10 @@ install: get_vendor_deps @echo "$(ansi_grn)Installing tools$(ansi_end)" @echo "$(ansi_yel)Install go-vendorinstall$(ansi_end)" go build -o bin/go-vendorinstall go-vendorinstall/*.go - + @echo "$(ansi_yel)Install gometalinter.v2$(ansi_end)" - GOBIN=$(CURDIR)/bin ./bin/go-vendorinstall gopkg.in/alecthomas/gometalinter.v2 - + GOBIN=$(CURDIR)/bin ./bin/go-vendorinstall github.com/alecthomas/gometalinter + @echo "$(ansi_yel)Install shelldown$(ansi_end)" GOBIN=$(CURDIR)/bin ./bin/go-vendorinstall github.com/rigelrozanski/shelldown/cmd/shelldown diff --git a/tools/glide.lock b/tools/glide.lock index db718da50d1c..f4af28eeccab 100644 --- a/tools/glide.lock +++ b/tools/glide.lock @@ -1,8 +1,8 @@ -hash: a163b1c4806024cfc9062db75a0abed285ec40461243e59af0e147db2c4bf0ce -updated: 2018-01-15T19:02:49.834182027-08:00 +hash: 934ad5be72c9c240e8555eb6e1b2319840266c04c0fa9e024008cf841c0cee65 +updated: 2018-02-23T19:33:08.596187+01:00 imports: -- name: github.com/inconshreveable/mousetrap - version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 +- name: github.com/alecthomas/gometalinter + version: 46cc1ea3778b247666c2949669a3333c532fa9c6 - name: github.com/rigelrozanski/common version: f691f115798593d783b9999b1263c2f4ffecc439 - name: github.com/rigelrozanski/shelldown @@ -12,7 +12,7 @@ imports: - name: github.com/spf13/cobra version: 7b2c5ac9fc04fc5efafb60700713d4fa609b777b - name: github.com/spf13/pflag - version: 97afa5e7ca8a08a383cb259e06636b5e2cc7897f -- name: gopkg.in/alecthomas/gometalinter.v2 - version: 88d47c66988c5a5cb3945925da47c883800a94df + version: e57e3eeb33f795204c1ca35f56c44f83227c6e66 +- name: github.com/spf13/viper + version: 25b30aa063fc18e48662b86996252eabdcf2f0c7 testImports: [] diff --git a/tools/glide.yaml b/tools/glide.yaml index 3a3e85d6a751..6ddd6443daef 100644 --- a/tools/glide.yaml +++ b/tools/glide.yaml @@ -1,6 +1,13 @@ package: github.com/cosmos/cosmos-sdk/tools import: +- package: github.com/alecthomas/gometalinter + version: ^2.0.5 - package: github.com/rigelrozanski/shelldown subpackages: - cmd/shelldown -- package: gopkg.in/alecthomas/gometalinter.v2 +- package: github.com/spf13/pflag + version: v1.0.0 +- package: github.com/spf13/cobra + version: v0.0.1 +- package: github.com/spf13/viper + version: ^1.0.0 From 1c0fdfe660033305231c4c02352b1e513f28439d Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Fri, 23 Feb 2018 19:49:00 +0100 Subject: [PATCH 55/70] Fix glide.lock syntax --- glide.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/glide.lock b/glide.lock index 57384c8673f9..f2fa12fefdf6 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: fa45c8a4f5512ed730f793b93d4876bdc604a1333a5a1f938c98a0f7dd55f22e -updated: 2018-02-23T14:49:05.743112+01:00 +updated: 2018-02-23T19:47:25.630102+01:00 imports: - name: github.com/bgentry/speakeasy version: 4aabc24848ce5fd31929f7d1e4ea74d3709c14cd @@ -123,7 +123,7 @@ imports: - extra25519 - name: github.com/tendermint/go-crypto version: 4fc3055dbd17aa1203d0abc64b9293f378da22ec - subpackages + subpackages: - keys - keys/bcrypt - keys/words From 05f514173404159ba3c0c771250e3a15b3387873 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 26 Feb 2018 13:44:27 +0100 Subject: [PATCH 56/70] Cleanup from rige's review --- client/input.go | 6 ++++++ x/bank/commands/sendtx.go | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/client/input.go b/client/input.go index 8670f42bf2c2..db06f404ca8f 100644 --- a/client/input.go +++ b/client/input.go @@ -71,10 +71,16 @@ func GetCheckPassword(prompt, prompt2 string) (string, error) { return pass, nil } +// inputIsTty returns true iff we have an interactive prompt, +// where we can disable echo and request to repeat the password. +// If false, we can optimize for piped input from another command func inputIsTty() bool { return isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) } +// stdinPassword reads one line from stdin. +// Subsequent calls reuse the same buffer, so we don't lose +// any input when reading a password twice (to verify) func stdinPassword() (string, error) { if buf == nil { buf = bufio.NewReader(os.Stdin) diff --git a/x/bank/commands/sendtx.go b/x/bank/commands/sendtx.go index 1cdc77cb01a1..dc0a6bdd9675 100644 --- a/x/bank/commands/sendtx.go +++ b/x/bank/commands/sendtx.go @@ -60,7 +60,7 @@ func sendTx(cmd *cobra.Command, args []string) error { result.CheckTx.Log) } if result.DeliverTx.Code != uint32(0) { - return errors.Errorf("CheckTx failed: (%d) %s", + return errors.Errorf("DeliverTx failed: (%d) %s", result.DeliverTx.Code, result.DeliverTx.Log) } From 03dc660797449ee13401ad4364bb5dc4b7d496b3 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 28 Feb 2018 15:20:46 +0100 Subject: [PATCH 57/70] Cleanup client/input.go per buckys request --- client/input.go | 31 +++++++++++++++---------------- client/keys/add.go | 8 ++++++-- client/keys/delete.go | 4 +++- client/keys/update.go | 8 ++++++-- x/bank/commands/account.go | 4 ++-- x/bank/commands/sendtx.go | 3 ++- 6 files changed, 34 insertions(+), 24 deletions(-) diff --git a/client/input.go b/client/input.go index db06f404ca8f..6036b68f9fe3 100644 --- a/client/input.go +++ b/client/input.go @@ -14,17 +14,19 @@ import ( // MinPassLength is the minimum acceptable password length const MinPassLength = 8 -// if we read from non-tty, we just need to init the buffer reader once, -// in case we try to read multiple passwords (eg. update) -var buf *bufio.Reader +// BufferStdin is used to allow reading prompts for stdin +// multiple times, when we read from non-tty +func BufferStdin() *bufio.Reader { + return bufio.NewReader(os.Stdin) +} // GetPassword will prompt for a password one-time (to sign a tx) // It enforces the password length -func GetPassword(prompt string) (pass string, err error) { +func GetPassword(prompt string, buf *bufio.Reader) (pass string, err error) { if inputIsTty() { pass, err = speakeasy.Ask(prompt) } else { - pass, err = stdinPassword() + pass, err = readLineFromBuf(buf) } if err != nil { return "", err @@ -37,11 +39,11 @@ func GetPassword(prompt string) (pass string, err error) { // GetSeed will request a seed phrase from stdin and trims off // leading/trailing spaces -func GetSeed(prompt string) (seed string, err error) { +func GetSeed(prompt string, buf *bufio.Reader) (seed string, err error) { if inputIsTty() { fmt.Println(prompt) } - seed, err = stdinPassword() + seed, err = readLineFromBuf(buf) seed = strings.TrimSpace(seed) return } @@ -50,18 +52,18 @@ func GetSeed(prompt string) (seed string, err error) { // match (for creating a new password). // It enforces the password length. Only parses password once if // input is piped in. -func GetCheckPassword(prompt, prompt2 string) (string, error) { +func GetCheckPassword(prompt, prompt2 string, buf *bufio.Reader) (string, error) { // simple read on no-tty if !inputIsTty() { - return GetPassword(prompt) + return GetPassword(prompt, buf) } // TODO: own function??? - pass, err := GetPassword(prompt) + pass, err := GetPassword(prompt, buf) if err != nil { return "", err } - pass2, err := GetPassword(prompt2) + pass2, err := GetPassword(prompt2, buf) if err != nil { return "", err } @@ -78,13 +80,10 @@ func inputIsTty() bool { return isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) } -// stdinPassword reads one line from stdin. +// readLineFromBuf reads one line from stdin. // Subsequent calls reuse the same buffer, so we don't lose // any input when reading a password twice (to verify) -func stdinPassword() (string, error) { - if buf == nil { - buf = bufio.NewReader(os.Stdin) - } +func readLineFromBuf(buf *bufio.Reader) (string, error) { pass, err := buf.ReadString('\n') if err != nil { return "", err diff --git a/client/keys/add.go b/client/keys/add.go index 07b1deba1c96..cef0b6260129 100644 --- a/client/keys/add.go +++ b/client/keys/add.go @@ -54,6 +54,7 @@ func runAddCmd(cmd *cobra.Command, args []string) error { var err error var name, pass string + buf := client.BufferStdin() if viper.GetBool(flagDryRun) { // we throw this away, so don't enforce args, // we want to get a new random seed phrase quickly @@ -69,14 +70,17 @@ func runAddCmd(cmd *cobra.Command, args []string) error { if err != nil { return err } - pass, err = client.GetCheckPassword("Enter a passphrase for your key:", "Repeat the passphrase:") + pass, err = client.GetCheckPassword( + "Enter a passphrase for your key:", + "Repeat the passphrase:", buf) if err != nil { return err } } if viper.GetBool(flagRecover) { - seed, err := client.GetSeed("Enter your recovery seed phrase:") + seed, err := client.GetSeed( + "Enter your recovery seed phrase:", buf) if err != nil { return err } diff --git a/client/keys/delete.go b/client/keys/delete.go index 58050087a5b5..3477ed6d4099 100644 --- a/client/keys/delete.go +++ b/client/keys/delete.go @@ -38,7 +38,9 @@ func runDeleteCmd(cmd *cobra.Command, args []string) error { } name := args[0] - oldpass, err := client.GetPassword("DANGER - enter password to permanently delete key:") + buf := client.BufferStdin() + oldpass, err := client.GetPassword( + "DANGER - enter password to permanently delete key:", buf) if err != nil { return err } diff --git a/client/keys/update.go b/client/keys/update.go index a7197dd96813..c809b988eef8 100644 --- a/client/keys/update.go +++ b/client/keys/update.go @@ -38,11 +38,15 @@ func runUpdateCmd(cmd *cobra.Command, args []string) error { } name := args[0] - oldpass, err := client.GetPassword("Enter the current passphrase:") + buf := client.BufferStdin() + oldpass, err := client.GetPassword( + "Enter the current passphrase:", buf) if err != nil { return err } - newpass, err := client.GetCheckPassword("Enter the new passphrase:", "Repeat the new passphrase:") + newpass, err := client.GetCheckPassword( + "Enter the new passphrase:", + "Repeat the new passphrase:", buf) if err != nil { return err } diff --git a/x/bank/commands/account.go b/x/bank/commands/account.go index 87f4066d9d1e..2f7f9132ef33 100644 --- a/x/bank/commands/account.go +++ b/x/bank/commands/account.go @@ -13,8 +13,8 @@ import ( rpcclient "github.com/tendermint/tendermint/rpc/client" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/examples/basecoin/app" - "github.com/cosmos/cosmos-sdk/examples/basecoin/types" + "github.com/cosmos/cosmos-sdk/examples/basecoin/app" // XXX: not good + "github.com/cosmos/cosmos-sdk/examples/basecoin/types" // XXX: not good ) // GetAccountCmd returns a query account that will display the diff --git a/x/bank/commands/sendtx.go b/x/bank/commands/sendtx.go index dc0a6bdd9675..6293661e6d69 100644 --- a/x/bank/commands/sendtx.go +++ b/x/bank/commands/sendtx.go @@ -90,8 +90,9 @@ func buildTx() ([]byte, error) { // sign and build bz := msg.GetSignBytes() + buf := client.BufferStdin() prompt := fmt.Sprintf("Password to sign with '%s':", name) - passphrase, err := client.GetPassword(prompt) + passphrase, err := client.GetPassword(prompt, buf) if err != nil { return nil, err } From bb74e84b29b3461ed9de2e3536aa7e1c33991570 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 28 Feb 2018 15:36:04 +0100 Subject: [PATCH 58/70] Clean up keys/keybase and comments --- client/keys.go | 16 +++------------- client/keys/README.md | 2 ++ client/keys/add.go | 14 -------------- client/keys/delete.go | 14 -------------- client/keys/list.go | 14 -------------- client/keys/root.go | 14 -------------- client/keys/show.go | 14 -------------- client/keys/update.go | 14 -------------- client/keys/utils.go | 8 ++++++-- x/bank/commands/sendtx.go | 7 +++---- 10 files changed, 14 insertions(+), 103 deletions(-) diff --git a/client/keys.go b/client/keys.go index f64ba405f6e3..42eb00b7eb98 100644 --- a/client/keys.go +++ b/client/keys.go @@ -6,28 +6,18 @@ import ( dbm "github.com/tendermint/tmlibs/db" ) -// KeyDBName is the directory under root where we store the keys -const KeyDBName = "keys" - // GetKeyBase initializes a keybase based on the configuration -func GetKeyBase(rootDir string) (keys.Keybase, error) { - db, err := dbm.NewGoLevelDB(KeyDBName, rootDir) - if err != nil { - return nil, err - } +func GetKeyBase(db dbm.DB) keys.Keybase { keybase := keys.New( db, words.MustLoadCodec("english"), ) - return keybase, nil + return keybase } // MockKeyBase generates an in-memory keybase that will be discarded // useful for --dry-run to generate a seed phrase without // storing the key func MockKeyBase() keys.Keybase { - return keys.New( - dbm.NewMemDB(), - words.MustLoadCodec("english"), - ) + return GetKeyBase(dbm.NewMemDB()) } diff --git a/client/keys/README.md b/client/keys/README.md index 8bf9ca73b628..029508ad5fd8 100644 --- a/client/keys/README.md +++ b/client/keys/README.md @@ -1,5 +1,7 @@ # Keys CLI +**WARNING: out-of-date and parts are wrong.... please update** + This is as much an example how to expose cobra/viper, as for a cli itself (I think this code is overkill for what go-keys needs). But please look at the commands, and give feedback and changes. diff --git a/client/keys/add.go b/client/keys/add.go index cef0b6260129..40bb0d103ed7 100644 --- a/client/keys/add.go +++ b/client/keys/add.go @@ -1,17 +1,3 @@ -// Copyright © 2017 Ethan Frey -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package keys import ( diff --git a/client/keys/delete.go b/client/keys/delete.go index 3477ed6d4099..65a9513272ca 100644 --- a/client/keys/delete.go +++ b/client/keys/delete.go @@ -1,17 +1,3 @@ -// Copyright © 2017 Ethan Frey -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package keys import ( diff --git a/client/keys/list.go b/client/keys/list.go index 8e11d36afc05..14c4408781d0 100644 --- a/client/keys/list.go +++ b/client/keys/list.go @@ -1,17 +1,3 @@ -// Copyright © 2017 Ethan Frey -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package keys import "github.com/spf13/cobra" diff --git a/client/keys/root.go b/client/keys/root.go index d067d15159da..962986c91a64 100644 --- a/client/keys/root.go +++ b/client/keys/root.go @@ -1,17 +1,3 @@ -// Copyright © 2017 Ethan Frey -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package keys import ( diff --git a/client/keys/show.go b/client/keys/show.go index e569f265ded5..a22cb4bc887d 100644 --- a/client/keys/show.go +++ b/client/keys/show.go @@ -1,17 +1,3 @@ -// Copyright © 2017 Ethan Frey -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package keys import ( diff --git a/client/keys/update.go b/client/keys/update.go index c809b988eef8..0e0f881c6e24 100644 --- a/client/keys/update.go +++ b/client/keys/update.go @@ -1,17 +1,3 @@ -// Copyright © 2017 Ethan Frey -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package keys import ( diff --git a/client/keys/utils.go b/client/keys/utils.go index 5c156cb74842..19e63d74827e 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -7,10 +7,14 @@ import ( keys "github.com/tendermint/go-crypto/keys" "github.com/tendermint/tmlibs/cli" + dbm "github.com/tendermint/tmlibs/db" "github.com/cosmos/cosmos-sdk/client" ) +// KeyDBName is the directory under root where we store the keys +const KeyDBName = "keys" + var ( // keybase is used to make GetKeyBase a singleton keybase keys.Keybase @@ -20,11 +24,11 @@ var ( func GetKeyBase() (keys.Keybase, error) { if keybase == nil { rootDir := viper.GetString(cli.HomeFlag) - kb, err := client.GetKeyBase(rootDir) + db, err := dbm.NewGoLevelDB(KeyDBName, rootDir) if err != nil { return nil, err } - keybase = kb + keybase = client.GetKeyBase(db) } return keybase, nil } diff --git a/x/bank/commands/sendtx.go b/x/bank/commands/sendtx.go index 6293661e6d69..b6d3ed31fcfa 100644 --- a/x/bank/commands/sendtx.go +++ b/x/bank/commands/sendtx.go @@ -9,11 +9,11 @@ import ( "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/examples/basecoin/app" // XXX: not good sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/bank" crypto "github.com/tendermint/go-crypto" - "github.com/tendermint/tmlibs/cli" ) const ( @@ -70,8 +70,7 @@ func sendTx(cmd *cobra.Command, args []string) error { } func buildTx() ([]byte, error) { - rootDir := viper.GetString(cli.HomeFlag) - keybase, err := client.GetKeyBase(rootDir) + keybase, err := keys.GetKeyBase() if err != nil { return nil, err } @@ -79,7 +78,7 @@ func buildTx() ([]byte, error) { name := viper.GetString(client.FlagName) info, err := keybase.Get(name) if err != nil { - return nil, errors.WithMessage(err, "No key for name") + return nil, errors.Errorf("No key for: %s", name) } from := info.PubKey.Address() From 94948746a0c5c9d6e042328c0fb3a56b0f3b5dce Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Wed, 28 Feb 2018 15:36:21 +0100 Subject: [PATCH 59/70] rpc cli output uses wire.MarshalJSON --- client/rpc/block.go | 8 +++++--- client/rpc/status.go | 5 +++-- client/rpc/validators.go | 5 +++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/client/rpc/block.go b/client/rpc/block.go index bc127ddb2179..3ae4e7615673 100644 --- a/client/rpc/block.go +++ b/client/rpc/block.go @@ -1,13 +1,14 @@ package rpc import ( - "encoding/json" "fmt" "strconv" - "github.com/cosmos/cosmos-sdk/client" "github.com/spf13/cobra" "github.com/spf13/viper" + + "github.com/cosmos/cosmos-sdk/client" + tmwire "github.com/tendermint/tendermint/wire" ) const ( @@ -54,7 +55,8 @@ func getBlock(cmd *cobra.Command, args []string) error { return err } - output, err := json.MarshalIndent(res, " ", "") + output, err := tmwire.MarshalJSON(res) + // output, err := json.MarshalIndent(res, " ", "") if err != nil { return err } diff --git a/client/rpc/status.go b/client/rpc/status.go index 61e922f4c664..9436a240a454 100644 --- a/client/rpc/status.go +++ b/client/rpc/status.go @@ -1,13 +1,13 @@ package rpc import ( - "encoding/json" "fmt" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/client" + tmwire "github.com/tendermint/tendermint/wire" ) func statusCommand() *cobra.Command { @@ -29,7 +29,8 @@ func checkStatus(cmd *cobra.Command, args []string) error { return err } - output, err := json.MarshalIndent(res, " ", "") + output, err := tmwire.MarshalJSON(res) + // output, err := json.MarshalIndent(res, " ", "") if err != nil { return err } diff --git a/client/rpc/validators.go b/client/rpc/validators.go index 143bbe17e66d..cb554e7c6b38 100644 --- a/client/rpc/validators.go +++ b/client/rpc/validators.go @@ -1,7 +1,6 @@ package rpc import ( - "encoding/json" "fmt" "strconv" @@ -9,6 +8,7 @@ import ( "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/client" + tmwire "github.com/tendermint/tendermint/wire" ) func validatorCommand() *cobra.Command { @@ -46,7 +46,8 @@ func getValidators(cmd *cobra.Command, args []string) error { return err } - output, err := json.MarshalIndent(res, " ", "") + output, err := tmwire.MarshalJSON(res) + // output, err := json.MarshalIndent(res, " ", "") if err != nil { return err } From 65f27f2daa24e017ced272859dd565ff8959173a Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Wed, 28 Feb 2018 23:26:39 +0000 Subject: [PATCH 60/70] basecli refactor --- client/flags.go | 1 + client/helpers.go | 71 ++++++++++++++++++++++++++++++++++++++ client/keys/add.go | 3 +- client/node.go | 8 ----- client/rpc/block.go | 7 ++-- client/rpc/status.go | 7 ++-- client/rpc/validators.go | 7 ++-- client/tx/search.go | 7 ++-- client/tx/tx.go | 8 ++--- server/init.go | 2 +- x/bank/commands/account.go | 50 ++++++++++----------------- x/bank/commands/sendtx.go | 32 ++++++----------- 12 files changed, 123 insertions(+), 80 deletions(-) create mode 100644 client/helpers.go delete mode 100644 client/node.go diff --git a/client/flags.go b/client/flags.go index 1f55c2921769..843cb52d1e93 100644 --- a/client/flags.go +++ b/client/flags.go @@ -2,6 +2,7 @@ package client import "github.com/spf13/cobra" +// nolint const ( FlagChainID = "chain-id" FlagNode = "node" diff --git a/client/helpers.go b/client/helpers.go new file mode 100644 index 000000000000..10ffcc88e639 --- /dev/null +++ b/client/helpers.go @@ -0,0 +1,71 @@ +package client + +import ( + "fmt" + + "github.com/pkg/errors" + "github.com/spf13/viper" + + rpcclient "github.com/tendermint/tendermint/rpc/client" + ctypes "github.com/tendermint/tendermint/rpc/core/types" + cmn "github.com/tendermint/tmlibs/common" +) + +// GetNode prepares a simple rpc.Client from the flags +func GetNode() (rpcclient.Client, error) { + uri := viper.GetString(FlagNode) + if uri == "" { + return nil, errors.New("Must define node using --node") + } + return rpcclient.NewHTTP(uri, "/websocket"), nil +} + +// Broadcast the transaction bytes to Tendermint +func BroadcastTx(tx []byte) (*ctypes.ResultBroadcastTxCommit, error) { + + node, err := GetNode() + if err != nil { + return nil, err + } + + res, err := node.BroadcastTxCommit(tx) + if err != nil { + return res, err + } + + if res.CheckTx.Code != uint32(0) { + return res, errors.Errorf("CheckTx failed: (%d) %s", + res.CheckTx.Code, + res.CheckTx.Log) + } + if res.DeliverTx.Code != uint32(0) { + return res, errors.Errorf("DeliverTx failed: (%d) %s", + res.DeliverTx.Code, + res.DeliverTx.Log) + } + return res, err +} + +// Query from Tendermint with the provided key and storename +func Query(key cmn.HexBytes, storeName string) (res []byte, err error) { + + path := fmt.Sprintf("/%s/key", storeName) + node, err := GetNode() + if err != nil { + return res, err + } + + opts := rpcclient.ABCIQueryOptions{ + Height: viper.GetInt64(FlagHeight), + Trusted: viper.GetBool(FlagTrustNode), + } + result, err := node.ABCIQueryWithOptions(path, key, opts) + if err != nil { + return res, err + } + resp := result.Response + if resp.Code != uint32(0) { + return res, errors.Errorf("Query failed: (%d) %s", resp.Code, resp.Log) + } + return resp.Value, nil +} diff --git a/client/keys/add.go b/client/keys/add.go index 40bb0d103ed7..7472dce27f9e 100644 --- a/client/keys/add.go +++ b/client/keys/add.go @@ -102,7 +102,8 @@ func printCreate(info keys.Info, seed string) { // print seed unless requested not to. if !viper.GetBool(flagNoBackup) { fmt.Println("**Important** write this seed phrase in a safe place.") - fmt.Println("It is the only way to recover your account if you ever forget your password.\n") + fmt.Println("It is the only way to recover your account if you ever forget your password.") + fmt.Println() fmt.Println(seed) } case "json": diff --git a/client/node.go b/client/node.go deleted file mode 100644 index 5328f49d84d9..000000000000 --- a/client/node.go +++ /dev/null @@ -1,8 +0,0 @@ -package client - -import rpcclient "github.com/tendermint/tendermint/rpc/client" - -// GetNode prepares a simple rpc.Client from the flags -func GetNode(uri string) rpcclient.Client { - return rpcclient.NewHTTP(uri, "/websocket") -} diff --git a/client/rpc/block.go b/client/rpc/block.go index 3ae4e7615673..c701061a2048 100644 --- a/client/rpc/block.go +++ b/client/rpc/block.go @@ -5,7 +5,6 @@ import ( "strconv" "github.com/spf13/cobra" - "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/client" tmwire "github.com/tendermint/tendermint/wire" @@ -43,8 +42,10 @@ func getBlock(cmd *cobra.Command, args []string) error { } // get the node - uri := viper.GetString(client.FlagNode) - node := client.GetNode(uri) + node, err := client.GetNode() + if err != nil { + return err + } // TODO: actually honor the --select flag! // header -> BlockchainInfo diff --git a/client/rpc/status.go b/client/rpc/status.go index 9436a240a454..c5888d99f5cc 100644 --- a/client/rpc/status.go +++ b/client/rpc/status.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/spf13/cobra" - "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/client" tmwire "github.com/tendermint/tendermint/wire" @@ -22,8 +21,10 @@ func statusCommand() *cobra.Command { func checkStatus(cmd *cobra.Command, args []string) error { // get the node - uri := viper.GetString(client.FlagNode) - node := client.GetNode(uri) + node, err := client.GetNode() + if err != nil { + return err + } res, err := node.Status() if err != nil { return err diff --git a/client/rpc/validators.go b/client/rpc/validators.go index cb554e7c6b38..8eebda8dd690 100644 --- a/client/rpc/validators.go +++ b/client/rpc/validators.go @@ -5,7 +5,6 @@ import ( "strconv" "github.com/spf13/cobra" - "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/client" tmwire "github.com/tendermint/tendermint/wire" @@ -38,8 +37,10 @@ func getValidators(cmd *cobra.Command, args []string) error { } // get the node - uri := viper.GetString(client.FlagNode) - node := client.GetNode(uri) + node, err := client.GetNode() + if err != nil { + return err + } res, err := node.Validators(height) if err != nil { diff --git a/client/tx/search.go b/client/tx/search.go index 51256470a308..adb3df32a448 100644 --- a/client/tx/search.go +++ b/client/tx/search.go @@ -41,11 +41,10 @@ func searchTx(cmd *cobra.Command, args []string) error { query := strings.Join(tags, " AND ") // get the node - uri := viper.GetString(client.FlagNode) - if uri == "" { - return errors.New("Must define which node to query with --node") + node, err := client.GetNode() + if err != nil { + return err } - node := client.GetNode(uri) prove := !viper.GetBool(client.FlagTrustNode) res, err := node.TxSearch(query, prove) diff --git a/client/tx/tx.go b/client/tx/tx.go index 98d72b84d76d..d79c341e606b 100644 --- a/client/tx/tx.go +++ b/client/tx/tx.go @@ -28,6 +28,7 @@ func txCommand() *cobra.Command { return cmd } +// command to query for a transaction func queryTx(cmd *cobra.Command, args []string) error { if len(args) != 1 || len(args[0]) == 0 { return errors.New("You must provide a tx hash") @@ -41,11 +42,10 @@ func queryTx(cmd *cobra.Command, args []string) error { } // get the node - uri := viper.GetString(client.FlagNode) - if uri == "" { - return errors.New("Must define which node to query with --node") + node, err := client.GetNode() + if err != nil { + return err } - node := client.GetNode(uri) prove := !viper.GetBool(client.FlagTrustNode) res, err := node.Tx(hash, prove) diff --git a/server/init.go b/server/init.go index a56ad5c37d7b..4dc724587336 100644 --- a/server/init.go +++ b/server/init.go @@ -178,5 +178,5 @@ func GetGenesisJSON(pubkey, chainID, denom, addr string, options string) string "plugin_options": [ "coin/issuer", {"app": "sigs", "addr": "%s"}%s ] -}`, chainID, pubkey, addr, denom, addr, options) +}`, addr, denom, addr, options) } diff --git a/x/bank/commands/account.go b/x/bank/commands/account.go index 2f7f9132ef33..ebe76e3edde6 100644 --- a/x/bank/commands/account.go +++ b/x/bank/commands/account.go @@ -7,33 +7,37 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/spf13/viper" crypto "github.com/tendermint/go-crypto" - rpcclient "github.com/tendermint/tendermint/rpc/client" + wire "github.com/tendermint/go-wire" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/examples/basecoin/app" // XXX: not good "github.com/cosmos/cosmos-sdk/examples/basecoin/types" // XXX: not good + + "github.com/cosmos/cosmos-sdk/x/bank" ) // GetAccountCmd returns a query account that will display the // state of the account at a given address func GetAccountCmd(storeName string) *cobra.Command { - cmd := acctCmd{storeName} - return &cobra.Command{ Use: "account
", Short: "Query account balance", - RunE: cmd.get, + RunE: newRunner(storeName).cmd, } } -type acctCmd struct { +type runner struct { storeName string } -func (a acctCmd) get(cmd *cobra.Command, args []string) error { +func newRunner(storeName string) runner { + return runner{ + storeName: storeName, + } +} + +func (r runner) cmd(cmd *cobra.Command, args []string) error { if len(args) != 1 || len(args[0]) == 0 { return errors.New("You must provide an account name") } @@ -45,41 +49,25 @@ func (a acctCmd) get(cmd *cobra.Command, args []string) error { return err } key := crypto.Address(bz) - path := fmt.Sprintf("/%s/key", a.storeName) - - uri := viper.GetString(client.FlagNode) - if uri == "" { - return errors.New("Must define which node to query with --node") - } - node := client.GetNode(uri) - opts := rpcclient.ABCIQueryOptions{ - Height: viper.GetInt64(client.FlagHeight), - Trusted: viper.GetBool(client.FlagTrustNode), - } - result, err := node.ABCIQueryWithOptions(path, key, opts) - if err != nil { - return err - } - resp := result.Response - if resp.Code != uint32(0) { - return errors.Errorf("Query failed: (%d) %s", resp.Code, resp.Log) - } + res, err := client.Query(key, r.storeName) // parse out the value acct := new(types.AppAccount) - cdc := app.MakeTxCodec() - err = cdc.UnmarshalBinary(resp.Value, acct) + cdc := wire.NewCodec() + bank.RegisterWire(cdc) + + err = cdc.UnmarshalBinary(res, acct) if err != nil { return err } - // print out whole account or just coins? + // print out whole account output, err := json.MarshalIndent(acct, "", " ") - // output, err := json.MarshalIndent(acct.BaseAccount.Coins, "", " ") if err != nil { return err } fmt.Println(string(output)) + return nil } diff --git a/x/bank/commands/sendtx.go b/x/bank/commands/sendtx.go index b6d3ed31fcfa..65b8fb1963c4 100644 --- a/x/bank/commands/sendtx.go +++ b/x/bank/commands/sendtx.go @@ -8,12 +8,14 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" + crypto "github.com/tendermint/go-crypto" + wire "github.com/tendermint/go-wire" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/keys" - "github.com/cosmos/cosmos-sdk/examples/basecoin/app" // XXX: not good sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/bank" - crypto "github.com/tendermint/go-crypto" ) const ( @@ -43,29 +45,12 @@ func sendTx(cmd *cobra.Command, args []string) error { return err } - uri := viper.GetString(client.FlagNode) - if uri == "" { - return errors.New("Must define which node to query with --node") - } - node := client.GetNode(uri) - - result, err := node.BroadcastTxCommit(txBytes) + res, err := client.BroadcastTx(txBytes) if err != nil { return err } - if result.CheckTx.Code != uint32(0) { - return errors.Errorf("CheckTx failed: (%d) %s", - result.CheckTx.Code, - result.CheckTx.Log) - } - if result.DeliverTx.Code != uint32(0) { - return errors.Errorf("DeliverTx failed: (%d) %s", - result.DeliverTx.Code, - result.DeliverTx.Log) - } - - fmt.Printf("Committed at block %d. Hash: %s\n", result.Height, result.Hash.String()) + fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String()) return nil } @@ -107,7 +92,9 @@ func buildTx() ([]byte, error) { // marshal bytes tx := sdk.NewStdTx(msg, sigs) - cdc := app.MakeTxCodec() + cdc := wire.NewCodec() + bank.RegisterWire(cdc) + txBytes, err := cdc.MarshalBinary(tx) if err != nil { return nil, err @@ -116,6 +103,7 @@ func buildTx() ([]byte, error) { } func buildMsg(from crypto.Address) (sdk.Msg, error) { + // parse coins amount := viper.GetString(flagAmount) coins, err := sdk.ParseCoins(amount) From 1d3904dfd3b842c90fd428d2b5a99906774639d4 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Wed, 28 Feb 2018 23:35:38 +0000 Subject: [PATCH 61/70] bank registers crypto --- examples/basecoin/app/app.go | 4 +--- x/bank/wire.go | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index 45226a4a4580..f1cb4841812c 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -4,7 +4,6 @@ import ( "encoding/json" abci "github.com/tendermint/abci/types" - crypto "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire" cmn "github.com/tendermint/tmlibs/common" dbm "github.com/tendermint/tmlibs/db" @@ -74,8 +73,7 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { func MakeTxCodec() *wire.Codec { cdc := wire.NewCodec() cdc.RegisterInterface((*sdk.Msg)(nil), nil) - crypto.RegisterWire(cdc) // Register crypto.[PubKey,PrivKey,Signature] types. - bank.RegisterWire(cdc) // Register bank.[SendMsg,IssueMsg] types. + bank.RegisterWire(cdc) // Register bank.[SendMsg,IssueMsg] types. return cdc } diff --git a/x/bank/wire.go b/x/bank/wire.go index 7162a416aa9a..ae496d6f67fe 100644 --- a/x/bank/wire.go +++ b/x/bank/wire.go @@ -1,6 +1,7 @@ package bank import ( + crypto "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire" ) @@ -8,4 +9,5 @@ func RegisterWire(cdc *wire.Codec) { // TODO include option to always include prefix bytes. cdc.RegisterConcrete(SendMsg{}, "cosmos-sdk/SendMsg", nil) cdc.RegisterConcrete(IssueMsg{}, "cosmos-sdk/IssueMsg", nil) + crypto.RegisterWire(cdc) // Register crypto.[PubKey,PrivKey,Signature] types. } From 5bc5135c1b5dbad8025ea872f41bfd05c28baf12 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Wed, 28 Feb 2018 23:42:39 +0000 Subject: [PATCH 62/70] wire --- examples/basecoin/app/app.go | 1 - x/bank/wire.go | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index f1cb4841812c..8e998c3ac9da 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -72,7 +72,6 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { // custom tx codec func MakeTxCodec() *wire.Codec { cdc := wire.NewCodec() - cdc.RegisterInterface((*sdk.Msg)(nil), nil) bank.RegisterWire(cdc) // Register bank.[SendMsg,IssueMsg] types. return cdc } diff --git a/x/bank/wire.go b/x/bank/wire.go index ae496d6f67fe..ee8391116ed8 100644 --- a/x/bank/wire.go +++ b/x/bank/wire.go @@ -1,6 +1,7 @@ package bank import ( + sdk "github.com/cosmos/cosmos-sdk/types" crypto "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire" ) @@ -9,5 +10,8 @@ func RegisterWire(cdc *wire.Codec) { // TODO include option to always include prefix bytes. cdc.RegisterConcrete(SendMsg{}, "cosmos-sdk/SendMsg", nil) cdc.RegisterConcrete(IssueMsg{}, "cosmos-sdk/IssueMsg", nil) + + cdc.RegisterInterface((*sdk.Msg)(nil), nil) + crypto.RegisterWire(cdc) // Register crypto.[PubKey,PrivKey,Signature] types. } From 3be46395cfa3e86c558e3da14c31942ab134a343 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Thu, 1 Mar 2018 01:57:38 +0000 Subject: [PATCH 63/70] restructure to remove deps on example --- client/tx/root.go | 13 ++++- client/tx/search.go | 20 +++---- client/tx/tx.go | 22 +++---- examples/basecoin/app/app.go | 9 ++- examples/basecoin/app/app_test.go | 2 +- examples/basecoin/cmd/basecli/main.go | 13 ++++- examples/basecoin/types/account.go | 10 ++++ types/account.go | 3 + x/auth/commands/account.go | 82 +++++++++++++++++++++++++++ x/bank/commands/account.go | 73 ------------------------ x/bank/commands/sendtx.go | 19 ++++--- x/bank/wire.go | 6 -- 12 files changed, 154 insertions(+), 118 deletions(-) create mode 100644 x/auth/commands/account.go delete mode 100644 x/bank/commands/account.go diff --git a/client/tx/root.go b/client/tx/root.go index e6c6786bebb8..2099fb2112ac 100644 --- a/client/tx/root.go +++ b/client/tx/root.go @@ -2,12 +2,19 @@ package tx import ( "github.com/spf13/cobra" + wire "github.com/tendermint/go-wire" ) +// type used to pass around the provided cdc +type commander struct { + cdc *wire.Codec +} + // AddCommands adds a number of tx-query related subcommands -func AddCommands(cmd *cobra.Command) { +func AddCommands(cmd *cobra.Command, cdc *wire.Codec) { + cmdr := commander{cdc} cmd.AddCommand( - txSearchCommand(), - txCommand(), + SearchTxCmd(cmdr), + QueryTxCmd(cmdr), ) } diff --git a/client/tx/search.go b/client/tx/search.go index adb3df32a448..ffe0ca323d94 100644 --- a/client/tx/search.go +++ b/client/tx/search.go @@ -9,7 +9,7 @@ import ( "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/examples/basecoin/app" + wire "github.com/tendermint/go-wire" ctypes "github.com/tendermint/tendermint/rpc/core/types" ) @@ -18,21 +18,22 @@ const ( flagAny = "any" ) -func txSearchCommand() *cobra.Command { +// default client command to search through tagged transactions +func SearchTxCmd(cmdr commander) *cobra.Command { cmd := &cobra.Command{ Use: "txs", Short: "Search for all transactions that match the given tags", - RunE: searchTx, + RunE: cmdr.searchTxCmd, } cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to") - // TODO: change this to false when we can + // TODO: change this to false once proofs built in cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses") cmd.Flags().StringSlice(flagTags, nil, "Tags that must match (may provide multiple)") cmd.Flags().Bool(flagAny, false, "Return transactions that match ANY tag, rather than ALL") return cmd } -func searchTx(cmd *cobra.Command, args []string) error { +func (c commander) searchTxCmd(cmd *cobra.Command, args []string) error { tags := viper.GetStringSlice(flagTags) if len(tags) == 0 { return errors.New("Must declare at least one tag to search") @@ -52,13 +53,12 @@ func searchTx(cmd *cobra.Command, args []string) error { return err } - info, err := formatTxResults(res) + info, err := formatTxResults(c.cdc, res) if err != nil { return err } - cdc := app.MakeTxCodec() - output, err := cdc.MarshalJSON(info) + output, err := c.cdc.MarshalJSON(info) if err != nil { return err } @@ -67,11 +67,11 @@ func searchTx(cmd *cobra.Command, args []string) error { return nil } -func formatTxResults(res []*ctypes.ResultTx) ([]txInfo, error) { +func formatTxResults(cdc *wire.Codec, res []*ctypes.ResultTx) ([]txInfo, error) { var err error out := make([]txInfo, len(res)) for i := range res { - out[i], err = formatTxResult(res[i]) + out[i], err = formatTxResult(cdc, res[i]) if err != nil { return nil, err } diff --git a/client/tx/tx.go b/client/tx/tx.go index d79c341e606b..9bf348dd0109 100644 --- a/client/tx/tx.go +++ b/client/tx/tx.go @@ -9,18 +9,19 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/examples/basecoin/app" // XXX: not good + "github.com/cosmos/cosmos-sdk/client" // XXX: not good sdk "github.com/cosmos/cosmos-sdk/types" abci "github.com/tendermint/abci/types" + wire "github.com/tendermint/go-wire" ctypes "github.com/tendermint/tendermint/rpc/core/types" ) -func txCommand() *cobra.Command { +// Get the default command for a tx query +func QueryTxCmd(cmdr commander) *cobra.Command { cmd := &cobra.Command{ - Use: "tx ", + Use: "tx [hash]", Short: "Matches this txhash over all committed blocks", - RunE: queryTx, + RunE: cmdr.queryTxCmd, } cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to") // TODO: change this to false when we can @@ -29,7 +30,7 @@ func txCommand() *cobra.Command { } // command to query for a transaction -func queryTx(cmd *cobra.Command, args []string) error { +func (c commander) queryTxCmd(cmd *cobra.Command, args []string) error { if len(args) != 1 || len(args[0]) == 0 { return errors.New("You must provide a tx hash") } @@ -52,7 +53,7 @@ func queryTx(cmd *cobra.Command, args []string) error { if err != nil { return err } - info, err := formatTxResult(res) + info, err := formatTxResult(c.cdc, res) if err != nil { return err } @@ -66,9 +67,9 @@ func queryTx(cmd *cobra.Command, args []string) error { return nil } -func formatTxResult(res *ctypes.ResultTx) (txInfo, error) { +func formatTxResult(cdc *wire.Codec, res *ctypes.ResultTx) (txInfo, error) { // TODO: verify the proof if requested - tx, err := parseTx(res.Tx) + tx, err := parseTx(cdc, res.Tx) if err != nil { return txInfo{}, err } @@ -88,9 +89,8 @@ type txInfo struct { Result abci.ResponseDeliverTx `json:"result"` } -func parseTx(txBytes []byte) (sdk.Tx, error) { +func parseTx(cdc *wire.Codec, txBytes []byte) (sdk.Tx, error) { var tx sdk.StdTx - cdc := app.MakeTxCodec() err := cdc.UnmarshalBinary(txBytes, &tx) if err != nil { return nil, err diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index 8e998c3ac9da..3c27faea92f8 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -4,6 +4,7 @@ import ( "encoding/json" abci "github.com/tendermint/abci/types" + crypto "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire" cmn "github.com/tendermint/tmlibs/common" dbm "github.com/tendermint/tmlibs/db" @@ -39,7 +40,7 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { // create your application object var app = &BasecoinApp{ BaseApp: bam.NewBaseApp(appName, logger, db), - cdc: MakeTxCodec(), + cdc: MakeCodec(), capKeyMainStore: sdk.NewKVStoreKey("main"), capKeyIBCStore: sdk.NewKVStoreKey("ibc"), } @@ -70,9 +71,11 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { } // custom tx codec -func MakeTxCodec() *wire.Codec { +func MakeCodec() *wire.Codec { cdc := wire.NewCodec() - bank.RegisterWire(cdc) // Register bank.[SendMsg,IssueMsg] types. + cdc.RegisterInterface((*sdk.Msg)(nil), nil) + bank.RegisterWire(cdc) // Register bank.[SendMsg,IssueMsg] types. + crypto.RegisterWire(cdc) // Register crypto.[PubKey,PrivKey,Signature] types. return cdc } diff --git a/examples/basecoin/app/app_test.go b/examples/basecoin/app/app_test.go index 49eb580fd413..e917936b658c 100644 --- a/examples/basecoin/app/app_test.go +++ b/examples/basecoin/app/app_test.go @@ -54,7 +54,7 @@ func TestSendMsg(t *testing.T) { }}) // just marshal/unmarshal! - cdc := MakeTxCodec() + cdc := MakeCodec() txBytes, err := cdc.MarshalBinary(tx) require.NoError(t, err) diff --git a/examples/basecoin/cmd/basecli/main.go b/examples/basecoin/cmd/basecli/main.go index 6cabc2ceffe0..638071d14f0e 100644 --- a/examples/basecoin/cmd/basecli/main.go +++ b/examples/basecoin/cmd/basecli/main.go @@ -14,7 +14,11 @@ import ( "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/version" + authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands" bankcmd "github.com/cosmos/cosmos-sdk/x/bank/commands" + + "github.com/cosmos/cosmos-sdk/examples/basecoin/app" + "github.com/cosmos/cosmos-sdk/examples/basecoin/types" ) // gaiacliCmd is the entry point for this binary @@ -33,20 +37,23 @@ func main() { // disable sorting cobra.EnableCommandSorting = false + // get the codec + cdc := app.MakeCodec() + // add standard rpc, and tx commands rpc.AddCommands(basecliCmd) basecliCmd.AddCommand(client.LineBreak) - tx.AddCommands(basecliCmd) + tx.AddCommands(basecliCmd, cdc) basecliCmd.AddCommand(client.LineBreak) // add query/post commands (custom to binary) basecliCmd.AddCommand( client.GetCommands( - bankcmd.GetAccountCmd("main"), + authcmd.GetAccountCmd("main", cdc, types.GetParseAccount(cdc)), )...) basecliCmd.AddCommand( client.PostCommands( - bankcmd.SendTxCommand(), + bankcmd.SendTxCmd(cdc), )...) // add proxy, version and key info diff --git a/examples/basecoin/types/account.go b/examples/basecoin/types/account.go index c9b1d8a239f8..f5b4f7aa012e 100644 --- a/examples/basecoin/types/account.go +++ b/examples/basecoin/types/account.go @@ -4,6 +4,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" crypto "github.com/tendermint/go-crypto" + wire "github.com/tendermint/go-wire" ) var _ sdk.Account = (*AppAccount)(nil) @@ -22,6 +23,15 @@ type AppAccount struct { func (acc AppAccount) GetName() string { return acc.Name } func (acc *AppAccount) SetName(name string) { acc.Name = name } +// Get the ParseAccount function for the custom AppAccount +func GetParseAccount(cdc *wire.Codec) sdk.ParseAccount { + return func(accBytes []byte) (res sdk.Account, err error) { + acct := new(AppAccount) + err = cdc.UnmarshalBinary(accBytes, acct) + return acct, err + } +} + //___________________________________________________________________________________ // State to Unmarshal diff --git a/types/account.go b/types/account.go index d2dd9df6cf66..3a6e5931f000 100644 --- a/types/account.go +++ b/types/account.go @@ -30,3 +30,6 @@ type AccountMapper interface { GetAccount(ctx Context, addr crypto.Address) Account SetAccount(ctx Context, acc Account) } + +// Application function variable used to unmarshal account +type ParseAccount func([]byte) (Account, error) diff --git a/x/auth/commands/account.go b/x/auth/commands/account.go new file mode 100644 index 000000000000..2766c8ca1b57 --- /dev/null +++ b/x/auth/commands/account.go @@ -0,0 +1,82 @@ +package commands + +import ( + "encoding/hex" + "encoding/json" + "fmt" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + + crypto "github.com/tendermint/go-crypto" + wire "github.com/tendermint/go-wire" + + "github.com/cosmos/cosmos-sdk/client" // XXX: not good + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" +) + +// GetAccountCmd for the auth.BaseAccount type +func GetAccountCmdDefault(storeName string, cdc *wire.Codec) *cobra.Command { + return GetAccountCmd(storeName, cdc, getParseAccount(cdc)) +} + +func getParseAccount(cdc *wire.Codec) sdk.ParseAccount { + return func(accBytes []byte) (sdk.Account, error) { + acct := new(auth.BaseAccount) + err := cdc.UnmarshalBinary(accBytes, acct) + return acct, err + } +} + +// GetAccountCmd returns a query account that will display the +// state of the account at a given address +func GetAccountCmd(storeName string, cdc *wire.Codec, parser sdk.ParseAccount) *cobra.Command { + cmdr := commander{ + storeName, + cdc, + parser, + } + return &cobra.Command{ + Use: "account
", + Short: "Query account balance", + RunE: cmdr.getAccountCmd, + } +} + +type commander struct { + storeName string + cdc *wire.Codec + parser sdk.ParseAccount +} + +func (c commander) getAccountCmd(cmd *cobra.Command, args []string) error { + if len(args) != 1 || len(args[0]) == 0 { + return errors.New("You must provide an account name") + } + + // find the key to look up the account + addr := args[0] + bz, err := hex.DecodeString(addr) + if err != nil { + return err + } + key := crypto.Address(bz) + + res, err := client.Query(key, c.storeName) + + // parse out the value + account, err := c.parser(res) + if err != nil { + return err + } + + // print out whole account + output, err := json.MarshalIndent(account, "", " ") + if err != nil { + return err + } + fmt.Println(string(output)) + + return nil +} diff --git a/x/bank/commands/account.go b/x/bank/commands/account.go deleted file mode 100644 index ebe76e3edde6..000000000000 --- a/x/bank/commands/account.go +++ /dev/null @@ -1,73 +0,0 @@ -package commands - -import ( - "encoding/hex" - "encoding/json" - "fmt" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - - crypto "github.com/tendermint/go-crypto" - wire "github.com/tendermint/go-wire" - - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/examples/basecoin/types" // XXX: not good - - "github.com/cosmos/cosmos-sdk/x/bank" -) - -// GetAccountCmd returns a query account that will display the -// state of the account at a given address -func GetAccountCmd(storeName string) *cobra.Command { - return &cobra.Command{ - Use: "account
", - Short: "Query account balance", - RunE: newRunner(storeName).cmd, - } -} - -type runner struct { - storeName string -} - -func newRunner(storeName string) runner { - return runner{ - storeName: storeName, - } -} - -func (r runner) cmd(cmd *cobra.Command, args []string) error { - if len(args) != 1 || len(args[0]) == 0 { - return errors.New("You must provide an account name") - } - - // find the key to look up the account - addr := args[0] - bz, err := hex.DecodeString(addr) - if err != nil { - return err - } - key := crypto.Address(bz) - - res, err := client.Query(key, r.storeName) - - // parse out the value - acct := new(types.AppAccount) - cdc := wire.NewCodec() - bank.RegisterWire(cdc) - - err = cdc.UnmarshalBinary(res, acct) - if err != nil { - return err - } - - // print out whole account - output, err := json.MarshalIndent(acct, "", " ") - if err != nil { - return err - } - fmt.Println(string(output)) - - return nil -} diff --git a/x/bank/commands/sendtx.go b/x/bank/commands/sendtx.go index 65b8fb1963c4..941b0b69f7f8 100644 --- a/x/bank/commands/sendtx.go +++ b/x/bank/commands/sendtx.go @@ -26,11 +26,12 @@ const ( ) // SendTxCommand will create a send tx and sign it with the given key -func SendTxCommand() *cobra.Command { +func SendTxCmd(cdc *wire.Codec) *cobra.Command { + cmdr := commander{cdc} cmd := &cobra.Command{ Use: "send", Short: "Create and sign a send tx", - RunE: sendTx, + RunE: cmdr.sendTxCmd, } cmd.Flags().String(flagTo, "", "Address to send coins") cmd.Flags().String(flagAmount, "", "Amount of coins to send") @@ -39,8 +40,12 @@ func SendTxCommand() *cobra.Command { return cmd } -func sendTx(cmd *cobra.Command, args []string) error { - txBytes, err := buildTx() +type commander struct { + cdc *wire.Codec +} + +func (c commander) sendTxCmd(cmd *cobra.Command, args []string) error { + txBytes, err := c.buildTx() if err != nil { return err } @@ -54,7 +59,7 @@ func sendTx(cmd *cobra.Command, args []string) error { return nil } -func buildTx() ([]byte, error) { +func (c commander) buildTx() ([]byte, error) { keybase, err := keys.GetKeyBase() if err != nil { return nil, err @@ -92,10 +97,8 @@ func buildTx() ([]byte, error) { // marshal bytes tx := sdk.NewStdTx(msg, sigs) - cdc := wire.NewCodec() - bank.RegisterWire(cdc) - txBytes, err := cdc.MarshalBinary(tx) + txBytes, err := c.cdc.MarshalBinary(tx) if err != nil { return nil, err } diff --git a/x/bank/wire.go b/x/bank/wire.go index ee8391116ed8..7162a416aa9a 100644 --- a/x/bank/wire.go +++ b/x/bank/wire.go @@ -1,8 +1,6 @@ package bank import ( - sdk "github.com/cosmos/cosmos-sdk/types" - crypto "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire" ) @@ -10,8 +8,4 @@ func RegisterWire(cdc *wire.Codec) { // TODO include option to always include prefix bytes. cdc.RegisterConcrete(SendMsg{}, "cosmos-sdk/SendMsg", nil) cdc.RegisterConcrete(IssueMsg{}, "cosmos-sdk/IssueMsg", nil) - - cdc.RegisterInterface((*sdk.Msg)(nil), nil) - - crypto.RegisterWire(cdc) // Register crypto.[PubKey,PrivKey,Signature] types. } From 5ba27147773a8749e38d72e6b92a77893bde7ce6 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Thu, 1 Mar 2018 02:24:38 +0000 Subject: [PATCH 64/70] removed gaia cli references (on reference branch now) --- examples/gaia/README.md | 3 - examples/gaia/gaiacli/client.go | 131 -------------------------------- examples/gaia/gaiacli/key.go | 77 ------------------- examples/gaia/gaiacli/main.go | 77 ------------------- examples/gaia/gaiad/main.go | 71 ----------------- 5 files changed, 359 deletions(-) delete mode 100644 examples/gaia/README.md delete mode 100644 examples/gaia/gaiacli/client.go delete mode 100644 examples/gaia/gaiacli/key.go delete mode 100644 examples/gaia/gaiacli/main.go delete mode 100644 examples/gaia/gaiad/main.go diff --git a/examples/gaia/README.md b/examples/gaia/README.md deleted file mode 100644 index 485af236f051..000000000000 --- a/examples/gaia/README.md +++ /dev/null @@ -1,3 +0,0 @@ -Gaiad is the abci application, which can be run stand-alone, or in-process with tendermint. - -Gaiacli is a client application, which connects to tendermint rpc, and sends transactions and queries the state. It uses light-client proofs to guarantee the results even if it doesn't have 100% trust in the node it connects to. diff --git a/examples/gaia/gaiacli/client.go b/examples/gaia/gaiacli/client.go deleted file mode 100644 index 682a571e6ee9..000000000000 --- a/examples/gaia/gaiacli/client.go +++ /dev/null @@ -1,131 +0,0 @@ -package main - -import "github.com/spf13/cobra" - -const ( - // these are needed for every init - flagChainID = "chain-id" - flagNode = "node" - - // one of the following should be provided to verify the connection - flagGenesis = "genesis" - flagCommit = "commit" - flagValHash = "validator-set" - - flagSelect = "select" - flagTags = "tag" - flagAny = "any" - - flagBind = "bind" - flagCORS = "cors" - flagTrustNode = "trust-node" - - // this is for signing - flagName = "name" -) - -var ( - statusCmd = &cobra.Command{ - Use: "status", - Short: "Query remote node for status", - RunE: todoNotImplemented, - } -) - -// AddClientCommands returns a sub-tree of all basic client commands -// -// Call AddGetCommand and AddPostCommand to add custom txs and queries -func AddClientCommands(cmd *cobra.Command) { - cmd.AddCommand( - initClientCommand(), - statusCmd, - blockCommand(), - validatorCommand(), - lineBreak, - txSearchCommand(), - txCommand(), - lineBreak, - ) -} - -// GetCommands adds common flags to query commands -func GetCommands(cmds ...*cobra.Command) []*cobra.Command { - for _, c := range cmds { - c.Flags().Bool(flagTrustNode, false, "Don't verify proofs for responses") - } - return cmds -} - -// PostCommands adds common flags for commands to post tx -func PostCommands(cmds ...*cobra.Command) []*cobra.Command { - for _, c := range cmds { - c.Flags().String(flagName, "", "Name of private key with which to sign") - c.Flags().String(flagPassword, "", "Password to use the named private key") - } - return cmds -} - -func initClientCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "init", - Short: "Initialize light client", - RunE: todoNotImplemented, - } - cmd.Flags().StringP(flagChainID, "c", "", "ID of chain we connect to") - cmd.Flags().StringP(flagNode, "n", "tcp://localhost:46657", "Node to connect to") - cmd.Flags().String(flagGenesis, "", "Genesis file to verify header validity") - cmd.Flags().String(flagCommit, "", "File with trusted and signed header") - cmd.Flags().String(flagValHash, "", "Hash of trusted validator set (hex-encoded)") - return cmd -} - -func blockCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "block ", - Short: "Get verified data for a the block at given height", - RunE: todoNotImplemented, - } - cmd.Flags().StringSlice(flagSelect, []string{"header", "tx"}, "Fields to return (header|txs|results)") - return cmd -} - -func validatorCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "validatorset ", - Short: "Get the full validator set at given height", - RunE: todoNotImplemented, - } - return cmd -} - -func serveCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "serve", - Short: "Start LCD (light-client daemon), a local REST server", - RunE: todoNotImplemented, - } - // TODO: handle unix sockets also? - cmd.Flags().StringP(flagBind, "b", "localhost:1317", "Interface and port that server binds to") - cmd.Flags().String(flagCORS, "", "Set to domains that can make CORS requests (* for all)") - return cmd -} - -func txSearchCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "txs", - Short: "Search for all transactions that match the given tags", - RunE: todoNotImplemented, - } - cmd.Flags().StringSlice(flagTags, nil, "Tags that must match (may provide multiple)") - cmd.Flags().Bool(flagAny, false, "Return transactions that match ANY tag, rather than ALL") - return cmd -} - -func txCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "tx ", - Short: "Matches this txhash over all committed blocks", - RunE: todoNotImplemented, - } - return cmd -} diff --git a/examples/gaia/gaiacli/key.go b/examples/gaia/gaiacli/key.go deleted file mode 100644 index b3ffa323aed6..000000000000 --- a/examples/gaia/gaiacli/key.go +++ /dev/null @@ -1,77 +0,0 @@ -package main - -import "github.com/spf13/cobra" - -const ( - flagPassword = "password" - flagNewPassword = "new-password" - flagType = "type" - flagSeed = "seed" - flagDryRun = "dry-run" -) - -var ( - listKeysCmd = &cobra.Command{ - Use: "list", - Short: "List all locally availably keys", - RunE: todoNotImplemented, - } - - showKeysCmd = &cobra.Command{ - Use: "show ", - Short: "Show key info for the given name", - RunE: todoNotImplemented, - } -) - -// KeyCommands registers a sub-tree of commands to interact with -// local private key storage. -func KeyCommands() *cobra.Command { - cmd := &cobra.Command{ - Use: "keys", - Short: "Add or view local private keys", - } - cmd.AddCommand( - addKeyCommand(), - listKeysCmd, - showKeysCmd, - lineBreak, - deleteKeyCommand(), - updateKeyCommand(), - ) - return cmd -} - -func addKeyCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "add ", - Short: "Create a new key, or import from seed", - RunE: todoNotImplemented, - } - cmd.Flags().StringP(flagPassword, "p", "", "Password to encrypt private key") - cmd.Flags().StringP(flagType, "t", "ed25519", "Type of private key (ed25519|secp256k1|ledger)") - cmd.Flags().StringP(flagSeed, "s", "", "Provide seed phrase to recover existing key instead of creating") - cmd.Flags().Bool(flagDryRun, false, "Perform action, but don't add key to local keystore") - return cmd -} - -func updateKeyCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "update ", - Short: "Change the password used to protect private key", - RunE: todoNotImplemented, - } - cmd.Flags().StringP(flagPassword, "p", "", "Current password to decrypt key") - cmd.Flags().String(flagNewPassword, "", "New password to use to protect key") - return cmd -} - -func deleteKeyCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "delete ", - Short: "Delete the given key", - RunE: todoNotImplemented, - } - cmd.Flags().StringP(flagPassword, "p", "", "Password of existing key to delete") - return cmd -} diff --git a/examples/gaia/gaiacli/main.go b/examples/gaia/gaiacli/main.go deleted file mode 100644 index dce125acbb0d..000000000000 --- a/examples/gaia/gaiacli/main.go +++ /dev/null @@ -1,77 +0,0 @@ -package main - -import ( - "errors" - "os" - - "github.com/spf13/cobra" - - "github.com/tendermint/tmlibs/cli" - - "github.com/cosmos/cosmos-sdk/version" -) - -const ( - flagTo = "to" - flagAmount = "amount" - flagFee = "fee" -) - -// gaiacliCmd is the entry point for this binary -var ( - gaiacliCmd = &cobra.Command{ - Use: "gaiacli", - Short: "Gaia light-client", - } - - lineBreak = &cobra.Command{Run: func(*cobra.Command, []string) {}} - - getAccountCmd = &cobra.Command{ - Use: "account
", - Short: "Query account balance", - RunE: todoNotImplemented, - } -) - -func todoNotImplemented(_ *cobra.Command, _ []string) error { - return errors.New("TODO: Command not yet implemented") -} - -func postSendCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "send", - Short: "Create and sign a send tx", - RunE: todoNotImplemented, - } - cmd.Flags().String(flagTo, "", "Address to send coins") - cmd.Flags().String(flagAmount, "", "Amount of coins to send") - cmd.Flags().String(flagFee, "", "Fee to pay along with transaction") - return cmd -} - -func main() { - // disable sorting - cobra.EnableCommandSorting = false - - // generic client commands - AddClientCommands(gaiacliCmd) - // query commands (custom to binary) - gaiacliCmd.AddCommand( - GetCommands(getAccountCmd)...) - // post tx commands (custom to binary) - gaiacliCmd.AddCommand( - PostCommands(postSendCommand())...) - - // add proxy, version and key info - gaiacliCmd.AddCommand( - lineBreak, - serveCommand(), - KeyCommands(), - lineBreak, - version.VersionCmd, - ) - - // prepare and add flags - executor := cli.PrepareBaseCmd(gaiacliCmd, "GA", os.ExpandEnv("$HOME/.gaiacli")) - executor.Execute() -} diff --git a/examples/gaia/gaiad/main.go b/examples/gaia/gaiad/main.go deleted file mode 100644 index 0c4c49eec7d0..000000000000 --- a/examples/gaia/gaiad/main.go +++ /dev/null @@ -1,71 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "os" - - "github.com/spf13/cobra" - - abci "github.com/tendermint/abci/types" - "github.com/tendermint/tmlibs/cli" - "github.com/tendermint/tmlibs/log" - - "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/server" - "github.com/cosmos/cosmos-sdk/version" -) - -// gaiadCmd is the entry point for this binary -var ( - gaiadCmd = &cobra.Command{ - Use: "gaiad", - Short: "Gaia Daemon (server)", - } -) - -// defaultOptions sets up the app_options for the -// default genesis file -func defaultOptions(args []string) (json.RawMessage, error) { - addr, secret, err := server.GenerateCoinKey() - if err != nil { - return nil, err - } - fmt.Println("Secret phrase to access coins:") - fmt.Println(secret) - - opts := fmt.Sprintf(`{ - "accounts": [{ - "address": "%s", - "coins": [ - { - "denom": "mycoin", - "amount": 9007199254740992 - } - ] - }] - }`, addr) - return json.RawMessage(opts), nil -} - -func generateApp(rootDir string, logger log.Logger) (abci.Application, error) { - // TODO: set this to something real - app := new(baseapp.BaseApp) - return app, nil -} - -func main() { - logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)). - With("module", "main") - - gaiadCmd.AddCommand( - server.InitCmd(defaultOptions, logger), - server.StartCmd(generateApp, logger), - server.UnsafeResetAllCmd(logger), - version.VersionCmd, - ) - - // prepare and add flags - executor := cli.PrepareBaseCmd(gaiadCmd, "GA", os.ExpandEnv("$HOME/.gaiad")) - executor.Execute() -} From 04693582860f9ca5775c90254eaaa12e633d6680 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Thu, 1 Mar 2018 03:17:48 +0000 Subject: [PATCH 65/70] rebase fixes --- baseapp/baseapp.go | 21 ++++++++------------- client/tx/tx.go | 2 +- mock/app_test.go | 20 ++++++++++---------- x/auth/commands/account.go | 2 +- 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 1b1b4da87fd1..c6ca46e0f830 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -236,13 +236,6 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC // NOTE: we don't commit, but BeginBlock for block 1 // starts from this deliverState - res = app.initChainer(ctx, req) - // TODO: handle error https://github.com/cosmos/cosmos-sdk/issues/468 - - // XXX this commits everything and bumps the version. - // https://github.com/cosmos/cosmos-sdk/issues/442#issuecomment-366470148 - app.cms.Commit() - return } @@ -368,12 +361,14 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk } // Run the ante handler. - newCtx, result, abort := app.anteHandler(ctx, tx) - if abort { - return result - } - if !newCtx.IsZero() { - ctx = newCtx + if app.anteHandler != nil { + newCtx, result, abort := app.anteHandler(ctx, tx) + if abort { + return result + } + if !newCtx.IsZero() { + ctx = newCtx + } } // Get the correct cache diff --git a/client/tx/tx.go b/client/tx/tx.go index 9bf348dd0109..f9ac0631bfeb 100644 --- a/client/tx/tx.go +++ b/client/tx/tx.go @@ -9,7 +9,7 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/cosmos/cosmos-sdk/client" // XXX: not good + "github.com/cosmos/cosmos-sdk/client" sdk "github.com/cosmos/cosmos-sdk/types" abci "github.com/tendermint/abci/types" wire "github.com/tendermint/go-wire" diff --git a/mock/app_test.go b/mock/app_test.go index f21abd594fdf..2e4099d3b666 100644 --- a/mock/app_test.go +++ b/mock/app_test.go @@ -13,6 +13,7 @@ import ( func TestInitApp(t *testing.T) { // set up an app app, closer, err := SetupApp() + // closer may need to be run, even when error in later stage if closer != nil { defer closer() @@ -25,15 +26,15 @@ func TestInitApp(t *testing.T) { req := abci.RequestInitChain{AppStateBytes: opts} app.InitChain(req) - // make sure we can query these values - query := abci.RequestQuery{ - Path: "/main/key", - Data: []byte("foo"), - } - qres := app.Query(query) - require.Equal(t, uint32(0), qres.Code, qres.Log) - assert.Equal(t, []byte("bar"), qres.Value) - + // XXX test failing + // // make sure we can query these values + //query := abci.RequestQuery{ + //Path: "/main/key", + //Data: []byte("foo"), + //} + //qres := app.Query(query) + //require.Equal(t, uint32(0), qres.Code, qres.Log) + //assert.Equal(t, []byte("bar"), qres.Value) } // TextDeliverTx ensures we can write a tx @@ -70,5 +71,4 @@ func TestDeliverTx(t *testing.T) { qres := app.Query(query) require.Equal(t, uint32(0), qres.Code, qres.Log) assert.Equal(t, []byte(value), qres.Value) - } diff --git a/x/auth/commands/account.go b/x/auth/commands/account.go index 2766c8ca1b57..613442d720d9 100644 --- a/x/auth/commands/account.go +++ b/x/auth/commands/account.go @@ -11,7 +11,7 @@ import ( crypto "github.com/tendermint/go-crypto" wire "github.com/tendermint/go-wire" - "github.com/cosmos/cosmos-sdk/client" // XXX: not good + "github.com/cosmos/cosmos-sdk/client" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" ) From 889551dbbde953b9000e0ced80f047630b6999f5 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Thu, 1 Mar 2018 03:40:39 +0000 Subject: [PATCH 66/70] fix broken mock test --- mock/app.go | 2 ++ mock/app_test.go | 17 +++++++++-------- mock/tx.go | 1 + 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/mock/app.go b/mock/app.go index 0168e4c0425c..3e9a3ad5f567 100644 --- a/mock/app.go +++ b/mock/app.go @@ -70,11 +70,13 @@ func KVStoreHandler(storeKey sdk.StoreKey) sdk.Handler { } } +// basic KV structure type KV struct { Key string `json:"key"` Value string `json:"value"` } +// What Genesis JSON is formatted as type GenesisJSON struct { Values []KV `json:"values"` } diff --git a/mock/app_test.go b/mock/app_test.go index 2e4099d3b666..103530e5014c 100644 --- a/mock/app_test.go +++ b/mock/app_test.go @@ -25,16 +25,17 @@ func TestInitApp(t *testing.T) { require.NoError(t, err) req := abci.RequestInitChain{AppStateBytes: opts} app.InitChain(req) + app.Commit() // XXX test failing - // // make sure we can query these values - //query := abci.RequestQuery{ - //Path: "/main/key", - //Data: []byte("foo"), - //} - //qres := app.Query(query) - //require.Equal(t, uint32(0), qres.Code, qres.Log) - //assert.Equal(t, []byte("bar"), qres.Value) + // make sure we can query these values + query := abci.RequestQuery{ + Path: "/main/key", + Data: []byte("foo"), + } + qres := app.Query(query) + require.Equal(t, uint32(0), qres.Code, qres.Log) + assert.Equal(t, []byte("bar"), qres.Value) } // TextDeliverTx ensures we can write a tx diff --git a/mock/tx.go b/mock/tx.go index 970549d2cdee..a9adff5deed8 100644 --- a/mock/tx.go +++ b/mock/tx.go @@ -1,3 +1,4 @@ +//nolint package mock import ( From 158dfcebdc43e0b1e132b840d21319e0c8d7af89 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 1 Mar 2018 00:38:39 -0500 Subject: [PATCH 67/70] changelog and version --- CHANGELOG.md | 20 +++++++++++++++++++- version/version.go | 4 ++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8f6ccdfe8d6..3efc147a4fdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,30 @@ # Changelog -## 0.11.0 (TBD) +## 0.11.0 (March 1, 2017) BREAKING CHANGES * [examples] dummy -> kvstore +* [examples] Remove gaia +* [examples/basecoin] MakeTxCodec -> MakeCodec * [types] CommitMultiStore interface has new `GetCommitKVStore(key StoreKey) CommitKVStore` method +FEATURES + +* [examples/basecoin] CLI for `basecli` and `basecoind` (!) +* [baseapp] router.AddRoute returns Router + +IMPROVEMENTS + +* [baseapp] Run msg handlers on CheckTx +* [docs] Add spec for REST API +* [all] More tests! + +BUG FIXES + +* [baseapp] Fix panic on app restart +* [baseapp] InitChain does not call Commit + ## 0.10.0 (February 20, 2017) BREAKING CHANGES diff --git a/version/version.go b/version/version.go index 96944d14cc55..6380ce453e0a 100644 --- a/version/version.go +++ b/version/version.go @@ -6,10 +6,10 @@ package version // TODO improve const Maj = "0" -const Min = "10" +const Min = "11" const Fix = "0" -const Version = "0.10.0-dev" +const Version = "0.11.0" // GitCommit set by build flags var GitCommit = "" From 86ac07f444551928a68c2428c7f63f27790cfcd1 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 1 Mar 2018 00:41:35 -0500 Subject: [PATCH 68/70] glide update --- glide.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/glide.lock b/glide.lock index f2fa12fefdf6..9ff8dde6121e 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: fa45c8a4f5512ed730f793b93d4876bdc604a1333a5a1f938c98a0f7dd55f22e -updated: 2018-02-23T19:47:25.630102+01:00 +updated: 2018-03-01T00:41:12.97082395-05:00 imports: - name: github.com/bgentry/speakeasy version: 4aabc24848ce5fd31929f7d1e4ea74d3709c14cd @@ -133,7 +133,7 @@ imports: - name: github.com/tendermint/iavl version: 1a59ec0c82dc940c25339dd7c834df5cb76a95cb - name: github.com/tendermint/tendermint - version: 6947e118f54e4df24f5e2c79bcdd66199e54d885 + version: c330b9e43c93351a5c3040333d7d0c7c27859a20 subpackages: - blockchain - cmd/tendermint/commands @@ -170,7 +170,7 @@ imports: - version - wire - name: github.com/tendermint/tmlibs - version: 1b9b5652a199ab0be2e781393fb275b66377309d + version: 26f2ab65f82cfc6873c312e8030104c47c05f10e subpackages: - autofile - cli From 2047abc8bc7b1eb2873f93c581b86dcadf3341d2 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 1 Mar 2018 02:00:44 -0500 Subject: [PATCH 69/70] add tests to show mounting multiple stores is broken. see #532 --- baseapp/baseapp_test.go | 27 +++++++++++++++++++++++++-- examples/basecoin/app/app_test.go | 12 ++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 1155ca24b40f..d94217c7f2a4 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -145,11 +145,15 @@ func TestInfo(t *testing.T) { } func TestInitChainer(t *testing.T) { - app := newBaseApp(t.Name()) + logger := defaultLogger() + db := dbm.NewMemDB() + name := t.Name() + app := NewBaseApp(name, logger, db) // make a cap key and mount the store capKey := sdk.NewKVStoreKey("main") - app.MountStoresIAVL(capKey) + capKey2 := sdk.NewKVStoreKey("key2") + app.MountStoresIAVL(capKey, capKey2) err := app.LoadLatestVersion(capKey) // needed to make stores non-nil assert.Nil(t, err) @@ -178,6 +182,25 @@ func TestInitChainer(t *testing.T) { app.Commit() res = app.Query(query) assert.Equal(t, value, res.Value) + + // reload app + app = NewBaseApp(name, logger, db) + capKey = sdk.NewKVStoreKey("main") + capKey2 = sdk.NewKVStoreKey("key2") + app.MountStoresIAVL(capKey, capKey2) + err = app.LoadLatestVersion(capKey) // needed to make stores non-nil + assert.Nil(t, err) + app.SetInitChainer(initChainer) + + // ensure we can still query after reloading + res = app.Query(query) + assert.Equal(t, value, res.Value) + + // commit and ensure we can still query + app.BeginBlock(abci.RequestBeginBlock{}) + app.Commit() + res = app.Query(query) + assert.Equal(t, value, res.Value) } // Test that successive CheckTx can see each others' effects diff --git a/examples/basecoin/app/app_test.go b/examples/basecoin/app/app_test.go index e917936b658c..b6ed35b16e21 100644 --- a/examples/basecoin/app/app_test.go +++ b/examples/basecoin/app/app_test.go @@ -71,7 +71,9 @@ func TestSendMsg(t *testing.T) { } func TestGenesis(t *testing.T) { - bapp := newBasecoinApp() + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app") + db := dbm.NewMemDB() + bapp := NewBasecoinApp(logger, db) // Construct some genesis bytes to reflect basecoin/types/AppAccount pk := crypto.GenPrivKeyEd25519().PubKey() @@ -97,9 +99,15 @@ func TestGenesis(t *testing.T) { // A checkTx context ctx := bapp.BaseApp.NewContext(true, abci.Header{}) - res1 := bapp.accountMapper.GetAccount(ctx, baseAcc.Address) assert.Equal(t, acc, res1) + + // reload app and ensure the account is still there + bapp = NewBasecoinApp(logger, db) + ctx = bapp.BaseApp.NewContext(true, abci.Header{}) + res1 = bapp.accountMapper.GetAccount(ctx, baseAcc.Address) + assert.Equal(t, acc, res1) + } func TestSendMsgWithAccounts(t *testing.T) { From cba7379f78768cae45715b9ad38dd227f8505356 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 1 Mar 2018 02:02:29 -0500 Subject: [PATCH 70/70] dont mount multiple stores while its broken --- CHANGELOG.md | 1 + baseapp/baseapp_test.go | 12 +++++++----- examples/basecoin/app/app.go | 4 +++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3efc147a4fdb..3f71b5cc703d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ BUG FIXES * [baseapp] Fix panic on app restart * [baseapp] InitChain does not call Commit +* [basecoin] Remove IBCStore because mounting multiple stores is currently broken ## 0.10.0 (February 20, 2017) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index d94217c7f2a4..8114ef93caee 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -150,10 +150,12 @@ func TestInitChainer(t *testing.T) { name := t.Name() app := NewBaseApp(name, logger, db) - // make a cap key and mount the store + // make cap keys and mount the stores + // NOTE/TODO: mounting multiple stores is broken + // see https://github.com/cosmos/cosmos-sdk/issues/532 capKey := sdk.NewKVStoreKey("main") - capKey2 := sdk.NewKVStoreKey("key2") - app.MountStoresIAVL(capKey, capKey2) + // capKey2 := sdk.NewKVStoreKey("key2") + app.MountStoresIAVL(capKey) // , capKey2) err := app.LoadLatestVersion(capKey) // needed to make stores non-nil assert.Nil(t, err) @@ -186,8 +188,8 @@ func TestInitChainer(t *testing.T) { // reload app app = NewBaseApp(name, logger, db) capKey = sdk.NewKVStoreKey("main") - capKey2 = sdk.NewKVStoreKey("key2") - app.MountStoresIAVL(capKey, capKey2) + // capKey2 = sdk.NewKVStoreKey("key2") // TODO + app.MountStoresIAVL(capKey) //, capKey2) err = app.LoadLatestVersion(capKey) // needed to make stores non-nil assert.Nil(t, err) app.SetInitChainer(initChainer) diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index 3c27faea92f8..34778456b200 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -60,7 +60,9 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp { // initialize BaseApp app.SetTxDecoder(app.txDecoder) app.SetInitChainer(app.initChainer) - app.MountStoresIAVL(app.capKeyMainStore, app.capKeyIBCStore) + // TODO: mounting multiple stores is broken + // https://github.com/cosmos/cosmos-sdk/issues/532 + app.MountStoresIAVL(app.capKeyMainStore) // , app.capKeyIBCStore) app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper)) err := app.LoadLatestVersion(app.capKeyMainStore) if err != nil {