Merge pull request #185 from tendermint/feature/cleanup-state
Cleanup storage
This commit is contained in:
commit
761fb0d0c6
75
app/app.go
75
app/app.go
|
@ -5,11 +5,10 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
||||||
"github.com/tendermint/basecoin"
|
|
||||||
eyes "github.com/tendermint/merkleeyes/client"
|
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
|
"github.com/tendermint/basecoin"
|
||||||
"github.com/tendermint/basecoin/errors"
|
"github.com/tendermint/basecoin/errors"
|
||||||
"github.com/tendermint/basecoin/modules/auth"
|
"github.com/tendermint/basecoin/modules/auth"
|
||||||
"github.com/tendermint/basecoin/modules/base"
|
"github.com/tendermint/basecoin/modules/base"
|
||||||
|
@ -30,9 +29,10 @@ const (
|
||||||
|
|
||||||
// Basecoin - The ABCI application
|
// Basecoin - The ABCI application
|
||||||
type Basecoin struct {
|
type Basecoin struct {
|
||||||
eyesCli *eyes.Client
|
info *sm.ChainState
|
||||||
state *sm.State
|
|
||||||
cacheState *sm.State
|
state *Store
|
||||||
|
|
||||||
handler basecoin.Handler
|
handler basecoin.Handler
|
||||||
height uint64
|
height uint64
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
|
@ -41,15 +41,11 @@ type Basecoin struct {
|
||||||
var _ abci.Application = &Basecoin{}
|
var _ abci.Application = &Basecoin{}
|
||||||
|
|
||||||
// NewBasecoin - create a new instance of the basecoin application
|
// NewBasecoin - create a new instance of the basecoin application
|
||||||
func NewBasecoin(handler basecoin.Handler, eyesCli *eyes.Client, logger log.Logger) *Basecoin {
|
func NewBasecoin(handler basecoin.Handler, store *Store, logger log.Logger) *Basecoin {
|
||||||
state := sm.NewState(eyesCli, logger.With("module", "state"))
|
|
||||||
|
|
||||||
return &Basecoin{
|
return &Basecoin{
|
||||||
handler: handler,
|
handler: handler,
|
||||||
eyesCli: eyesCli,
|
info: sm.NewChainState(),
|
||||||
state: state,
|
state: store,
|
||||||
cacheState: nil,
|
|
||||||
height: 0,
|
|
||||||
logger: logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,23 +64,27 @@ func DefaultHandler(feeDenom string) basecoin.Handler {
|
||||||
stack.Recovery{},
|
stack.Recovery{},
|
||||||
auth.Signatures{},
|
auth.Signatures{},
|
||||||
base.Chain{},
|
base.Chain{},
|
||||||
|
stack.Checkpoint{OnCheck: true},
|
||||||
nonce.ReplayCheck{},
|
nonce.ReplayCheck{},
|
||||||
roles.NewMiddleware(),
|
roles.NewMiddleware(),
|
||||||
fee.NewSimpleFeeMiddleware(coin.Coin{feeDenom, 0}, fee.Bank),
|
fee.NewSimpleFeeMiddleware(coin.Coin{feeDenom, 0}, fee.Bank),
|
||||||
|
stack.Checkpoint{OnDeliver: true},
|
||||||
).Use(d)
|
).Use(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetState - XXX For testing, not thread safe!
|
// GetChainID returns the currently stored chain
|
||||||
func (app *Basecoin) GetState() *sm.State {
|
func (app *Basecoin) GetChainID() string {
|
||||||
return app.state.CacheWrap()
|
return app.info.GetChainID(app.state.Committed())
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetState is back... please kill me
|
||||||
|
func (app *Basecoin) GetState() sm.SimpleDB {
|
||||||
|
return app.state.Append()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info - ABCI
|
// Info - ABCI
|
||||||
func (app *Basecoin) Info() abci.ResponseInfo {
|
func (app *Basecoin) Info() abci.ResponseInfo {
|
||||||
resp, err := app.eyesCli.InfoSync()
|
resp := app.state.Info()
|
||||||
if err != nil {
|
|
||||||
cmn.PanicCrisis(err)
|
|
||||||
}
|
|
||||||
app.height = resp.LastBlockHeight
|
app.height = resp.LastBlockHeight
|
||||||
return abci.ResponseInfo{
|
return abci.ResponseInfo{
|
||||||
Data: fmt.Sprintf("Basecoin v%v", version.Version),
|
Data: fmt.Sprintf("Basecoin v%v", version.Version),
|
||||||
|
@ -97,16 +97,17 @@ func (app *Basecoin) Info() abci.ResponseInfo {
|
||||||
func (app *Basecoin) SetOption(key string, value string) string {
|
func (app *Basecoin) SetOption(key string, value string) string {
|
||||||
|
|
||||||
module, key := splitKey(key)
|
module, key := splitKey(key)
|
||||||
|
state := app.state.Append()
|
||||||
|
|
||||||
if module == ModuleNameBase {
|
if module == ModuleNameBase {
|
||||||
if key == ChainKey {
|
if key == ChainKey {
|
||||||
app.state.SetChainID(value)
|
app.info.SetChainID(state, value)
|
||||||
return "Success"
|
return "Success"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("Error: unknown base option: %s", key)
|
return fmt.Sprintf("Error: unknown base option: %s", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
log, err := app.handler.SetOption(app.logger, app.state, module, key, value)
|
log, err := app.handler.SetOption(app.logger, state, module, key, value)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return log
|
return log
|
||||||
}
|
}
|
||||||
|
@ -120,21 +121,16 @@ func (app *Basecoin) DeliverTx(txBytes []byte) abci.Result {
|
||||||
return errors.Result(err)
|
return errors.Result(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: can we abstract this setup and commit logic??
|
|
||||||
cache := app.state.CacheWrap()
|
|
||||||
ctx := stack.NewContext(
|
ctx := stack.NewContext(
|
||||||
app.state.GetChainID(),
|
app.GetChainID(),
|
||||||
app.height,
|
app.height,
|
||||||
app.logger.With("call", "delivertx"),
|
app.logger.With("call", "delivertx"),
|
||||||
)
|
)
|
||||||
res, err := app.handler.DeliverTx(ctx, cache, tx)
|
res, err := app.handler.DeliverTx(ctx, app.state.Append(), tx)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// discard the cache...
|
|
||||||
return errors.Result(err)
|
return errors.Result(err)
|
||||||
}
|
}
|
||||||
// commit the cache and return result
|
|
||||||
cache.CacheSync()
|
|
||||||
return res.ToABCI()
|
return res.ToABCI()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,22 +141,16 @@ func (app *Basecoin) CheckTx(txBytes []byte) abci.Result {
|
||||||
return errors.Result(err)
|
return errors.Result(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// we also need to discard error changes, so we don't increment checktx
|
|
||||||
// sequence on error, but not delivertx
|
|
||||||
cache := app.cacheState.CacheWrap()
|
|
||||||
ctx := stack.NewContext(
|
ctx := stack.NewContext(
|
||||||
app.state.GetChainID(),
|
app.GetChainID(),
|
||||||
app.height,
|
app.height,
|
||||||
app.logger.With("call", "checktx"),
|
app.logger.With("call", "checktx"),
|
||||||
)
|
)
|
||||||
// checktx generally shouldn't touch the state, but we don't care
|
res, err := app.handler.CheckTx(ctx, app.state.Check(), tx)
|
||||||
// here on the framework level, since the cacheState is thrown away next block
|
|
||||||
res, err := app.handler.CheckTx(ctx, cache, tx)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Result(err)
|
return errors.Result(err)
|
||||||
}
|
}
|
||||||
cache.CacheSync()
|
|
||||||
return res.ToABCI()
|
return res.ToABCI()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,24 +162,13 @@ func (app *Basecoin) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQu
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resQuery, err := app.eyesCli.QuerySync(reqQuery)
|
return app.state.Query(reqQuery)
|
||||||
if err != nil {
|
|
||||||
resQuery.Log = "Failed to query MerkleEyes: " + err.Error()
|
|
||||||
resQuery.Code = abci.CodeType_InternalError
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit - ABCI
|
// Commit - ABCI
|
||||||
func (app *Basecoin) Commit() (res abci.Result) {
|
func (app *Basecoin) Commit() (res abci.Result) {
|
||||||
|
|
||||||
// Commit state
|
// Commit state
|
||||||
res = app.state.Commit()
|
res = app.state.Commit()
|
||||||
|
|
||||||
// Wrap the committed state in cache for CheckTx
|
|
||||||
app.cacheState = app.state.CacheWrap()
|
|
||||||
|
|
||||||
if res.IsErr() {
|
if res.IsErr() {
|
||||||
cmn.PanicSanity("Error getting hash: " + res.Error())
|
cmn.PanicSanity("Error getting hash: " + res.Error())
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -18,7 +17,6 @@ import (
|
||||||
"github.com/tendermint/basecoin/stack"
|
"github.com/tendermint/basecoin/stack"
|
||||||
"github.com/tendermint/basecoin/state"
|
"github.com/tendermint/basecoin/state"
|
||||||
wire "github.com/tendermint/go-wire"
|
wire "github.com/tendermint/go-wire"
|
||||||
eyes "github.com/tendermint/merkleeyes/client"
|
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -82,14 +80,16 @@ func (at *appTest) reset() {
|
||||||
at.acctIn = coin.NewAccountWithKey(coin.Coins{{"mycoin", 7}})
|
at.acctIn = coin.NewAccountWithKey(coin.Coins{{"mycoin", 7}})
|
||||||
at.acctOut = coin.NewAccountWithKey(coin.Coins{{"mycoin", 7}})
|
at.acctOut = coin.NewAccountWithKey(coin.Coins{{"mycoin", 7}})
|
||||||
|
|
||||||
eyesCli := eyes.NewLocalClient("", 0)
|
// Note: switch logger if you want to get more info
|
||||||
// logger := log.TestingLogger().With("module", "app"),
|
logger := log.TestingLogger()
|
||||||
logger := log.NewTMLogger(os.Stdout).With("module", "app")
|
// logger := log.NewTracingLogger(log.NewTMLogger(os.Stdout))
|
||||||
logger = log.NewTracingLogger(logger)
|
store, err := NewStore("", 0, logger.With("module", "store"))
|
||||||
|
require.Nil(at.t, err, "%+v", err)
|
||||||
|
|
||||||
at.app = NewBasecoin(
|
at.app = NewBasecoin(
|
||||||
DefaultHandler("mycoin"),
|
DefaultHandler("mycoin"),
|
||||||
eyesCli,
|
store,
|
||||||
logger,
|
logger.With("module", "app"),
|
||||||
)
|
)
|
||||||
|
|
||||||
res := at.app.SetOption("base/chain_id", at.chainID)
|
res := at.app.SetOption("base/chain_id", at.chainID)
|
||||||
|
@ -102,13 +102,13 @@ func (at *appTest) reset() {
|
||||||
require.True(at.t, resabci.IsOK(), resabci)
|
require.True(at.t, resabci.IsOK(), resabci)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBalance(key basecoin.Actor, store state.KVStore) (coin.Coins, error) {
|
func getBalance(key basecoin.Actor, store state.SimpleDB) (coin.Coins, error) {
|
||||||
cspace := stack.PrefixedStore(coin.NameCoin, store)
|
cspace := stack.PrefixedStore(coin.NameCoin, store)
|
||||||
acct, err := coin.GetAccount(cspace, key)
|
acct, err := coin.GetAccount(cspace, key)
|
||||||
return acct.Coins, err
|
return acct.Coins, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAddr(addr []byte, state state.KVStore) (coin.Coins, error) {
|
func getAddr(addr []byte, state state.SimpleDB) (coin.Coins, error) {
|
||||||
actor := auth.SigPerm(addr)
|
actor := auth.SigPerm(addr)
|
||||||
return getBalance(actor, state)
|
return getBalance(actor, state)
|
||||||
}
|
}
|
||||||
|
@ -142,17 +142,20 @@ func TestSetOption(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
eyesCli := eyes.NewLocalClient("", 0)
|
logger := log.TestingLogger()
|
||||||
|
store, err := NewStore("", 0, logger.With("module", "store"))
|
||||||
|
require.Nil(err, "%+v", err)
|
||||||
|
|
||||||
app := NewBasecoin(
|
app := NewBasecoin(
|
||||||
DefaultHandler("atom"),
|
DefaultHandler("atom"),
|
||||||
eyesCli,
|
store,
|
||||||
log.TestingLogger().With("module", "app"),
|
logger.With("module", "app"),
|
||||||
)
|
)
|
||||||
|
|
||||||
//testing ChainID
|
//testing ChainID
|
||||||
chainID := "testChain"
|
chainID := "testChain"
|
||||||
res := app.SetOption("base/chain_id", chainID)
|
res := app.SetOption("base/chain_id", chainID)
|
||||||
assert.EqualValues(app.GetState().GetChainID(), chainID)
|
assert.EqualValues(app.GetChainID(), chainID)
|
||||||
assert.EqualValues(res, "Success")
|
assert.EqualValues(res, "Success")
|
||||||
|
|
||||||
// make a nice account...
|
// make a nice account...
|
||||||
|
@ -162,7 +165,7 @@ func TestSetOption(t *testing.T) {
|
||||||
require.EqualValues(res, "Success")
|
require.EqualValues(res, "Success")
|
||||||
|
|
||||||
// make sure it is set correctly, with some balance
|
// make sure it is set correctly, with some balance
|
||||||
coins, err := getBalance(acct.Actor(), app.state)
|
coins, err := getBalance(acct.Actor(), app.GetState())
|
||||||
require.Nil(err)
|
require.Nil(err)
|
||||||
assert.Equal(bal, coins)
|
assert.Equal(bal, coins)
|
||||||
|
|
||||||
|
@ -189,7 +192,7 @@ func TestSetOption(t *testing.T) {
|
||||||
res = app.SetOption("coin/account", unsortAcc)
|
res = app.SetOption("coin/account", unsortAcc)
|
||||||
require.EqualValues(res, "Success")
|
require.EqualValues(res, "Success")
|
||||||
|
|
||||||
coins, err = getAddr(unsortAddr, app.state)
|
coins, err = getAddr(unsortAddr, app.GetState())
|
||||||
require.Nil(err)
|
require.Nil(err)
|
||||||
assert.True(coins.IsValid())
|
assert.True(coins.IsValid())
|
||||||
assert.Equal(unsortCoins, coins)
|
assert.Equal(unsortCoins, coins)
|
||||||
|
@ -213,6 +216,8 @@ func TestTx(t *testing.T) {
|
||||||
//Bad Balance
|
//Bad Balance
|
||||||
at.acctIn.Coins = coin.Coins{{"mycoin", 2}}
|
at.acctIn.Coins = coin.Coins{{"mycoin", 2}}
|
||||||
at.initAccount(at.acctIn)
|
at.initAccount(at.acctIn)
|
||||||
|
at.app.Commit()
|
||||||
|
|
||||||
res, _, _ := at.exec(t, at.getTx(coin.Coins{{"mycoin", 5}}, 1), true)
|
res, _, _ := at.exec(t, at.getTx(coin.Coins{{"mycoin", 5}}, 1), true)
|
||||||
assert.True(res.IsErr(), "ExecTx/Bad CheckTx: Expected error return from ExecTx, returned: %v", res)
|
assert.True(res.IsErr(), "ExecTx/Bad CheckTx: Expected error return from ExecTx, returned: %v", res)
|
||||||
res, diffIn, diffOut := at.exec(t, at.getTx(coin.Coins{{"mycoin", 5}}, 1), false)
|
res, diffIn, diffOut := at.exec(t, at.getTx(coin.Coins{{"mycoin", 5}}, 1), false)
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
eyescli "github.com/tendermint/merkleeyes/client"
|
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
|
@ -19,27 +18,32 @@ const genesisFilepath = "./testdata/genesis.json"
|
||||||
const genesisAcctFilepath = "./testdata/genesis2.json"
|
const genesisAcctFilepath = "./testdata/genesis2.json"
|
||||||
|
|
||||||
func TestLoadGenesisDoNotFailIfAppOptionsAreMissing(t *testing.T) {
|
func TestLoadGenesisDoNotFailIfAppOptionsAreMissing(t *testing.T) {
|
||||||
eyesCli := eyescli.NewLocalClient("", 0)
|
logger := log.TestingLogger()
|
||||||
app := NewBasecoin(DefaultHandler("mycoin"), eyesCli, log.TestingLogger())
|
store, err := NewStore("", 0, logger)
|
||||||
err := app.LoadGenesis("./testdata/genesis3.json")
|
require.Nil(t, err, "%+v", err)
|
||||||
|
app := NewBasecoin(DefaultHandler("mycoin"), store, logger)
|
||||||
|
err = app.LoadGenesis("./testdata/genesis3.json")
|
||||||
require.Nil(t, err, "%+v", err)
|
require.Nil(t, err, "%+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadGenesis(t *testing.T) {
|
func TestLoadGenesis(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
|
||||||
eyesCli := eyescli.NewLocalClient("", 0)
|
logger := log.TestingLogger()
|
||||||
app := NewBasecoin(DefaultHandler("mycoin"), eyesCli, log.TestingLogger())
|
store, err := NewStore("", 0, logger)
|
||||||
err := app.LoadGenesis(genesisFilepath)
|
require.Nil(err, "%+v", err)
|
||||||
|
|
||||||
|
app := NewBasecoin(DefaultHandler("mycoin"), store, logger)
|
||||||
|
err = app.LoadGenesis(genesisFilepath)
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
|
|
||||||
// check the chain id
|
// check the chain id
|
||||||
assert.Equal("foo_bar_chain", app.GetState().GetChainID())
|
assert.Equal("foo_bar_chain", app.GetChainID())
|
||||||
|
|
||||||
// and check the account info - previously calculated values
|
// and check the account info - previously calculated values
|
||||||
addr, _ := hex.DecodeString("eb98e0688217cfdeb70eddf4b33cdcc37fc53197")
|
addr, _ := hex.DecodeString("eb98e0688217cfdeb70eddf4b33cdcc37fc53197")
|
||||||
|
|
||||||
coins, err := getAddr(addr, app.state)
|
coins, err := getAddr(addr, app.GetState())
|
||||||
require.Nil(err)
|
require.Nil(err)
|
||||||
assert.True(coins.IsPositive())
|
assert.True(coins.IsPositive())
|
||||||
|
|
||||||
|
@ -57,13 +61,16 @@ func TestLoadGenesis(t *testing.T) {
|
||||||
func TestLoadGenesisAccountAddress(t *testing.T) {
|
func TestLoadGenesisAccountAddress(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
|
||||||
eyesCli := eyescli.NewLocalClient("", 0)
|
logger := log.TestingLogger()
|
||||||
app := NewBasecoin(DefaultHandler("mycoin"), eyesCli, log.TestingLogger())
|
store, err := NewStore("", 0, logger)
|
||||||
err := app.LoadGenesis(genesisAcctFilepath)
|
require.Nil(err, "%+v", err)
|
||||||
|
|
||||||
|
app := NewBasecoin(DefaultHandler("mycoin"), store, logger)
|
||||||
|
err = app.LoadGenesis(genesisAcctFilepath)
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
|
|
||||||
// check the chain id
|
// check the chain id
|
||||||
assert.Equal("addr_accounts_chain", app.GetState().GetChainID())
|
assert.Equal("addr_accounts_chain", app.GetChainID())
|
||||||
|
|
||||||
// make sure the accounts were set properly
|
// make sure the accounts were set properly
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
|
@ -86,7 +93,7 @@ func TestLoadGenesisAccountAddress(t *testing.T) {
|
||||||
for i, tc := range cases {
|
for i, tc := range cases {
|
||||||
addr, err := hex.DecodeString(tc.addr)
|
addr, err := hex.DecodeString(tc.addr)
|
||||||
require.Nil(err, tc.addr)
|
require.Nil(err, tc.addr)
|
||||||
coins, err := getAddr(addr, app.state)
|
coins, err := getAddr(addr, app.GetState())
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
if !tc.exists {
|
if !tc.exists {
|
||||||
assert.True(coins.IsZero(), "%d", i)
|
assert.True(coins.IsZero(), "%d", i)
|
||||||
|
|
|
@ -0,0 +1,189 @@
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
|
"github.com/tendermint/go-wire"
|
||||||
|
"github.com/tendermint/merkleeyes/iavl"
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
|
"github.com/tendermint/basecoin/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Store contains the merkle tree, and all info to handle abci requests
|
||||||
|
type Store struct {
|
||||||
|
state.State
|
||||||
|
height uint64
|
||||||
|
hash []byte
|
||||||
|
persisted bool
|
||||||
|
|
||||||
|
logger log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
var stateKey = []byte("merkle:state") // Database key for merkle tree save value db values
|
||||||
|
|
||||||
|
// ChainState contains the latest Merkle root hash and the number of times `Commit` has been called
|
||||||
|
type ChainState struct {
|
||||||
|
Hash []byte
|
||||||
|
Height uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStore initializes an in-memory IAVLTree, or attempts to load a persistant
|
||||||
|
// tree from disk
|
||||||
|
func NewStore(dbName string, cacheSize int, logger log.Logger) (*Store, error) {
|
||||||
|
// start at 1 so the height returned by query is for the
|
||||||
|
// next block, ie. the one that includes the AppHash for our current state
|
||||||
|
initialHeight := uint64(1)
|
||||||
|
|
||||||
|
// Non-persistent case
|
||||||
|
if dbName == "" {
|
||||||
|
tree := iavl.NewIAVLTree(
|
||||||
|
0,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
store := &Store{
|
||||||
|
State: state.NewState(tree, false),
|
||||||
|
height: initialHeight,
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
return store, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expand the path fully
|
||||||
|
dbPath, err := filepath.Abs(dbName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "Invalid Database Name")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some external calls accidently add a ".db", which is now removed
|
||||||
|
dbPath = strings.TrimSuffix(dbPath, path.Ext(dbPath))
|
||||||
|
|
||||||
|
// Split the database name into it's components (dir, name)
|
||||||
|
dir := path.Dir(dbPath)
|
||||||
|
name := path.Base(dbPath)
|
||||||
|
|
||||||
|
// Make sure the path exists
|
||||||
|
empty, _ := cmn.IsDirEmpty(dbPath + ".db")
|
||||||
|
|
||||||
|
// Open database called "dir/name.db", if it doesn't exist it will be created
|
||||||
|
db := dbm.NewDB(name, dbm.LevelDBBackendStr, dir)
|
||||||
|
tree := iavl.NewIAVLTree(cacheSize, db)
|
||||||
|
|
||||||
|
var chainState ChainState
|
||||||
|
if empty {
|
||||||
|
logger.Info("no existing db, creating new db")
|
||||||
|
chainState = ChainState{
|
||||||
|
Hash: tree.Save(),
|
||||||
|
Height: initialHeight,
|
||||||
|
}
|
||||||
|
db.Set(stateKey, wire.BinaryBytes(chainState))
|
||||||
|
} else {
|
||||||
|
logger.Info("loading existing db")
|
||||||
|
eyesStateBytes := db.Get(stateKey)
|
||||||
|
err = wire.ReadBinaryBytes(eyesStateBytes, &chainState)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "Reading MerkleEyesState")
|
||||||
|
}
|
||||||
|
tree.Load(chainState.Hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
res := &Store{
|
||||||
|
State: state.NewState(tree, true),
|
||||||
|
height: chainState.Height,
|
||||||
|
hash: chainState.Hash,
|
||||||
|
persisted: true,
|
||||||
|
logger: logger,
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info implements abci.Application. It returns the height, hash and size (in the data).
|
||||||
|
// The height is the block that holds the transactions, not the apphash itself.
|
||||||
|
func (s *Store) Info() abci.ResponseInfo {
|
||||||
|
s.logger.Info("Info synced",
|
||||||
|
"height", s.height,
|
||||||
|
"hash", fmt.Sprintf("%X", s.hash))
|
||||||
|
return abci.ResponseInfo{
|
||||||
|
Data: cmn.Fmt("size:%v", s.State.Committed().Size()),
|
||||||
|
LastBlockHeight: s.height - 1,
|
||||||
|
LastBlockAppHash: s.hash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit implements abci.Application
|
||||||
|
func (s *Store) Commit() abci.Result {
|
||||||
|
var err error
|
||||||
|
s.height++
|
||||||
|
s.hash, err = s.State.Hash()
|
||||||
|
if err != nil {
|
||||||
|
return abci.NewError(abci.CodeType_InternalError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logger.Debug("Commit synced",
|
||||||
|
"height", s.height,
|
||||||
|
"hash", fmt.Sprintf("%X", s.hash))
|
||||||
|
|
||||||
|
s.State.BatchSet(stateKey, wire.BinaryBytes(ChainState{
|
||||||
|
Hash: s.hash,
|
||||||
|
Height: s.height,
|
||||||
|
}))
|
||||||
|
|
||||||
|
hash, err := s.State.Commit()
|
||||||
|
if err != nil {
|
||||||
|
return abci.NewError(abci.CodeType_InternalError, err.Error())
|
||||||
|
}
|
||||||
|
if !bytes.Equal(hash, s.hash) {
|
||||||
|
return abci.NewError(abci.CodeType_InternalError, "AppHash is incorrect")
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.State.Committed().Size() == 0 {
|
||||||
|
return abci.NewResultOK(nil, "Empty hash for empty tree")
|
||||||
|
}
|
||||||
|
return abci.NewResultOK(s.hash, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query implements abci.Application
|
||||||
|
func (s *Store) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
|
||||||
|
|
||||||
|
if reqQuery.Height != 0 {
|
||||||
|
// TODO: support older commits
|
||||||
|
resQuery.Code = abci.CodeType_InternalError
|
||||||
|
resQuery.Log = "merkleeyes only supports queries on latest commit"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the query response height to current
|
||||||
|
resQuery.Height = s.height
|
||||||
|
|
||||||
|
tree := s.State.Committed()
|
||||||
|
|
||||||
|
switch reqQuery.Path {
|
||||||
|
case "/store", "/key": // Get by key
|
||||||
|
key := reqQuery.Data // Data holds the key bytes
|
||||||
|
resQuery.Key = key
|
||||||
|
if reqQuery.Prove {
|
||||||
|
value, proof, exists := tree.Proof(key)
|
||||||
|
if !exists {
|
||||||
|
resQuery.Log = "Key not found"
|
||||||
|
}
|
||||||
|
resQuery.Value = value
|
||||||
|
resQuery.Proof = proof
|
||||||
|
} else {
|
||||||
|
value := tree.Get(key)
|
||||||
|
resQuery.Value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
resQuery.Code = abci.CodeType_UnknownRequest
|
||||||
|
resQuery.Log = cmn.Fmt("Unexpected Query path: %v", reqQuery.Path)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
|
@ -6,8 +6,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
wire "github.com/tendermint/go-wire"
|
wire "github.com/tendermint/go-wire"
|
||||||
eyesApp "github.com/tendermint/merkleeyes/app"
|
|
||||||
eyes "github.com/tendermint/merkleeyes/client"
|
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
|
@ -56,18 +54,22 @@ func NewBenchApp(h basecoin.Handler, chainID string, n int,
|
||||||
// logger = log.NewTracingLogger(logger)
|
// logger = log.NewTracingLogger(logger)
|
||||||
|
|
||||||
// TODO: disk writing
|
// TODO: disk writing
|
||||||
var eyesCli *eyes.Client
|
var store *app.Store
|
||||||
|
var err error
|
||||||
|
|
||||||
if persist {
|
if persist {
|
||||||
tmpDir, _ := ioutil.TempDir("", "bc-app-benchmark")
|
tmpDir, _ := ioutil.TempDir("", "bc-app-benchmark")
|
||||||
eyesCli = eyes.NewLocalClient(tmpDir, 500)
|
store, err = app.NewStore(tmpDir, 500, logger)
|
||||||
} else {
|
} else {
|
||||||
eyesCli = eyes.NewLocalClient("", 0)
|
store, err = app.NewStore("", 0, logger)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
eyesApp.SetLogger(logger.With("module", "merkle"))
|
|
||||||
|
|
||||||
app := app.NewBasecoin(
|
app := app.NewBasecoin(
|
||||||
h,
|
h,
|
||||||
eyesCli,
|
store,
|
||||||
logger.With("module", "app"),
|
logger.With("module", "app"),
|
||||||
)
|
)
|
||||||
res := app.SetOption("base/chain_id", chainID)
|
res := app.SetOption("base/chain_id", chainID)
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
BenchmarkMakeTx-4 2000 603153 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-10-nofee-memdb-4 5000 313154 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-10-fee-memdb-4 5000 366534 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-200-nofee-memdb-4 5000 296381 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-200-fee-memdb-4 5000 350973 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-10-nofee-memdb-4 5000 351425 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-10-fee-memdb-4 3000 410855 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-200-nofee-memdb-4 5000 344839 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-200-fee-memdb-4 5000 394080 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-10-nofee-persist-4 3000 433890 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-10-fee-persist-4 3000 496133 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-200-nofee-persist-4 5000 310174 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-200-fee-persist-4 5000 366868 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-10-nofee-persist-4 2000 815755 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-10-fee-persist-4 2000 874532 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-200-nofee-persist-4 5000 567349 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-200-fee-persist-4 5000 621833 ns/op
|
||||||
|
PASS
|
||||||
|
ok github.com/tendermint/basecoin/benchmarks 93.047s
|
|
@ -0,0 +1,19 @@
|
||||||
|
BenchmarkMakeTx-4 2000 648379 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-10-nofee-memdb-4 5000 356487 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-10-fee-memdb-4 5000 413435 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-200-nofee-memdb-4 5000 321859 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-200-fee-memdb-4 5000 393578 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-10-nofee-memdb-4 5000 379129 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-10-fee-memdb-4 3000 480334 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-200-nofee-memdb-4 5000 384398 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-200-fee-memdb-4 3000 443481 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-10-nofee-persist-4 3000 498460 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-10-fee-persist-4 3000 559034 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-200-nofee-persist-4 5000 314090 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-200-fee-persist-4 5000 397457 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-10-nofee-persist-4 2000 845872 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-10-fee-persist-4 2000 929205 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-200-nofee-persist-4 5000 596601 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-200-fee-persist-4 5000 667093 ns/op
|
||||||
|
PASS
|
||||||
|
ok github.com/tendermint/basecoin/benchmarks 97.097s
|
|
@ -0,0 +1,19 @@
|
||||||
|
BenchmarkMakeTx-4 2000 660064 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-10-nofee-memdb-4 5000 338378 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-10-fee-memdb-4 5000 380171 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-200-nofee-memdb-4 5000 306365 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-200-fee-memdb-4 5000 359344 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-10-nofee-memdb-4 5000 366057 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-10-fee-memdb-4 3000 433549 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-200-nofee-memdb-4 5000 351662 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-200-fee-memdb-4 3000 421573 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-10-nofee-persist-4 3000 479848 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-10-fee-persist-4 3000 544164 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-200-nofee-persist-4 5000 327999 ns/op
|
||||||
|
BenchmarkSimpleTransfer/100-200-fee-persist-4 5000 385751 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-10-nofee-persist-4 2000 852128 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-10-fee-persist-4 2000 1055130 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-200-nofee-persist-4 5000 642872 ns/op
|
||||||
|
BenchmarkSimpleTransfer/10000-200-fee-persist-4 3000 686337 ns/op
|
||||||
|
PASS
|
||||||
|
ok github.com/tendermint/basecoin/benchmarks 91.717s
|
|
@ -11,8 +11,6 @@ import (
|
||||||
|
|
||||||
"github.com/tendermint/abci/server"
|
"github.com/tendermint/abci/server"
|
||||||
"github.com/tendermint/basecoin"
|
"github.com/tendermint/basecoin"
|
||||||
eyesApp "github.com/tendermint/merkleeyes/app"
|
|
||||||
eyes "github.com/tendermint/merkleeyes/client"
|
|
||||||
"github.com/tendermint/tmlibs/cli"
|
"github.com/tendermint/tmlibs/cli"
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
|
|
||||||
|
@ -37,7 +35,6 @@ const EyesCacheSize = 10000
|
||||||
//nolint
|
//nolint
|
||||||
const (
|
const (
|
||||||
FlagAddress = "address"
|
FlagAddress = "address"
|
||||||
FlagEyes = "eyes"
|
|
||||||
FlagWithoutTendermint = "without-tendermint"
|
FlagWithoutTendermint = "without-tendermint"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -50,7 +47,6 @@ var (
|
||||||
func init() {
|
func init() {
|
||||||
flags := StartCmd.Flags()
|
flags := StartCmd.Flags()
|
||||||
flags.String(FlagAddress, "tcp://0.0.0.0:46658", "Listen address")
|
flags.String(FlagAddress, "tcp://0.0.0.0:46658", "Listen address")
|
||||||
flags.String(FlagEyes, "local", "MerkleEyes address, or 'local' for embedded")
|
|
||||||
flags.Bool(FlagWithoutTendermint, false, "Only run basecoin abci app, assume external tendermint process")
|
flags.Bool(FlagWithoutTendermint, false, "Only run basecoin abci app, assume external tendermint process")
|
||||||
// add all standard 'tendermint node' flags
|
// add all standard 'tendermint node' flags
|
||||||
tcmd.AddNodeFlags(StartCmd)
|
tcmd.AddNodeFlags(StartCmd)
|
||||||
|
@ -58,27 +54,22 @@ func init() {
|
||||||
|
|
||||||
func startCmd(cmd *cobra.Command, args []string) error {
|
func startCmd(cmd *cobra.Command, args []string) error {
|
||||||
rootDir := viper.GetString(cli.HomeFlag)
|
rootDir := viper.GetString(cli.HomeFlag)
|
||||||
meyes := viper.GetString(FlagEyes)
|
|
||||||
|
|
||||||
// Connect to MerkleEyes
|
store, err := app.NewStore(
|
||||||
var eyesCli *eyes.Client
|
path.Join(rootDir, "data", "merkleeyes.db"),
|
||||||
if meyes == "local" {
|
EyesCacheSize,
|
||||||
eyesApp.SetLogger(logger.With("module", "merkleeyes"))
|
logger.With("module", "store"),
|
||||||
eyesCli = eyes.NewLocalClient(path.Join(rootDir, "data", "merkleeyes.db"), EyesCacheSize)
|
)
|
||||||
} else {
|
|
||||||
var err error
|
|
||||||
eyesCli, err = eyes.NewClient(meyes)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Errorf("Error connecting to MerkleEyes: %v\n", err)
|
return err
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create Basecoin app
|
// Create Basecoin app
|
||||||
basecoinApp := app.NewBasecoin(Handler, eyesCli, logger.With("module", "app"))
|
basecoinApp := app.NewBasecoin(Handler, store, logger.With("module", "app"))
|
||||||
|
|
||||||
// if chain_id has not been set yet, load the genesis.
|
// if chain_id has not been set yet, load the genesis.
|
||||||
// else, assume it's been loaded
|
// else, assume it's been loaded
|
||||||
if basecoinApp.GetState().GetChainID() == "" {
|
if basecoinApp.GetChainID() == "" {
|
||||||
// If genesis file exists, set key-value options
|
// If genesis file exists, set key-value options
|
||||||
genesisFile := path.Join(rootDir, "genesis.json")
|
genesisFile := path.Join(rootDir, "genesis.json")
|
||||||
if _, err := os.Stat(genesisFile); err == nil {
|
if _, err := os.Stat(genesisFile); err == nil {
|
||||||
|
@ -91,7 +82,7 @@ func startCmd(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
chainID := basecoinApp.GetState().GetChainID()
|
chainID := basecoinApp.GetChainID()
|
||||||
if viper.GetBool(FlagWithoutTendermint) {
|
if viper.GetBool(FlagWithoutTendermint) {
|
||||||
logger.Info("Starting Basecoin without Tendermint", "chain_id", chainID)
|
logger.Info("Starting Basecoin without Tendermint", "chain_id", chainID)
|
||||||
// run just the abci app/server
|
// run just the abci app/server
|
||||||
|
|
|
@ -102,8 +102,10 @@ func NewHandler(feeDenom string) basecoin.Handler {
|
||||||
stack.Recovery{},
|
stack.Recovery{},
|
||||||
auth.Signatures{},
|
auth.Signatures{},
|
||||||
base.Chain{},
|
base.Chain{},
|
||||||
|
stack.Checkpoint{OnCheck: true},
|
||||||
nonce.ReplayCheck{},
|
nonce.ReplayCheck{},
|
||||||
fee.NewSimpleFeeMiddleware(coin.Coin{feeDenom, 0}, fee.Bank),
|
fee.NewSimpleFeeMiddleware(coin.Coin{feeDenom, 0}, fee.Bank),
|
||||||
|
stack.Checkpoint{OnDeliver: true},
|
||||||
).Use(dispatcher)
|
).Use(dispatcher)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,13 +125,13 @@ func (Handler) Name() string {
|
||||||
func (Handler) AssertDispatcher() {}
|
func (Handler) AssertDispatcher() {}
|
||||||
|
|
||||||
// CheckTx checks if the tx is properly structured
|
// CheckTx checks if the tx is properly structured
|
||||||
func (h Handler) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, _ basecoin.Checker) (res basecoin.Result, err error) {
|
func (h Handler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, _ basecoin.Checker) (res basecoin.Result, err error) {
|
||||||
_, err = checkTx(ctx, tx)
|
_, err = checkTx(ctx, tx)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx executes the tx if valid
|
// DeliverTx executes the tx if valid
|
||||||
func (h Handler) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, dispatch basecoin.Deliver) (res basecoin.Result, err error) {
|
func (h Handler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, dispatch basecoin.Deliver) (res basecoin.Result, err error) {
|
||||||
ctr, err := checkTx(ctx, tx)
|
ctr, err := checkTx(ctx, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
@ -201,7 +203,7 @@ func StateKey() []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadState - retrieve the counter state from the store
|
// LoadState - retrieve the counter state from the store
|
||||||
func LoadState(store state.KVStore) (state State, err error) {
|
func LoadState(store state.SimpleDB) (state State, err error) {
|
||||||
bytes := store.Get(StateKey())
|
bytes := store.Get(StateKey())
|
||||||
if len(bytes) > 0 {
|
if len(bytes) > 0 {
|
||||||
err = wire.ReadBinaryBytes(bytes, &state)
|
err = wire.ReadBinaryBytes(bytes, &state)
|
||||||
|
@ -213,7 +215,7 @@ func LoadState(store state.KVStore) (state State, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveState - save the counter state to the provided store
|
// SaveState - save the counter state to the provided store
|
||||||
func SaveState(store state.KVStore, state State) error {
|
func SaveState(store state.SimpleDB, state State) error {
|
||||||
bytes := wire.BinaryBytes(state)
|
bytes := wire.BinaryBytes(state)
|
||||||
store.Set(StateKey(), bytes)
|
store.Set(StateKey(), bytes)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -1,37 +1,40 @@
|
||||||
package counter
|
package counter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
||||||
|
"github.com/tendermint/go-wire"
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin"
|
"github.com/tendermint/basecoin"
|
||||||
"github.com/tendermint/basecoin/app"
|
"github.com/tendermint/basecoin/app"
|
||||||
"github.com/tendermint/basecoin/modules/auth"
|
"github.com/tendermint/basecoin/modules/auth"
|
||||||
"github.com/tendermint/basecoin/modules/base"
|
"github.com/tendermint/basecoin/modules/base"
|
||||||
"github.com/tendermint/basecoin/modules/coin"
|
"github.com/tendermint/basecoin/modules/coin"
|
||||||
"github.com/tendermint/basecoin/modules/nonce"
|
"github.com/tendermint/basecoin/modules/nonce"
|
||||||
"github.com/tendermint/go-wire"
|
|
||||||
eyescli "github.com/tendermint/merkleeyes/client"
|
|
||||||
"github.com/tendermint/tmlibs/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCounterPlugin(t *testing.T) {
|
func TestCounterPlugin(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
// Basecoin initialization
|
// Basecoin initialization
|
||||||
eyesCli := eyescli.NewLocalClient("", 0)
|
|
||||||
chainID := "test_chain_id"
|
chainID := "test_chain_id"
|
||||||
|
logger := log.TestingLogger()
|
||||||
|
// logger := log.NewTracingLogger(log.NewTMLogger(os.Stdout))
|
||||||
|
|
||||||
// logger := log.TestingLogger().With("module", "app"),
|
store, err := app.NewStore("", 0, logger.With("module", "store"))
|
||||||
logger := log.NewTMLogger(os.Stdout).With("module", "app")
|
require.Nil(err, "%+v", err)
|
||||||
// logger = log.NewTracingLogger(logger)
|
|
||||||
|
h := NewHandler("gold")
|
||||||
bcApp := app.NewBasecoin(
|
bcApp := app.NewBasecoin(
|
||||||
NewHandler("gold"),
|
h,
|
||||||
eyesCli,
|
store,
|
||||||
logger,
|
logger.With("module", "app"),
|
||||||
)
|
)
|
||||||
bcApp.SetOption("base/chain_id", chainID)
|
bcApp.SetOption("base/chain_id", chainID)
|
||||||
|
|
||||||
|
@ -39,7 +42,7 @@ func TestCounterPlugin(t *testing.T) {
|
||||||
bal := coin.Coins{{"", 1000}, {"gold", 1000}}
|
bal := coin.Coins{{"", 1000}, {"gold", 1000}}
|
||||||
acct := coin.NewAccountWithKey(bal)
|
acct := coin.NewAccountWithKey(bal)
|
||||||
log := bcApp.SetOption("coin/account", acct.MakeOption())
|
log := bcApp.SetOption("coin/account", acct.MakeOption())
|
||||||
require.Equal(t, "Success", log)
|
require.Equal("Success", log)
|
||||||
|
|
||||||
// Deliver a CounterTx
|
// Deliver a CounterTx
|
||||||
DeliverCounterTx := func(valid bool, counterFee coin.Coins, sequence uint32) abci.Result {
|
DeliverCounterTx := func(valid bool, counterFee coin.Coins, sequence uint32) abci.Result {
|
||||||
|
@ -60,11 +63,15 @@ func TestCounterPlugin(t *testing.T) {
|
||||||
res = DeliverCounterTx(false, nil, 2)
|
res = DeliverCounterTx(false, nil, 2)
|
||||||
assert.True(res.IsErr(), res.String())
|
assert.True(res.IsErr(), res.String())
|
||||||
|
|
||||||
// Test an invalid send, with supported fee
|
// Test an invalid sequence
|
||||||
res = DeliverCounterTx(true, coin.Coins{{"gold", 100}}, 2)
|
res = DeliverCounterTx(true, nil, 2)
|
||||||
|
assert.True(res.IsErr(), res.String())
|
||||||
|
|
||||||
|
// Test an valid send, with supported fee
|
||||||
|
res = DeliverCounterTx(true, coin.Coins{{"gold", 100}}, 3)
|
||||||
assert.True(res.IsOK(), res.String())
|
assert.True(res.IsOK(), res.String())
|
||||||
|
|
||||||
// Test unsupported fee
|
// Test unsupported fee
|
||||||
res = DeliverCounterTx(true, coin.Coins{{"silver", 100}}, 3)
|
res = DeliverCounterTx(true, coin.Coins{{"silver", 100}}, 4)
|
||||||
assert.True(res.IsErr(), res.String())
|
assert.True(res.IsErr(), res.String())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
hash: 8c438edb7d269da439141e62f3e0c931fa9efaee54b13ce1e7330dc99179fddd
|
hash: 45eed61138603d4d03518ea822068cf32b45d0a219bb7f3b836e52129f2a3a2b
|
||||||
updated: 2017-07-20T15:39:36.659717024+02:00
|
updated: 2017-07-26T19:44:39.753066441-04:00
|
||||||
imports:
|
imports:
|
||||||
- name: github.com/bgentry/speakeasy
|
- name: github.com/bgentry/speakeasy
|
||||||
version: 4aabc24848ce5fd31929f7d1e4ea74d3709c14cd
|
version: 4aabc24848ce5fd31929f7d1e4ea74d3709c14cd
|
||||||
|
@ -133,16 +133,15 @@ imports:
|
||||||
- data
|
- data
|
||||||
- data/base58
|
- data/base58
|
||||||
- name: github.com/tendermint/light-client
|
- name: github.com/tendermint/light-client
|
||||||
version: d63415027075bc5d74a98a718393b59b5c4279a5
|
version: 1c53d04dcc65c2fd15526152ed0651af10a09982
|
||||||
subpackages:
|
subpackages:
|
||||||
- certifiers
|
- certifiers
|
||||||
- certifiers/client
|
- certifiers/client
|
||||||
- certifiers/files
|
- certifiers/files
|
||||||
- proofs
|
- proofs
|
||||||
- name: github.com/tendermint/merkleeyes
|
- name: github.com/tendermint/merkleeyes
|
||||||
version: 102aaf5a8ffda1846413fb22805a94def2045b9f
|
version: 0310013053953eef80def3619aeb1e3a3254f452
|
||||||
subpackages:
|
subpackages:
|
||||||
- app
|
|
||||||
- client
|
- client
|
||||||
- iavl
|
- iavl
|
||||||
- name: github.com/tendermint/tendermint
|
- name: github.com/tendermint/tendermint
|
||||||
|
@ -172,7 +171,7 @@ imports:
|
||||||
- types
|
- types
|
||||||
- version
|
- version
|
||||||
- name: github.com/tendermint/tmlibs
|
- name: github.com/tendermint/tmlibs
|
||||||
version: efb56aaea7517220bb3f42ff87b8004d554a17ff
|
version: 2f6f3e6aa70bb19b70a6e73210273fa127041070
|
||||||
subpackages:
|
subpackages:
|
||||||
- autofile
|
- autofile
|
||||||
- cli
|
- cli
|
||||||
|
|
|
@ -22,14 +22,14 @@ import:
|
||||||
subpackages:
|
subpackages:
|
||||||
- data
|
- data
|
||||||
- package: github.com/tendermint/light-client
|
- package: github.com/tendermint/light-client
|
||||||
version: unstable
|
version: 1c53d04dcc65c2fd15526152ed0651af10a09982
|
||||||
subpackages:
|
subpackages:
|
||||||
- proofs
|
- proofs
|
||||||
- certifiers
|
- certifiers
|
||||||
- certifiers/client
|
- certifiers/client
|
||||||
- certifiers/files
|
- certifiers/files
|
||||||
- package: github.com/tendermint/merkleeyes
|
- package: github.com/tendermint/merkleeyes
|
||||||
version: develop
|
version: unstable
|
||||||
subpackages:
|
subpackages:
|
||||||
- client
|
- client
|
||||||
- iavl
|
- iavl
|
||||||
|
|
30
handler.go
30
handler.go
|
@ -15,9 +15,9 @@ type Handler interface {
|
||||||
SetOptioner
|
SetOptioner
|
||||||
Named
|
Named
|
||||||
// TODO: flesh these out as well
|
// TODO: flesh these out as well
|
||||||
// InitChain(store state.KVStore, vals []*abci.Validator)
|
// InitChain(store state.SimpleDB, vals []*abci.Validator)
|
||||||
// BeginBlock(store state.KVStore, hash []byte, header *abci.Header)
|
// BeginBlock(store state.SimpleDB, hash []byte, header *abci.Header)
|
||||||
// EndBlock(store state.KVStore, height uint64) abci.ResponseEndBlock
|
// EndBlock(store state.SimpleDB, height uint64) abci.ResponseEndBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
type Named interface {
|
type Named interface {
|
||||||
|
@ -25,35 +25,35 @@ type Named interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Checker interface {
|
type Checker interface {
|
||||||
CheckTx(ctx Context, store state.KVStore, tx Tx) (Result, error)
|
CheckTx(ctx Context, store state.SimpleDB, tx Tx) (Result, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckerFunc (like http.HandlerFunc) is a shortcut for making wrapers
|
// CheckerFunc (like http.HandlerFunc) is a shortcut for making wrapers
|
||||||
type CheckerFunc func(Context, state.KVStore, Tx) (Result, error)
|
type CheckerFunc func(Context, state.SimpleDB, Tx) (Result, error)
|
||||||
|
|
||||||
func (c CheckerFunc) CheckTx(ctx Context, store state.KVStore, tx Tx) (Result, error) {
|
func (c CheckerFunc) CheckTx(ctx Context, store state.SimpleDB, tx Tx) (Result, error) {
|
||||||
return c(ctx, store, tx)
|
return c(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Deliver interface {
|
type Deliver interface {
|
||||||
DeliverTx(ctx Context, store state.KVStore, tx Tx) (Result, error)
|
DeliverTx(ctx Context, store state.SimpleDB, tx Tx) (Result, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverFunc (like http.HandlerFunc) is a shortcut for making wrapers
|
// DeliverFunc (like http.HandlerFunc) is a shortcut for making wrapers
|
||||||
type DeliverFunc func(Context, state.KVStore, Tx) (Result, error)
|
type DeliverFunc func(Context, state.SimpleDB, Tx) (Result, error)
|
||||||
|
|
||||||
func (c DeliverFunc) DeliverTx(ctx Context, store state.KVStore, tx Tx) (Result, error) {
|
func (c DeliverFunc) DeliverTx(ctx Context, store state.SimpleDB, tx Tx) (Result, error) {
|
||||||
return c(ctx, store, tx)
|
return c(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetOptioner interface {
|
type SetOptioner interface {
|
||||||
SetOption(l log.Logger, store state.KVStore, module, key, value string) (string, error)
|
SetOption(l log.Logger, store state.SimpleDB, module, key, value string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOptionFunc (like http.HandlerFunc) is a shortcut for making wrapers
|
// SetOptionFunc (like http.HandlerFunc) is a shortcut for making wrapers
|
||||||
type SetOptionFunc func(log.Logger, state.KVStore, string, string, string) (string, error)
|
type SetOptionFunc func(log.Logger, state.SimpleDB, string, string, string) (string, error)
|
||||||
|
|
||||||
func (c SetOptionFunc) SetOption(l log.Logger, store state.KVStore, module, key, value string) (string, error) {
|
func (c SetOptionFunc) SetOption(l log.Logger, store state.SimpleDB, module, key, value string) (string, error) {
|
||||||
return c(l, store, module, key, value)
|
return c(l, store, module, key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,14 +75,14 @@ func (r Result) ToABCI() abci.Result {
|
||||||
// holders
|
// holders
|
||||||
type NopCheck struct{}
|
type NopCheck struct{}
|
||||||
|
|
||||||
func (_ NopCheck) CheckTx(Context, state.KVStore, Tx) (r Result, e error) { return }
|
func (_ NopCheck) CheckTx(Context, state.SimpleDB, Tx) (r Result, e error) { return }
|
||||||
|
|
||||||
type NopDeliver struct{}
|
type NopDeliver struct{}
|
||||||
|
|
||||||
func (_ NopDeliver) DeliverTx(Context, state.KVStore, Tx) (r Result, e error) { return }
|
func (_ NopDeliver) DeliverTx(Context, state.SimpleDB, Tx) (r Result, e error) { return }
|
||||||
|
|
||||||
type NopOption struct{}
|
type NopOption struct{}
|
||||||
|
|
||||||
func (_ NopOption) SetOption(log.Logger, state.KVStore, string, string, string) (string, error) {
|
func (_ NopOption) SetOption(log.Logger, state.SimpleDB, string, string, string) (string, error) {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ type Signable interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTx verifies the signatures are correct - fulfills Middlware interface
|
// CheckTx verifies the signatures are correct - fulfills Middlware interface
|
||||||
func (Signatures) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
func (Signatures) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
||||||
sigs, tnext, err := getSigners(tx)
|
sigs, tnext, err := getSigners(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
@ -49,7 +49,7 @@ func (Signatures) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx verifies the signatures are correct - fulfills Middlware interface
|
// DeliverTx verifies the signatures are correct - fulfills Middlware interface
|
||||||
func (Signatures) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
func (Signatures) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
||||||
sigs, tnext, err := getSigners(tx)
|
sigs, tnext, err := getSigners(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
|
|
@ -24,7 +24,7 @@ func (Chain) Name() string {
|
||||||
var _ stack.Middleware = Chain{}
|
var _ stack.Middleware = Chain{}
|
||||||
|
|
||||||
// CheckTx makes sure we are on the proper chain - fulfills Middlware interface
|
// CheckTx makes sure we are on the proper chain - fulfills Middlware interface
|
||||||
func (c Chain) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
func (c Chain) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
||||||
stx, err := c.checkChainTx(ctx.ChainID(), ctx.BlockHeight(), tx)
|
stx, err := c.checkChainTx(ctx.ChainID(), ctx.BlockHeight(), tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
@ -33,7 +33,7 @@ func (c Chain) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx makes sure we are on the proper chain - fulfills Middlware interface
|
// DeliverTx makes sure we are on the proper chain - fulfills Middlware interface
|
||||||
func (c Chain) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
func (c Chain) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
||||||
stx, err := c.checkChainTx(ctx.ChainID(), ctx.BlockHeight(), tx)
|
stx, err := c.checkChainTx(ctx.ChainID(), ctx.BlockHeight(), tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
|
|
@ -26,7 +26,7 @@ func (Logger) Name() string {
|
||||||
var _ stack.Middleware = Logger{}
|
var _ stack.Middleware = Logger{}
|
||||||
|
|
||||||
// CheckTx logs time and result - fulfills Middlware interface
|
// CheckTx logs time and result - fulfills Middlware interface
|
||||||
func (Logger) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
func (Logger) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
res, err = next.CheckTx(ctx, store, tx)
|
res, err = next.CheckTx(ctx, store, tx)
|
||||||
delta := time.Now().Sub(start)
|
delta := time.Now().Sub(start)
|
||||||
|
@ -41,7 +41,7 @@ func (Logger) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx,
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx logs time and result - fulfills Middlware interface
|
// DeliverTx logs time and result - fulfills Middlware interface
|
||||||
func (Logger) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
func (Logger) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
res, err = next.DeliverTx(ctx, store, tx)
|
res, err = next.DeliverTx(ctx, store, tx)
|
||||||
delta := time.Now().Sub(start)
|
delta := time.Now().Sub(start)
|
||||||
|
@ -56,7 +56,7 @@ func (Logger) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.T
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOption logs time and result - fulfills Middlware interface
|
// SetOption logs time and result - fulfills Middlware interface
|
||||||
func (Logger) SetOption(l log.Logger, store state.KVStore, module, key, value string, next basecoin.SetOptioner) (string, error) {
|
func (Logger) SetOption(l log.Logger, store state.SimpleDB, module, key, value string, next basecoin.SetOptioner) (string, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
res, err := next.SetOption(l, store, module, key, value)
|
res, err := next.SetOption(l, store, module, key, value)
|
||||||
delta := time.Now().Sub(start)
|
delta := time.Now().Sub(start)
|
||||||
|
|
|
@ -29,7 +29,7 @@ func (Multiplexer) Name() string {
|
||||||
var _ stack.Middleware = Multiplexer{}
|
var _ stack.Middleware = Multiplexer{}
|
||||||
|
|
||||||
// CheckTx splits the input tx and checks them all - fulfills Middlware interface
|
// CheckTx splits the input tx and checks them all - fulfills Middlware interface
|
||||||
func (Multiplexer) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
func (Multiplexer) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
||||||
if mtx, ok := tx.Unwrap().(*MultiTx); ok {
|
if mtx, ok := tx.Unwrap().(*MultiTx); ok {
|
||||||
return runAll(ctx, store, mtx.Txs, next.CheckTx)
|
return runAll(ctx, store, mtx.Txs, next.CheckTx)
|
||||||
}
|
}
|
||||||
|
@ -37,14 +37,14 @@ func (Multiplexer) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoi
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx splits the input tx and checks them all - fulfills Middlware interface
|
// DeliverTx splits the input tx and checks them all - fulfills Middlware interface
|
||||||
func (Multiplexer) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
func (Multiplexer) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
||||||
if mtx, ok := tx.Unwrap().(*MultiTx); ok {
|
if mtx, ok := tx.Unwrap().(*MultiTx); ok {
|
||||||
return runAll(ctx, store, mtx.Txs, next.DeliverTx)
|
return runAll(ctx, store, mtx.Txs, next.DeliverTx)
|
||||||
}
|
}
|
||||||
return next.DeliverTx(ctx, store, tx)
|
return next.DeliverTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runAll(ctx basecoin.Context, store state.KVStore, txs []basecoin.Tx, next basecoin.CheckerFunc) (res basecoin.Result, err error) {
|
func runAll(ctx basecoin.Context, store state.SimpleDB, txs []basecoin.Tx, next basecoin.CheckerFunc) (res basecoin.Result, err error) {
|
||||||
// store all results, unless anything errors
|
// store all results, unless anything errors
|
||||||
rs := make([]basecoin.Result, len(txs))
|
rs := make([]basecoin.Result, len(txs))
|
||||||
for i, stx := range txs {
|
for i, stx := range txs {
|
||||||
|
|
|
@ -33,7 +33,7 @@ func accountQueryCmd(cmd *cobra.Command, args []string) error {
|
||||||
acc := coin.Account{}
|
acc := coin.Account{}
|
||||||
proof, err := proofcmd.GetAndParseAppProof(key, &acc)
|
proof, err := proofcmd.GetAndParseAppProof(key, &acc)
|
||||||
if lc.IsNoDataErr(err) {
|
if lc.IsNoDataErr(err) {
|
||||||
return errors.Errorf("Account bytes are empty for address %X ", addr)
|
return errors.Errorf("Account bytes are empty for address %s ", addr)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,16 +58,8 @@ func readSendTxFlags() (tx basecoin.Tx, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// craft the inputs and outputs
|
// craft the inputs and outputs
|
||||||
ins := []coin.TxInput{{
|
tx = coin.NewSendOneTx(fromAddr, toAddr, amountCoins)
|
||||||
Address: fromAddr,
|
return
|
||||||
Coins: amountCoins,
|
|
||||||
}}
|
|
||||||
outs := []coin.TxOutput{{
|
|
||||||
Address: toAddr,
|
|
||||||
Coins: amountCoins,
|
|
||||||
}}
|
|
||||||
|
|
||||||
return coin.NewSendTx(ins, outs), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func readFromAddr() (basecoin.Actor, error) {
|
func readFromAddr() (basecoin.Actor, error) {
|
||||||
|
|
|
@ -29,7 +29,7 @@ func (Handler) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTx checks if there is enough money in the account
|
// CheckTx checks if there is enough money in the account
|
||||||
func (h Handler) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (h Handler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||||
send, err := checkTx(ctx, tx)
|
send, err := checkTx(ctx, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
@ -48,7 +48,7 @@ func (h Handler) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx moves the money
|
// DeliverTx moves the money
|
||||||
func (h Handler) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (h Handler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||||
send, err := checkTx(ctx, tx)
|
send, err := checkTx(ctx, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
@ -75,7 +75,7 @@ func (h Handler) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoi
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOption - sets the genesis account balance
|
// SetOption - sets the genesis account balance
|
||||||
func (h Handler) SetOption(l log.Logger, store state.KVStore, module, key, value string) (log string, err error) {
|
func (h Handler) SetOption(l log.Logger, store state.SimpleDB, module, key, value string) (log string, err error) {
|
||||||
if module != NameCoin {
|
if module != NameCoin {
|
||||||
return "", errors.ErrUnknownModule(module)
|
return "", errors.ErrUnknownModule(module)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetAccount - Get account from store and address
|
// GetAccount - Get account from store and address
|
||||||
func GetAccount(store state.KVStore, addr basecoin.Actor) (Account, error) {
|
func GetAccount(store state.SimpleDB, addr basecoin.Actor) (Account, error) {
|
||||||
acct, err := loadAccount(store, addr.Bytes())
|
acct, err := loadAccount(store, addr.Bytes())
|
||||||
|
|
||||||
// for empty accounts, don't return an error, but rather an empty account
|
// for empty accounts, don't return an error, but rather an empty account
|
||||||
|
@ -22,13 +22,13 @@ func GetAccount(store state.KVStore, addr basecoin.Actor) (Account, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckCoins makes sure there are funds, but doesn't change anything
|
// CheckCoins makes sure there are funds, but doesn't change anything
|
||||||
func CheckCoins(store state.KVStore, addr basecoin.Actor, coins Coins) (Coins, error) {
|
func CheckCoins(store state.SimpleDB, addr basecoin.Actor, coins Coins) (Coins, error) {
|
||||||
acct, err := updateCoins(store, addr, coins)
|
acct, err := updateCoins(store, addr, coins)
|
||||||
return acct.Coins, err
|
return acct.Coins, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeCoins changes the money, returns error if it would be negative
|
// ChangeCoins changes the money, returns error if it would be negative
|
||||||
func ChangeCoins(store state.KVStore, addr basecoin.Actor, coins Coins) (Coins, error) {
|
func ChangeCoins(store state.SimpleDB, addr basecoin.Actor, coins Coins) (Coins, error) {
|
||||||
acct, err := updateCoins(store, addr, coins)
|
acct, err := updateCoins(store, addr, coins)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return acct.Coins, err
|
return acct.Coins, err
|
||||||
|
@ -41,7 +41,7 @@ func ChangeCoins(store state.KVStore, addr basecoin.Actor, coins Coins) (Coins,
|
||||||
// updateCoins will load the account, make all checks, and return the updated account.
|
// updateCoins will load the account, make all checks, and return the updated account.
|
||||||
//
|
//
|
||||||
// it doesn't save anything, that is up to you to decide (Check/Change Coins)
|
// it doesn't save anything, that is up to you to decide (Check/Change Coins)
|
||||||
func updateCoins(store state.KVStore, addr basecoin.Actor, coins Coins) (acct Account, err error) {
|
func updateCoins(store state.SimpleDB, addr basecoin.Actor, coins Coins) (acct Account, err error) {
|
||||||
acct, err = loadAccount(store, addr.Bytes())
|
acct, err = loadAccount(store, addr.Bytes())
|
||||||
// we can increase an empty account...
|
// we can increase an empty account...
|
||||||
if IsNoAccountErr(err) && coins.IsPositive() {
|
if IsNoAccountErr(err) && coins.IsPositive() {
|
||||||
|
@ -66,7 +66,7 @@ type Account struct {
|
||||||
Coins Coins `json:"coins"`
|
Coins Coins `json:"coins"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadAccount(store state.KVStore, key []byte) (acct Account, err error) {
|
func loadAccount(store state.SimpleDB, key []byte) (acct Account, err error) {
|
||||||
// fmt.Printf("load: %X\n", key)
|
// fmt.Printf("load: %X\n", key)
|
||||||
data := store.Get(key)
|
data := store.Get(key)
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
|
@ -80,7 +80,7 @@ func loadAccount(store state.KVStore, key []byte) (acct Account, err error) {
|
||||||
return acct, nil
|
return acct, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func storeAccount(store state.KVStore, key []byte, acct Account) error {
|
func storeAccount(store state.SimpleDB, key []byte, acct Account) error {
|
||||||
// fmt.Printf("store: %X\n", key)
|
// fmt.Printf("store: %X\n", key)
|
||||||
bin := wire.BinaryBytes(acct)
|
bin := wire.BinaryBytes(acct)
|
||||||
store.Set(key, bin)
|
store.Set(key, bin)
|
||||||
|
|
|
@ -45,16 +45,16 @@ func (SimpleFeeMiddleware) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTx - check the transaction
|
// CheckTx - check the transaction
|
||||||
func (h SimpleFeeMiddleware) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
func (h SimpleFeeMiddleware) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
||||||
return h.doTx(ctx, store, tx, next.CheckTx)
|
return h.doTx(ctx, store, tx, next.CheckTx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx - send the fee handler transaction
|
// DeliverTx - send the fee handler transaction
|
||||||
func (h SimpleFeeMiddleware) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
func (h SimpleFeeMiddleware) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
||||||
return h.doTx(ctx, store, tx, next.DeliverTx)
|
return h.doTx(ctx, store, tx, next.DeliverTx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h SimpleFeeMiddleware) doTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.CheckerFunc) (res basecoin.Result, err error) {
|
func (h SimpleFeeMiddleware) doTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.CheckerFunc) (res basecoin.Result, err error) {
|
||||||
feeTx, ok := tx.Unwrap().(Fee)
|
feeTx, ok := tx.Unwrap().(Fee)
|
||||||
if !ok {
|
if !ok {
|
||||||
// the fee wrapper is not required if there is no minimum
|
// the fee wrapper is not required if there is no minimum
|
||||||
|
|
|
@ -24,7 +24,7 @@ func (ReplayCheck) Name() string {
|
||||||
var _ stack.Middleware = ReplayCheck{}
|
var _ stack.Middleware = ReplayCheck{}
|
||||||
|
|
||||||
// CheckTx verifies tx is not being replayed - fulfills Middlware interface
|
// CheckTx verifies tx is not being replayed - fulfills Middlware interface
|
||||||
func (r ReplayCheck) CheckTx(ctx basecoin.Context, store state.KVStore,
|
func (r ReplayCheck) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
||||||
|
|
||||||
stx, err := r.checkIncrementNonceTx(ctx, store, tx)
|
stx, err := r.checkIncrementNonceTx(ctx, store, tx)
|
||||||
|
@ -38,7 +38,7 @@ func (r ReplayCheck) CheckTx(ctx basecoin.Context, store state.KVStore,
|
||||||
// DeliverTx verifies tx is not being replayed - fulfills Middlware interface
|
// DeliverTx verifies tx is not being replayed - fulfills Middlware interface
|
||||||
// NOTE It is okay to modify the sequence before running the wrapped TX because if the
|
// NOTE It is okay to modify the sequence before running the wrapped TX because if the
|
||||||
// wrapped Tx fails, the state changes are not applied
|
// wrapped Tx fails, the state changes are not applied
|
||||||
func (r ReplayCheck) DeliverTx(ctx basecoin.Context, store state.KVStore,
|
func (r ReplayCheck) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
||||||
|
|
||||||
stx, err := r.checkIncrementNonceTx(ctx, store, tx)
|
stx, err := r.checkIncrementNonceTx(ctx, store, tx)
|
||||||
|
@ -50,7 +50,7 @@ func (r ReplayCheck) DeliverTx(ctx basecoin.Context, store state.KVStore,
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkNonceTx varifies the nonce sequence, an increment sequence number
|
// checkNonceTx varifies the nonce sequence, an increment sequence number
|
||||||
func (r ReplayCheck) checkIncrementNonceTx(ctx basecoin.Context, store state.KVStore,
|
func (r ReplayCheck) checkIncrementNonceTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx) (basecoin.Tx, error) {
|
tx basecoin.Tx) (basecoin.Tx, error) {
|
||||||
|
|
||||||
// make sure it is a the nonce Tx (Tx from this package)
|
// make sure it is a the nonce Tx (Tx from this package)
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/tendermint/basecoin/state"
|
"github.com/tendermint/basecoin/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getSeq(store state.KVStore, key []byte) (seq uint32, err error) {
|
func getSeq(store state.SimpleDB, key []byte) (seq uint32, err error) {
|
||||||
data := store.Get(key)
|
data := store.Get(key)
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
//if the key is not stored, its a new key with a sequence of zero!
|
//if the key is not stored, its a new key with a sequence of zero!
|
||||||
|
@ -23,7 +23,7 @@ func getSeq(store state.KVStore, key []byte) (seq uint32, err error) {
|
||||||
return seq, nil
|
return seq, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setSeq(store state.KVStore, key []byte, seq uint32) error {
|
func setSeq(store state.SimpleDB, key []byte, seq uint32) error {
|
||||||
bin := wire.BinaryBytes(seq)
|
bin := wire.BinaryBytes(seq)
|
||||||
store.Set(key, bin)
|
store.Set(key, bin)
|
||||||
return nil // real stores can return error...
|
return nil // real stores can return error...
|
||||||
|
|
|
@ -62,7 +62,7 @@ func (n Tx) ValidateBasic() error {
|
||||||
// and further increment the sequence number
|
// and further increment the sequence number
|
||||||
// NOTE It is okay to modify the sequence before running the wrapped TX because if the
|
// NOTE It is okay to modify the sequence before running the wrapped TX because if the
|
||||||
// wrapped Tx fails, the state changes are not applied
|
// wrapped Tx fails, the state changes are not applied
|
||||||
func (n Tx) CheckIncrementSeq(ctx basecoin.Context, store state.KVStore) error {
|
func (n Tx) CheckIncrementSeq(ctx basecoin.Context, store state.SimpleDB) error {
|
||||||
|
|
||||||
seqKey := n.getSeqKey()
|
seqKey := n.getSeqKey()
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ func (Handler) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTx verifies if the transaction is properly formated
|
// CheckTx verifies if the transaction is properly formated
|
||||||
func (h Handler) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (h Handler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||||
var cr CreateRoleTx
|
var cr CreateRoleTx
|
||||||
cr, err = checkTx(ctx, tx)
|
cr, err = checkTx(ctx, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -40,7 +40,7 @@ func (h Handler) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.
|
||||||
// DeliverTx tries to create a new role.
|
// DeliverTx tries to create a new role.
|
||||||
//
|
//
|
||||||
// Returns an error if the role already exists
|
// Returns an error if the role already exists
|
||||||
func (h Handler) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (h Handler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||||
create, err := checkTx(ctx, tx)
|
create, err := checkTx(ctx, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
|
|
@ -27,7 +27,7 @@ func (Middleware) Name() string {
|
||||||
// CheckTx tries to assume the named role if requested.
|
// CheckTx tries to assume the named role if requested.
|
||||||
// If no role is requested, do nothing.
|
// If no role is requested, do nothing.
|
||||||
// If insufficient authority to assume the role, return error.
|
// If insufficient authority to assume the role, return error.
|
||||||
func (m Middleware) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
func (m Middleware) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
||||||
// if this is not an AssumeRoleTx, then continue
|
// if this is not an AssumeRoleTx, then continue
|
||||||
assume, ok := tx.Unwrap().(AssumeRoleTx)
|
assume, ok := tx.Unwrap().(AssumeRoleTx)
|
||||||
if !ok { // this also breaks the recursion below
|
if !ok { // this also breaks the recursion below
|
||||||
|
@ -46,7 +46,7 @@ func (m Middleware) CheckTx(ctx basecoin.Context, store state.KVStore, tx baseco
|
||||||
// DeliverTx tries to assume the named role if requested.
|
// DeliverTx tries to assume the named role if requested.
|
||||||
// If no role is requested, do nothing.
|
// If no role is requested, do nothing.
|
||||||
// If insufficient authority to assume the role, return error.
|
// If insufficient authority to assume the role, return error.
|
||||||
func (m Middleware) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
func (m Middleware) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
||||||
// if this is not an AssumeRoleTx, then continue
|
// if this is not an AssumeRoleTx, then continue
|
||||||
assume, ok := tx.Unwrap().(AssumeRoleTx)
|
assume, ok := tx.Unwrap().(AssumeRoleTx)
|
||||||
if !ok { // this also breaks the recursion below
|
if !ok { // this also breaks the recursion below
|
||||||
|
@ -62,7 +62,7 @@ func (m Middleware) DeliverTx(ctx basecoin.Context, store state.KVStore, tx base
|
||||||
return m.DeliverTx(ctx, store, assume.Tx, next)
|
return m.DeliverTx(ctx, store, assume.Tx, next)
|
||||||
}
|
}
|
||||||
|
|
||||||
func assumeRole(ctx basecoin.Context, store state.KVStore, assume AssumeRoleTx) (basecoin.Context, error) {
|
func assumeRole(ctx basecoin.Context, store state.SimpleDB, assume AssumeRoleTx) (basecoin.Context, error) {
|
||||||
err := assume.ValidateBasic()
|
err := assume.ValidateBasic()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -17,7 +17,7 @@ import (
|
||||||
// shortcut for the lazy
|
// shortcut for the lazy
|
||||||
type ba []basecoin.Actor
|
type ba []basecoin.Actor
|
||||||
|
|
||||||
func createRole(app basecoin.Handler, store state.KVStore,
|
func createRole(app basecoin.Handler, store state.SimpleDB,
|
||||||
name []byte, min uint32, sigs ...basecoin.Actor) (basecoin.Actor, error) {
|
name []byte, min uint32, sigs ...basecoin.Actor) (basecoin.Actor, error) {
|
||||||
tx := roles.NewCreateRoleTx(name, min, sigs)
|
tx := roles.NewCreateRoleTx(name, min, sigs)
|
||||||
ctx := stack.MockContext("foo", 1)
|
ctx := stack.MockContext("foo", 1)
|
||||||
|
|
|
@ -56,7 +56,7 @@ func (r Role) IsAuthorized(ctx basecoin.Context) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadRole(store state.KVStore, key []byte) (role Role, err error) {
|
func loadRole(store state.SimpleDB, key []byte) (role Role, err error) {
|
||||||
data := store.Get(key)
|
data := store.Get(key)
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return role, ErrNoRole()
|
return role, ErrNoRole()
|
||||||
|
@ -69,7 +69,7 @@ func loadRole(store state.KVStore, key []byte) (role Role, err error) {
|
||||||
return role, nil
|
return role, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkNoRole(store state.KVStore, key []byte) error {
|
func checkNoRole(store state.SimpleDB, key []byte) error {
|
||||||
if _, err := loadRole(store, key); !IsNoRoleErr(err) {
|
if _, err := loadRole(store, key); !IsNoRoleErr(err) {
|
||||||
return ErrRoleExists()
|
return ErrRoleExists()
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ func checkNoRole(store state.KVStore, key []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// we only have create here, no update, since we don't allow update yet
|
// we only have create here, no update, since we don't allow update yet
|
||||||
func createRole(store state.KVStore, key []byte, role Role) error {
|
func createRole(store state.SimpleDB, key []byte, role Role) error {
|
||||||
if err := checkNoRole(store, key); err != nil {
|
if err := checkNoRole(store, key); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ package ibc
|
||||||
// // GetSequenceNumber gets the sequence number for packets being sent from the src chain to the dst chain.
|
// // GetSequenceNumber gets the sequence number for packets being sent from the src chain to the dst chain.
|
||||||
// // The sequence number counts how many packets have been sent.
|
// // The sequence number counts how many packets have been sent.
|
||||||
// // The next packet must include the latest sequence number.
|
// // The next packet must include the latest sequence number.
|
||||||
// func GetSequenceNumber(store state.KVStore, src, dst string) uint64 {
|
// func GetSequenceNumber(store state.SimpleDB, src, dst string) uint64 {
|
||||||
// sequenceKey := toKey(_IBC, _EGRESS, src, dst)
|
// sequenceKey := toKey(_IBC, _EGRESS, src, dst)
|
||||||
// seqBytes := store.Get(sequenceKey)
|
// seqBytes := store.Get(sequenceKey)
|
||||||
// if seqBytes == nil {
|
// if seqBytes == nil {
|
||||||
|
@ -86,14 +86,14 @@ package ibc
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// // SetSequenceNumber sets the sequence number for packets being sent from the src chain to the dst chain
|
// // SetSequenceNumber sets the sequence number for packets being sent from the src chain to the dst chain
|
||||||
// func SetSequenceNumber(store state.KVStore, src, dst string, seq uint64) {
|
// func SetSequenceNumber(store state.SimpleDB, src, dst string, seq uint64) {
|
||||||
// sequenceKey := toKey(_IBC, _EGRESS, src, dst)
|
// sequenceKey := toKey(_IBC, _EGRESS, src, dst)
|
||||||
// store.Set(sequenceKey, []byte(strconv.FormatUint(seq, 10)))
|
// store.Set(sequenceKey, []byte(strconv.FormatUint(seq, 10)))
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// // SaveNewIBCPacket creates an IBC packet with the given payload from the src chain to the dst chain
|
// // SaveNewIBCPacket creates an IBC packet with the given payload from the src chain to the dst chain
|
||||||
// // using the correct sequence number. It also increments the sequence number by 1
|
// // using the correct sequence number. It also increments the sequence number by 1
|
||||||
// func SaveNewIBCPacket(state state.KVStore, src, dst string, payload Payload) {
|
// func SaveNewIBCPacket(state state.SimpleDB, src, dst string, payload Payload) {
|
||||||
// // fetch sequence number and increment by 1
|
// // fetch sequence number and increment by 1
|
||||||
// seq := GetSequenceNumber(state, src, dst)
|
// seq := GetSequenceNumber(state, src, dst)
|
||||||
// SetSequenceNumber(state, src, dst, seq+1)
|
// SetSequenceNumber(state, src, dst, seq+1)
|
||||||
|
@ -104,7 +104,7 @@ package ibc
|
||||||
// save(state, packetKey, packet)
|
// save(state, packetKey, packet)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// func GetIBCPacket(state state.KVStore, src, dst string, seq uint64) (Packet, error) {
|
// func GetIBCPacket(state state.SimpleDB, src, dst string, seq uint64) (Packet, error) {
|
||||||
// packetKey := toKey(_IBC, _EGRESS, src, dst, cmn.Fmt("%v", seq))
|
// packetKey := toKey(_IBC, _EGRESS, src, dst, cmn.Fmt("%v", seq))
|
||||||
// packetBytes := state.Get(packetKey)
|
// packetBytes := state.Get(packetKey)
|
||||||
|
|
||||||
|
@ -251,11 +251,11 @@ package ibc
|
||||||
// return &IBCPlugin{}
|
// return &IBCPlugin{}
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// func (ibc *IBCPlugin) SetOption(store state.KVStore, key string, value string) (log string) {
|
// func (ibc *IBCPlugin) SetOption(store state.SimpleDB, key string, value string) (log string) {
|
||||||
// return ""
|
// return ""
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// func (ibc *IBCPlugin) RunTx(store state.KVStore, ctx types.CallContext, txBytes []byte) (res abci.Result) {
|
// func (ibc *IBCPlugin) RunTx(store state.SimpleDB, ctx types.CallContext, txBytes []byte) (res abci.Result) {
|
||||||
// // Decode tx
|
// // Decode tx
|
||||||
// var tx IBCTx
|
// var tx IBCTx
|
||||||
// err := wire.ReadBinaryBytes(txBytes, &tx)
|
// err := wire.ReadBinaryBytes(txBytes, &tx)
|
||||||
|
@ -295,7 +295,7 @@ package ibc
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// type IBCStateMachine struct {
|
// type IBCStateMachine struct {
|
||||||
// store state.KVStore
|
// store state.SimpleDB
|
||||||
// ctx types.CallContext
|
// ctx types.CallContext
|
||||||
// res abci.Result
|
// res abci.Result
|
||||||
// }
|
// }
|
||||||
|
@ -499,13 +499,13 @@ package ibc
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// func (ibc *IBCPlugin) InitChain(store state.KVStore, vals []*abci.Validator) {
|
// func (ibc *IBCPlugin) InitChain(store state.SimpleDB, vals []*abci.Validator) {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// func (cp *IBCPlugin) BeginBlock(store state.KVStore, hash []byte, header *abci.Header) {
|
// func (cp *IBCPlugin) BeginBlock(store state.SimpleDB, hash []byte, header *abci.Header) {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// func (cp *IBCPlugin) EndBlock(store state.KVStore, height uint64) (res abci.ResponseEndBlock) {
|
// func (cp *IBCPlugin) EndBlock(store state.SimpleDB, height uint64) (res abci.ResponseEndBlock) {
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ -513,7 +513,7 @@ package ibc
|
||||||
// // TODO: move to utils
|
// // TODO: move to utils
|
||||||
|
|
||||||
// // Returns true if exists, false if nil.
|
// // Returns true if exists, false if nil.
|
||||||
// func exists(store state.KVStore, key []byte) (exists bool) {
|
// func exists(store state.SimpleDB, key []byte) (exists bool) {
|
||||||
// value := store.Get(key)
|
// value := store.Get(key)
|
||||||
// return len(value) > 0
|
// return len(value) > 0
|
||||||
// }
|
// }
|
||||||
|
@ -521,7 +521,7 @@ package ibc
|
||||||
// // Load bytes from store by reading value for key and read into ptr.
|
// // Load bytes from store by reading value for key and read into ptr.
|
||||||
// // Returns true if exists, false if nil.
|
// // Returns true if exists, false if nil.
|
||||||
// // Returns err if decoding error.
|
// // Returns err if decoding error.
|
||||||
// func load(store state.KVStore, key []byte, ptr interface{}) (exists bool, err error) {
|
// func load(store state.SimpleDB, key []byte, ptr interface{}) (exists bool, err error) {
|
||||||
// value := store.Get(key)
|
// value := store.Get(key)
|
||||||
// if len(value) > 0 {
|
// if len(value) > 0 {
|
||||||
// err = wire.ReadBinaryBytes(value, ptr)
|
// err = wire.ReadBinaryBytes(value, ptr)
|
||||||
|
@ -537,7 +537,7 @@ package ibc
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// // Save bytes to store by writing obj's go-wire binary bytes.
|
// // Save bytes to store by writing obj's go-wire binary bytes.
|
||||||
// func save(store state.KVStore, key []byte, obj interface{}) {
|
// func save(store state.SimpleDB, key []byte, obj interface{}) {
|
||||||
// store.Set(key, wire.BinaryBytes(obj))
|
// store.Set(key, wire.BinaryBytes(obj))
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tendermint/basecoin"
|
||||||
|
"github.com/tendermint/basecoin/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
//nolint
|
||||||
|
const (
|
||||||
|
NameCheckpoint = "check"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Checkpoint isolates all data store below this
|
||||||
|
type Checkpoint struct {
|
||||||
|
OnCheck bool
|
||||||
|
OnDeliver bool
|
||||||
|
PassOption
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name of the module - fulfills Middleware interface
|
||||||
|
func (Checkpoint) Name() string {
|
||||||
|
return NameCheckpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Middleware = Checkpoint{}
|
||||||
|
|
||||||
|
// CheckTx reverts all data changes if there was an error
|
||||||
|
func (c Checkpoint) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
||||||
|
if !c.OnCheck {
|
||||||
|
return next.CheckTx(ctx, store, tx)
|
||||||
|
}
|
||||||
|
ps := store.Checkpoint()
|
||||||
|
res, err = next.CheckTx(ctx, ps, tx)
|
||||||
|
if err == nil {
|
||||||
|
err = store.Commit(ps)
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeliverTx reverts all data changes if there was an error
|
||||||
|
func (c Checkpoint) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
||||||
|
if !c.OnDeliver {
|
||||||
|
return next.DeliverTx(ctx, store, tx)
|
||||||
|
}
|
||||||
|
ps := store.Checkpoint()
|
||||||
|
res, err = next.DeliverTx(ctx, ps, tx)
|
||||||
|
if err == nil {
|
||||||
|
err = store.Commit(ps)
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/tendermint/merkleeyes/iavl"
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
|
"github.com/tendermint/basecoin"
|
||||||
|
"github.com/tendermint/basecoin/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeState() state.SimpleDB {
|
||||||
|
// return state.NewMemKVStore()
|
||||||
|
|
||||||
|
return state.NewBonsai(iavl.NewIAVLTree(0, nil))
|
||||||
|
|
||||||
|
// tree with persistence....
|
||||||
|
// tmpDir, err := ioutil.TempDir("", "state-tests")
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// db := dbm.NewDB("test-get-dbs", dbm.LevelDBBackendStr, tmpDir)
|
||||||
|
// persist := iavl.NewIAVLTree(500, db)
|
||||||
|
// return state.NewBonsai(persist)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckpointer(t *testing.T) {
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
|
||||||
|
good := writerHand{"foo", []byte{1, 2}, []byte("bar")}
|
||||||
|
bad := FailHandler{Err: errors.New("no go")}
|
||||||
|
|
||||||
|
app := New(
|
||||||
|
Checkpoint{OnCheck: true},
|
||||||
|
writerMid{"bing", []byte{1, 2}, []byte("bang")},
|
||||||
|
Checkpoint{OnDeliver: true},
|
||||||
|
).Use(
|
||||||
|
NewDispatcher(
|
||||||
|
WrapHandler(good),
|
||||||
|
WrapHandler(bad),
|
||||||
|
))
|
||||||
|
|
||||||
|
basecoin.TxMapper.RegisterImplementation(RawTx{}, good.Name(), byte(80))
|
||||||
|
|
||||||
|
mid := state.Model{
|
||||||
|
Key: []byte{'b', 'i', 'n', 'g', 0, 1, 2},
|
||||||
|
Value: []byte("bang"),
|
||||||
|
}
|
||||||
|
end := state.Model{
|
||||||
|
Key: []byte{'f', 'o', 'o', 0, 1, 2},
|
||||||
|
Value: []byte("bar"),
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
// tx to send down the line
|
||||||
|
tx basecoin.Tx
|
||||||
|
// expect no error?
|
||||||
|
valid bool
|
||||||
|
// models to check afterwards
|
||||||
|
toGetCheck []state.Model
|
||||||
|
// models to check afterwards
|
||||||
|
toGetDeliver []state.Model
|
||||||
|
}{
|
||||||
|
// everything writen on success
|
||||||
|
{
|
||||||
|
tx: NewRawTx([]byte{45, 67}),
|
||||||
|
valid: true,
|
||||||
|
toGetCheck: []state.Model{mid, end},
|
||||||
|
toGetDeliver: []state.Model{mid, end},
|
||||||
|
},
|
||||||
|
// mostly reverted on failure
|
||||||
|
{
|
||||||
|
tx: NewFailTx(),
|
||||||
|
valid: false,
|
||||||
|
toGetCheck: []state.Model{},
|
||||||
|
toGetDeliver: []state.Model{mid},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range cases {
|
||||||
|
ctx := NewContext("foo", 100, log.NewNopLogger())
|
||||||
|
|
||||||
|
store := makeState()
|
||||||
|
_, err := app.CheckTx(ctx, store, tc.tx)
|
||||||
|
if tc.valid {
|
||||||
|
require.Nil(err, "%+v", err)
|
||||||
|
} else {
|
||||||
|
require.NotNil(err)
|
||||||
|
}
|
||||||
|
for _, m := range tc.toGetCheck {
|
||||||
|
val := store.Get(m.Key)
|
||||||
|
assert.EqualValues(m.Value, val, "%d: %#v", i, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
store = makeState()
|
||||||
|
_, err = app.DeliverTx(ctx, store, tc.tx)
|
||||||
|
if tc.valid {
|
||||||
|
require.Nil(err, "%+v", err)
|
||||||
|
} else {
|
||||||
|
require.NotNil(err)
|
||||||
|
}
|
||||||
|
for _, m := range tc.toGetDeliver {
|
||||||
|
val := store.Get(m.Key)
|
||||||
|
assert.EqualValues(m.Value, val, "%d: %#v", i, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -76,7 +76,7 @@ func withApp(ctx basecoin.Context, app string) basecoin.Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
func secureCheck(h basecoin.Checker, parent basecoin.Context) basecoin.Checker {
|
func secureCheck(h basecoin.Checker, parent basecoin.Context) basecoin.Checker {
|
||||||
next := func(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
next := func(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||||
if !parent.IsParent(ctx) {
|
if !parent.IsParent(ctx) {
|
||||||
return res, errors.New("Passing in non-child Context")
|
return res, errors.New("Passing in non-child Context")
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ func secureCheck(h basecoin.Checker, parent basecoin.Context) basecoin.Checker {
|
||||||
}
|
}
|
||||||
|
|
||||||
func secureDeliver(h basecoin.Deliver, parent basecoin.Context) basecoin.Deliver {
|
func secureDeliver(h basecoin.Deliver, parent basecoin.Context) basecoin.Deliver {
|
||||||
next := func(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
next := func(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||||
if !parent.IsParent(ctx) {
|
if !parent.IsParent(ctx) {
|
||||||
return res, errors.New("Passing in non-child Context")
|
return res, errors.New("Passing in non-child Context")
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ func (d *Dispatcher) Name() string {
|
||||||
// Tries to find a registered module (Dispatchable) based on the name of the tx.
|
// Tries to find a registered module (Dispatchable) based on the name of the tx.
|
||||||
// The tx name (as registered with go-data) should be in the form `<module name>/XXXX`,
|
// The tx name (as registered with go-data) should be in the form `<module name>/XXXX`,
|
||||||
// where `module name` must match the name of a dispatchable and XXX can be any string.
|
// where `module name` must match the name of a dispatchable and XXX can be any string.
|
||||||
func (d *Dispatcher) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (d *Dispatcher) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||||
r, err := d.lookupTx(tx)
|
r, err := d.lookupTx(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
@ -85,7 +85,7 @@ func (d *Dispatcher) CheckTx(ctx basecoin.Context, store state.KVStore, tx basec
|
||||||
// Tries to find a registered module (Dispatchable) based on the name of the tx.
|
// Tries to find a registered module (Dispatchable) based on the name of the tx.
|
||||||
// The tx name (as registered with go-data) should be in the form `<module name>/XXXX`,
|
// The tx name (as registered with go-data) should be in the form `<module name>/XXXX`,
|
||||||
// where `module name` must match the name of a dispatchable and XXX can be any string.
|
// where `module name` must match the name of a dispatchable and XXX can be any string.
|
||||||
func (d *Dispatcher) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (d *Dispatcher) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||||
r, err := d.lookupTx(tx)
|
r, err := d.lookupTx(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
@ -105,7 +105,7 @@ func (d *Dispatcher) DeliverTx(ctx basecoin.Context, store state.KVStore, tx bas
|
||||||
//
|
//
|
||||||
// Tries to find a registered module (Dispatchable) based on the
|
// Tries to find a registered module (Dispatchable) based on the
|
||||||
// module name from SetOption of the tx.
|
// module name from SetOption of the tx.
|
||||||
func (d *Dispatcher) SetOption(l log.Logger, store state.KVStore, module, key, value string) (string, error) {
|
func (d *Dispatcher) SetOption(l log.Logger, store state.SimpleDB, module, key, value string) (string, error) {
|
||||||
r, err := d.lookupModule(module)
|
r, err := d.lookupModule(module)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
|
@ -20,9 +20,11 @@ const (
|
||||||
const (
|
const (
|
||||||
ByteRawTx = 0xF0
|
ByteRawTx = 0xF0
|
||||||
ByteCheckTx = 0xF1
|
ByteCheckTx = 0xF1
|
||||||
|
ByteFailTx = 0xF2
|
||||||
|
|
||||||
TypeRawTx = NameOK + "/raw" // this will just say a-ok to RawTx
|
TypeRawTx = NameOK + "/raw" // this will just say a-ok to RawTx
|
||||||
TypeCheckTx = NameCheck + "/tx"
|
TypeCheckTx = NameCheck + "/tx"
|
||||||
|
TypeFailTx = NameFail + "/tx"
|
||||||
|
|
||||||
rawMaxSize = 2000 * 1000
|
rawMaxSize = 2000 * 1000
|
||||||
)
|
)
|
||||||
|
@ -30,7 +32,8 @@ const (
|
||||||
func init() {
|
func init() {
|
||||||
basecoin.TxMapper.
|
basecoin.TxMapper.
|
||||||
RegisterImplementation(RawTx{}, TypeRawTx, ByteRawTx).
|
RegisterImplementation(RawTx{}, TypeRawTx, ByteRawTx).
|
||||||
RegisterImplementation(CheckTx{}, TypeCheckTx, ByteCheckTx)
|
RegisterImplementation(CheckTx{}, TypeCheckTx, ByteCheckTx).
|
||||||
|
RegisterImplementation(FailTx{}, TypeFailTx, ByteFailTx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RawTx just contains bytes that can be hex-ified
|
// RawTx just contains bytes that can be hex-ified
|
||||||
|
@ -72,6 +75,22 @@ func (CheckTx) ValidateBasic() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FailTx just gets routed to filaure
|
||||||
|
type FailTx struct{}
|
||||||
|
|
||||||
|
var _ basecoin.TxInner = FailTx{}
|
||||||
|
|
||||||
|
func NewFailTx() basecoin.Tx {
|
||||||
|
return FailTx{}.Wrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FailTx) Wrap() basecoin.Tx {
|
||||||
|
return basecoin.Tx{f}
|
||||||
|
}
|
||||||
|
func (r FailTx) ValidateBasic() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// OKHandler just used to return okay to everything
|
// OKHandler just used to return okay to everything
|
||||||
type OKHandler struct {
|
type OKHandler struct {
|
||||||
Log string
|
Log string
|
||||||
|
@ -86,12 +105,12 @@ func (OKHandler) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTx always returns an empty success tx
|
// CheckTx always returns an empty success tx
|
||||||
func (ok OKHandler) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (ok OKHandler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||||
return basecoin.Result{Log: ok.Log}, nil
|
return basecoin.Result{Log: ok.Log}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx always returns an empty success tx
|
// DeliverTx always returns an empty success tx
|
||||||
func (ok OKHandler) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (ok OKHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||||
return basecoin.Result{Log: ok.Log}, nil
|
return basecoin.Result{Log: ok.Log}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,13 +127,13 @@ func (EchoHandler) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTx always returns an empty success tx
|
// CheckTx always returns an empty success tx
|
||||||
func (EchoHandler) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (EchoHandler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||||
data, err := data.ToWire(tx)
|
data, err := data.ToWire(tx)
|
||||||
return basecoin.Result{Data: data}, err
|
return basecoin.Result{Data: data}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx always returns an empty success tx
|
// DeliverTx always returns an empty success tx
|
||||||
func (EchoHandler) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (EchoHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||||
data, err := data.ToWire(tx)
|
data, err := data.ToWire(tx)
|
||||||
return basecoin.Result{Data: data}, err
|
return basecoin.Result{Data: data}, err
|
||||||
}
|
}
|
||||||
|
@ -133,12 +152,12 @@ func (FailHandler) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTx always returns the given error
|
// CheckTx always returns the given error
|
||||||
func (f FailHandler) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (f FailHandler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||||
return res, errors.Wrap(f.Err)
|
return res, errors.Wrap(f.Err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx always returns the given error
|
// DeliverTx always returns the given error
|
||||||
func (f FailHandler) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (f FailHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||||
return res, errors.Wrap(f.Err)
|
return res, errors.Wrap(f.Err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +176,7 @@ func (PanicHandler) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTx always panics
|
// CheckTx always panics
|
||||||
func (p PanicHandler) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (p PanicHandler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||||
if p.Err != nil {
|
if p.Err != nil {
|
||||||
panic(p.Err)
|
panic(p.Err)
|
||||||
}
|
}
|
||||||
|
@ -165,7 +184,7 @@ func (p PanicHandler) CheckTx(ctx basecoin.Context, store state.KVStore, tx base
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx always panics
|
// DeliverTx always panics
|
||||||
func (p PanicHandler) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (p PanicHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||||
if p.Err != nil {
|
if p.Err != nil {
|
||||||
panic(p.Err)
|
panic(p.Err)
|
||||||
}
|
}
|
||||||
|
@ -185,7 +204,7 @@ func (CheckHandler) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTx verifies the permissions
|
// CheckTx verifies the permissions
|
||||||
func (c CheckHandler) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (c CheckHandler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||||
check, ok := tx.Unwrap().(CheckTx)
|
check, ok := tx.Unwrap().(CheckTx)
|
||||||
if !ok {
|
if !ok {
|
||||||
return res, errors.ErrUnknownTxType(tx)
|
return res, errors.ErrUnknownTxType(tx)
|
||||||
|
@ -200,7 +219,7 @@ func (c CheckHandler) CheckTx(ctx basecoin.Context, store state.KVStore, tx base
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx verifies the permissions
|
// DeliverTx verifies the permissions
|
||||||
func (c CheckHandler) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (c CheckHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||||
// until something changes, just do the same as check
|
// until something changes, just do the same as check
|
||||||
return c.CheckTx(ctx, store, tx)
|
return c.CheckTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,14 +25,14 @@ func (_ CheckMiddleware) Name() string {
|
||||||
return NameCheck
|
return NameCheck
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p CheckMiddleware) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
func (p CheckMiddleware) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
||||||
if !ctx.HasPermission(p.Required) {
|
if !ctx.HasPermission(p.Required) {
|
||||||
return res, errors.ErrUnauthorized()
|
return res, errors.ErrUnauthorized()
|
||||||
}
|
}
|
||||||
return next.CheckTx(ctx, store, tx)
|
return next.CheckTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p CheckMiddleware) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
func (p CheckMiddleware) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
||||||
if !ctx.HasPermission(p.Required) {
|
if !ctx.HasPermission(p.Required) {
|
||||||
return res, errors.ErrUnauthorized()
|
return res, errors.ErrUnauthorized()
|
||||||
}
|
}
|
||||||
|
@ -51,12 +51,12 @@ func (_ GrantMiddleware) Name() string {
|
||||||
return NameGrant
|
return NameGrant
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g GrantMiddleware) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
func (g GrantMiddleware) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
||||||
ctx = ctx.WithPermissions(g.Auth)
|
ctx = ctx.WithPermissions(g.Auth)
|
||||||
return next.CheckTx(ctx, store, tx)
|
return next.CheckTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g GrantMiddleware) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
func (g GrantMiddleware) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
||||||
ctx = ctx.WithPermissions(g.Auth)
|
ctx = ctx.WithPermissions(g.Auth)
|
||||||
return next.DeliverTx(ctx, store, tx)
|
return next.DeliverTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,40 +20,40 @@ type Middleware interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type CheckerMiddle interface {
|
type CheckerMiddle interface {
|
||||||
CheckTx(ctx basecoin.Context, store state.KVStore,
|
CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, next basecoin.Checker) (basecoin.Result, error)
|
tx basecoin.Tx, next basecoin.Checker) (basecoin.Result, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type CheckerMiddleFunc func(basecoin.Context, state.KVStore,
|
type CheckerMiddleFunc func(basecoin.Context, state.SimpleDB,
|
||||||
basecoin.Tx, basecoin.Checker) (basecoin.Result, error)
|
basecoin.Tx, basecoin.Checker) (basecoin.Result, error)
|
||||||
|
|
||||||
func (c CheckerMiddleFunc) CheckTx(ctx basecoin.Context, store state.KVStore,
|
func (c CheckerMiddleFunc) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, next basecoin.Checker) (basecoin.Result, error) {
|
tx basecoin.Tx, next basecoin.Checker) (basecoin.Result, error) {
|
||||||
return c(ctx, store, tx, next)
|
return c(ctx, store, tx, next)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeliverMiddle interface {
|
type DeliverMiddle interface {
|
||||||
DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx,
|
DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx,
|
||||||
next basecoin.Deliver) (basecoin.Result, error)
|
next basecoin.Deliver) (basecoin.Result, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeliverMiddleFunc func(basecoin.Context, state.KVStore,
|
type DeliverMiddleFunc func(basecoin.Context, state.SimpleDB,
|
||||||
basecoin.Tx, basecoin.Deliver) (basecoin.Result, error)
|
basecoin.Tx, basecoin.Deliver) (basecoin.Result, error)
|
||||||
|
|
||||||
func (d DeliverMiddleFunc) DeliverTx(ctx basecoin.Context, store state.KVStore,
|
func (d DeliverMiddleFunc) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, next basecoin.Deliver) (basecoin.Result, error) {
|
tx basecoin.Tx, next basecoin.Deliver) (basecoin.Result, error) {
|
||||||
return d(ctx, store, tx, next)
|
return d(ctx, store, tx, next)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetOptionMiddle interface {
|
type SetOptionMiddle interface {
|
||||||
SetOption(l log.Logger, store state.KVStore, module,
|
SetOption(l log.Logger, store state.SimpleDB, module,
|
||||||
key, value string, next basecoin.SetOptioner) (string, error)
|
key, value string, next basecoin.SetOptioner) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetOptionMiddleFunc func(log.Logger, state.KVStore,
|
type SetOptionMiddleFunc func(log.Logger, state.SimpleDB,
|
||||||
string, string, string, basecoin.SetOptioner) (string, error)
|
string, string, string, basecoin.SetOptioner) (string, error)
|
||||||
|
|
||||||
func (c SetOptionMiddleFunc) SetOption(l log.Logger, store state.KVStore,
|
func (c SetOptionMiddleFunc) SetOption(l log.Logger, store state.SimpleDB,
|
||||||
module, key, value string, next basecoin.SetOptioner) (string, error) {
|
module, key, value string, next basecoin.SetOptioner) (string, error) {
|
||||||
return c(l, store, module, key, value, next)
|
return c(l, store, module, key, value, next)
|
||||||
}
|
}
|
||||||
|
@ -61,28 +61,28 @@ func (c SetOptionMiddleFunc) SetOption(l log.Logger, store state.KVStore,
|
||||||
// holders
|
// holders
|
||||||
type PassCheck struct{}
|
type PassCheck struct{}
|
||||||
|
|
||||||
func (_ PassCheck) CheckTx(ctx basecoin.Context, store state.KVStore,
|
func (_ PassCheck) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, next basecoin.Checker) (basecoin.Result, error) {
|
tx basecoin.Tx, next basecoin.Checker) (basecoin.Result, error) {
|
||||||
return next.CheckTx(ctx, store, tx)
|
return next.CheckTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
type PassDeliver struct{}
|
type PassDeliver struct{}
|
||||||
|
|
||||||
func (_ PassDeliver) DeliverTx(ctx basecoin.Context, store state.KVStore,
|
func (_ PassDeliver) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, next basecoin.Deliver) (basecoin.Result, error) {
|
tx basecoin.Tx, next basecoin.Deliver) (basecoin.Result, error) {
|
||||||
return next.DeliverTx(ctx, store, tx)
|
return next.DeliverTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
type PassOption struct{}
|
type PassOption struct{}
|
||||||
|
|
||||||
func (_ PassOption) SetOption(l log.Logger, store state.KVStore, module,
|
func (_ PassOption) SetOption(l log.Logger, store state.SimpleDB, module,
|
||||||
key, value string, next basecoin.SetOptioner) (string, error) {
|
key, value string, next basecoin.SetOptioner) (string, error) {
|
||||||
return next.SetOption(l, store, module, key, value)
|
return next.SetOption(l, store, module, key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
type NopOption struct{}
|
type NopOption struct{}
|
||||||
|
|
||||||
func (_ NopOption) SetOption(l log.Logger, store state.KVStore, module,
|
func (_ NopOption) SetOption(l log.Logger, store state.SimpleDB, module,
|
||||||
key, value string, next basecoin.SetOptioner) (string, error) {
|
key, value string, next basecoin.SetOptioner) (string, error) {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
@ -112,17 +112,17 @@ func (w wrapped) Name() string {
|
||||||
return w.h.Name()
|
return w.h.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w wrapped) CheckTx(ctx basecoin.Context, store state.KVStore,
|
func (w wrapped) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, _ basecoin.Checker) (basecoin.Result, error) {
|
tx basecoin.Tx, _ basecoin.Checker) (basecoin.Result, error) {
|
||||||
return w.h.CheckTx(ctx, store, tx)
|
return w.h.CheckTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w wrapped) DeliverTx(ctx basecoin.Context, store state.KVStore,
|
func (w wrapped) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, _ basecoin.Deliver) (basecoin.Result, error) {
|
tx basecoin.Tx, _ basecoin.Deliver) (basecoin.Result, error) {
|
||||||
return w.h.DeliverTx(ctx, store, tx)
|
return w.h.DeliverTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w wrapped) SetOption(l log.Logger, store state.KVStore,
|
func (w wrapped) SetOption(l log.Logger, store state.SimpleDB,
|
||||||
module, key, value string, _ basecoin.SetOptioner) (string, error) {
|
module, key, value string, _ basecoin.SetOptioner) (string, error) {
|
||||||
return w.h.SetOption(l, store, module, key, value)
|
return w.h.SetOption(l, store, module, key, value)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ func (m *middleware) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTx always returns an empty success tx
|
// CheckTx always returns an empty success tx
|
||||||
func (m *middleware) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (basecoin.Result, error) {
|
func (m *middleware) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (basecoin.Result, error) {
|
||||||
// make sure we pass in proper context to child
|
// make sure we pass in proper context to child
|
||||||
next := secureCheck(m.next, ctx)
|
next := secureCheck(m.next, ctx)
|
||||||
// set the permissions for this app
|
// set the permissions for this app
|
||||||
|
@ -33,7 +33,7 @@ func (m *middleware) CheckTx(ctx basecoin.Context, store state.KVStore, tx basec
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx always returns an empty success tx
|
// DeliverTx always returns an empty success tx
|
||||||
func (m *middleware) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (m *middleware) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
||||||
// make sure we pass in proper context to child
|
// make sure we pass in proper context to child
|
||||||
next := secureDeliver(m.next, ctx)
|
next := secureDeliver(m.next, ctx)
|
||||||
// set the permissions for this app
|
// set the permissions for this app
|
||||||
|
@ -43,7 +43,7 @@ func (m *middleware) DeliverTx(ctx basecoin.Context, store state.KVStore, tx bas
|
||||||
return m.middleware.DeliverTx(ctx, store, tx, next)
|
return m.middleware.DeliverTx(ctx, store, tx, next)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *middleware) SetOption(l log.Logger, store state.KVStore, module, key, value string) (string, error) {
|
func (m *middleware) SetOption(l log.Logger, store state.SimpleDB, module, key, value string) (string, error) {
|
||||||
// set the namespace for the app
|
// set the namespace for the app
|
||||||
store = stateSpace(store, m.Name())
|
store = stateSpace(store, m.Name())
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
package stack
|
package stack
|
||||||
|
|
||||||
import "github.com/tendermint/basecoin/state"
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/tendermint/basecoin/state"
|
||||||
|
)
|
||||||
|
|
||||||
type prefixStore struct {
|
type prefixStore struct {
|
||||||
prefix []byte
|
prefix []byte
|
||||||
store state.KVStore
|
store state.SimpleDB
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ state.KVStore = prefixStore{}
|
var _ state.SimpleDB = prefixStore{}
|
||||||
|
|
||||||
func (p prefixStore) Set(key, value []byte) {
|
func (p prefixStore) Set(key, value []byte) {
|
||||||
key = append(p.prefix, key...)
|
key = append(p.prefix, key...)
|
||||||
|
@ -19,11 +24,78 @@ func (p prefixStore) Get(key []byte) (value []byte) {
|
||||||
return p.store.Get(key)
|
return p.store.Get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p prefixStore) Has(key []byte) bool {
|
||||||
|
key = append(p.prefix, key...)
|
||||||
|
return p.store.Has(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p prefixStore) Remove(key []byte) (value []byte) {
|
||||||
|
key = append(p.prefix, key...)
|
||||||
|
return p.store.Remove(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p prefixStore) List(start, end []byte, limit int) []state.Model {
|
||||||
|
start = append(p.prefix, start...)
|
||||||
|
end = append(p.prefix, end...)
|
||||||
|
res := p.store.List(start, end, limit)
|
||||||
|
|
||||||
|
trim := len(p.prefix)
|
||||||
|
for i := range res {
|
||||||
|
res[i].Key = res[i].Key[trim:]
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p prefixStore) First(start, end []byte) state.Model {
|
||||||
|
start = append(p.prefix, start...)
|
||||||
|
end = append(p.prefix, end...)
|
||||||
|
res := p.store.First(start, end)
|
||||||
|
if len(res.Key) > 0 {
|
||||||
|
res.Key = res.Key[len(p.prefix):]
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p prefixStore) Last(start, end []byte) state.Model {
|
||||||
|
start = append(p.prefix, start...)
|
||||||
|
end = append(p.prefix, end...)
|
||||||
|
res := p.store.Last(start, end)
|
||||||
|
if len(res.Key) > 0 {
|
||||||
|
res.Key = res.Key[len(p.prefix):]
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p prefixStore) Checkpoint() state.SimpleDB {
|
||||||
|
return prefixStore{
|
||||||
|
prefix: p.prefix,
|
||||||
|
store: p.store.Checkpoint(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p prefixStore) Commit(sub state.SimpleDB) error {
|
||||||
|
ps, ok := sub.(prefixStore)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("Must commit prefixStore")
|
||||||
|
}
|
||||||
|
if !bytes.Equal(ps.prefix, p.prefix) {
|
||||||
|
return errors.New("Cannot commit sub-tx with different prefix")
|
||||||
|
}
|
||||||
|
|
||||||
|
// commit the wrapped data, don't worry about the prefix here
|
||||||
|
p.store.Commit(ps.store)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p prefixStore) Discard() {
|
||||||
|
p.store.Discard()
|
||||||
|
}
|
||||||
|
|
||||||
// stateSpace will unwrap any prefixStore and then add the prefix
|
// stateSpace will unwrap any prefixStore and then add the prefix
|
||||||
//
|
//
|
||||||
// this can be used by the middleware and dispatcher to isolate one space,
|
// this can be used by the middleware and dispatcher to isolate one space,
|
||||||
// then unwrap and isolate another space
|
// then unwrap and isolate another space
|
||||||
func stateSpace(store state.KVStore, app string) state.KVStore {
|
func stateSpace(store state.SimpleDB, app string) state.SimpleDB {
|
||||||
// unwrap one-level if wrapped
|
// unwrap one-level if wrapped
|
||||||
if pstore, ok := store.(prefixStore); ok {
|
if pstore, ok := store.(prefixStore); ok {
|
||||||
store = pstore.store
|
store = pstore.store
|
||||||
|
@ -31,13 +103,21 @@ func stateSpace(store state.KVStore, app string) state.KVStore {
|
||||||
return PrefixedStore(app, store)
|
return PrefixedStore(app, store)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func unwrap(store state.SimpleDB) state.SimpleDB {
|
||||||
|
// unwrap one-level if wrapped
|
||||||
|
if pstore, ok := store.(prefixStore); ok {
|
||||||
|
store = pstore.store
|
||||||
|
}
|
||||||
|
return store
|
||||||
|
}
|
||||||
|
|
||||||
// PrefixedStore allows one to create an isolated state-space for a given
|
// PrefixedStore allows one to create an isolated state-space for a given
|
||||||
// app prefix, but it cannot easily be unwrapped
|
// app prefix, but it cannot easily be unwrapped
|
||||||
//
|
//
|
||||||
// This is useful for tests or utilities that have access to the global
|
// This is useful for tests or utilities that have access to the global
|
||||||
// state to check individual app spaces. Individual apps should not be able
|
// state to check individual app spaces. Individual apps should not be able
|
||||||
// to use this to read each other's space
|
// to use this to read each other's space
|
||||||
func PrefixedStore(app string, store state.KVStore) state.KVStore {
|
func PrefixedStore(app string, store state.SimpleDB) state.SimpleDB {
|
||||||
prefix := append([]byte(app), byte(0))
|
prefix := append([]byte(app), byte(0))
|
||||||
return prefixStore{prefix, store}
|
return prefixStore{prefix, store}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ func (Recovery) Name() string {
|
||||||
var _ Middleware = Recovery{}
|
var _ Middleware = Recovery{}
|
||||||
|
|
||||||
// CheckTx catches any panic and converts to error - fulfills Middlware interface
|
// CheckTx catches any panic and converts to error - fulfills Middlware interface
|
||||||
func (Recovery) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
func (Recovery) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
err = normalizePanic(r)
|
err = normalizePanic(r)
|
||||||
|
@ -36,7 +36,7 @@ func (Recovery) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.T
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx catches any panic and converts to error - fulfills Middlware interface
|
// DeliverTx catches any panic and converts to error - fulfills Middlware interface
|
||||||
func (Recovery) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
func (Recovery) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
err = normalizePanic(r)
|
err = normalizePanic(r)
|
||||||
|
@ -46,7 +46,7 @@ func (Recovery) DeliverTx(ctx basecoin.Context, store state.KVStore, tx basecoin
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOption catches any panic and converts to error - fulfills Middlware interface
|
// SetOption catches any panic and converts to error - fulfills Middlware interface
|
||||||
func (Recovery) SetOption(l log.Logger, store state.KVStore, module, key, value string, next basecoin.SetOptioner) (log string, err error) {
|
func (Recovery) SetOption(l log.Logger, store state.SimpleDB, module, key, value string, next basecoin.SetOptioner) (log string, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
err = normalizePanic(r)
|
err = normalizePanic(r)
|
||||||
|
|
|
@ -23,19 +23,19 @@ var _ Middleware = writerMid{}
|
||||||
|
|
||||||
func (w writerMid) Name() string { return w.name }
|
func (w writerMid) Name() string { return w.name }
|
||||||
|
|
||||||
func (w writerMid) CheckTx(ctx basecoin.Context, store state.KVStore,
|
func (w writerMid) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, next basecoin.Checker) (basecoin.Result, error) {
|
tx basecoin.Tx, next basecoin.Checker) (basecoin.Result, error) {
|
||||||
store.Set(w.key, w.value)
|
store.Set(w.key, w.value)
|
||||||
return next.CheckTx(ctx, store, tx)
|
return next.CheckTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w writerMid) DeliverTx(ctx basecoin.Context, store state.KVStore,
|
func (w writerMid) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, next basecoin.Deliver) (basecoin.Result, error) {
|
tx basecoin.Tx, next basecoin.Deliver) (basecoin.Result, error) {
|
||||||
store.Set(w.key, w.value)
|
store.Set(w.key, w.value)
|
||||||
return next.DeliverTx(ctx, store, tx)
|
return next.DeliverTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w writerMid) SetOption(l log.Logger, store state.KVStore, module,
|
func (w writerMid) SetOption(l log.Logger, store state.SimpleDB, module,
|
||||||
key, value string, next basecoin.SetOptioner) (string, error) {
|
key, value string, next basecoin.SetOptioner) (string, error) {
|
||||||
store.Set([]byte(key), []byte(value))
|
store.Set([]byte(key), []byte(value))
|
||||||
return next.SetOption(l, store, module, key, value)
|
return next.SetOption(l, store, module, key, value)
|
||||||
|
@ -51,19 +51,19 @@ var _ basecoin.Handler = writerHand{}
|
||||||
|
|
||||||
func (w writerHand) Name() string { return w.name }
|
func (w writerHand) Name() string { return w.name }
|
||||||
|
|
||||||
func (w writerHand) CheckTx(ctx basecoin.Context, store state.KVStore,
|
func (w writerHand) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx) (basecoin.Result, error) {
|
tx basecoin.Tx) (basecoin.Result, error) {
|
||||||
store.Set(w.key, w.value)
|
store.Set(w.key, w.value)
|
||||||
return basecoin.Result{}, nil
|
return basecoin.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w writerHand) DeliverTx(ctx basecoin.Context, store state.KVStore,
|
func (w writerHand) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx) (basecoin.Result, error) {
|
tx basecoin.Tx) (basecoin.Result, error) {
|
||||||
store.Set(w.key, w.value)
|
store.Set(w.key, w.value)
|
||||||
return basecoin.Result{}, nil
|
return basecoin.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w writerHand) SetOption(l log.Logger, store state.KVStore, module,
|
func (w writerHand) SetOption(l log.Logger, store state.SimpleDB, module,
|
||||||
key, value string) (string, error) {
|
key, value string) (string, error) {
|
||||||
store.Set([]byte(key), []byte(value))
|
store.Set([]byte(key), []byte(value))
|
||||||
return "Success", nil
|
return "Success", nil
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
"github.com/tendermint/tmlibs/merkle"
|
||||||
|
)
|
||||||
|
|
||||||
|
// store nonce as it's own type so no one can even try to fake it
|
||||||
|
type nonce int64
|
||||||
|
|
||||||
|
// Bonsai is a deformed tree forced to fit in a small pot
|
||||||
|
type Bonsai struct {
|
||||||
|
id nonce
|
||||||
|
merkle.Tree
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ SimpleDB = &Bonsai{}
|
||||||
|
|
||||||
|
// NewBonsai wraps a merkle tree and tags it to track children
|
||||||
|
func NewBonsai(tree merkle.Tree) *Bonsai {
|
||||||
|
return &Bonsai{
|
||||||
|
id: nonce(rand.Int63()),
|
||||||
|
Tree: tree,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get matches the signature of KVStore
|
||||||
|
func (b *Bonsai) Get(key []byte) []byte {
|
||||||
|
_, value, _ := b.Tree.Get(key)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set matches the signature of KVStore
|
||||||
|
func (b *Bonsai) Set(key, value []byte) {
|
||||||
|
b.Tree.Set(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bonsai) Remove(key []byte) (value []byte) {
|
||||||
|
value, _ = b.Tree.Remove(key)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bonsai) List(start, end []byte, limit int) []Model {
|
||||||
|
res := []Model{}
|
||||||
|
stopAtCount := func(key []byte, value []byte) (stop bool) {
|
||||||
|
m := Model{key, value}
|
||||||
|
res = append(res, m)
|
||||||
|
return limit > 0 && len(res) >= limit
|
||||||
|
}
|
||||||
|
b.Tree.IterateRange(start, end, true, stopAtCount)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bonsai) First(start, end []byte) Model {
|
||||||
|
var m Model
|
||||||
|
stopAtFirst := func(key []byte, value []byte) (stop bool) {
|
||||||
|
m = Model{key, value}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
b.Tree.IterateRange(start, end, true, stopAtFirst)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bonsai) Last(start, end []byte) Model {
|
||||||
|
var m Model
|
||||||
|
stopAtFirst := func(key []byte, value []byte) (stop bool) {
|
||||||
|
m = Model{key, value}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
b.Tree.IterateRange(start, end, false, stopAtFirst)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bonsai) Checkpoint() SimpleDB {
|
||||||
|
return NewMemKVCache(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bonsai) Commit(sub SimpleDB) error {
|
||||||
|
cache, ok := sub.(*MemKVCache)
|
||||||
|
if !ok {
|
||||||
|
return ErrNotASubTransaction()
|
||||||
|
}
|
||||||
|
// see if it was wrapping this struct
|
||||||
|
bb, ok := cache.store.(*Bonsai)
|
||||||
|
if !ok || (b.id != bb.id) {
|
||||||
|
return ErrNotASubTransaction()
|
||||||
|
}
|
||||||
|
|
||||||
|
// apply the cached data to the Bonsai
|
||||||
|
cache.applyCache()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------
|
||||||
|
// This is the checkpointing I want, but apparently iavl-tree is not
|
||||||
|
// as immutable as I hoped... paniced in multiple go-routines :(
|
||||||
|
//
|
||||||
|
// FIXME: use this code when iavltree is improved
|
||||||
|
|
||||||
|
// func (b *Bonsai) Checkpoint() SimpleDB {
|
||||||
|
// return &Bonsai{
|
||||||
|
// id: b.id,
|
||||||
|
// Tree: b.Tree.Copy(),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Commit will take all changes from the checkpoint and write
|
||||||
|
// // them to the parent.
|
||||||
|
// // Returns an error if this is not a child of this one
|
||||||
|
// func (b *Bonsai) Commit(sub SimpleDB) error {
|
||||||
|
// bb, ok := sub.(*Bonsai)
|
||||||
|
// if !ok || (b.id != bb.id) {
|
||||||
|
// return ErrNotASubTransaction()
|
||||||
|
// }
|
||||||
|
// b.Tree = bb.Tree
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Discard will remove reference to this
|
||||||
|
func (b *Bonsai) Discard() {
|
||||||
|
b.id = 0
|
||||||
|
b.Tree = nil
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package state
|
||||||
|
|
||||||
|
// ChainState maintains general information for the chain
|
||||||
|
type ChainState struct {
|
||||||
|
chainID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewChainState creates a blank state
|
||||||
|
func NewChainState() *ChainState {
|
||||||
|
return &ChainState{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var baseChainIDKey = []byte("base/chain_id")
|
||||||
|
|
||||||
|
// SetChainID stores the chain id in the store
|
||||||
|
func (s *ChainState) SetChainID(store KVStore, chainID string) {
|
||||||
|
s.chainID = chainID
|
||||||
|
store.Set(baseChainIDKey, []byte(chainID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetChainID gets the chain id from the cache or the store
|
||||||
|
func (s *ChainState) GetChainID(store KVStore) string {
|
||||||
|
if s.chainID != "" {
|
||||||
|
return s.chainID
|
||||||
|
}
|
||||||
|
s.chainID = string(store.Get(baseChainIDKey))
|
||||||
|
return s.chainID
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
//nolint
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
|
"github.com/tendermint/basecoin/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errNotASubTransaction = fmt.Errorf("Not a sub-transaction")
|
||||||
|
)
|
||||||
|
|
||||||
|
func ErrNotASubTransaction() errors.TMError {
|
||||||
|
return errors.WithCode(errNotASubTransaction, abci.CodeType_InternalError)
|
||||||
|
}
|
||||||
|
func IsNotASubTransactionErr(err error) bool {
|
||||||
|
return errors.IsSameError(errNotASubTransaction, err)
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
package state
|
||||||
|
|
||||||
|
// MemKVCache is designed to wrap MemKVStore as a cache
|
||||||
|
type MemKVCache struct {
|
||||||
|
store SimpleDB
|
||||||
|
cache *MemKVStore
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ SimpleDB = (*MemKVCache)(nil)
|
||||||
|
|
||||||
|
// NewMemKVCache wraps a cache around MemKVStore
|
||||||
|
//
|
||||||
|
// You probably don't want to use directly, but rather
|
||||||
|
// via MemKVCache.Checkpoint()
|
||||||
|
func NewMemKVCache(store SimpleDB) *MemKVCache {
|
||||||
|
if store == nil {
|
||||||
|
panic("wtf")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &MemKVCache{
|
||||||
|
store: store,
|
||||||
|
cache: NewMemKVStore(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets a key, fulfills KVStore interface
|
||||||
|
func (c *MemKVCache) Set(key []byte, value []byte) {
|
||||||
|
c.cache.Set(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get gets a key, fulfills KVStore interface
|
||||||
|
func (c *MemKVCache) Get(key []byte) (value []byte) {
|
||||||
|
value, ok := c.cache.m[string(key)]
|
||||||
|
if !ok {
|
||||||
|
value = c.store.Get(key)
|
||||||
|
c.cache.Set(key, value)
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has checks existence of a key, fulfills KVStore interface
|
||||||
|
func (c *MemKVCache) Has(key []byte) bool {
|
||||||
|
value := c.Get(key)
|
||||||
|
return value != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove uses nil value as a flag to delete... not ideal but good enough
|
||||||
|
// for testing
|
||||||
|
func (c *MemKVCache) Remove(key []byte) (value []byte) {
|
||||||
|
value = c.Get(key)
|
||||||
|
c.cache.Set(key, nil)
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
// List is also inefficiently implemented...
|
||||||
|
func (c *MemKVCache) List(start, end []byte, limit int) []Model {
|
||||||
|
orig := c.store.List(start, end, 0)
|
||||||
|
cached := c.cache.List(start, end, 0)
|
||||||
|
keys := c.combineLists(orig, cached)
|
||||||
|
|
||||||
|
// apply limit (too late)
|
||||||
|
if limit > 0 && len(keys) > 0 {
|
||||||
|
if limit > len(keys) {
|
||||||
|
limit = len(keys)
|
||||||
|
}
|
||||||
|
keys = keys[:limit]
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MemKVCache) combineLists(orig, cache []Model) []Model {
|
||||||
|
store := NewMemKVStore()
|
||||||
|
for _, m := range orig {
|
||||||
|
store.Set(m.Key, m.Value)
|
||||||
|
}
|
||||||
|
for _, m := range cache {
|
||||||
|
if m.Value == nil {
|
||||||
|
store.Remove([]byte(m.Key))
|
||||||
|
} else {
|
||||||
|
store.Set([]byte(m.Key), m.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return store.List(nil, nil, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// First is done with List, but could be much more efficient
|
||||||
|
func (c *MemKVCache) First(start, end []byte) Model {
|
||||||
|
data := c.List(start, end, 0)
|
||||||
|
if len(data) == 0 {
|
||||||
|
return Model{}
|
||||||
|
}
|
||||||
|
return data[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last is done with List, but could be much more efficient
|
||||||
|
func (c *MemKVCache) Last(start, end []byte) Model {
|
||||||
|
data := c.List(start, end, 0)
|
||||||
|
if len(data) == 0 {
|
||||||
|
return Model{}
|
||||||
|
}
|
||||||
|
return data[len(data)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checkpoint returns the same state, but where writes
|
||||||
|
// are buffered and don't affect the parent
|
||||||
|
func (c *MemKVCache) Checkpoint() SimpleDB {
|
||||||
|
return NewMemKVCache(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit will take all changes from the checkpoint and write
|
||||||
|
// them to the parent.
|
||||||
|
// Returns an error if this is not a child of this one
|
||||||
|
func (c *MemKVCache) Commit(sub SimpleDB) error {
|
||||||
|
cache, ok := sub.(*MemKVCache)
|
||||||
|
if !ok {
|
||||||
|
return ErrNotASubTransaction()
|
||||||
|
}
|
||||||
|
// TODO: see if it points to us
|
||||||
|
|
||||||
|
// apply the cached data to us
|
||||||
|
cache.applyCache()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// applyCache will apply all the cache methods to the underlying store
|
||||||
|
func (c *MemKVCache) applyCache() {
|
||||||
|
for k, v := range c.cache.m {
|
||||||
|
if v == nil {
|
||||||
|
c.store.Remove([]byte(k))
|
||||||
|
} else {
|
||||||
|
c.store.Set([]byte(k), v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discard will remove reference to this
|
||||||
|
func (c *MemKVCache) Discard() {
|
||||||
|
c.cache = NewMemKVStore()
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCache(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
init []Model
|
||||||
|
toGet []Model
|
||||||
|
toList []listQuery
|
||||||
|
|
||||||
|
setCache []Model
|
||||||
|
removeCache []Model
|
||||||
|
getCache []Model
|
||||||
|
listCache []listQuery
|
||||||
|
}{
|
||||||
|
// simple add
|
||||||
|
{
|
||||||
|
init: []Model{m("a", "1"), m("c", "2")},
|
||||||
|
toGet: []Model{m("a", "1"), m("c", "2"), m("d", "")},
|
||||||
|
toList: []listQuery{{
|
||||||
|
"a", "e", 0,
|
||||||
|
[]Model{m("a", "1"), m("c", "2")},
|
||||||
|
m("c", "2"),
|
||||||
|
}},
|
||||||
|
setCache: []Model{m("d", "3")},
|
||||||
|
removeCache: []Model{m("a", "1")},
|
||||||
|
getCache: []Model{m("a", ""), m("c", "2"), m("d", "3")},
|
||||||
|
listCache: []listQuery{{
|
||||||
|
"a", "e", 0,
|
||||||
|
[]Model{m("c", "2"), m("d", "3")},
|
||||||
|
m("d", "3"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
checkGet := func(db SimpleDB, m Model, msg string) {
|
||||||
|
val := db.Get(m.Key)
|
||||||
|
assert.EqualValues(m.Value, val, msg)
|
||||||
|
has := db.Has(m.Key)
|
||||||
|
assert.Equal(len(m.Value) != 0, has, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkList := func(db SimpleDB, lq listQuery, msg string) {
|
||||||
|
start, end := []byte(lq.start), []byte(lq.end)
|
||||||
|
list := db.List(start, end, lq.limit)
|
||||||
|
if assert.EqualValues(lq.expected, list, msg) {
|
||||||
|
var first Model
|
||||||
|
if len(lq.expected) > 0 {
|
||||||
|
first = lq.expected[0]
|
||||||
|
}
|
||||||
|
f := db.First(start, end)
|
||||||
|
assert.EqualValues(first, f, msg)
|
||||||
|
l := db.Last(start, end)
|
||||||
|
assert.EqualValues(lq.last, l, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range cases {
|
||||||
|
for j, db := range GetDBs() {
|
||||||
|
for _, s := range tc.init {
|
||||||
|
db.Set(s.Key, s.Value)
|
||||||
|
}
|
||||||
|
for k, g := range tc.toGet {
|
||||||
|
msg := fmt.Sprintf("%d/%d/%d: %#v", i, j, k, g)
|
||||||
|
checkGet(db, g, msg)
|
||||||
|
}
|
||||||
|
for k, lq := range tc.toList {
|
||||||
|
msg := fmt.Sprintf("%d/%d/%d", i, j, k)
|
||||||
|
checkList(db, lq, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make cache
|
||||||
|
cache := db.Checkpoint()
|
||||||
|
|
||||||
|
for _, s := range tc.setCache {
|
||||||
|
cache.Set(s.Key, s.Value)
|
||||||
|
}
|
||||||
|
for k, r := range tc.removeCache {
|
||||||
|
val := cache.Remove(r.Key)
|
||||||
|
assert.EqualValues(r.Value, val, "%d/%d/%d: %#v", i, j, k, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure data is in cache
|
||||||
|
for k, g := range tc.getCache {
|
||||||
|
msg := fmt.Sprintf("%d/%d/%d: %#v", i, j, k, g)
|
||||||
|
checkGet(cache, g, msg)
|
||||||
|
}
|
||||||
|
for k, lq := range tc.listCache {
|
||||||
|
msg := fmt.Sprintf("%d/%d/%d", i, j, k)
|
||||||
|
checkList(cache, lq, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// data not in basic store
|
||||||
|
for k, g := range tc.toGet {
|
||||||
|
msg := fmt.Sprintf("%d/%d/%d: %#v", i, j, k, g)
|
||||||
|
checkGet(db, g, msg)
|
||||||
|
}
|
||||||
|
for k, lq := range tc.toList {
|
||||||
|
msg := fmt.Sprintf("%d/%d/%d", i, j, k)
|
||||||
|
checkList(db, lq, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// commit
|
||||||
|
db.Commit(cache)
|
||||||
|
|
||||||
|
// make sure data is in cache
|
||||||
|
for k, g := range tc.getCache {
|
||||||
|
msg := fmt.Sprintf("%d/%d/%d", i, j, k)
|
||||||
|
checkGet(db, g, msg)
|
||||||
|
}
|
||||||
|
for k, lq := range tc.listCache {
|
||||||
|
msg := fmt.Sprintf("%d/%d/%d", i, j, k)
|
||||||
|
checkList(db, lq, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
216
state/kvstore.go
216
state/kvstore.go
|
@ -1,12 +1,12 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"container/list"
|
"sort"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
. "github.com/tendermint/tmlibs/common"
|
"github.com/tendermint/go-wire/data"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// KVStore is a simple interface to get/set data
|
||||||
type KVStore interface {
|
type KVStore interface {
|
||||||
Set(key, value []byte)
|
Set(key, value []byte)
|
||||||
Get(key []byte) (value []byte)
|
Get(key []byte) (value []byte)
|
||||||
|
@ -14,125 +14,157 @@ type KVStore interface {
|
||||||
|
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
|
|
||||||
|
// Model grabs together key and value to allow easier return values
|
||||||
|
type Model struct {
|
||||||
|
Key data.Bytes
|
||||||
|
Value data.Bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// SimpleDB allows us to do some basic range queries on a db
|
||||||
|
type SimpleDB interface {
|
||||||
|
KVStore
|
||||||
|
|
||||||
|
Has(key []byte) (has bool)
|
||||||
|
Remove(key []byte) (value []byte) // returns old value if there was one
|
||||||
|
|
||||||
|
// Start is inclusive, End is exclusive...
|
||||||
|
// Thus List ([]byte{12, 13}, []byte{12, 14}) will return anything with
|
||||||
|
// the prefix []byte{12, 13}
|
||||||
|
List(start, end []byte, limit int) []Model
|
||||||
|
First(start, end []byte) Model
|
||||||
|
Last(start, end []byte) Model
|
||||||
|
|
||||||
|
// Checkpoint returns the same state, but where writes
|
||||||
|
// are buffered and don't affect the parent
|
||||||
|
Checkpoint() SimpleDB
|
||||||
|
|
||||||
|
// Commit will take all changes from the checkpoint and write
|
||||||
|
// them to the parent.
|
||||||
|
// Returns an error if this is not a child of this one
|
||||||
|
Commit(SimpleDB) error
|
||||||
|
|
||||||
|
// Discard will remove reference to this
|
||||||
|
Discard()
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------
|
||||||
|
|
||||||
|
// MemKVStore is a simple implementation of SimpleDB.
|
||||||
|
// It is only intended for quick testing, not to be used
|
||||||
|
// in production or with large data stores.
|
||||||
type MemKVStore struct {
|
type MemKVStore struct {
|
||||||
m map[string][]byte
|
m map[string][]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ SimpleDB = NewMemKVStore()
|
||||||
|
|
||||||
|
// NewMemKVStore initializes a MemKVStore
|
||||||
func NewMemKVStore() *MemKVStore {
|
func NewMemKVStore() *MemKVStore {
|
||||||
return &MemKVStore{
|
return &MemKVStore{
|
||||||
m: make(map[string][]byte, 0),
|
m: make(map[string][]byte, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mkv *MemKVStore) Set(key []byte, value []byte) {
|
func (m *MemKVStore) Set(key []byte, value []byte) {
|
||||||
mkv.m[string(key)] = value
|
m.m[string(key)] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mkv *MemKVStore) Get(key []byte) (value []byte) {
|
func (m *MemKVStore) Get(key []byte) (value []byte) {
|
||||||
return mkv.m[string(key)]
|
return m.m[string(key)]
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------
|
func (m *MemKVStore) Has(key []byte) (has bool) {
|
||||||
|
_, ok := m.m[string(key)]
|
||||||
// A Cache that enforces deterministic sync order.
|
return ok
|
||||||
type KVCache struct {
|
|
||||||
store KVStore
|
|
||||||
cache map[string]kvCacheValue
|
|
||||||
keys *list.List
|
|
||||||
logging bool
|
|
||||||
logLines []string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type kvCacheValue struct {
|
func (m *MemKVStore) Remove(key []byte) (value []byte) {
|
||||||
v []byte // The value of some key
|
val := m.m[string(key)]
|
||||||
e *list.Element // The KVCache.keys element
|
delete(m.m, string(key))
|
||||||
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: If store is nil, creates a new MemKVStore
|
func (m *MemKVStore) List(start, end []byte, limit int) []Model {
|
||||||
func NewKVCache(store KVStore) *KVCache {
|
keys := m.keysInRange(start, end)
|
||||||
if store == nil {
|
if limit > 0 && len(keys) > 0 {
|
||||||
store = NewMemKVStore()
|
if limit > len(keys) {
|
||||||
|
limit = len(keys)
|
||||||
}
|
}
|
||||||
return (&KVCache{
|
keys = keys[:limit]
|
||||||
store: store,
|
|
||||||
}).Reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kvc *KVCache) SetLogging() {
|
|
||||||
kvc.logging = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kvc *KVCache) GetLogLines() []string {
|
|
||||||
return kvc.logLines
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kvc *KVCache) ClearLogLines() {
|
|
||||||
kvc.logLines = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kvc *KVCache) Reset() *KVCache {
|
|
||||||
kvc.cache = make(map[string]kvCacheValue)
|
|
||||||
kvc.keys = list.New()
|
|
||||||
return kvc
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kvc *KVCache) Set(key []byte, value []byte) {
|
|
||||||
if kvc.logging {
|
|
||||||
line := fmt.Sprintf("Set %v = %v", LegibleBytes(key), LegibleBytes(value))
|
|
||||||
kvc.logLines = append(kvc.logLines, line)
|
|
||||||
}
|
}
|
||||||
cacheValue, ok := kvc.cache[string(key)]
|
|
||||||
if ok {
|
res := make([]Model, len(keys))
|
||||||
kvc.keys.MoveToBack(cacheValue.e)
|
for i, k := range keys {
|
||||||
} else {
|
res[i] = Model{
|
||||||
cacheValue.e = kvc.keys.PushBack(key)
|
Key: []byte(k),
|
||||||
|
Value: m.m[k],
|
||||||
}
|
}
|
||||||
cacheValue.v = value
|
}
|
||||||
kvc.cache[string(key)] = cacheValue
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kvc *KVCache) Get(key []byte) (value []byte) {
|
// First iterates through all keys to find the one that matches
|
||||||
cacheValue, ok := kvc.cache[string(key)]
|
func (m *MemKVStore) First(start, end []byte) Model {
|
||||||
if ok {
|
key := ""
|
||||||
if kvc.logging {
|
for _, k := range m.keysInRange(start, end) {
|
||||||
line := fmt.Sprintf("Get (hit) %v = %v", LegibleBytes(key), LegibleBytes(cacheValue.v))
|
if key == "" || k < key {
|
||||||
kvc.logLines = append(kvc.logLines, line)
|
key = k
|
||||||
}
|
}
|
||||||
return cacheValue.v
|
|
||||||
} else {
|
|
||||||
value := kvc.store.Get(key)
|
|
||||||
kvc.cache[string(key)] = kvCacheValue{
|
|
||||||
v: value,
|
|
||||||
e: kvc.keys.PushBack(key),
|
|
||||||
}
|
}
|
||||||
if kvc.logging {
|
if key == "" {
|
||||||
line := fmt.Sprintf("Get (miss) %v = %v", LegibleBytes(key), LegibleBytes(value))
|
return Model{}
|
||||||
kvc.logLines = append(kvc.logLines, line)
|
|
||||||
}
|
}
|
||||||
return value
|
return Model{
|
||||||
|
Key: []byte(key),
|
||||||
|
Value: m.m[key],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Update the store with the values from the cache
|
func (m *MemKVStore) Last(start, end []byte) Model {
|
||||||
func (kvc *KVCache) Sync() {
|
key := ""
|
||||||
for e := kvc.keys.Front(); e != nil; e = e.Next() {
|
for _, k := range m.keysInRange(start, end) {
|
||||||
key := e.Value.([]byte)
|
if key == "" || k > key {
|
||||||
value := kvc.cache[string(key)]
|
key = k
|
||||||
kvc.store.Set(key, value.v)
|
}
|
||||||
|
}
|
||||||
|
if key == "" {
|
||||||
|
return Model{}
|
||||||
|
}
|
||||||
|
return Model{
|
||||||
|
Key: []byte(key),
|
||||||
|
Value: m.m[key],
|
||||||
}
|
}
|
||||||
kvc.Reset()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------
|
func (m *MemKVStore) Discard() {
|
||||||
|
m.m = make(map[string][]byte, 0)
|
||||||
func LegibleBytes(data []byte) string {
|
}
|
||||||
s := ""
|
|
||||||
for _, b := range data {
|
func (m *MemKVStore) Checkpoint() SimpleDB {
|
||||||
if 0x21 <= b && b < 0x7F {
|
return NewMemKVCache(m)
|
||||||
s += Green(string(b))
|
}
|
||||||
} else {
|
|
||||||
s += Blue(Fmt("%02X", b))
|
func (m *MemKVStore) Commit(sub SimpleDB) error {
|
||||||
}
|
cache, ok := sub.(*MemKVCache)
|
||||||
}
|
if !ok {
|
||||||
return s
|
return ErrNotASubTransaction()
|
||||||
|
}
|
||||||
|
// TODO: see if it points to us
|
||||||
|
|
||||||
|
// apply the cached data to us
|
||||||
|
cache.applyCache()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MemKVStore) keysInRange(start, end []byte) (res []string) {
|
||||||
|
s, e := string(start), string(end)
|
||||||
|
for k := range m.m {
|
||||||
|
afterStart := s == "" || k >= s
|
||||||
|
beforeEnd := e == "" || k < e
|
||||||
|
if afterStart && beforeEnd {
|
||||||
|
res = append(res, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(res)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
package state
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestKVStore(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
//stores to be tested
|
|
||||||
ms := NewMemKVStore()
|
|
||||||
store := NewMemKVStore()
|
|
||||||
kvc := NewKVCache(store)
|
|
||||||
|
|
||||||
//key value pairs to be tested within the system
|
|
||||||
var keyvalue = []struct {
|
|
||||||
key string
|
|
||||||
value string
|
|
||||||
}{
|
|
||||||
{"foo", "snake"},
|
|
||||||
{"bar", "mouse"},
|
|
||||||
}
|
|
||||||
|
|
||||||
//set the kvc to have all the key value pairs
|
|
||||||
setRecords := func(kv KVStore) {
|
|
||||||
for _, n := range keyvalue {
|
|
||||||
kv.Set([]byte(n.key), []byte(n.value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//store has all the key value pairs
|
|
||||||
storeHasAll := func(kv KVStore) bool {
|
|
||||||
for _, n := range keyvalue {
|
|
||||||
if !bytes.Equal(kv.Get([]byte(n.key)), []byte(n.value)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
//test read/write for MemKVStore
|
|
||||||
setRecords(ms)
|
|
||||||
assert.True(storeHasAll(ms), "MemKVStore doesn't retrieve after Set")
|
|
||||||
|
|
||||||
//test read/write for KVCache
|
|
||||||
setRecords(kvc)
|
|
||||||
assert.True(storeHasAll(kvc), "KVCache doesn't retrieve after Set")
|
|
||||||
|
|
||||||
//test reset
|
|
||||||
kvc.Reset()
|
|
||||||
assert.False(storeHasAll(kvc), "KVCache retrieving after reset")
|
|
||||||
|
|
||||||
//test sync
|
|
||||||
setRecords(kvc)
|
|
||||||
assert.False(storeHasAll(store), "store retrieving before synced")
|
|
||||||
kvc.Sync()
|
|
||||||
assert.True(storeHasAll(store), "store isn't retrieving after synced")
|
|
||||||
|
|
||||||
//test logging
|
|
||||||
assert.Zero(len(kvc.GetLogLines()), "logging events existed before using SetLogging")
|
|
||||||
kvc.SetLogging()
|
|
||||||
setRecords(kvc)
|
|
||||||
assert.Equal(len(kvc.GetLogLines()), 2, "incorrect number of logging events recorded")
|
|
||||||
kvc.ClearLogLines()
|
|
||||||
assert.Zero(len(kvc.GetLogLines()), "logging events still exists after ClearLogLines")
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tendermint/merkleeyes/iavl"
|
||||||
|
"github.com/tendermint/tmlibs/merkle"
|
||||||
|
)
|
||||||
|
|
||||||
|
// State represents the app states, separating the commited state (for queries)
|
||||||
|
// from the working state (for CheckTx and AppendTx)
|
||||||
|
type State struct {
|
||||||
|
committed *Bonsai
|
||||||
|
deliverTx SimpleDB
|
||||||
|
checkTx SimpleDB
|
||||||
|
persistent bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewState(tree merkle.Tree, persistent bool) State {
|
||||||
|
base := NewBonsai(tree)
|
||||||
|
return State{
|
||||||
|
committed: base,
|
||||||
|
deliverTx: base.Checkpoint(),
|
||||||
|
checkTx: base.Checkpoint(),
|
||||||
|
persistent: persistent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s State) Committed() *Bonsai {
|
||||||
|
return s.committed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s State) Append() SimpleDB {
|
||||||
|
return s.deliverTx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s State) Check() SimpleDB {
|
||||||
|
return s.checkTx
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash applies deliverTx to committed and calculates hash
|
||||||
|
//
|
||||||
|
// Note the usage:
|
||||||
|
// Hash -> gets the calculated hash but doesn't save
|
||||||
|
// BatchSet -> adds some out-of-bounds data
|
||||||
|
// Commit -> Save everything to disk and the same hash
|
||||||
|
func (s *State) Hash() ([]byte, error) {
|
||||||
|
err := s.committed.Commit(s.deliverTx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.deliverTx = s.committed.Checkpoint()
|
||||||
|
return s.committed.Tree.Hash(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchSet is used for some weird magic in storing the new height
|
||||||
|
func (s *State) BatchSet(key, value []byte) {
|
||||||
|
if s.persistent {
|
||||||
|
// This is in the batch with the Save, but not in the tree
|
||||||
|
tree, ok := s.committed.Tree.(*iavl.IAVLTree)
|
||||||
|
if ok {
|
||||||
|
tree.BatchSet(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit save persistent nodes to the database and re-copies the trees
|
||||||
|
func (s *State) Commit() ([]byte, error) {
|
||||||
|
// commit (if we didn't do hash earlier)
|
||||||
|
err := s.committed.Commit(s.deliverTx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var hash []byte
|
||||||
|
if s.persistent {
|
||||||
|
hash = s.committed.Tree.Save()
|
||||||
|
} else {
|
||||||
|
hash = s.committed.Tree.Hash()
|
||||||
|
}
|
||||||
|
|
||||||
|
s.deliverTx = s.committed.Checkpoint()
|
||||||
|
s.checkTx = s.committed.Checkpoint()
|
||||||
|
return hash, nil
|
||||||
|
}
|
|
@ -1,84 +0,0 @@
|
||||||
package state
|
|
||||||
|
|
||||||
import (
|
|
||||||
abci "github.com/tendermint/abci/types"
|
|
||||||
eyes "github.com/tendermint/merkleeyes/client"
|
|
||||||
"github.com/tendermint/tmlibs/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CONTRACT: State should be quick to copy.
|
|
||||||
// See CacheWrap().
|
|
||||||
type State struct {
|
|
||||||
chainID string
|
|
||||||
store KVStore
|
|
||||||
readCache map[string][]byte // optional, for caching writes to store
|
|
||||||
writeCache *KVCache // optional, for caching writes w/o writing to store
|
|
||||||
logger log.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewState(store KVStore, l log.Logger) *State {
|
|
||||||
return &State{
|
|
||||||
chainID: "",
|
|
||||||
store: store,
|
|
||||||
readCache: make(map[string][]byte),
|
|
||||||
writeCache: nil,
|
|
||||||
logger: l,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *State) SetChainID(chainID string) {
|
|
||||||
s.chainID = chainID
|
|
||||||
s.store.Set([]byte("base/chain_id"), []byte(chainID))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *State) GetChainID() string {
|
|
||||||
if s.chainID != "" {
|
|
||||||
return s.chainID
|
|
||||||
}
|
|
||||||
s.chainID = string(s.store.Get([]byte("base/chain_id")))
|
|
||||||
return s.chainID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *State) Get(key []byte) (value []byte) {
|
|
||||||
if s.readCache != nil { //if not a cachewrap
|
|
||||||
value, ok := s.readCache[string(key)]
|
|
||||||
if ok {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s.store.Get(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *State) Set(key []byte, value []byte) {
|
|
||||||
if s.readCache != nil { //if not a cachewrap
|
|
||||||
s.readCache[string(key)] = value
|
|
||||||
}
|
|
||||||
s.store.Set(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *State) CacheWrap() *State {
|
|
||||||
cache := NewKVCache(s)
|
|
||||||
return &State{
|
|
||||||
chainID: s.chainID,
|
|
||||||
store: cache,
|
|
||||||
readCache: nil,
|
|
||||||
writeCache: cache,
|
|
||||||
logger: s.logger,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: errors if s is not from CacheWrap()
|
|
||||||
func (s *State) CacheSync() {
|
|
||||||
s.writeCache.Sync()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *State) Commit() abci.Result {
|
|
||||||
switch s.store.(type) {
|
|
||||||
case *eyes.Client:
|
|
||||||
s.readCache = make(map[string][]byte)
|
|
||||||
return s.store.(*eyes.Client).CommitSync()
|
|
||||||
default:
|
|
||||||
return abci.NewError(abci.CodeType_InternalError, "can only use Commit if store is merkleeyes")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
package state
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
eyes "github.com/tendermint/merkleeyes/client"
|
|
||||||
"github.com/tendermint/tmlibs/log"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestState(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
//States and Stores for tests
|
|
||||||
store := NewMemKVStore()
|
|
||||||
state := NewState(store, log.TestingLogger())
|
|
||||||
cache := state.CacheWrap()
|
|
||||||
eyesCli := eyes.NewLocalClient("", 0)
|
|
||||||
|
|
||||||
//reset the store/state/cache
|
|
||||||
reset := func() {
|
|
||||||
store = NewMemKVStore()
|
|
||||||
state = NewState(store, log.TestingLogger())
|
|
||||||
cache = state.CacheWrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
//set the state to using the eyesCli instead of MemKVStore
|
|
||||||
useEyesCli := func() {
|
|
||||||
state = NewState(eyesCli, log.TestingLogger())
|
|
||||||
cache = state.CacheWrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
//key value pairs to be tested within the system
|
|
||||||
keyvalue := []struct {
|
|
||||||
key string
|
|
||||||
value string
|
|
||||||
}{
|
|
||||||
{"foo", "snake"},
|
|
||||||
{"bar", "mouse"},
|
|
||||||
}
|
|
||||||
|
|
||||||
//set the kvc to have all the key value pairs
|
|
||||||
setRecords := func(kv KVStore) {
|
|
||||||
for _, n := range keyvalue {
|
|
||||||
kv.Set([]byte(n.key), []byte(n.value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//store has all the key value pairs
|
|
||||||
storeHasAll := func(kv KVStore) bool {
|
|
||||||
for _, n := range keyvalue {
|
|
||||||
if !bytes.Equal(kv.Get([]byte(n.key)), []byte(n.value)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
//test chainID
|
|
||||||
state.SetChainID("testchain")
|
|
||||||
assert.Equal(state.GetChainID(), "testchain", "ChainID is improperly stored")
|
|
||||||
|
|
||||||
//test basic retrieve
|
|
||||||
setRecords(state)
|
|
||||||
assert.True(storeHasAll(state), "state doesn't retrieve after Set")
|
|
||||||
|
|
||||||
//Test CacheWrap with local mem store
|
|
||||||
reset()
|
|
||||||
setRecords(cache)
|
|
||||||
assert.False(storeHasAll(store), "store retrieving before CacheSync")
|
|
||||||
cache.CacheSync()
|
|
||||||
assert.True(storeHasAll(store), "store doesn't retrieve after CacheSync")
|
|
||||||
|
|
||||||
//Test Commit on state with non-merkle store
|
|
||||||
assert.True(state.Commit().IsErr(), "Commit shouldn't work with non-merkle store")
|
|
||||||
|
|
||||||
//Test CacheWrap with merkleeyes client store
|
|
||||||
useEyesCli()
|
|
||||||
setRecords(cache)
|
|
||||||
assert.False(storeHasAll(eyesCli), "eyesCli retrieving before Commit")
|
|
||||||
cache.CacheSync()
|
|
||||||
assert.True(state.Commit().IsOK(), "Bad Commit")
|
|
||||||
assert.True(storeHasAll(eyesCli), "eyesCli doesn't retrieve after Commit")
|
|
||||||
}
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/tendermint/merkleeyes/iavl"
|
||||||
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetDBs() []SimpleDB {
|
||||||
|
// tree with persistence....
|
||||||
|
tmpDir, err := ioutil.TempDir("", "state-tests")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
db := dbm.NewDB("test-get-dbs", dbm.LevelDBBackendStr, tmpDir)
|
||||||
|
persist := iavl.NewIAVLTree(500, db)
|
||||||
|
|
||||||
|
return []SimpleDB{
|
||||||
|
NewMemKVStore(),
|
||||||
|
NewBonsai(iavl.NewIAVLTree(0, nil)),
|
||||||
|
NewBonsai(persist),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func b(k string) []byte {
|
||||||
|
if k == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return []byte(k)
|
||||||
|
}
|
||||||
|
|
||||||
|
func m(k, v string) Model {
|
||||||
|
return Model{
|
||||||
|
Key: b(k),
|
||||||
|
Value: b(v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type listQuery struct {
|
||||||
|
// this is the list query
|
||||||
|
start, end string
|
||||||
|
limit int
|
||||||
|
// expected result from List, first element also expected for First
|
||||||
|
expected []Model
|
||||||
|
// expected result from Last
|
||||||
|
last Model
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestKVStore makes sure that get/set/remove operations work,
|
||||||
|
// as well as list
|
||||||
|
func TestKVStore(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
toSet []Model
|
||||||
|
toRemove []Model
|
||||||
|
toGet []Model
|
||||||
|
toList []listQuery
|
||||||
|
}{
|
||||||
|
// simple add
|
||||||
|
{
|
||||||
|
toSet: []Model{m("a", "b"), m("c", "d")},
|
||||||
|
toRemove: nil,
|
||||||
|
toGet: []Model{m("a", "b")},
|
||||||
|
toList: []listQuery{
|
||||||
|
{
|
||||||
|
"a", "d", 0,
|
||||||
|
[]Model{m("a", "b"), m("c", "d")},
|
||||||
|
m("c", "d"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"a", "c", 10,
|
||||||
|
[]Model{m("a", "b")},
|
||||||
|
m("a", "b"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// over-write data, remove
|
||||||
|
{
|
||||||
|
toSet: []Model{
|
||||||
|
m("a", "1"),
|
||||||
|
m("b", "2"),
|
||||||
|
m("c", "3"),
|
||||||
|
m("b", "4"),
|
||||||
|
},
|
||||||
|
toRemove: []Model{m("c", "3")},
|
||||||
|
toGet: []Model{
|
||||||
|
m("a", "1"),
|
||||||
|
m("b", "4"),
|
||||||
|
m("c", ""),
|
||||||
|
},
|
||||||
|
toList: []listQuery{
|
||||||
|
{
|
||||||
|
"0d", "h", 1,
|
||||||
|
[]Model{m("a", "1")},
|
||||||
|
m("b", "4"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ad", "ak", 10,
|
||||||
|
[]Model{},
|
||||||
|
Model{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ad", "k", 0,
|
||||||
|
[]Model{m("b", "4")},
|
||||||
|
m("b", "4"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range cases {
|
||||||
|
for j, db := range GetDBs() {
|
||||||
|
for _, s := range tc.toSet {
|
||||||
|
db.Set(s.Key, s.Value)
|
||||||
|
}
|
||||||
|
for k, r := range tc.toRemove {
|
||||||
|
val := db.Remove(r.Key)
|
||||||
|
assert.EqualValues(r.Value, val, "%d/%d/%d", i, j, k)
|
||||||
|
}
|
||||||
|
for k, g := range tc.toGet {
|
||||||
|
val := db.Get(g.Key)
|
||||||
|
assert.EqualValues(g.Value, val, "%d/%d/%d", i, j, k)
|
||||||
|
has := db.Has(g.Key)
|
||||||
|
assert.Equal(len(g.Value) != 0, has, "%d/%d/%d", i, j, k)
|
||||||
|
}
|
||||||
|
for k, lq := range tc.toList {
|
||||||
|
start, end := []byte(lq.start), []byte(lq.end)
|
||||||
|
list := db.List(start, end, lq.limit)
|
||||||
|
if assert.EqualValues(lq.expected, list, "%d/%d/%d", i, j, k) {
|
||||||
|
var first Model
|
||||||
|
if len(lq.expected) > 0 {
|
||||||
|
first = lq.expected[0]
|
||||||
|
}
|
||||||
|
f := db.First(start, end)
|
||||||
|
assert.EqualValues(first, f, "%d/%d/%d", i, j, k)
|
||||||
|
l := db.Last(start, end)
|
||||||
|
assert.EqualValues(lq.last, l, "%d/%d/%d", i, j, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -68,7 +68,7 @@ test03SendMultiFromRole() {
|
||||||
# let's try to send money from the role directly without multisig
|
# let's try to send money from the role directly without multisig
|
||||||
FAIL=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=6000mycoin --from=$BANK --to=$TWO --sequence=1 --name=$POOR 2>/dev/null)
|
FAIL=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=6000mycoin --from=$BANK --to=$TWO --sequence=1 --name=$POOR 2>/dev/null)
|
||||||
assertFalse "need to assume role" $?
|
assertFalse "need to assume role" $?
|
||||||
FAIL=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=6000mycoin --from=$BANK --to=$TWO --sequence=1 --assume-role=bank --name=$POOR 2>/dev/null)
|
FAIL=$(echo qwertyuiop | ${CLIENT_EXE} tx send --amount=6000mycoin --from=$BANK --to=$TWO --sequence=2 --assume-role=bank --name=$POOR 2>/dev/null)
|
||||||
assertFalse "need two signatures" $?
|
assertFalse "need two signatures" $?
|
||||||
|
|
||||||
# okay, begin a multisig transaction mr. poor...
|
# okay, begin a multisig transaction mr. poor...
|
||||||
|
@ -76,8 +76,8 @@ test03SendMultiFromRole() {
|
||||||
echo qwertyuiop | ${CLIENT_EXE} tx send --amount=6000mycoin --from=$BANK --to=$TWO --sequence=1 --assume-role=bank --name=$POOR --multi --prepare=$TX_FILE
|
echo qwertyuiop | ${CLIENT_EXE} tx send --amount=6000mycoin --from=$BANK --to=$TWO --sequence=1 --assume-role=bank --name=$POOR --multi --prepare=$TX_FILE
|
||||||
assertTrue "line=${LINENO}, successfully prepare tx" $?
|
assertTrue "line=${LINENO}, successfully prepare tx" $?
|
||||||
# and get some dude to sign it
|
# and get some dude to sign it
|
||||||
FAIL=$(echo qwertyuiop | ${CLIENT_EXE} tx --in=$TX_FILE --name=$POOR 2>/dev/null)
|
# FAIL=$(echo qwertyuiop | ${CLIENT_EXE} tx --in=$TX_FILE --name=$POOR 2>/dev/null)
|
||||||
assertFalse "line=${LINENO}, double signing doesn't get bank" $?
|
# assertFalse "line=${LINENO}, double signing doesn't get bank" $?
|
||||||
# and get some dude to sign it for the full access
|
# and get some dude to sign it for the full access
|
||||||
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx --in=$TX_FILE --name=$DUDE)
|
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx --in=$TX_FILE --name=$DUDE)
|
||||||
txSucceeded $? "$TX" "multi-bank"
|
txSucceeded $? "$TX" "multi-bank"
|
||||||
|
|
Loading…
Reference in New Issue