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"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
"github.com/tendermint/basecoin"
|
||||
eyes "github.com/tendermint/merkleeyes/client"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
"github.com/tendermint/basecoin"
|
||||
"github.com/tendermint/basecoin/errors"
|
||||
"github.com/tendermint/basecoin/modules/auth"
|
||||
"github.com/tendermint/basecoin/modules/base"
|
||||
|
@ -30,9 +29,10 @@ const (
|
|||
|
||||
// Basecoin - The ABCI application
|
||||
type Basecoin struct {
|
||||
eyesCli *eyes.Client
|
||||
state *sm.State
|
||||
cacheState *sm.State
|
||||
info *sm.ChainState
|
||||
|
||||
state *Store
|
||||
|
||||
handler basecoin.Handler
|
||||
height uint64
|
||||
logger log.Logger
|
||||
|
@ -41,15 +41,11 @@ type Basecoin struct {
|
|||
var _ abci.Application = &Basecoin{}
|
||||
|
||||
// NewBasecoin - create a new instance of the basecoin application
|
||||
func NewBasecoin(handler basecoin.Handler, eyesCli *eyes.Client, logger log.Logger) *Basecoin {
|
||||
state := sm.NewState(eyesCli, logger.With("module", "state"))
|
||||
|
||||
func NewBasecoin(handler basecoin.Handler, store *Store, logger log.Logger) *Basecoin {
|
||||
return &Basecoin{
|
||||
handler: handler,
|
||||
eyesCli: eyesCli,
|
||||
state: state,
|
||||
cacheState: nil,
|
||||
height: 0,
|
||||
info: sm.NewChainState(),
|
||||
state: store,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
@ -68,23 +64,27 @@ func DefaultHandler(feeDenom string) basecoin.Handler {
|
|||
stack.Recovery{},
|
||||
auth.Signatures{},
|
||||
base.Chain{},
|
||||
stack.Checkpoint{OnCheck: true},
|
||||
nonce.ReplayCheck{},
|
||||
roles.NewMiddleware(),
|
||||
fee.NewSimpleFeeMiddleware(coin.Coin{feeDenom, 0}, fee.Bank),
|
||||
stack.Checkpoint{OnDeliver: true},
|
||||
).Use(d)
|
||||
}
|
||||
|
||||
// GetState - XXX For testing, not thread safe!
|
||||
func (app *Basecoin) GetState() *sm.State {
|
||||
return app.state.CacheWrap()
|
||||
// GetChainID returns the currently stored chain
|
||||
func (app *Basecoin) GetChainID() string {
|
||||
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
|
||||
func (app *Basecoin) Info() abci.ResponseInfo {
|
||||
resp, err := app.eyesCli.InfoSync()
|
||||
if err != nil {
|
||||
cmn.PanicCrisis(err)
|
||||
}
|
||||
resp := app.state.Info()
|
||||
app.height = resp.LastBlockHeight
|
||||
return abci.ResponseInfo{
|
||||
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 {
|
||||
|
||||
module, key := splitKey(key)
|
||||
state := app.state.Append()
|
||||
|
||||
if module == ModuleNameBase {
|
||||
if key == ChainKey {
|
||||
app.state.SetChainID(value)
|
||||
app.info.SetChainID(state, value)
|
||||
return "Success"
|
||||
}
|
||||
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 {
|
||||
return log
|
||||
}
|
||||
|
@ -120,21 +121,16 @@ func (app *Basecoin) DeliverTx(txBytes []byte) abci.Result {
|
|||
return errors.Result(err)
|
||||
}
|
||||
|
||||
// TODO: can we abstract this setup and commit logic??
|
||||
cache := app.state.CacheWrap()
|
||||
ctx := stack.NewContext(
|
||||
app.state.GetChainID(),
|
||||
app.GetChainID(),
|
||||
app.height,
|
||||
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 {
|
||||
// discard the cache...
|
||||
return errors.Result(err)
|
||||
}
|
||||
// commit the cache and return result
|
||||
cache.CacheSync()
|
||||
return res.ToABCI()
|
||||
}
|
||||
|
||||
|
@ -145,22 +141,16 @@ func (app *Basecoin) CheckTx(txBytes []byte) abci.Result {
|
|||
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(
|
||||
app.state.GetChainID(),
|
||||
app.GetChainID(),
|
||||
app.height,
|
||||
app.logger.With("call", "checktx"),
|
||||
)
|
||||
// checktx generally shouldn't touch the state, but we don't care
|
||||
// here on the framework level, since the cacheState is thrown away next block
|
||||
res, err := app.handler.CheckTx(ctx, cache, tx)
|
||||
res, err := app.handler.CheckTx(ctx, app.state.Check(), tx)
|
||||
|
||||
if err != nil {
|
||||
return errors.Result(err)
|
||||
}
|
||||
cache.CacheSync()
|
||||
return res.ToABCI()
|
||||
}
|
||||
|
||||
|
@ -172,24 +162,13 @@ func (app *Basecoin) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQu
|
|||
return
|
||||
}
|
||||
|
||||
resQuery, err := app.eyesCli.QuerySync(reqQuery)
|
||||
if err != nil {
|
||||
resQuery.Log = "Failed to query MerkleEyes: " + err.Error()
|
||||
resQuery.Code = abci.CodeType_InternalError
|
||||
return
|
||||
}
|
||||
return
|
||||
return app.state.Query(reqQuery)
|
||||
}
|
||||
|
||||
// Commit - ABCI
|
||||
func (app *Basecoin) Commit() (res abci.Result) {
|
||||
|
||||
// Commit state
|
||||
res = app.state.Commit()
|
||||
|
||||
// Wrap the committed state in cache for CheckTx
|
||||
app.cacheState = app.state.CacheWrap()
|
||||
|
||||
if res.IsErr() {
|
||||
cmn.PanicSanity("Error getting hash: " + res.Error())
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package app
|
|||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -18,7 +17,6 @@ import (
|
|||
"github.com/tendermint/basecoin/stack"
|
||||
"github.com/tendermint/basecoin/state"
|
||||
wire "github.com/tendermint/go-wire"
|
||||
eyes "github.com/tendermint/merkleeyes/client"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
)
|
||||
|
||||
|
@ -82,14 +80,16 @@ func (at *appTest) reset() {
|
|||
at.acctIn = coin.NewAccountWithKey(coin.Coins{{"mycoin", 7}})
|
||||
at.acctOut = coin.NewAccountWithKey(coin.Coins{{"mycoin", 7}})
|
||||
|
||||
eyesCli := eyes.NewLocalClient("", 0)
|
||||
// logger := log.TestingLogger().With("module", "app"),
|
||||
logger := log.NewTMLogger(os.Stdout).With("module", "app")
|
||||
logger = log.NewTracingLogger(logger)
|
||||
// Note: switch logger if you want to get more info
|
||||
logger := log.TestingLogger()
|
||||
// logger := log.NewTracingLogger(log.NewTMLogger(os.Stdout))
|
||||
store, err := NewStore("", 0, logger.With("module", "store"))
|
||||
require.Nil(at.t, err, "%+v", err)
|
||||
|
||||
at.app = NewBasecoin(
|
||||
DefaultHandler("mycoin"),
|
||||
eyesCli,
|
||||
logger,
|
||||
store,
|
||||
logger.With("module", "app"),
|
||||
)
|
||||
|
||||
res := at.app.SetOption("base/chain_id", at.chainID)
|
||||
|
@ -102,13 +102,13 @@ func (at *appTest) reset() {
|
|||
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)
|
||||
acct, err := coin.GetAccount(cspace, key)
|
||||
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)
|
||||
return getBalance(actor, state)
|
||||
}
|
||||
|
@ -142,17 +142,20 @@ func TestSetOption(t *testing.T) {
|
|||
assert := assert.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(
|
||||
DefaultHandler("atom"),
|
||||
eyesCli,
|
||||
log.TestingLogger().With("module", "app"),
|
||||
store,
|
||||
logger.With("module", "app"),
|
||||
)
|
||||
|
||||
//testing ChainID
|
||||
chainID := "testChain"
|
||||
res := app.SetOption("base/chain_id", chainID)
|
||||
assert.EqualValues(app.GetState().GetChainID(), chainID)
|
||||
assert.EqualValues(app.GetChainID(), chainID)
|
||||
assert.EqualValues(res, "Success")
|
||||
|
||||
// make a nice account...
|
||||
|
@ -162,7 +165,7 @@ func TestSetOption(t *testing.T) {
|
|||
require.EqualValues(res, "Success")
|
||||
|
||||
// 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)
|
||||
assert.Equal(bal, coins)
|
||||
|
||||
|
@ -189,7 +192,7 @@ func TestSetOption(t *testing.T) {
|
|||
res = app.SetOption("coin/account", unsortAcc)
|
||||
require.EqualValues(res, "Success")
|
||||
|
||||
coins, err = getAddr(unsortAddr, app.state)
|
||||
coins, err = getAddr(unsortAddr, app.GetState())
|
||||
require.Nil(err)
|
||||
assert.True(coins.IsValid())
|
||||
assert.Equal(unsortCoins, coins)
|
||||
|
@ -213,6 +216,8 @@ func TestTx(t *testing.T) {
|
|||
//Bad Balance
|
||||
at.acctIn.Coins = coin.Coins{{"mycoin", 2}}
|
||||
at.initAccount(at.acctIn)
|
||||
at.app.Commit()
|
||||
|
||||
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)
|
||||
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/require"
|
||||
|
||||
eyescli "github.com/tendermint/merkleeyes/client"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
|
@ -19,27 +18,32 @@ const genesisFilepath = "./testdata/genesis.json"
|
|||
const genesisAcctFilepath = "./testdata/genesis2.json"
|
||||
|
||||
func TestLoadGenesisDoNotFailIfAppOptionsAreMissing(t *testing.T) {
|
||||
eyesCli := eyescli.NewLocalClient("", 0)
|
||||
app := NewBasecoin(DefaultHandler("mycoin"), eyesCli, log.TestingLogger())
|
||||
err := app.LoadGenesis("./testdata/genesis3.json")
|
||||
logger := log.TestingLogger()
|
||||
store, err := NewStore("", 0, logger)
|
||||
require.Nil(t, err, "%+v", err)
|
||||
app := NewBasecoin(DefaultHandler("mycoin"), store, logger)
|
||||
err = app.LoadGenesis("./testdata/genesis3.json")
|
||||
require.Nil(t, err, "%+v", err)
|
||||
}
|
||||
|
||||
func TestLoadGenesis(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
eyesCli := eyescli.NewLocalClient("", 0)
|
||||
app := NewBasecoin(DefaultHandler("mycoin"), eyesCli, log.TestingLogger())
|
||||
err := app.LoadGenesis(genesisFilepath)
|
||||
logger := log.TestingLogger()
|
||||
store, err := NewStore("", 0, logger)
|
||||
require.Nil(err, "%+v", err)
|
||||
|
||||
app := NewBasecoin(DefaultHandler("mycoin"), store, logger)
|
||||
err = app.LoadGenesis(genesisFilepath)
|
||||
require.Nil(err, "%+v", err)
|
||||
|
||||
// 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
|
||||
addr, _ := hex.DecodeString("eb98e0688217cfdeb70eddf4b33cdcc37fc53197")
|
||||
|
||||
coins, err := getAddr(addr, app.state)
|
||||
coins, err := getAddr(addr, app.GetState())
|
||||
require.Nil(err)
|
||||
assert.True(coins.IsPositive())
|
||||
|
||||
|
@ -57,13 +61,16 @@ func TestLoadGenesis(t *testing.T) {
|
|||
func TestLoadGenesisAccountAddress(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
eyesCli := eyescli.NewLocalClient("", 0)
|
||||
app := NewBasecoin(DefaultHandler("mycoin"), eyesCli, log.TestingLogger())
|
||||
err := app.LoadGenesis(genesisAcctFilepath)
|
||||
logger := log.TestingLogger()
|
||||
store, err := NewStore("", 0, logger)
|
||||
require.Nil(err, "%+v", err)
|
||||
|
||||
app := NewBasecoin(DefaultHandler("mycoin"), store, logger)
|
||||
err = app.LoadGenesis(genesisAcctFilepath)
|
||||
require.Nil(err, "%+v", err)
|
||||
|
||||
// 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
|
||||
cases := []struct {
|
||||
|
@ -86,7 +93,7 @@ func TestLoadGenesisAccountAddress(t *testing.T) {
|
|||
for i, tc := range cases {
|
||||
addr, err := hex.DecodeString(tc.addr)
|
||||
require.Nil(err, tc.addr)
|
||||
coins, err := getAddr(addr, app.state)
|
||||
coins, err := getAddr(addr, app.GetState())
|
||||
require.Nil(err, "%+v", err)
|
||||
if !tc.exists {
|
||||
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"
|
||||
|
||||
wire "github.com/tendermint/go-wire"
|
||||
eyesApp "github.com/tendermint/merkleeyes/app"
|
||||
eyes "github.com/tendermint/merkleeyes/client"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
|
@ -56,18 +54,22 @@ func NewBenchApp(h basecoin.Handler, chainID string, n int,
|
|||
// logger = log.NewTracingLogger(logger)
|
||||
|
||||
// TODO: disk writing
|
||||
var eyesCli *eyes.Client
|
||||
var store *app.Store
|
||||
var err error
|
||||
|
||||
if persist {
|
||||
tmpDir, _ := ioutil.TempDir("", "bc-app-benchmark")
|
||||
eyesCli = eyes.NewLocalClient(tmpDir, 500)
|
||||
store, err = app.NewStore(tmpDir, 500, logger)
|
||||
} 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(
|
||||
h,
|
||||
eyesCli,
|
||||
store,
|
||||
logger.With("module", "app"),
|
||||
)
|
||||
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/basecoin"
|
||||
eyesApp "github.com/tendermint/merkleeyes/app"
|
||||
eyes "github.com/tendermint/merkleeyes/client"
|
||||
"github.com/tendermint/tmlibs/cli"
|
||||
cmn "github.com/tendermint/tmlibs/common"
|
||||
|
||||
|
@ -37,7 +35,6 @@ const EyesCacheSize = 10000
|
|||
//nolint
|
||||
const (
|
||||
FlagAddress = "address"
|
||||
FlagEyes = "eyes"
|
||||
FlagWithoutTendermint = "without-tendermint"
|
||||
)
|
||||
|
||||
|
@ -50,7 +47,6 @@ var (
|
|||
func init() {
|
||||
flags := StartCmd.Flags()
|
||||
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")
|
||||
// add all standard 'tendermint node' flags
|
||||
tcmd.AddNodeFlags(StartCmd)
|
||||
|
@ -58,27 +54,22 @@ func init() {
|
|||
|
||||
func startCmd(cmd *cobra.Command, args []string) error {
|
||||
rootDir := viper.GetString(cli.HomeFlag)
|
||||
meyes := viper.GetString(FlagEyes)
|
||||
|
||||
// Connect to MerkleEyes
|
||||
var eyesCli *eyes.Client
|
||||
if meyes == "local" {
|
||||
eyesApp.SetLogger(logger.With("module", "merkleeyes"))
|
||||
eyesCli = eyes.NewLocalClient(path.Join(rootDir, "data", "merkleeyes.db"), EyesCacheSize)
|
||||
} else {
|
||||
var err error
|
||||
eyesCli, err = eyes.NewClient(meyes)
|
||||
store, err := app.NewStore(
|
||||
path.Join(rootDir, "data", "merkleeyes.db"),
|
||||
EyesCacheSize,
|
||||
logger.With("module", "store"),
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Errorf("Error connecting to MerkleEyes: %v\n", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 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.
|
||||
// else, assume it's been loaded
|
||||
if basecoinApp.GetState().GetChainID() == "" {
|
||||
if basecoinApp.GetChainID() == "" {
|
||||
// If genesis file exists, set key-value options
|
||||
genesisFile := path.Join(rootDir, "genesis.json")
|
||||
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) {
|
||||
logger.Info("Starting Basecoin without Tendermint", "chain_id", chainID)
|
||||
// run just the abci app/server
|
||||
|
|
|
@ -102,8 +102,10 @@ func NewHandler(feeDenom string) basecoin.Handler {
|
|||
stack.Recovery{},
|
||||
auth.Signatures{},
|
||||
base.Chain{},
|
||||
stack.Checkpoint{OnCheck: true},
|
||||
nonce.ReplayCheck{},
|
||||
fee.NewSimpleFeeMiddleware(coin.Coin{feeDenom, 0}, fee.Bank),
|
||||
stack.Checkpoint{OnDeliver: true},
|
||||
).Use(dispatcher)
|
||||
}
|
||||
|
||||
|
@ -123,13 +125,13 @@ func (Handler) Name() string {
|
|||
func (Handler) AssertDispatcher() {}
|
||||
|
||||
// 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)
|
||||
return
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return res, err
|
||||
|
@ -201,7 +203,7 @@ func StateKey() []byte {
|
|||
}
|
||||
|
||||
// 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())
|
||||
if len(bytes) > 0 {
|
||||
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
|
||||
func SaveState(store state.KVStore, state State) error {
|
||||
func SaveState(store state.SimpleDB, state State) error {
|
||||
bytes := wire.BinaryBytes(state)
|
||||
store.Set(StateKey(), bytes)
|
||||
return nil
|
||||
|
|
|
@ -1,37 +1,40 @@
|
|||
package counter
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
abci "github.com/tendermint/abci/types"
|
||||
"github.com/tendermint/go-wire"
|
||||
"github.com/tendermint/tmlibs/log"
|
||||
|
||||
"github.com/tendermint/basecoin"
|
||||
"github.com/tendermint/basecoin/app"
|
||||
"github.com/tendermint/basecoin/modules/auth"
|
||||
"github.com/tendermint/basecoin/modules/base"
|
||||
"github.com/tendermint/basecoin/modules/coin"
|
||||
"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) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
// Basecoin initialization
|
||||
eyesCli := eyescli.NewLocalClient("", 0)
|
||||
chainID := "test_chain_id"
|
||||
logger := log.TestingLogger()
|
||||
// logger := log.NewTracingLogger(log.NewTMLogger(os.Stdout))
|
||||
|
||||
// logger := log.TestingLogger().With("module", "app"),
|
||||
logger := log.NewTMLogger(os.Stdout).With("module", "app")
|
||||
// logger = log.NewTracingLogger(logger)
|
||||
store, err := app.NewStore("", 0, logger.With("module", "store"))
|
||||
require.Nil(err, "%+v", err)
|
||||
|
||||
h := NewHandler("gold")
|
||||
bcApp := app.NewBasecoin(
|
||||
NewHandler("gold"),
|
||||
eyesCli,
|
||||
logger,
|
||||
h,
|
||||
store,
|
||||
logger.With("module", "app"),
|
||||
)
|
||||
bcApp.SetOption("base/chain_id", chainID)
|
||||
|
||||
|
@ -39,7 +42,7 @@ func TestCounterPlugin(t *testing.T) {
|
|||
bal := coin.Coins{{"", 1000}, {"gold", 1000}}
|
||||
acct := coin.NewAccountWithKey(bal)
|
||||
log := bcApp.SetOption("coin/account", acct.MakeOption())
|
||||
require.Equal(t, "Success", log)
|
||||
require.Equal("Success", log)
|
||||
|
||||
// Deliver a CounterTx
|
||||
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)
|
||||
assert.True(res.IsErr(), res.String())
|
||||
|
||||
// Test an invalid send, with supported fee
|
||||
res = DeliverCounterTx(true, coin.Coins{{"gold", 100}}, 2)
|
||||
// Test an invalid sequence
|
||||
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())
|
||||
|
||||
// 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())
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
hash: 8c438edb7d269da439141e62f3e0c931fa9efaee54b13ce1e7330dc99179fddd
|
||||
updated: 2017-07-20T15:39:36.659717024+02:00
|
||||
hash: 45eed61138603d4d03518ea822068cf32b45d0a219bb7f3b836e52129f2a3a2b
|
||||
updated: 2017-07-26T19:44:39.753066441-04:00
|
||||
imports:
|
||||
- name: github.com/bgentry/speakeasy
|
||||
version: 4aabc24848ce5fd31929f7d1e4ea74d3709c14cd
|
||||
|
@ -133,16 +133,15 @@ imports:
|
|||
- data
|
||||
- data/base58
|
||||
- name: github.com/tendermint/light-client
|
||||
version: d63415027075bc5d74a98a718393b59b5c4279a5
|
||||
version: 1c53d04dcc65c2fd15526152ed0651af10a09982
|
||||
subpackages:
|
||||
- certifiers
|
||||
- certifiers/client
|
||||
- certifiers/files
|
||||
- proofs
|
||||
- name: github.com/tendermint/merkleeyes
|
||||
version: 102aaf5a8ffda1846413fb22805a94def2045b9f
|
||||
version: 0310013053953eef80def3619aeb1e3a3254f452
|
||||
subpackages:
|
||||
- app
|
||||
- client
|
||||
- iavl
|
||||
- name: github.com/tendermint/tendermint
|
||||
|
@ -172,7 +171,7 @@ imports:
|
|||
- types
|
||||
- version
|
||||
- name: github.com/tendermint/tmlibs
|
||||
version: efb56aaea7517220bb3f42ff87b8004d554a17ff
|
||||
version: 2f6f3e6aa70bb19b70a6e73210273fa127041070
|
||||
subpackages:
|
||||
- autofile
|
||||
- cli
|
||||
|
|
|
@ -22,14 +22,14 @@ import:
|
|||
subpackages:
|
||||
- data
|
||||
- package: github.com/tendermint/light-client
|
||||
version: unstable
|
||||
version: 1c53d04dcc65c2fd15526152ed0651af10a09982
|
||||
subpackages:
|
||||
- proofs
|
||||
- certifiers
|
||||
- certifiers/client
|
||||
- certifiers/files
|
||||
- package: github.com/tendermint/merkleeyes
|
||||
version: develop
|
||||
version: unstable
|
||||
subpackages:
|
||||
- client
|
||||
- iavl
|
||||
|
|
30
handler.go
30
handler.go
|
@ -15,9 +15,9 @@ type Handler interface {
|
|||
SetOptioner
|
||||
Named
|
||||
// TODO: flesh these out as well
|
||||
// InitChain(store state.KVStore, vals []*abci.Validator)
|
||||
// BeginBlock(store state.KVStore, hash []byte, header *abci.Header)
|
||||
// EndBlock(store state.KVStore, height uint64) abci.ResponseEndBlock
|
||||
// InitChain(store state.SimpleDB, vals []*abci.Validator)
|
||||
// BeginBlock(store state.SimpleDB, hash []byte, header *abci.Header)
|
||||
// EndBlock(store state.SimpleDB, height uint64) abci.ResponseEndBlock
|
||||
}
|
||||
|
||||
type Named interface {
|
||||
|
@ -25,35 +25,35 @@ type Named 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
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -75,14 +75,14 @@ func (r Result) ToABCI() abci.Result {
|
|||
// holders
|
||||
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{}
|
||||
|
||||
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{}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ type Signable 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)
|
||||
if err != nil {
|
||||
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
|
||||
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)
|
||||
if err != nil {
|
||||
return res, err
|
||||
|
|
|
@ -24,7 +24,7 @@ func (Chain) Name() string {
|
|||
var _ stack.Middleware = Chain{}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
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
|
||||
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)
|
||||
if err != nil {
|
||||
return res, err
|
||||
|
|
|
@ -26,7 +26,7 @@ func (Logger) Name() string {
|
|||
var _ stack.Middleware = Logger{}
|
||||
|
||||
// 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()
|
||||
res, err = next.CheckTx(ctx, store, tx)
|
||||
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
|
||||
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()
|
||||
res, err = next.DeliverTx(ctx, store, tx)
|
||||
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
|
||||
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()
|
||||
res, err := next.SetOption(l, store, module, key, value)
|
||||
delta := time.Now().Sub(start)
|
||||
|
|
|
@ -29,7 +29,7 @@ func (Multiplexer) Name() string {
|
|||
var _ stack.Middleware = Multiplexer{}
|
||||
|
||||
// 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 {
|
||||
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
|
||||
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 {
|
||||
return runAll(ctx, store, mtx.Txs, next.DeliverTx)
|
||||
}
|
||||
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
|
||||
rs := make([]basecoin.Result, len(txs))
|
||||
for i, stx := range txs {
|
||||
|
|
|
@ -33,7 +33,7 @@ func accountQueryCmd(cmd *cobra.Command, args []string) error {
|
|||
acc := coin.Account{}
|
||||
proof, err := proofcmd.GetAndParseAppProof(key, &acc)
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -58,16 +58,8 @@ func readSendTxFlags() (tx basecoin.Tx, err error) {
|
|||
}
|
||||
|
||||
// craft the inputs and outputs
|
||||
ins := []coin.TxInput{{
|
||||
Address: fromAddr,
|
||||
Coins: amountCoins,
|
||||
}}
|
||||
outs := []coin.TxOutput{{
|
||||
Address: toAddr,
|
||||
Coins: amountCoins,
|
||||
}}
|
||||
|
||||
return coin.NewSendTx(ins, outs), nil
|
||||
tx = coin.NewSendOneTx(fromAddr, toAddr, amountCoins)
|
||||
return
|
||||
}
|
||||
|
||||
func readFromAddr() (basecoin.Actor, error) {
|
||||
|
|
|
@ -29,7 +29,7 @@ func (Handler) Name() string {
|
|||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return res, err
|
||||
|
@ -48,7 +48,7 @@ func (h Handler) CheckTx(ctx basecoin.Context, store state.KVStore, tx basecoin.
|
|||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
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
|
||||
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 {
|
||||
return "", errors.ErrUnknownModule(module)
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
)
|
||||
|
||||
// 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())
|
||||
|
||||
// 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
|
||||
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)
|
||||
return acct.Coins, err
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
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.
|
||||
//
|
||||
// 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())
|
||||
// we can increase an empty account...
|
||||
if IsNoAccountErr(err) && coins.IsPositive() {
|
||||
|
@ -66,7 +66,7 @@ type Account struct {
|
|||
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)
|
||||
data := store.Get(key)
|
||||
if len(data) == 0 {
|
||||
|
@ -80,7 +80,7 @@ func loadAccount(store state.KVStore, key []byte) (acct Account, err error) {
|
|||
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)
|
||||
bin := wire.BinaryBytes(acct)
|
||||
store.Set(key, bin)
|
||||
|
|
|
@ -45,16 +45,16 @@ func (SimpleFeeMiddleware) Name() string {
|
|||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
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)
|
||||
if !ok {
|
||||
// the fee wrapper is not required if there is no minimum
|
||||
|
|
|
@ -24,7 +24,7 @@ func (ReplayCheck) Name() string {
|
|||
var _ stack.Middleware = ReplayCheck{}
|
||||
|
||||
// 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) {
|
||||
|
||||
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
|
||||
// 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
|
||||
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) {
|
||||
|
||||
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
|
||||
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) {
|
||||
|
||||
// make sure it is a the nonce Tx (Tx from this package)
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"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)
|
||||
if len(data) == 0 {
|
||||
//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
|
||||
}
|
||||
|
||||
func setSeq(store state.KVStore, key []byte, seq uint32) error {
|
||||
func setSeq(store state.SimpleDB, key []byte, seq uint32) error {
|
||||
bin := wire.BinaryBytes(seq)
|
||||
store.Set(key, bin)
|
||||
return nil // real stores can return error...
|
||||
|
|
|
@ -62,7 +62,7 @@ func (n Tx) ValidateBasic() error {
|
|||
// and further increment the sequence number
|
||||
// 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
|
||||
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()
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ func (Handler) Name() string {
|
|||
}
|
||||
|
||||
// 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
|
||||
cr, err = checkTx(ctx, tx)
|
||||
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.
|
||||
//
|
||||
// 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)
|
||||
if err != nil {
|
||||
return res, err
|
||||
|
|
|
@ -27,7 +27,7 @@ func (Middleware) Name() string {
|
|||
// CheckTx tries to assume the named role if requested.
|
||||
// If no role is requested, do nothing.
|
||||
// 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
|
||||
assume, ok := tx.Unwrap().(AssumeRoleTx)
|
||||
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.
|
||||
// If no role is requested, do nothing.
|
||||
// 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
|
||||
assume, ok := tx.Unwrap().(AssumeRoleTx)
|
||||
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)
|
||||
}
|
||||
|
||||
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()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
// shortcut for the lazy
|
||||
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) {
|
||||
tx := roles.NewCreateRoleTx(name, min, sigs)
|
||||
ctx := stack.MockContext("foo", 1)
|
||||
|
|
|
@ -56,7 +56,7 @@ func (r Role) IsAuthorized(ctx basecoin.Context) bool {
|
|||
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)
|
||||
if len(data) == 0 {
|
||||
return role, ErrNoRole()
|
||||
|
@ -69,7 +69,7 @@ func loadRole(store state.KVStore, key []byte) (role Role, err error) {
|
|||
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) {
|
||||
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
|
||||
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 {
|
||||
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.
|
||||
// // The sequence number counts how many packets have been sent.
|
||||
// // 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)
|
||||
// seqBytes := store.Get(sequenceKey)
|
||||
// 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
|
||||
// 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)
|
||||
// store.Set(sequenceKey, []byte(strconv.FormatUint(seq, 10)))
|
||||
// }
|
||||
|
||||
// // SaveNewIBCPacket creates an IBC packet with the given payload from the src chain to the dst chain
|
||||
// // using the correct sequence number. It also increments the sequence number by 1
|
||||
// func SaveNewIBCPacket(state state.KVStore, src, dst string, payload Payload) {
|
||||
// func SaveNewIBCPacket(state state.SimpleDB, src, dst string, payload Payload) {
|
||||
// // fetch sequence number and increment by 1
|
||||
// seq := GetSequenceNumber(state, src, dst)
|
||||
// SetSequenceNumber(state, src, dst, seq+1)
|
||||
|
@ -104,7 +104,7 @@ package ibc
|
|||
// 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))
|
||||
// packetBytes := state.Get(packetKey)
|
||||
|
||||
|
@ -251,11 +251,11 @@ package ibc
|
|||
// 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 ""
|
||||
// }
|
||||
|
||||
// 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
|
||||
// var tx IBCTx
|
||||
// err := wire.ReadBinaryBytes(txBytes, &tx)
|
||||
|
@ -295,7 +295,7 @@ package ibc
|
|||
// }
|
||||
|
||||
// type IBCStateMachine struct {
|
||||
// store state.KVStore
|
||||
// store state.SimpleDB
|
||||
// ctx types.CallContext
|
||||
// res abci.Result
|
||||
// }
|
||||
|
@ -499,13 +499,13 @@ package ibc
|
|||
// 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
|
||||
// }
|
||||
|
||||
|
@ -513,7 +513,7 @@ package ibc
|
|||
// // TODO: move to utils
|
||||
|
||||
// // 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)
|
||||
// return len(value) > 0
|
||||
// }
|
||||
|
@ -521,7 +521,7 @@ package ibc
|
|||
// // Load bytes from store by reading value for key and read into ptr.
|
||||
// // Returns true if exists, false if nil.
|
||||
// // 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)
|
||||
// if len(value) > 0 {
|
||||
// err = wire.ReadBinaryBytes(value, ptr)
|
||||
|
@ -537,7 +537,7 @@ package ibc
|
|||
// }
|
||||
|
||||
// // 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))
|
||||
// }
|
||||
|
||||
|
|
|
@ -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 {
|
||||
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) {
|
||||
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 {
|
||||
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) {
|
||||
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.
|
||||
// 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.
|
||||
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)
|
||||
if err != nil {
|
||||
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.
|
||||
// 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.
|
||||
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)
|
||||
if err != nil {
|
||||
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
|
||||
// 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)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
|
@ -20,9 +20,11 @@ const (
|
|||
const (
|
||||
ByteRawTx = 0xF0
|
||||
ByteCheckTx = 0xF1
|
||||
ByteFailTx = 0xF2
|
||||
|
||||
TypeRawTx = NameOK + "/raw" // this will just say a-ok to RawTx
|
||||
TypeCheckTx = NameCheck + "/tx"
|
||||
TypeFailTx = NameFail + "/tx"
|
||||
|
||||
rawMaxSize = 2000 * 1000
|
||||
)
|
||||
|
@ -30,7 +32,8 @@ const (
|
|||
func init() {
|
||||
basecoin.TxMapper.
|
||||
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
|
||||
|
@ -72,6 +75,22 @@ func (CheckTx) ValidateBasic() error {
|
|||
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
|
||||
type OKHandler struct {
|
||||
Log string
|
||||
|
@ -86,12 +105,12 @@ func (OKHandler) Name() string {
|
|||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -108,13 +127,13 @@ func (EchoHandler) Name() string {
|
|||
}
|
||||
|
||||
// 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)
|
||||
return basecoin.Result{Data: data}, err
|
||||
}
|
||||
|
||||
// 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)
|
||||
return basecoin.Result{Data: data}, err
|
||||
}
|
||||
|
@ -133,12 +152,12 @@ func (FailHandler) Name() string {
|
|||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
|
@ -157,7 +176,7 @@ func (PanicHandler) Name() string {
|
|||
}
|
||||
|
||||
// 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 {
|
||||
panic(p.Err)
|
||||
}
|
||||
|
@ -165,7 +184,7 @@ func (p PanicHandler) CheckTx(ctx basecoin.Context, store state.KVStore, tx base
|
|||
}
|
||||
|
||||
// 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 {
|
||||
panic(p.Err)
|
||||
}
|
||||
|
@ -185,7 +204,7 @@ func (CheckHandler) Name() string {
|
|||
}
|
||||
|
||||
// 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)
|
||||
if !ok {
|
||||
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
|
||||
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
|
||||
return c.CheckTx(ctx, store, tx)
|
||||
}
|
||||
|
|
|
@ -25,14 +25,14 @@ func (_ CheckMiddleware) Name() string {
|
|||
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) {
|
||||
return res, errors.ErrUnauthorized()
|
||||
}
|
||||
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) {
|
||||
return res, errors.ErrUnauthorized()
|
||||
}
|
||||
|
@ -51,12 +51,12 @@ func (_ GrantMiddleware) Name() string {
|
|||
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)
|
||||
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)
|
||||
return next.DeliverTx(ctx, store, tx)
|
||||
}
|
||||
|
|
|
@ -20,40 +20,40 @@ type Middleware 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)
|
||||
}
|
||||
|
||||
type CheckerMiddleFunc func(basecoin.Context, state.KVStore,
|
||||
type CheckerMiddleFunc func(basecoin.Context, state.SimpleDB,
|
||||
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) {
|
||||
return c(ctx, store, tx, next)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
type DeliverMiddleFunc func(basecoin.Context, state.KVStore,
|
||||
type DeliverMiddleFunc func(basecoin.Context, state.SimpleDB,
|
||||
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) {
|
||||
return d(ctx, store, tx, next)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
type SetOptionMiddleFunc func(log.Logger, state.KVStore,
|
||||
type SetOptionMiddleFunc func(log.Logger, state.SimpleDB,
|
||||
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) {
|
||||
return c(l, store, module, key, value, next)
|
||||
}
|
||||
|
@ -61,28 +61,28 @@ func (c SetOptionMiddleFunc) SetOption(l log.Logger, store state.KVStore,
|
|||
// holders
|
||||
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) {
|
||||
return next.CheckTx(ctx, store, tx)
|
||||
}
|
||||
|
||||
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) {
|
||||
return next.DeliverTx(ctx, store, tx)
|
||||
}
|
||||
|
||||
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) {
|
||||
return next.SetOption(l, store, module, key, value)
|
||||
}
|
||||
|
||||
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) {
|
||||
return "", nil
|
||||
}
|
||||
|
@ -112,17 +112,17 @@ func (w wrapped) Name() string {
|
|||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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
|
||||
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
|
||||
next := secureCheck(m.next, ctx)
|
||||
// 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
|
||||
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
|
||||
next := secureDeliver(m.next, ctx)
|
||||
// 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)
|
||||
}
|
||||
|
||||
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
|
||||
store = stateSpace(store, m.Name())
|
||||
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
package stack
|
||||
|
||||
import "github.com/tendermint/basecoin/state"
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
|
||||
"github.com/tendermint/basecoin/state"
|
||||
)
|
||||
|
||||
type prefixStore struct {
|
||||
prefix []byte
|
||||
store state.KVStore
|
||||
store state.SimpleDB
|
||||
}
|
||||
|
||||
var _ state.KVStore = prefixStore{}
|
||||
var _ state.SimpleDB = prefixStore{}
|
||||
|
||||
func (p prefixStore) Set(key, value []byte) {
|
||||
key = append(p.prefix, key...)
|
||||
|
@ -19,11 +24,78 @@ func (p prefixStore) Get(key []byte) (value []byte) {
|
|||
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
|
||||
//
|
||||
// this can be used by the middleware and dispatcher to isolate one 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
|
||||
if pstore, ok := store.(prefixStore); ok {
|
||||
store = pstore.store
|
||||
|
@ -31,13 +103,21 @@ func stateSpace(store state.KVStore, app string) state.KVStore {
|
|||
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
|
||||
// app prefix, but it cannot easily be unwrapped
|
||||
//
|
||||
// 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
|
||||
// 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))
|
||||
return prefixStore{prefix, store}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ func (Recovery) Name() string {
|
|||
var _ Middleware = Recovery{}
|
||||
|
||||
// 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() {
|
||||
if r := recover(); r != nil {
|
||||
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
|
||||
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() {
|
||||
if r := recover(); r != nil {
|
||||
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
|
||||
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() {
|
||||
if r := recover(); r != nil {
|
||||
err = normalizePanic(r)
|
||||
|
|
|
@ -23,19 +23,19 @@ var _ Middleware = writerMid{}
|
|||
|
||||
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) {
|
||||
store.Set(w.key, w.value)
|
||||
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) {
|
||||
store.Set(w.key, w.value)
|
||||
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) {
|
||||
store.Set([]byte(key), []byte(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) CheckTx(ctx basecoin.Context, store state.KVStore,
|
||||
func (w writerHand) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
||||
tx basecoin.Tx) (basecoin.Result, error) {
|
||||
store.Set(w.key, w.value)
|
||||
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) {
|
||||
store.Set(w.key, w.value)
|
||||
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) {
|
||||
store.Set([]byte(key), []byte(value))
|
||||
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
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
. "github.com/tendermint/tmlibs/common"
|
||||
"github.com/tendermint/go-wire/data"
|
||||
)
|
||||
|
||||
// KVStore is a simple interface to get/set data
|
||||
type KVStore interface {
|
||||
Set(key, 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 {
|
||||
m map[string][]byte
|
||||
}
|
||||
|
||||
var _ SimpleDB = NewMemKVStore()
|
||||
|
||||
// NewMemKVStore initializes a MemKVStore
|
||||
func NewMemKVStore() *MemKVStore {
|
||||
return &MemKVStore{
|
||||
m: make(map[string][]byte, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (mkv *MemKVStore) Set(key []byte, value []byte) {
|
||||
mkv.m[string(key)] = value
|
||||
func (m *MemKVStore) Set(key []byte, value []byte) {
|
||||
m.m[string(key)] = value
|
||||
}
|
||||
|
||||
func (mkv *MemKVStore) Get(key []byte) (value []byte) {
|
||||
return mkv.m[string(key)]
|
||||
func (m *MemKVStore) Get(key []byte) (value []byte) {
|
||||
return m.m[string(key)]
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
// A Cache that enforces deterministic sync order.
|
||||
type KVCache struct {
|
||||
store KVStore
|
||||
cache map[string]kvCacheValue
|
||||
keys *list.List
|
||||
logging bool
|
||||
logLines []string
|
||||
func (m *MemKVStore) Has(key []byte) (has bool) {
|
||||
_, ok := m.m[string(key)]
|
||||
return ok
|
||||
}
|
||||
|
||||
type kvCacheValue struct {
|
||||
v []byte // The value of some key
|
||||
e *list.Element // The KVCache.keys element
|
||||
func (m *MemKVStore) Remove(key []byte) (value []byte) {
|
||||
val := m.m[string(key)]
|
||||
delete(m.m, string(key))
|
||||
return val
|
||||
}
|
||||
|
||||
// NOTE: If store is nil, creates a new MemKVStore
|
||||
func NewKVCache(store KVStore) *KVCache {
|
||||
if store == nil {
|
||||
store = NewMemKVStore()
|
||||
func (m *MemKVStore) List(start, end []byte, limit int) []Model {
|
||||
keys := m.keysInRange(start, end)
|
||||
if limit > 0 && len(keys) > 0 {
|
||||
if limit > len(keys) {
|
||||
limit = len(keys)
|
||||
}
|
||||
return (&KVCache{
|
||||
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)
|
||||
keys = keys[:limit]
|
||||
}
|
||||
cacheValue, ok := kvc.cache[string(key)]
|
||||
if ok {
|
||||
kvc.keys.MoveToBack(cacheValue.e)
|
||||
} else {
|
||||
cacheValue.e = kvc.keys.PushBack(key)
|
||||
|
||||
res := make([]Model, len(keys))
|
||||
for i, k := range keys {
|
||||
res[i] = Model{
|
||||
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) {
|
||||
cacheValue, ok := kvc.cache[string(key)]
|
||||
if ok {
|
||||
if kvc.logging {
|
||||
line := fmt.Sprintf("Get (hit) %v = %v", LegibleBytes(key), LegibleBytes(cacheValue.v))
|
||||
kvc.logLines = append(kvc.logLines, line)
|
||||
// First iterates through all keys to find the one that matches
|
||||
func (m *MemKVStore) First(start, end []byte) Model {
|
||||
key := ""
|
||||
for _, k := range m.keysInRange(start, end) {
|
||||
if key == "" || k < key {
|
||||
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 {
|
||||
line := fmt.Sprintf("Get (miss) %v = %v", LegibleBytes(key), LegibleBytes(value))
|
||||
kvc.logLines = append(kvc.logLines, line)
|
||||
if key == "" {
|
||||
return Model{}
|
||||
}
|
||||
return value
|
||||
return Model{
|
||||
Key: []byte(key),
|
||||
Value: m.m[key],
|
||||
}
|
||||
}
|
||||
|
||||
//Update the store with the values from the cache
|
||||
func (kvc *KVCache) Sync() {
|
||||
for e := kvc.keys.Front(); e != nil; e = e.Next() {
|
||||
key := e.Value.([]byte)
|
||||
value := kvc.cache[string(key)]
|
||||
kvc.store.Set(key, value.v)
|
||||
func (m *MemKVStore) Last(start, end []byte) Model {
|
||||
key := ""
|
||||
for _, k := range m.keysInRange(start, end) {
|
||||
if key == "" || k > key {
|
||||
key = k
|
||||
}
|
||||
}
|
||||
if key == "" {
|
||||
return Model{}
|
||||
}
|
||||
return Model{
|
||||
Key: []byte(key),
|
||||
Value: m.m[key],
|
||||
}
|
||||
kvc.Reset()
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
func LegibleBytes(data []byte) string {
|
||||
s := ""
|
||||
for _, b := range data {
|
||||
if 0x21 <= b && b < 0x7F {
|
||||
s += Green(string(b))
|
||||
} else {
|
||||
s += Blue(Fmt("%02X", b))
|
||||
}
|
||||
}
|
||||
return s
|
||||
func (m *MemKVStore) Discard() {
|
||||
m.m = make(map[string][]byte, 0)
|
||||
}
|
||||
|
||||
func (m *MemKVStore) Checkpoint() SimpleDB {
|
||||
return NewMemKVCache(m)
|
||||
}
|
||||
|
||||
func (m *MemKVStore) 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
|
||||
}
|
||||
|
||||
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
|
||||
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" $?
|
||||
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" $?
|
||||
|
||||
# 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
|
||||
assertTrue "line=${LINENO}, successfully prepare tx" $?
|
||||
# and get some dude to sign it
|
||||
FAIL=$(echo qwertyuiop | ${CLIENT_EXE} tx --in=$TX_FILE --name=$POOR 2>/dev/null)
|
||||
assertFalse "line=${LINENO}, double signing doesn't get bank" $?
|
||||
# FAIL=$(echo qwertyuiop | ${CLIENT_EXE} tx --in=$TX_FILE --name=$POOR 2>/dev/null)
|
||||
# assertFalse "line=${LINENO}, double signing doesn't get bank" $?
|
||||
# and get some dude to sign it for the full access
|
||||
TX=$(echo qwertyuiop | ${CLIENT_EXE} tx --in=$TX_FILE --name=$DUDE)
|
||||
txSucceeded $? "$TX" "multi-bank"
|
||||
|
|
Loading…
Reference in New Issue