commit
f4c2b504f4
26
CHANGELOG.md
26
CHANGELOG.md
@ -1,5 +1,31 @@
|
||||
# Changelog
|
||||
|
||||
## 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
|
||||
* [basecoin] Remove IBCStore because mounting multiple stores is currently broken
|
||||
|
||||
## 0.10.0 (February 20, 2017)
|
||||
|
||||
BREAKING CHANGES
|
||||
|
||||
12
Makefile
12
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
|
||||
@ -58,16 +59,19 @@ godocs:
|
||||
########################################
|
||||
### Testing
|
||||
|
||||
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)
|
||||
|
||||
test_cover:
|
||||
@rm -rf examples/basecoin/vendor/
|
||||
@bash tests/test_cover.sh
|
||||
|
||||
benchmark:
|
||||
|
||||
@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
@ -16,12 +15,16 @@ 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 {
|
||||
// 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
|
||||
@ -38,24 +41,22 @@ 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.
|
||||
// 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
|
||||
// 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 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
|
||||
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),
|
||||
@ -94,11 +95,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
|
||||
@ -125,36 +124,46 @@ 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://<mainHeaderKey>
|
||||
if !lastCommitID.IsZero() {
|
||||
headerBytes := main.Get(mainHeaderKey)
|
||||
if len(headerBytes) == 0 {
|
||||
errStr := fmt.Sprintf("Version > 0 but missing key %s", mainHeaderKey)
|
||||
return errors.New(errStr)
|
||||
// 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 <dbHeaderKey> to exist in the db
|
||||
/*
|
||||
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)
|
||||
}
|
||||
}
|
||||
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", mainHeaderKey, lastVersion, header.Height)
|
||||
return errors.New(errStr)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// initialize Check state
|
||||
app.msCheck = app.cms.CacheMultiStore()
|
||||
app.ctxCheck = app.NewContext(true, abci.Header{})
|
||||
app.setCheckState(abci.Header{})
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -162,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)
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
@ -172,7 +206,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{
|
||||
@ -196,16 +229,12 @@ 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 and run initChain
|
||||
app.setDeliverState(abci.Header{})
|
||||
app.initChainer(app.deliverState.ctx, req) // no error
|
||||
|
||||
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()
|
||||
// NOTE: we don't commit, but BeginBlock for block 1
|
||||
// starts from this deliverState
|
||||
|
||||
return
|
||||
}
|
||||
@ -223,18 +252,22 @@ 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 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
|
||||
}
|
||||
|
||||
// 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 +292,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 +332,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 {
|
||||
@ -324,32 +355,41 @@ 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)
|
||||
}
|
||||
|
||||
// TODO: override default ante handler w/ custom ante handler.
|
||||
|
||||
// Run the ante handler.
|
||||
newCtx, result, abort := app.anteHandler(ctx, tx)
|
||||
if isCheckTx || 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
|
||||
}
|
||||
}
|
||||
|
||||
// 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.checkState.ms in case it fails.
|
||||
msCache = app.checkState.CacheMultiStore()
|
||||
ctx = ctx.WithMultiStore(msCache)
|
||||
} else {
|
||||
// CacheWrap app.deliverState.ms in case it fails.
|
||||
msCache = app.deliverState.CacheMultiStore()
|
||||
ctx = ctx.WithMultiStore(msCache)
|
||||
|
||||
}
|
||||
|
||||
// Match and run route.
|
||||
msgType := msg.Type()
|
||||
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()
|
||||
}
|
||||
@ -360,7 +400,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
|
||||
}
|
||||
@ -369,19 +409,30 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc
|
||||
|
||||
// Implements ABCI
|
||||
func (app *BaseApp) Commit() (res abci.ResponseCommit) {
|
||||
header := app.deliverState.ctx.BlockHeader()
|
||||
/*
|
||||
// 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()
|
||||
app.deliverState.ms.Write()
|
||||
commitID := app.cms.Commit()
|
||||
app.logger.Debug("Commit synced",
|
||||
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.
|
||||
header := app.ctxDeliver.BlockHeader()
|
||||
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,
|
||||
|
||||
@ -42,31 +42,59 @@ 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
|
||||
// Test that we can make commits and then reload old versions.
|
||||
// Test that LoadLatestVersion actually does.
|
||||
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)
|
||||
}
|
||||
|
||||
func TestTxDecoder(t *testing.T) {
|
||||
// TODO
|
||||
// Test that txs can be unmarshalled and read and that
|
||||
// correct error codes are returned when not
|
||||
}
|
||||
|
||||
func TestInfo(t *testing.T) {
|
||||
// TODO
|
||||
// Test that Info returns the latest committed state.
|
||||
}
|
||||
|
||||
func TestInitChainer(t *testing.T) {
|
||||
// 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
|
||||
@ -75,6 +103,62 @@ func TestInitChainer(t *testing.T) {
|
||||
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) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
// 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, t.Name(), res.GetData())
|
||||
assert.Equal(t, int64(0), res.LastBlockHeight)
|
||||
assert.Equal(t, []uint8(nil), res.LastBlockAppHash)
|
||||
|
||||
// ----- test a proper response -------
|
||||
// TODO
|
||||
|
||||
}
|
||||
|
||||
func TestInitChainer(t *testing.T) {
|
||||
logger := defaultLogger()
|
||||
db := dbm.NewMemDB()
|
||||
name := t.Name()
|
||||
app := NewBaseApp(name, logger, db)
|
||||
|
||||
// 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)
|
||||
err := app.LoadLatestVersion(capKey) // needed to make stores non-nil
|
||||
assert.Nil(t, err)
|
||||
|
||||
key, value := []byte("hello"), []byte("goodbye")
|
||||
|
||||
// initChainer sets a value in the store
|
||||
@ -97,18 +181,38 @@ 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)
|
||||
|
||||
// reload app
|
||||
app = NewBaseApp(name, logger, db)
|
||||
capKey = sdk.NewKVStoreKey("main")
|
||||
// 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)
|
||||
|
||||
// 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 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())
|
||||
@ -231,7 +335,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)
|
||||
@ -245,7 +350,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))
|
||||
|
||||
@ -354,17 +459,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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
38
client/flags.go
Normal file
38
client/flags.go
Normal file
@ -0,0 +1,38 @@
|
||||
package client
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
// nolint
|
||||
const (
|
||||
FlagChainID = "chain-id"
|
||||
FlagNode = "node"
|
||||
FlagHeight = "height"
|
||||
FlagTrustNode = "trust-node"
|
||||
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 {
|
||||
// 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, "tcp://localhost:46657", "<host>:<port> 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, "tcp://localhost:46657", "<host>:<port> to tendermint rpc interface for this chain")
|
||||
}
|
||||
return cmds
|
||||
}
|
||||
71
client/helpers.go
Normal file
71
client/helpers.go
Normal file
@ -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
|
||||
}
|
||||
92
client/input.go
Normal file
92
client/input.go
Normal file
@ -0,0 +1,92 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/bgentry/speakeasy"
|
||||
isatty "github.com/mattn/go-isatty"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// MinPassLength is the minimum acceptable password length
|
||||
const MinPassLength = 8
|
||||
|
||||
// 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, buf *bufio.Reader) (pass string, err error) {
|
||||
if inputIsTty() {
|
||||
pass, err = speakeasy.Ask(prompt)
|
||||
} else {
|
||||
pass, err = readLineFromBuf(buf)
|
||||
}
|
||||
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, buf *bufio.Reader) (seed string, err error) {
|
||||
if inputIsTty() {
|
||||
fmt.Println(prompt)
|
||||
}
|
||||
seed, err = readLineFromBuf(buf)
|
||||
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, buf *bufio.Reader) (string, error) {
|
||||
// simple read on no-tty
|
||||
if !inputIsTty() {
|
||||
return GetPassword(prompt, buf)
|
||||
}
|
||||
|
||||
// TODO: own function???
|
||||
pass, err := GetPassword(prompt, buf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
pass2, err := GetPassword(prompt2, buf)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if pass != pass2 {
|
||||
return "", errors.New("Passphrases don't match")
|
||||
}
|
||||
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())
|
||||
}
|
||||
|
||||
// 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 readLineFromBuf(buf *bufio.Reader) (string, error) {
|
||||
pass, err := buf.ReadString('\n')
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(pass), nil
|
||||
}
|
||||
23
client/keys.go
Normal file
23
client/keys.go
Normal file
@ -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"
|
||||
)
|
||||
|
||||
// GetKeyBase initializes a keybase based on the configuration
|
||||
func GetKeyBase(db dbm.DB) keys.Keybase {
|
||||
keybase := keys.New(
|
||||
db,
|
||||
words.MustLoadCodec("english"),
|
||||
)
|
||||
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 GetKeyBase(dbm.NewMemDB())
|
||||
}
|
||||
119
client/keys/README.md
Normal file
119
client/keys/README.md
Normal file
@ -0,0 +1,119 @@
|
||||
# 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.
|
||||
|
||||
`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="
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
122
client/keys/add.go
Normal file
122
client/keys/add.go
Normal file
@ -0,0 +1,122 @@
|
||||
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 <name>",
|
||||
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
|
||||
|
||||
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
|
||||
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 = 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:", buf)
|
||||
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) {
|
||||
output := viper.Get(cli.OutputFlag)
|
||||
switch output {
|
||||
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.")
|
||||
fmt.Println()
|
||||
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))
|
||||
default:
|
||||
panic(fmt.Sprintf("I can't speak: %s", output))
|
||||
}
|
||||
}
|
||||
45
client/keys/delete.go
Normal file
45
client/keys/delete.go
Normal file
@ -0,0 +1,45 @@
|
||||
package keys
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func deleteKeyCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "delete <name>",
|
||||
Short: "Delete the given key",
|
||||
RunE: runDeleteCmd,
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
buf := client.BufferStdin()
|
||||
oldpass, err := client.GetPassword(
|
||||
"DANGER - enter password to permanently delete key:", buf)
|
||||
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
|
||||
}
|
||||
25
client/keys/list.go
Normal file
25
client/keys/list.go
Normal file
@ -0,0 +1,25 @@
|
||||
package keys
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
// 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
|
||||
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
|
||||
}
|
||||
29
client/keys/root.go
Normal file
29
client/keys/root.go
Normal file
@ -0,0 +1,29 @@
|
||||
package keys
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// 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.
|
||||
|
||||
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,
|
||||
client.LineBreak,
|
||||
deleteKeyCommand(),
|
||||
updateKeyCommand(),
|
||||
)
|
||||
return cmd
|
||||
}
|
||||
32
client/keys/show.go
Normal file
32
client/keys/show.go
Normal file
@ -0,0 +1,32 @@
|
||||
package keys
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var showKeysCmd = &cobra.Command{
|
||||
Use: "show <name>",
|
||||
Short: "Show key info for the given name",
|
||||
Long: `Return public details of one local key.`,
|
||||
RunE: runShowCmd,
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
kb, err := GetKeyBase()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info, err := kb.Get(name)
|
||||
if err == nil {
|
||||
printInfo(info)
|
||||
}
|
||||
return err
|
||||
}
|
||||
50
client/keys/update.go
Normal file
50
client/keys/update.go
Normal file
@ -0,0 +1,50 @@
|
||||
package keys
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func updateKeyCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "update <name>",
|
||||
Short: "Change the password used to protect private key",
|
||||
RunE: runUpdateCmd,
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
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]
|
||||
|
||||
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:", buf)
|
||||
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
|
||||
}
|
||||
68
client/keys/utils.go
Normal file
68
client/keys/utils.go
Normal file
@ -0,0 +1,68 @@
|
||||
package keys
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
// GetKeyBase initializes a keybase based on the configuration
|
||||
func GetKeyBase() (keys.Keybase, error) {
|
||||
if keybase == nil {
|
||||
rootDir := viper.GetString(cli.HomeFlag)
|
||||
db, err := dbm.NewGoLevelDB(KeyDBName, rootDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keybase = client.GetKeyBase(db)
|
||||
}
|
||||
return keybase, 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))
|
||||
}
|
||||
}
|
||||
17
client/keys/wire.go
Normal file
17
client/keys/wire.go
Normal file
@ -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)
|
||||
}
|
||||
36
client/lcd/root.go
Normal file
36
client/lcd/root.go
Normal file
@ -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
|
||||
}
|
||||
66
client/rpc/block.go
Normal file
66
client/rpc/block.go
Normal file
@ -0,0 +1,66 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
tmwire "github.com/tendermint/tendermint/wire"
|
||||
)
|
||||
|
||||
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
|
||||
node, err := client.GetNode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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 := tmwire.MarshalJSON(res)
|
||||
// output, err := json.MarshalIndent(res, " ", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
return nil
|
||||
}
|
||||
44
client/rpc/root.go
Normal file
44
client/rpc/root.go
Normal file
@ -0,0 +1,44 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// 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(),
|
||||
statusCommand(),
|
||||
blockCommand(),
|
||||
validatorCommand(),
|
||||
)
|
||||
}
|
||||
|
||||
func initClientCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "init",
|
||||
Short: "Initialize light client",
|
||||
RunE: todoNotImplemented,
|
||||
}
|
||||
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)")
|
||||
return cmd
|
||||
}
|
||||
40
client/rpc/status.go
Normal file
40
client/rpc/status.go
Normal file
@ -0,0 +1,40 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
tmwire "github.com/tendermint/tendermint/wire"
|
||||
)
|
||||
|
||||
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
|
||||
node, err := client.GetNode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := node.Status()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
output, err := tmwire.MarshalJSON(res)
|
||||
// output, err := json.MarshalIndent(res, " ", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
return nil
|
||||
}
|
||||
57
client/rpc/validators.go
Normal file
57
client/rpc/validators.go
Normal file
@ -0,0 +1,57 @@
|
||||
package rpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
tmwire "github.com/tendermint/tendermint/wire"
|
||||
)
|
||||
|
||||
func validatorCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "validatorset <height>",
|
||||
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
|
||||
node, err := client.GetNode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := node.Validators(height)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
output, err := tmwire.MarshalJSON(res)
|
||||
// output, err := json.MarshalIndent(res, " ", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
return nil
|
||||
}
|
||||
20
client/tx/root.go
Normal file
20
client/tx/root.go
Normal file
@ -0,0 +1,20 @@
|
||||
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, cdc *wire.Codec) {
|
||||
cmdr := commander{cdc}
|
||||
cmd.AddCommand(
|
||||
SearchTxCmd(cmdr),
|
||||
QueryTxCmd(cmdr),
|
||||
)
|
||||
}
|
||||
80
client/tx/search.go
Normal file
80
client/tx/search.go
Normal file
@ -0,0 +1,80 @@
|
||||
package tx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
wire "github.com/tendermint/go-wire"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
)
|
||||
|
||||
const (
|
||||
flagTags = "tag"
|
||||
flagAny = "any"
|
||||
)
|
||||
|
||||
// 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: cmdr.searchTxCmd,
|
||||
}
|
||||
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to")
|
||||
// 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 (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")
|
||||
}
|
||||
// XXX: implement ANY
|
||||
query := strings.Join(tags, " AND ")
|
||||
|
||||
// get the node
|
||||
node, err := client.GetNode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prove := !viper.GetBool(client.FlagTrustNode)
|
||||
res, err := node.TxSearch(query, prove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info, err := formatTxResults(c.cdc, res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
output, err := c.cdc.MarshalJSON(info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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(cdc, res[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
99
client/tx/tx.go
Normal file
99
client/tx/tx.go
Normal file
@ -0,0 +1,99 @@
|
||||
package tx
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"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"
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
)
|
||||
|
||||
// Get the default command for a tx query
|
||||
func QueryTxCmd(cmdr commander) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "tx [hash]",
|
||||
Short: "Matches this txhash over all committed blocks",
|
||||
RunE: cmdr.queryTxCmd,
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// command to query for a transaction
|
||||
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")
|
||||
}
|
||||
|
||||
// find the key to look up the account
|
||||
hexStr := args[0]
|
||||
hash, err := hex.DecodeString(hexStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// get the node
|
||||
node, err := client.GetNode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
prove := !viper.GetBool(client.FlagTrustNode)
|
||||
|
||||
res, err := node.Tx(hash, prove)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := formatTxResult(c.cdc, res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
output, err := json.MarshalIndent(info, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(output))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatTxResult(cdc *wire.Codec, res *ctypes.ResultTx) (txInfo, error) {
|
||||
// TODO: verify the proof if requested
|
||||
tx, err := parseTx(cdc, res.Tx)
|
||||
if err != nil {
|
||||
return txInfo{}, err
|
||||
}
|
||||
|
||||
info := txInfo{
|
||||
Height: res.Height,
|
||||
Tx: tx,
|
||||
Result: res.TxResult,
|
||||
}
|
||||
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(cdc *wire.Codec, txBytes []byte) (sdk.Tx, error) {
|
||||
var tx sdk.StdTx
|
||||
err := cdc.UnmarshalBinary(txBytes, &tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tx, nil
|
||||
}
|
||||
12
docs/apps.md
12
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.
|
||||
|
||||
@ -282,7 +282,7 @@ func NewHandler(am sdk.AccountMapper) sdk.Handler {
|
||||
### vs encoding/json
|
||||
### vs protobuf
|
||||
|
||||
## Dummy example
|
||||
## KVStore example
|
||||
|
||||
## Basecoin example
|
||||
|
||||
|
||||
741
docs/sdk/lcd-rest-api.yaml
Normal file
741
docs/sdk/lcd-rest-api.yaml
Normal file
@ -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
|
||||
@ -421,8 +421,8 @@ vs encoding/json
|
||||
vs protobuf
|
||||
~~~~~~~~~~~
|
||||
|
||||
Dummy example
|
||||
-------------
|
||||
KVStore example
|
||||
---------------
|
||||
|
||||
Basecoin example
|
||||
----------------
|
||||
|
||||
@ -1,19 +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_vendor_deps build test
|
||||
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: build get_vendor_deps test benchmark
|
||||
.PHONY: all build test benchmark
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -40,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"),
|
||||
}
|
||||
@ -53,13 +53,16 @@ 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)
|
||||
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 {
|
||||
@ -70,10 +73,11 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
|
||||
}
|
||||
|
||||
// custom tx codec
|
||||
func MakeTxCodec() *wire.Codec {
|
||||
func MakeCodec() *wire.Codec {
|
||||
cdc := wire.NewCodec()
|
||||
crypto.RegisterWire(cdc) // Register crypto.[PubKey,PrivKey,Signature] 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
|
||||
}
|
||||
|
||||
|
||||
@ -53,18 +53,27 @@ func TestSendMsg(t *testing.T) {
|
||||
Signature: sig,
|
||||
}})
|
||||
|
||||
// just marshal/unmarshal!
|
||||
cdc := MakeCodec()
|
||||
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) {
|
||||
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()
|
||||
@ -86,12 +95,19 @@ 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{})
|
||||
|
||||
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) {
|
||||
@ -127,6 +143,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{})
|
||||
|
||||
71
examples/basecoin/cmd/basecli/main.go
Normal file
71
examples/basecoin/cmd/basecli/main.go
Normal file
@ -0,0 +1,71 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
|
||||
"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"
|
||||
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
|
||||
var (
|
||||
basecliCmd = &cobra.Command{
|
||||
Use: "basecli",
|
||||
Short: "Basecoin light-client",
|
||||
}
|
||||
)
|
||||
|
||||
func todoNotImplemented(_ *cobra.Command, _ []string) error {
|
||||
return errors.New("TODO: Command not yet implemented")
|
||||
}
|
||||
|
||||
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, cdc)
|
||||
basecliCmd.AddCommand(client.LineBreak)
|
||||
|
||||
// add query/post commands (custom to binary)
|
||||
basecliCmd.AddCommand(
|
||||
client.GetCommands(
|
||||
authcmd.GetAccountCmd("main", cdc, types.GetParseAccount(cdc)),
|
||||
)...)
|
||||
basecliCmd.AddCommand(
|
||||
client.PostCommands(
|
||||
bankcmd.SendTxCmd(cdc),
|
||||
)...)
|
||||
|
||||
// add proxy, version and key info
|
||||
basecliCmd.AddCommand(
|
||||
client.LineBreak,
|
||||
lcd.ServeCommand(),
|
||||
keys.Commands(),
|
||||
client.LineBreak,
|
||||
version.VersionCmd,
|
||||
)
|
||||
|
||||
// prepare and add flags
|
||||
executor := cli.PrepareMainCmd(basecliCmd, "BC", os.ExpandEnv("$HOME/.basecli"))
|
||||
executor.Execute()
|
||||
}
|
||||
@ -1,27 +1,76 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"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"
|
||||
|
||||
"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
|
||||
// basecoindCmd is the entry point for this binary
|
||||
var (
|
||||
basecoindCmd = &cobra.Command{
|
||||
Use: "gaiad",
|
||||
Short: "Gaia Daemon (server)",
|
||||
}
|
||||
)
|
||||
|
||||
// TODO CREATE CLI
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "main")
|
||||
db, err := dbm.NewGoLevelDB("basecoind", "data")
|
||||
// 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 {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
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) {
|
||||
db, err := dbm.NewGoLevelDB("basecoin", rootDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bapp := app.NewBasecoinApp(logger, db)
|
||||
baseapp.RunForever(bapp)
|
||||
return bapp, nil
|
||||
}
|
||||
|
||||
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
|
||||
rootDir := os.ExpandEnv("$HOME/.basecoind")
|
||||
executor := cli.PrepareBaseCmd(basecoindCmd, "BC", rootDir)
|
||||
executor.Execute()
|
||||
}
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
package: github.com/cosmos/cosmos-sdk/examples/basecoin
|
||||
import:
|
||||
- package: github.com/cosmos/cosmos-sdk
|
||||
version: develop
|
||||
@ -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)
|
||||
@ -15,13 +16,22 @@ 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
|
||||
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
|
||||
|
||||
@ -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 <height>",
|
||||
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 <height>",
|
||||
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 <hash>",
|
||||
Short: "Matches this txhash over all committed blocks",
|
||||
RunE: todoNotImplemented,
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
@ -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 <name>",
|
||||
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 <name>",
|
||||
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 <name>",
|
||||
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 <name>",
|
||||
Short: "Delete the given key",
|
||||
RunE: todoNotImplemented,
|
||||
}
|
||||
cmd.Flags().StringP(flagPassword, "p", "", "Password of existing key to delete")
|
||||
return cmd
|
||||
}
|
||||
@ -1,75 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
)
|
||||
|
||||
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 <address>",
|
||||
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,
|
||||
VersionCmd,
|
||||
)
|
||||
|
||||
// prepare and add flags
|
||||
executor := cli.PrepareBaseCmd(gaiacliCmd, "GA", os.ExpandEnv("$HOME/.gaiacli"))
|
||||
executor.Execute()
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/baseapp"
|
||||
)
|
||||
|
||||
const (
|
||||
flagTo = "to"
|
||||
flagAmount = "amount"
|
||||
flagFee = "fee"
|
||||
)
|
||||
|
||||
// gaiadCmd is the entry point for this binary
|
||||
var (
|
||||
gaiadCmd = &cobra.Command{
|
||||
Use: "gaiad",
|
||||
Short: "Gaia Daemon (server)",
|
||||
}
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
AddNodeCommands(gaiadCmd, node)
|
||||
gaiadCmd.AddCommand(
|
||||
VersionCmd,
|
||||
)
|
||||
|
||||
// prepare and add flags
|
||||
executor := cli.PrepareBaseCmd(gaiadCmd, "GA", os.ExpandEnv("$HOME/.gaiad"))
|
||||
executor.Execute()
|
||||
}
|
||||
@ -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 <flags???>",
|
||||
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
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
@ -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
|
||||
@ -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 =")
|
||||
}
|
||||
76
glide.lock
generated
76
glide.lock
generated
@ -1,6 +1,8 @@
|
||||
hash: 2b4ad3bf1489a7cb5e62c6cb4c1fa976d4ae21993743e4968418c4e81925fb99
|
||||
updated: 2018-02-19T17:13:04.368106064Z
|
||||
hash: fa45c8a4f5512ed730f793b93d4876bdc604a1333a5a1f938c98a0f7dd55f22e
|
||||
updated: 2018-03-01T00:41:12.97082395-05:00
|
||||
imports:
|
||||
- name: github.com/bgentry/speakeasy
|
||||
version: 4aabc24848ce5fd31929f7d1e4ea74d3709c14cd
|
||||
- name: github.com/btcsuite/btcd
|
||||
version: 50de9da05b50eb15658bb350f6ea24368a111ab7
|
||||
subpackages:
|
||||
@ -9,6 +11,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 +44,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 +57,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
|
||||
@ -59,12 +67,16 @@ 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
|
||||
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 +109,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 +123,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/tmlibs
|
||||
version: a0f652dc2e131be86fc8d9e4e2beec9831a8a6ec
|
||||
- name: github.com/tendermint/tendermint
|
||||
version: c330b9e43c93351a5c3040333d7d0c7c27859a20
|
||||
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: 26f2ab65f82cfc6873c312e8030104c47c05f10e
|
||||
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
|
||||
|
||||
12
glide.yaml
12
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
|
||||
@ -25,6 +29,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
|
||||
|
||||
122
mock/app.go
Normal file
122
mock/app.go
Normal file
@ -0,0 +1,122 @@
|
||||
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(rootDir string, logger log.Logger) (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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
75
mock/app_test.go
Normal file
75
mock/app_test.go
Normal file
@ -0,0 +1,75 @@
|
||||
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)
|
||||
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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
27
mock/helpers.go
Normal file
27
mock/helpers.go
Normal file
@ -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(rootDir, logger)
|
||||
return app, cleanup, err
|
||||
}
|
||||
89
mock/tx.go
Normal file
89
mock/tx.go
Normal file
@ -0,0 +1,89 @@
|
||||
//nolint
|
||||
package mock
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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:
|
||||
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
|
||||
}
|
||||
182
server/init.go
Normal file
182
server/init.go
Normal file
@ -0,0 +1,182 @@
|
||||
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"
|
||||
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"
|
||||
)
|
||||
|
||||
// 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, logger log.Logger) *cobra.Command {
|
||||
cmd := initCmd{
|
||||
gen: gen,
|
||||
logger: logger,
|
||||
}
|
||||
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
|
||||
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
|
||||
config, err := tcmd.ParseConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.initTendermintFiles(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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 := 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("Generated 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("Generated genesis file", "path", genFile)
|
||||
}
|
||||
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 GenesisDoc
|
||||
err = json.Unmarshal(bz, &doc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
doc["app_state"] = 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(`{
|
||||
"accounts": [{
|
||||
"address": "%s",
|
||||
"coins": [
|
||||
{
|
||||
"denom": "%s",
|
||||
"amount": 9007199254740992
|
||||
}
|
||||
]
|
||||
}],
|
||||
"plugin_options": [
|
||||
"coin/issuer", {"app": "sigs", "addr": "%s"}%s
|
||||
]
|
||||
}`, addr, denom, addr, options)
|
||||
}
|
||||
36
server/init_test.go
Normal file
36
server/init_test.go
Normal file
@ -0,0 +1,36 @@
|
||||
package server
|
||||
|
||||
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)
|
||||
}
|
||||
31
server/reset.go
Normal file
31
server/reset.go
Normal file
@ -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
|
||||
}
|
||||
119
server/start.go
Normal file
119
server/start.go
Normal file
@ -0,0 +1,119 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"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/tendermint/tmlibs/log"
|
||||
)
|
||||
|
||||
const (
|
||||
flagWithTendermint = "with-tendermint"
|
||||
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 appGenerator, logger log.Logger) *cobra.Command {
|
||||
start := startCmd{
|
||||
app: app,
|
||||
logger: logger,
|
||||
}
|
||||
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 startCmd struct {
|
||||
app appGenerator
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func (s startCmd) run(cmd *cobra.Command, args []string) error {
|
||||
if !viper.GetBool(flagWithTendermint) {
|
||||
s.logger.Info("Starting ABCI without Tendermint")
|
||||
return s.startStandAlone()
|
||||
}
|
||||
s.logger.Info("Starting ABCI with Tendermint")
|
||||
return s.startInProcess()
|
||||
}
|
||||
|
||||
func (s startCmd) startStandAlone() error {
|
||||
// Generate the app in the proper dir
|
||||
addr := viper.GetString(flagAddress)
|
||||
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)
|
||||
}
|
||||
svr.SetLogger(s.logger.With("module", "abci-server"))
|
||||
svr.Start()
|
||||
|
||||
// Wait forever
|
||||
cmn.TrapSignal(func() {
|
||||
// Cleanup
|
||||
svr.Stop()
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s startCmd) startInProcess() error {
|
||||
cfg, err := tcmd.ParseConfig()
|
||||
if err != nil {
|
||||
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(app),
|
||||
node.DefaultGenesisDocProviderFunc(cfg),
|
||||
node.DefaultDBProvider,
|
||||
s.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
|
||||
}
|
||||
72
server/start_test.go
Normal file
72
server/start_test.go
Normal file
@ -0,0 +1,72 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/mock"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
)
|
||||
|
||||
func TestStartStandAlone(t *testing.T) {
|
||||
defer setupViper()()
|
||||
|
||||
logger := log.NewNopLogger()
|
||||
initCmd := InitCmd(mock.GenInitOptions, logger)
|
||||
err := initCmd.RunE(nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// set up app and start up
|
||||
viper.Set(flagWithTendermint, false)
|
||||
viper.Set(flagAddress, "localhost:11122")
|
||||
startCmd := StartCmd(mock.NewApp, 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)).
|
||||
With("module", "mock-cmd")
|
||||
// logger := log.NewNopLogger()
|
||||
initCmd := InitCmd(mock.GenInitOptions, logger)
|
||||
err := initCmd.RunE(nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// set up app and start up
|
||||
viper.Set(flagWithTendermint, true)
|
||||
startCmd := StartCmd(mock.NewApp, 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
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
69
tests/check_basecli.sh
Executable file
69
tests/check_basecli.sh
Executable file
@ -0,0 +1,69 @@
|
||||
#!/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....
|
||||
./build/basecli status | jq .latest_block_height
|
||||
sleep 2
|
||||
./build/basecli status | jq .latest_block_height
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
kill $PID_SERVER
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
14
tools/glide.lock
generated
14
tools/glide.lock
generated
@ -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: []
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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"`
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
@ -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"
|
||||
const Version = "0.11.0"
|
||||
|
||||
// GitCommit set by build flags
|
||||
var GitCommit = ""
|
||||
|
||||
82
x/auth/commands/account.go
Normal file
82
x/auth/commands/account.go
Normal file
@ -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"
|
||||
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 <address>",
|
||||
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
|
||||
}
|
||||
129
x/bank/commands/sendtx.go
Normal file
129
x/bank/commands/sendtx.go
Normal file
@ -0,0 +1,129 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"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"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||
)
|
||||
|
||||
const (
|
||||
flagTo = "to"
|
||||
flagAmount = "amount"
|
||||
flagFee = "fee"
|
||||
flagSequence = "seq"
|
||||
)
|
||||
|
||||
// SendTxCommand will create a send tx and sign it with the given key
|
||||
func SendTxCmd(cdc *wire.Codec) *cobra.Command {
|
||||
cmdr := commander{cdc}
|
||||
cmd := &cobra.Command{
|
||||
Use: "send",
|
||||
Short: "Create and sign a send tx",
|
||||
RunE: cmdr.sendTxCmd,
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
res, err := client.BroadcastTx(txBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c commander) buildTx() ([]byte, error) {
|
||||
keybase, err := keys.GetKeyBase()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name := viper.GetString(client.FlagName)
|
||||
info, err := keybase.Get(name)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("No key for: %s", name)
|
||||
}
|
||||
from := info.PubKey.Address()
|
||||
|
||||
msg, err := buildMsg(from)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// sign and build
|
||||
bz := msg.GetSignBytes()
|
||||
buf := client.BufferStdin()
|
||||
prompt := fmt.Sprintf("Password to sign with '%s':", name)
|
||||
passphrase, err := client.GetPassword(prompt, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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)
|
||||
|
||||
txBytes, err := c.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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package bank
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -10,6 +11,30 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
func TestNewSendMsg(t *testing.T) {}
|
||||
|
||||
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}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// TODO some failures for bad result
|
||||
assert.Equal(t, msg.Type(), "bank")
|
||||
}
|
||||
|
||||
func TestInputValidation(t *testing.T) {
|
||||
addr1 := crypto.Address([]byte{1, 2})
|
||||
addr2 := crypto.Address([]byte{7, 8})
|
||||
@ -97,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}}
|
||||
@ -165,8 +189,89 @@ 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()
|
||||
// TODO some failures for bad results
|
||||
assert.Equal(t, res, "SendMsg{[Input{696E707574,10atom}]->[Output{364637353734373037353734,10atom}]}")
|
||||
}
|
||||
|
||||
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("output")),
|
||||
Coins: sdk.Coins{{"atom", 10}},
|
||||
},
|
||||
},
|
||||
}
|
||||
res := msg.Get(nil)
|
||||
assert.Nil(t, res)
|
||||
}
|
||||
|
||||
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("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}]}]}`)
|
||||
}
|
||||
|
||||
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},
|
||||
@ -184,3 +289,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]")
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user