Merge pull request #196 from tendermint/feature/184-overhaul-handler-interface
Feature/184 overhaul handler interface
This commit is contained in:
commit
2113c9f884
|
@ -0,0 +1,21 @@
|
||||||
|
Alexis:
|
||||||
|
|
||||||
|
* merkle - proof (non-existence - maybe range)
|
||||||
|
* intro to light-client and proofs
|
||||||
|
light-client proofs:
|
||||||
|
* make this sensible -> very tied to merkle proofs and API
|
||||||
|
* support new proof types
|
||||||
|
|
||||||
|
* expose more proof types in basecoin.Query
|
||||||
|
|
||||||
|
|
||||||
|
* merkle - api cleanup (also Bonsai)
|
||||||
|
* later: C bindings (to Bonsai?)
|
||||||
|
|
||||||
|
|
||||||
|
* crypto-ledger (while ethan gone)
|
||||||
|
|
||||||
|
light-client provider:
|
||||||
|
* caching checkpoint on Verify
|
||||||
|
* cleanup (trim old node)
|
||||||
|
|
52
app/app.go
52
app/app.go
|
@ -1,6 +1,7 @@
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -23,11 +24,12 @@ const (
|
||||||
|
|
||||||
// Basecoin - The ABCI application
|
// Basecoin - The ABCI application
|
||||||
type Basecoin struct {
|
type Basecoin struct {
|
||||||
info *sm.ChainState
|
info *sm.ChainState
|
||||||
|
|
||||||
state *Store
|
state *Store
|
||||||
|
|
||||||
handler basecoin.Handler
|
handler basecoin.Handler
|
||||||
|
|
||||||
|
pending []*abci.Validator
|
||||||
height uint64
|
height uint64
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
}
|
}
|
||||||
|
@ -65,8 +67,9 @@ func (app *Basecoin) Info() abci.ResponseInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOption - ABCI
|
// InitState - used to setup state (was SetOption)
|
||||||
func (app *Basecoin) SetOption(key string, value string) string {
|
// to be used by InitChain later
|
||||||
|
func (app *Basecoin) InitState(key string, value string) string {
|
||||||
|
|
||||||
module, key := splitKey(key)
|
module, key := splitKey(key)
|
||||||
state := app.state.Append()
|
state := app.state.Append()
|
||||||
|
@ -79,13 +82,18 @@ func (app *Basecoin) SetOption(key string, value string) string {
|
||||||
return fmt.Sprintf("Error: unknown base option: %s", key)
|
return fmt.Sprintf("Error: unknown base option: %s", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
log, err := app.handler.SetOption(app.logger, state, module, key, value)
|
log, err := app.handler.InitState(app.logger, state, module, key, value)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return log
|
return log
|
||||||
}
|
}
|
||||||
return "Error: " + err.Error()
|
return "Error: " + err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetOption - ABCI
|
||||||
|
func (app *Basecoin) SetOption(key string, value string) string {
|
||||||
|
return "Not Implemented"
|
||||||
|
}
|
||||||
|
|
||||||
// DeliverTx - ABCI
|
// DeliverTx - ABCI
|
||||||
func (app *Basecoin) DeliverTx(txBytes []byte) abci.Result {
|
func (app *Basecoin) DeliverTx(txBytes []byte) abci.Result {
|
||||||
tx, err := basecoin.LoadTx(txBytes)
|
tx, err := basecoin.LoadTx(txBytes)
|
||||||
|
@ -103,7 +111,8 @@ func (app *Basecoin) DeliverTx(txBytes []byte) abci.Result {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Result(err)
|
return errors.Result(err)
|
||||||
}
|
}
|
||||||
return res.ToABCI()
|
app.addValChange(res.Diff)
|
||||||
|
return basecoin.ToABCI(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTx - ABCI
|
// CheckTx - ABCI
|
||||||
|
@ -123,7 +132,7 @@ func (app *Basecoin) CheckTx(txBytes []byte) abci.Result {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Result(err)
|
return errors.Result(err)
|
||||||
}
|
}
|
||||||
return res.ToABCI()
|
return basecoin.ToABCI(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query - ABCI
|
// Query - ABCI
|
||||||
|
@ -163,14 +172,35 @@ func (app *Basecoin) BeginBlock(hash []byte, header *abci.Header) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// EndBlock - ABCI
|
// EndBlock - ABCI
|
||||||
|
// Returns a list of all validator changes made in this block
|
||||||
func (app *Basecoin) EndBlock(height uint64) (res abci.ResponseEndBlock) {
|
func (app *Basecoin) EndBlock(height uint64) (res abci.ResponseEndBlock) {
|
||||||
// for _, plugin := range app.plugins.GetList() {
|
// TODO: cleanup in case a validator exists multiple times in the list
|
||||||
// pluginRes := plugin.EndBlock(app.state, height)
|
res.Diffs = app.pending
|
||||||
// res.Diffs = append(res.Diffs, pluginRes.Diffs...)
|
app.pending = nil
|
||||||
// }
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (app *Basecoin) addValChange(diffs []*abci.Validator) {
|
||||||
|
for _, d := range diffs {
|
||||||
|
idx := pubKeyIndex(d, app.pending)
|
||||||
|
if idx >= 0 {
|
||||||
|
app.pending[idx] = d
|
||||||
|
} else {
|
||||||
|
app.pending = append(app.pending, d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return index of list with validator of same PubKey, or -1 if no match
|
||||||
|
func pubKeyIndex(val *abci.Validator, list []*abci.Validator) int {
|
||||||
|
for i, v := range list {
|
||||||
|
if bytes.Equal(val.PubKey, v.PubKey) {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
//TODO move split key to tmlibs?
|
//TODO move split key to tmlibs?
|
||||||
|
|
||||||
// Splits the string at the first '/'.
|
// Splits the string at the first '/'.
|
||||||
|
|
|
@ -98,9 +98,9 @@ func (at *appTest) feeTx(coins coin.Coins, toll coin.Coin, sequence uint32) base
|
||||||
return at.signTx(tx)
|
return at.signTx(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the account on the app through SetOption
|
// set the account on the app through InitState
|
||||||
func (at *appTest) initAccount(acct *coin.AccountWithKey) {
|
func (at *appTest) initAccount(acct *coin.AccountWithKey) {
|
||||||
res := at.app.SetOption("coin/account", acct.MakeOption())
|
res := at.app.InitState("coin/account", acct.MakeOption())
|
||||||
require.EqualValues(at.t, res, "Success")
|
require.EqualValues(at.t, res, "Success")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ func (at *appTest) reset() {
|
||||||
logger.With("module", "app"),
|
logger.With("module", "app"),
|
||||||
)
|
)
|
||||||
|
|
||||||
res := at.app.SetOption("base/chain_id", at.chainID)
|
res := at.app.InitState("base/chain_id", at.chainID)
|
||||||
require.EqualValues(at.t, res, "Success")
|
require.EqualValues(at.t, res, "Success")
|
||||||
|
|
||||||
at.initAccount(at.acctIn)
|
at.initAccount(at.acctIn)
|
||||||
|
@ -167,7 +167,7 @@ func (at *appTest) exec(t *testing.T, tx basecoin.Tx, checkTx bool) (res abci.Re
|
||||||
|
|
||||||
//--------------------------------------------------------
|
//--------------------------------------------------------
|
||||||
|
|
||||||
func TestSetOption(t *testing.T) {
|
func TestInitState(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
|
@ -183,14 +183,14 @@ func TestSetOption(t *testing.T) {
|
||||||
|
|
||||||
//testing ChainID
|
//testing ChainID
|
||||||
chainID := "testChain"
|
chainID := "testChain"
|
||||||
res := app.SetOption("base/chain_id", chainID)
|
res := app.InitState("base/chain_id", chainID)
|
||||||
assert.EqualValues(app.GetChainID(), chainID)
|
assert.EqualValues(app.GetChainID(), chainID)
|
||||||
assert.EqualValues(res, "Success")
|
assert.EqualValues(res, "Success")
|
||||||
|
|
||||||
// make a nice account...
|
// make a nice account...
|
||||||
bal := coin.Coins{{"atom", 77}, {"eth", 12}}
|
bal := coin.Coins{{"atom", 77}, {"eth", 12}}
|
||||||
acct := coin.NewAccountWithKey(bal)
|
acct := coin.NewAccountWithKey(bal)
|
||||||
res = app.SetOption("coin/account", acct.MakeOption())
|
res = app.InitState("coin/account", acct.MakeOption())
|
||||||
require.EqualValues(res, "Success")
|
require.EqualValues(res, "Success")
|
||||||
|
|
||||||
// make sure it is set correctly, with some balance
|
// make sure it is set correctly, with some balance
|
||||||
|
@ -218,7 +218,7 @@ func TestSetOption(t *testing.T) {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`
|
}`
|
||||||
res = app.SetOption("coin/account", unsortAcc)
|
res = app.InitState("coin/account", unsortAcc)
|
||||||
require.EqualValues(res, "Success")
|
require.EqualValues(res, "Success")
|
||||||
|
|
||||||
coins, err = getAddr(unsortAddr, app.GetState())
|
coins, err = getAddr(unsortAddr, app.GetState())
|
||||||
|
@ -226,13 +226,13 @@ func TestSetOption(t *testing.T) {
|
||||||
assert.True(coins.IsValid())
|
assert.True(coins.IsValid())
|
||||||
assert.Equal(unsortCoins, coins)
|
assert.Equal(unsortCoins, coins)
|
||||||
|
|
||||||
res = app.SetOption("base/dslfkgjdas", "")
|
res = app.InitState("base/dslfkgjdas", "")
|
||||||
assert.NotEqual(res, "Success")
|
assert.NotEqual(res, "Success")
|
||||||
|
|
||||||
res = app.SetOption("dslfkgjdas", "")
|
res = app.InitState("dslfkgjdas", "")
|
||||||
assert.NotEqual(res, "Success")
|
assert.NotEqual(res, "Success")
|
||||||
|
|
||||||
res = app.SetOption("dslfkgjdas/szfdjzs", "")
|
res = app.InitState("dslfkgjdas/szfdjzs", "")
|
||||||
assert.NotEqual(res, "Success")
|
assert.NotEqual(res, "Success")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
|
"github.com/tendermint/basecoin/modules/base"
|
||||||
|
wire "github.com/tendermint/go-wire"
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
//-----------------------------------
|
||||||
|
// Test cases start here
|
||||||
|
|
||||||
|
func randPower() uint64 {
|
||||||
|
return uint64(cmn.RandInt()%50 + 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeVal() *abci.Validator {
|
||||||
|
return &abci.Validator{
|
||||||
|
PubKey: cmn.RandBytes(10),
|
||||||
|
Power: randPower(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// withNewPower returns a copy of the validator with a different power
|
||||||
|
func withNewPower(val *abci.Validator) *abci.Validator {
|
||||||
|
res := *val
|
||||||
|
res.Power = randPower()
|
||||||
|
if res.Power == val.Power {
|
||||||
|
panic("no no")
|
||||||
|
}
|
||||||
|
return &res
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEndBlock(t *testing.T) {
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
|
||||||
|
logger := log.NewNopLogger()
|
||||||
|
store := MockStore()
|
||||||
|
handler := base.ValSetHandler{}
|
||||||
|
app := NewBasecoin(handler, store, logger)
|
||||||
|
|
||||||
|
val1 := makeVal()
|
||||||
|
val2 := makeVal()
|
||||||
|
val3 := makeVal()
|
||||||
|
val1a := withNewPower(val1)
|
||||||
|
val2a := withNewPower(val2)
|
||||||
|
|
||||||
|
cases := [...]struct {
|
||||||
|
changes [][]*abci.Validator
|
||||||
|
expected []*abci.Validator
|
||||||
|
}{
|
||||||
|
// Nothing in, nothing out, no crash
|
||||||
|
0: {},
|
||||||
|
// One in, one out, no problem
|
||||||
|
1: {
|
||||||
|
changes: [][]*abci.Validator{{val1}},
|
||||||
|
expected: []*abci.Validator{val1},
|
||||||
|
},
|
||||||
|
// Combine a few ones
|
||||||
|
2: {
|
||||||
|
changes: [][]*abci.Validator{{val1}, {val2, val3}},
|
||||||
|
expected: []*abci.Validator{val1, val2, val3},
|
||||||
|
},
|
||||||
|
// Make sure changes all to one validators are squished into one diff
|
||||||
|
3: {
|
||||||
|
changes: [][]*abci.Validator{{val1}, {val2, val1a}, {val2a, val3}},
|
||||||
|
expected: []*abci.Validator{val1a, val2a, val3},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range cases {
|
||||||
|
app.BeginBlock(nil, nil)
|
||||||
|
for _, c := range tc.changes {
|
||||||
|
tx := base.ValChangeTx{c}.Wrap()
|
||||||
|
txBytes := wire.BinaryBytes(tx)
|
||||||
|
res := app.DeliverTx(txBytes)
|
||||||
|
require.True(res.IsOK(), "%#v", res)
|
||||||
|
}
|
||||||
|
diff := app.EndBlock(app.height)
|
||||||
|
// TODO: don't care about order here...
|
||||||
|
assert.Equal(tc.expected, diff.Diffs, "%d", i)
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,16 +16,16 @@ func (app *Basecoin) LoadGenesis(path string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// set chain_id
|
// set chain_id
|
||||||
app.SetOption("base/chain_id", genDoc.ChainID)
|
app.InitState("base/chain_id", genDoc.ChainID)
|
||||||
|
|
||||||
// set accounts
|
// set accounts
|
||||||
for _, acct := range genDoc.AppOptions.Accounts {
|
for _, acct := range genDoc.AppOptions.Accounts {
|
||||||
_ = app.SetOption("coin/account", string(acct))
|
_ = app.InitState("coin/account", string(acct))
|
||||||
}
|
}
|
||||||
|
|
||||||
// set plugin options
|
// set plugin options
|
||||||
for _, kv := range genDoc.AppOptions.pluginOptions {
|
for _, kv := range genDoc.AppOptions.pluginOptions {
|
||||||
_ = app.SetOption(kv.Key, kv.Value)
|
_ = app.InitState(kv.Key, kv.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
10
app/store.go
10
app/store.go
|
@ -36,6 +36,16 @@ type ChainState struct {
|
||||||
Height uint64
|
Height uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MockStore returns an in-memory store only intended for testing
|
||||||
|
func MockStore() *Store {
|
||||||
|
res, err := NewStore("", 0, log.NewNopLogger())
|
||||||
|
if err != nil {
|
||||||
|
// should never happen, abort test if it does
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
// NewStore initializes an in-memory IAVLTree, or attempts to load a persistant
|
// NewStore initializes an in-memory IAVLTree, or attempts to load a persistant
|
||||||
// tree from disk
|
// tree from disk
|
||||||
func NewStore(dbName string, cacheSize int, logger log.Logger) (*Store, error) {
|
func NewStore(dbName string, cacheSize int, logger log.Logger) (*Store, error) {
|
||||||
|
|
|
@ -72,7 +72,7 @@ func NewBenchApp(h basecoin.Handler, chainID string, n int,
|
||||||
store,
|
store,
|
||||||
logger.With("module", "app"),
|
logger.With("module", "app"),
|
||||||
)
|
)
|
||||||
res := app.SetOption("base/chain_id", chainID)
|
res := app.InitState("base/chain_id", chainID)
|
||||||
if res != "Success" {
|
if res != "Success" {
|
||||||
panic("cannot set chain")
|
panic("cannot set chain")
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ func NewBenchApp(h basecoin.Handler, chainID string, n int,
|
||||||
accts := make([]*coin.AccountWithKey, n)
|
accts := make([]*coin.AccountWithKey, n)
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
accts[i] = coin.NewAccountWithKey(money)
|
accts[i] = coin.NewAccountWithKey(money)
|
||||||
res := app.SetOption("coin/account", accts[i].MakeOption())
|
res := app.InitState("coin/account", accts[i].MakeOption())
|
||||||
if res != "Success" {
|
if res != "Success" {
|
||||||
panic("can't set account")
|
panic("can't set account")
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,7 +114,8 @@ func NewHandler(feeDenom string) basecoin.Handler {
|
||||||
|
|
||||||
// Handler the counter transaction processing handler
|
// Handler the counter transaction processing handler
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
stack.NopOption
|
stack.PassInitState
|
||||||
|
stack.PassInitValidate
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ stack.Dispatchable = Handler{}
|
var _ stack.Dispatchable = Handler{}
|
||||||
|
@ -128,13 +129,13 @@ func (Handler) Name() string {
|
||||||
func (Handler) AssertDispatcher() {}
|
func (Handler) AssertDispatcher() {}
|
||||||
|
|
||||||
// CheckTx checks if the tx is properly structured
|
// CheckTx checks if the tx is properly structured
|
||||||
func (h Handler) CheckTx(ctx basecoin.Context, store state.SimpleDB, 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.CheckResult, err error) {
|
||||||
_, err = checkTx(ctx, tx)
|
_, err = checkTx(ctx, tx)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx executes the tx if valid
|
// DeliverTx executes the tx if valid
|
||||||
func (h Handler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, 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.DeliverResult, err error) {
|
||||||
ctr, err := checkTx(ctx, tx)
|
ctr, err := checkTx(ctx, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
|
|
@ -36,12 +36,12 @@ func TestCounterPlugin(t *testing.T) {
|
||||||
store,
|
store,
|
||||||
logger.With("module", "app"),
|
logger.With("module", "app"),
|
||||||
)
|
)
|
||||||
bcApp.SetOption("base/chain_id", chainID)
|
bcApp.InitState("base/chain_id", chainID)
|
||||||
|
|
||||||
// Account initialization
|
// Account initialization
|
||||||
bal := coin.Coins{{"", 1000}, {"gold", 1000}}
|
bal := coin.Coins{{"", 1000}, {"gold", 1000}}
|
||||||
acct := coin.NewAccountWithKey(bal)
|
acct := coin.NewAccountWithKey(bal)
|
||||||
log := bcApp.SetOption("coin/account", acct.MakeOption())
|
log := bcApp.InitState("coin/account", acct.MakeOption())
|
||||||
require.Equal("Success", log)
|
require.Equal("Success", log)
|
||||||
|
|
||||||
// Deliver a CounterTx
|
// Deliver a CounterTx
|
||||||
|
|
133
handler.go
133
handler.go
|
@ -10,79 +10,154 @@ import (
|
||||||
|
|
||||||
// Handler is anything that processes a transaction
|
// Handler is anything that processes a transaction
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
|
// Checker verifies there are valid fees and estimates work
|
||||||
Checker
|
Checker
|
||||||
|
// Deliver performs the tx once it makes it in the block
|
||||||
Deliver
|
Deliver
|
||||||
SetOptioner
|
// InitStater sets state from the genesis file
|
||||||
|
InitStater
|
||||||
|
// InitValidater sets the initial validator set
|
||||||
|
InitValidater
|
||||||
|
// Named ensures there is a name for the item
|
||||||
Named
|
Named
|
||||||
// TODO: flesh these out as well
|
|
||||||
// InitChain(store state.SimpleDB, vals []*abci.Validator)
|
// TODO????
|
||||||
// BeginBlock(store state.SimpleDB, hash []byte, header *abci.Header)
|
// BeginBlock(store state.SimpleDB, hash []byte, header *abci.Header)
|
||||||
// EndBlock(store state.SimpleDB, height uint64) abci.ResponseEndBlock
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Named ensures there is a name for the item
|
||||||
type Named interface {
|
type Named interface {
|
||||||
Name() string
|
Name() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checker verifies there are valid fees and estimates work
|
||||||
type Checker interface {
|
type Checker interface {
|
||||||
CheckTx(ctx Context, store state.SimpleDB, tx Tx) (Result, error)
|
CheckTx(ctx Context, store state.SimpleDB, tx Tx) (CheckResult, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckerFunc (like http.HandlerFunc) is a shortcut for making wrapers
|
// CheckerFunc (like http.HandlerFunc) is a shortcut for making wrappers
|
||||||
type CheckerFunc func(Context, state.SimpleDB, Tx) (Result, error)
|
type CheckerFunc func(Context, state.SimpleDB, Tx) (CheckResult, error)
|
||||||
|
|
||||||
func (c CheckerFunc) CheckTx(ctx Context, store state.SimpleDB, tx Tx) (Result, error) {
|
func (c CheckerFunc) CheckTx(ctx Context, store state.SimpleDB, tx Tx) (CheckResult, error) {
|
||||||
return c(ctx, store, tx)
|
return c(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deliver performs the tx once it makes it in the block
|
||||||
type Deliver interface {
|
type Deliver interface {
|
||||||
DeliverTx(ctx Context, store state.SimpleDB, tx Tx) (Result, error)
|
DeliverTx(ctx Context, store state.SimpleDB, tx Tx) (DeliverResult, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverFunc (like http.HandlerFunc) is a shortcut for making wrapers
|
// DeliverFunc (like http.HandlerFunc) is a shortcut for making wrappers
|
||||||
type DeliverFunc func(Context, state.SimpleDB, Tx) (Result, error)
|
type DeliverFunc func(Context, state.SimpleDB, Tx) (DeliverResult, error)
|
||||||
|
|
||||||
func (c DeliverFunc) DeliverTx(ctx Context, store state.SimpleDB, tx Tx) (Result, error) {
|
func (c DeliverFunc) DeliverTx(ctx Context, store state.SimpleDB, tx Tx) (DeliverResult, error) {
|
||||||
return c(ctx, store, tx)
|
return c(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetOptioner interface {
|
// InitStater sets state from the genesis file
|
||||||
SetOption(l log.Logger, store state.SimpleDB, module, key, value string) (string, error)
|
type InitStater interface {
|
||||||
|
InitState(l log.Logger, store state.SimpleDB, module, key, value string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOptionFunc (like http.HandlerFunc) is a shortcut for making wrapers
|
// InitStateFunc (like http.HandlerFunc) is a shortcut for making wrappers
|
||||||
type SetOptionFunc func(log.Logger, state.SimpleDB, string, string, string) (string, error)
|
type InitStateFunc func(log.Logger, state.SimpleDB, string, string, string) (string, error)
|
||||||
|
|
||||||
func (c SetOptionFunc) SetOption(l log.Logger, store state.SimpleDB, module, key, value string) (string, error) {
|
func (c InitStateFunc) InitState(l log.Logger, store state.SimpleDB, module, key, value string) (string, error) {
|
||||||
return c(l, store, module, key, value)
|
return c(l, store, module, key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result captures any non-error abci result
|
// InitValidater sets the initial validator set
|
||||||
// to make sure people use error for error cases
|
type InitValidater interface {
|
||||||
type Result struct {
|
InitValidate(log log.Logger, store state.SimpleDB, vals []*abci.Validator)
|
||||||
Data data.Bytes
|
|
||||||
Log string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r Result) ToABCI() abci.Result {
|
// InitValidateFunc (like http.HandlerFunc) is a shortcut for making wrappers
|
||||||
|
type InitValidateFunc func(log.Logger, state.SimpleDB, []*abci.Validator)
|
||||||
|
|
||||||
|
func (c InitValidateFunc) InitValidate(l log.Logger, store state.SimpleDB, vals []*abci.Validator) {
|
||||||
|
c(l, store, vals)
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------- results and some wrappers --------
|
||||||
|
|
||||||
|
// Result is a common interface of CheckResult and GetResult
|
||||||
|
type Result interface {
|
||||||
|
GetData() data.Bytes
|
||||||
|
GetLog() string
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToABCI(r Result) abci.Result {
|
||||||
return abci.Result{
|
return abci.Result{
|
||||||
Data: r.Data,
|
Data: r.GetData(),
|
||||||
Log: r.Log,
|
Log: r.GetLog(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckResult captures any non-error abci result
|
||||||
|
// to make sure people use error for error cases
|
||||||
|
type CheckResult struct {
|
||||||
|
Data data.Bytes
|
||||||
|
Log string
|
||||||
|
// GasAllocated is the maximum units of work we allow this tx to perform
|
||||||
|
GasAllocated uint64
|
||||||
|
// GasPayment is the total fees for this tx (or other source of payment)
|
||||||
|
GasPayment uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCheck sets the gas used and the response data but no more info
|
||||||
|
// these are the most common info needed to be set by the Handler
|
||||||
|
func NewCheck(gasAllocated uint64, log string) CheckResult {
|
||||||
|
return CheckResult{
|
||||||
|
GasAllocated: gasAllocated,
|
||||||
|
Log: log,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Result = CheckResult{}
|
||||||
|
|
||||||
|
func (r CheckResult) GetData() data.Bytes {
|
||||||
|
return r.Data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r CheckResult) GetLog() string {
|
||||||
|
return r.Log
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeliverResult captures any non-error abci result
|
||||||
|
// to make sure people use error for error cases
|
||||||
|
type DeliverResult struct {
|
||||||
|
Data data.Bytes
|
||||||
|
Log string
|
||||||
|
Diff []*abci.Validator
|
||||||
|
GasUsed uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Result = DeliverResult{}
|
||||||
|
|
||||||
|
func (r DeliverResult) GetData() data.Bytes {
|
||||||
|
return r.Data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r DeliverResult) GetLog() string {
|
||||||
|
return r.Log
|
||||||
|
}
|
||||||
|
|
||||||
// placeholders
|
// placeholders
|
||||||
// holders
|
// holders
|
||||||
type NopCheck struct{}
|
type NopCheck struct{}
|
||||||
|
|
||||||
func (_ NopCheck) CheckTx(Context, state.SimpleDB, Tx) (r Result, e error) { return }
|
func (_ NopCheck) CheckTx(Context, state.SimpleDB, Tx) (r CheckResult, e error) { return }
|
||||||
|
|
||||||
type NopDeliver struct{}
|
type NopDeliver struct{}
|
||||||
|
|
||||||
func (_ NopDeliver) DeliverTx(Context, state.SimpleDB, Tx) (r Result, e error) { return }
|
func (_ NopDeliver) DeliverTx(Context, state.SimpleDB, Tx) (r DeliverResult, e error) { return }
|
||||||
|
|
||||||
type NopOption struct{}
|
type NopInitState struct{}
|
||||||
|
|
||||||
func (_ NopOption) SetOption(log.Logger, state.SimpleDB, string, string, string) (string, error) {
|
func (_ NopInitState) InitState(log.Logger, state.SimpleDB, string, string, string) (string, error) {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NopInitValidate struct{}
|
||||||
|
|
||||||
|
func (_ NopInitValidate) InitValidate(log log.Logger, store state.SimpleDB, vals []*abci.Validator) {}
|
||||||
|
|
|
@ -17,7 +17,8 @@ const (
|
||||||
// Signatures parses out go-crypto signatures and adds permissions to the
|
// Signatures parses out go-crypto signatures and adds permissions to the
|
||||||
// context for use inside the application
|
// context for use inside the application
|
||||||
type Signatures struct {
|
type Signatures struct {
|
||||||
stack.PassOption
|
stack.PassInitState
|
||||||
|
stack.PassInitValidate
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name of the module - fulfills Middleware interface
|
// Name of the module - fulfills Middleware interface
|
||||||
|
@ -39,7 +40,7 @@ type Signable interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTx verifies the signatures are correct - fulfills Middlware interface
|
// CheckTx verifies the signatures are correct - fulfills Middlware interface
|
||||||
func (Signatures) CheckTx(ctx basecoin.Context, store state.SimpleDB, 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.CheckResult, err error) {
|
||||||
sigs, tnext, err := getSigners(tx)
|
sigs, tnext, err := getSigners(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
@ -49,7 +50,7 @@ func (Signatures) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoi
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx verifies the signatures are correct - fulfills Middlware interface
|
// DeliverTx verifies the signatures are correct - fulfills Middlware interface
|
||||||
func (Signatures) DeliverTx(ctx basecoin.Context, store state.SimpleDB, 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.DeliverResult, err error) {
|
||||||
sigs, tnext, err := getSigners(tx)
|
sigs, tnext, err := getSigners(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
|
|
@ -13,7 +13,8 @@ const (
|
||||||
|
|
||||||
// Chain enforces that this tx was bound to the named chain
|
// Chain enforces that this tx was bound to the named chain
|
||||||
type Chain struct {
|
type Chain struct {
|
||||||
stack.PassOption
|
stack.PassInitState
|
||||||
|
stack.PassInitValidate
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name of the module - fulfills Middleware interface
|
// Name of the module - fulfills Middleware interface
|
||||||
|
@ -24,7 +25,7 @@ func (Chain) Name() string {
|
||||||
var _ stack.Middleware = Chain{}
|
var _ stack.Middleware = Chain{}
|
||||||
|
|
||||||
// CheckTx makes sure we are on the proper chain - fulfills Middlware interface
|
// CheckTx makes sure we are on the proper chain - fulfills Middlware interface
|
||||||
func (c Chain) CheckTx(ctx basecoin.Context, store state.SimpleDB, 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.CheckResult, err error) {
|
||||||
stx, err := c.checkChainTx(ctx.ChainID(), ctx.BlockHeight(), tx)
|
stx, err := c.checkChainTx(ctx.ChainID(), ctx.BlockHeight(), tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
@ -33,7 +34,7 @@ func (c Chain) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.T
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx makes sure we are on the proper chain - fulfills Middlware interface
|
// DeliverTx makes sure we are on the proper chain - fulfills Middlware interface
|
||||||
func (c Chain) DeliverTx(ctx basecoin.Context, store state.SimpleDB, 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.DeliverResult, err error) {
|
||||||
stx, err := c.checkChainTx(ctx.ChainID(), ctx.BlockHeight(), tx)
|
stx, err := c.checkChainTx(ctx.ChainID(), ctx.BlockHeight(), tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
|
|
@ -82,10 +82,10 @@ func TestChain(t *testing.T) {
|
||||||
i := strconv.Itoa(idx)
|
i := strconv.Itoa(idx)
|
||||||
|
|
||||||
// make sure check returns error, not a panic crash
|
// make sure check returns error, not a panic crash
|
||||||
res, err := app.CheckTx(ctx, store, tc.tx)
|
cres, err := app.CheckTx(ctx, store, tc.tx)
|
||||||
if tc.valid {
|
if tc.valid {
|
||||||
assert.Nil(err, "%d: %+v", idx, err)
|
assert.Nil(err, "%d: %+v", idx, err)
|
||||||
assert.Equal(msg, res.Log, i)
|
assert.Equal(msg, cres.Log, i)
|
||||||
} else {
|
} else {
|
||||||
if assert.NotNil(err, i) {
|
if assert.NotNil(err, i) {
|
||||||
assert.Contains(err.Error(), tc.errorMsg, i)
|
assert.Contains(err.Error(), tc.errorMsg, i)
|
||||||
|
@ -93,10 +93,10 @@ func TestChain(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure deliver returns error, not a panic crash
|
// make sure deliver returns error, not a panic crash
|
||||||
res, err = app.DeliverTx(ctx, store, tc.tx)
|
dres, err := app.DeliverTx(ctx, store, tc.tx)
|
||||||
if tc.valid {
|
if tc.valid {
|
||||||
assert.Nil(err, "%d: %+v", idx, err)
|
assert.Nil(err, "%d: %+v", idx, err)
|
||||||
assert.Equal(msg, res.Log, i)
|
assert.Equal(msg, dres.Log, i)
|
||||||
} else {
|
} else {
|
||||||
if assert.NotNil(err, i) {
|
if assert.NotNil(err, i) {
|
||||||
assert.Contains(err.Error(), tc.errorMsg, i)
|
assert.Contains(err.Error(), tc.errorMsg, i)
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
|
|
||||||
|
"github.com/tendermint/basecoin"
|
||||||
|
"github.com/tendermint/basecoin/errors"
|
||||||
|
"github.com/tendermint/basecoin/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
//nolint
|
||||||
|
const (
|
||||||
|
NameVal = "val"
|
||||||
|
NamePrice = "price"
|
||||||
|
|
||||||
|
TypeValChange = NameVal + "/change"
|
||||||
|
ByteValChange = 0xfe
|
||||||
|
|
||||||
|
TypePriceShow = NamePrice + "/show"
|
||||||
|
BytePriceShow = 0xfd
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
basecoin.TxMapper.
|
||||||
|
RegisterImplementation(ValChangeTx{}, TypeValChange, ByteValChange).
|
||||||
|
RegisterImplementation(PriceShowTx{}, TypePriceShow, BytePriceShow)
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------
|
||||||
|
// Setup tx and handler for validation test cases
|
||||||
|
|
||||||
|
type ValSetHandler struct {
|
||||||
|
basecoin.NopCheck
|
||||||
|
basecoin.NopInitState
|
||||||
|
basecoin.NopInitValidate
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ basecoin.Handler = ValSetHandler{}
|
||||||
|
|
||||||
|
func (ValSetHandler) Name() string {
|
||||||
|
return NameVal
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ValSetHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
|
tx basecoin.Tx) (res basecoin.DeliverResult, err error) {
|
||||||
|
change, ok := tx.Unwrap().(ValChangeTx)
|
||||||
|
if !ok {
|
||||||
|
return res, errors.ErrUnknownTxType(tx)
|
||||||
|
}
|
||||||
|
res.Diff = change.Diff
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type ValChangeTx struct {
|
||||||
|
Diff []*abci.Validator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v ValChangeTx) Wrap() basecoin.Tx {
|
||||||
|
return basecoin.Tx{v}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v ValChangeTx) ValidateBasic() error { return nil }
|
||||||
|
|
||||||
|
//--------------------------------
|
||||||
|
// Setup tx and handler for testing checktx fees/gas
|
||||||
|
|
||||||
|
// PriceData is the data we ping back
|
||||||
|
var PriceData = []byte{0xCA, 0xFE}
|
||||||
|
|
||||||
|
// PriceHandler returns checktx results based on the input
|
||||||
|
type PriceHandler struct {
|
||||||
|
basecoin.NopInitState
|
||||||
|
basecoin.NopInitValidate
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ basecoin.Handler = PriceHandler{}
|
||||||
|
|
||||||
|
func (PriceHandler) Name() string {
|
||||||
|
return NamePrice
|
||||||
|
}
|
||||||
|
|
||||||
|
func (PriceHandler) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
|
tx basecoin.Tx) (res basecoin.CheckResult, err error) {
|
||||||
|
price, ok := tx.Unwrap().(PriceShowTx)
|
||||||
|
if !ok {
|
||||||
|
return res, errors.ErrUnknownTxType(tx)
|
||||||
|
}
|
||||||
|
res.GasAllocated = price.GasAllocated
|
||||||
|
res.GasPayment = price.GasPayment
|
||||||
|
res.Data = PriceData
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (PriceHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
|
tx basecoin.Tx) (res basecoin.DeliverResult, err error) {
|
||||||
|
_, ok := tx.Unwrap().(PriceShowTx)
|
||||||
|
if !ok {
|
||||||
|
return res, errors.ErrUnknownTxType(tx)
|
||||||
|
}
|
||||||
|
res.Data = PriceData
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// PriceShowTx lets us bounce back a given fee/gas on CheckTx
|
||||||
|
type PriceShowTx struct {
|
||||||
|
GasAllocated uint64
|
||||||
|
GasPayment uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPriceShowTx(gasAllocated, gasPayment uint64) basecoin.Tx {
|
||||||
|
return PriceShowTx{GasAllocated: gasAllocated, GasPayment: gasPayment}.Wrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p PriceShowTx) Wrap() basecoin.Tx {
|
||||||
|
return basecoin.Tx{p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v PriceShowTx) ValidateBasic() error { return nil }
|
|
@ -3,6 +3,7 @@ package base
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin"
|
"github.com/tendermint/basecoin"
|
||||||
|
@ -26,7 +27,7 @@ func (Logger) Name() string {
|
||||||
var _ stack.Middleware = Logger{}
|
var _ stack.Middleware = Logger{}
|
||||||
|
|
||||||
// CheckTx logs time and result - fulfills Middlware interface
|
// CheckTx logs time and result - fulfills Middlware interface
|
||||||
func (Logger) CheckTx(ctx basecoin.Context, store state.SimpleDB, 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.CheckResult, err error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
res, err = next.CheckTx(ctx, store, tx)
|
res, err = next.CheckTx(ctx, store, tx)
|
||||||
delta := time.Now().Sub(start)
|
delta := time.Now().Sub(start)
|
||||||
|
@ -41,7 +42,7 @@ func (Logger) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx logs time and result - fulfills Middlware interface
|
// DeliverTx logs time and result - fulfills Middlware interface
|
||||||
func (Logger) DeliverTx(ctx basecoin.Context, store state.SimpleDB, 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.DeliverResult, err error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
res, err = next.DeliverTx(ctx, store, tx)
|
res, err = next.DeliverTx(ctx, store, tx)
|
||||||
delta := time.Now().Sub(start)
|
delta := time.Now().Sub(start)
|
||||||
|
@ -55,21 +56,30 @@ func (Logger) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOption logs time and result - fulfills Middlware interface
|
// InitState logs time and result - fulfills Middlware interface
|
||||||
func (Logger) SetOption(l log.Logger, store state.SimpleDB, module, key, value string, next basecoin.SetOptioner) (string, error) {
|
func (Logger) InitState(l log.Logger, store state.SimpleDB, module, key, value string, next basecoin.InitStater) (string, error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
res, err := next.SetOption(l, store, module, key, value)
|
res, err := next.InitState(l, store, module, key, value)
|
||||||
delta := time.Now().Sub(start)
|
delta := time.Now().Sub(start)
|
||||||
// TODO: log the value being set also?
|
// TODO: log the value being set also?
|
||||||
l = l.With("duration", micros(delta)).With("mod", module).With("key", key)
|
l = l.With("duration", micros(delta)).With("mod", module).With("key", key)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
l.Info("SetOption", "log", res)
|
l.Info("InitState", "log", res)
|
||||||
} else {
|
} else {
|
||||||
l.Error("SetOption", "err", err)
|
l.Error("InitState", "err", err)
|
||||||
}
|
}
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitValidate logs time and result - fulfills Middlware interface
|
||||||
|
func (Logger) InitValidate(l log.Logger, store state.SimpleDB, vals []*abci.Validator, next basecoin.InitValidater) {
|
||||||
|
start := time.Now()
|
||||||
|
next.InitValidate(l, store, vals)
|
||||||
|
delta := time.Now().Sub(start)
|
||||||
|
l = l.With("duration", micros(delta))
|
||||||
|
l.Info("InitValidate")
|
||||||
|
}
|
||||||
|
|
||||||
// micros returns how many microseconds passed in a call
|
// micros returns how many microseconds passed in a call
|
||||||
func micros(d time.Duration) int {
|
func micros(d time.Duration) int {
|
||||||
return int(d.Seconds() * 1000000)
|
return int(d.Seconds() * 1000000)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package base
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
wire "github.com/tendermint/go-wire"
|
wire "github.com/tendermint/go-wire"
|
||||||
"github.com/tendermint/go-wire/data"
|
"github.com/tendermint/go-wire/data"
|
||||||
|
|
||||||
|
@ -18,7 +19,8 @@ const (
|
||||||
|
|
||||||
// Multiplexer grabs a MultiTx and sends them sequentially down the line
|
// Multiplexer grabs a MultiTx and sends them sequentially down the line
|
||||||
type Multiplexer struct {
|
type Multiplexer struct {
|
||||||
stack.PassOption
|
stack.PassInitState
|
||||||
|
stack.PassInitValidate
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name of the module - fulfills Middleware interface
|
// Name of the module - fulfills Middleware interface
|
||||||
|
@ -29,45 +31,86 @@ func (Multiplexer) Name() string {
|
||||||
var _ stack.Middleware = Multiplexer{}
|
var _ stack.Middleware = Multiplexer{}
|
||||||
|
|
||||||
// CheckTx splits the input tx and checks them all - fulfills Middlware interface
|
// CheckTx splits the input tx and checks them all - fulfills Middlware interface
|
||||||
func (Multiplexer) CheckTx(ctx basecoin.Context, store state.SimpleDB, 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.CheckResult, err error) {
|
||||||
if mtx, ok := tx.Unwrap().(*MultiTx); ok {
|
if mtx, ok := tx.Unwrap().(MultiTx); ok {
|
||||||
return runAll(ctx, store, mtx.Txs, next.CheckTx)
|
return runAllChecks(ctx, store, mtx.Txs, next)
|
||||||
}
|
}
|
||||||
return next.CheckTx(ctx, store, tx)
|
return next.CheckTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx splits the input tx and checks them all - fulfills Middlware interface
|
// DeliverTx splits the input tx and checks them all - fulfills Middlware interface
|
||||||
func (Multiplexer) DeliverTx(ctx basecoin.Context, store state.SimpleDB, 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.DeliverResult, err error) {
|
||||||
if mtx, ok := tx.Unwrap().(*MultiTx); ok {
|
if mtx, ok := tx.Unwrap().(MultiTx); ok {
|
||||||
return runAll(ctx, store, mtx.Txs, next.DeliverTx)
|
return runAllDelivers(ctx, store, mtx.Txs, next)
|
||||||
}
|
}
|
||||||
return next.DeliverTx(ctx, store, tx)
|
return next.DeliverTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runAll(ctx basecoin.Context, store state.SimpleDB, txs []basecoin.Tx, next basecoin.CheckerFunc) (res basecoin.Result, err error) {
|
func runAllChecks(ctx basecoin.Context, store state.SimpleDB, txs []basecoin.Tx, next basecoin.Checker) (res basecoin.CheckResult, err error) {
|
||||||
// store all results, unless anything errors
|
// store all results, unless anything errors
|
||||||
rs := make([]basecoin.Result, len(txs))
|
rs := make([]basecoin.CheckResult, len(txs))
|
||||||
for i, stx := range txs {
|
for i, stx := range txs {
|
||||||
rs[i], err = next(ctx, store, stx)
|
rs[i], err = next.CheckTx(ctx, store, stx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// now combine the results into one...
|
// now combine the results into one...
|
||||||
return combine(rs), nil
|
return combineChecks(rs), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runAllDelivers(ctx basecoin.Context, store state.SimpleDB, txs []basecoin.Tx, next basecoin.Deliver) (res basecoin.DeliverResult, err error) {
|
||||||
|
// store all results, unless anything errors
|
||||||
|
rs := make([]basecoin.DeliverResult, len(txs))
|
||||||
|
for i, stx := range txs {
|
||||||
|
rs[i], err = next.DeliverTx(ctx, store, stx)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// now combine the results into one...
|
||||||
|
return combineDelivers(rs), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// combines all data bytes as a go-wire array.
|
// combines all data bytes as a go-wire array.
|
||||||
// joins all log messages with \n
|
// joins all log messages with \n
|
||||||
func combine(all []basecoin.Result) basecoin.Result {
|
func combineChecks(all []basecoin.CheckResult) basecoin.CheckResult {
|
||||||
datas := make([]data.Bytes, len(all))
|
datas := make([]data.Bytes, len(all))
|
||||||
logs := make([]string, len(all))
|
logs := make([]string, len(all))
|
||||||
|
var allocated, payments uint64
|
||||||
for i, r := range all {
|
for i, r := range all {
|
||||||
datas[i] = r.Data
|
datas[i] = r.Data
|
||||||
logs[i] = r.Log
|
logs[i] = r.Log
|
||||||
|
allocated += r.GasAllocated
|
||||||
|
payments += r.GasPayment
|
||||||
}
|
}
|
||||||
return basecoin.Result{
|
return basecoin.CheckResult{
|
||||||
Data: wire.BinaryBytes(datas),
|
Data: wire.BinaryBytes(datas),
|
||||||
Log: strings.Join(logs, "\n"),
|
Log: strings.Join(logs, "\n"),
|
||||||
|
GasAllocated: allocated,
|
||||||
|
GasPayment: payments,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// combines all data bytes as a go-wire array.
|
||||||
|
// joins all log messages with \n
|
||||||
|
func combineDelivers(all []basecoin.DeliverResult) basecoin.DeliverResult {
|
||||||
|
datas := make([]data.Bytes, len(all))
|
||||||
|
logs := make([]string, len(all))
|
||||||
|
var used uint64
|
||||||
|
var diffs []*abci.Validator
|
||||||
|
for i, r := range all {
|
||||||
|
datas[i] = r.Data
|
||||||
|
logs[i] = r.Log
|
||||||
|
used += r.GasUsed
|
||||||
|
if len(r.Diff) > 0 {
|
||||||
|
diffs = append(diffs, r.Diff...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return basecoin.DeliverResult{
|
||||||
|
Data: wire.BinaryBytes(datas),
|
||||||
|
Log: strings.Join(logs, "\n"),
|
||||||
|
GasUsed: used,
|
||||||
|
Diff: diffs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/tendermint/basecoin"
|
||||||
|
"github.com/tendermint/basecoin/stack"
|
||||||
|
"github.com/tendermint/basecoin/state"
|
||||||
|
wire "github.com/tendermint/go-wire"
|
||||||
|
"github.com/tendermint/go-wire/data"
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMultiplexer(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
msg := "diddly"
|
||||||
|
chainID := "multi-verse"
|
||||||
|
height := uint64(100)
|
||||||
|
|
||||||
|
// Generic args here...
|
||||||
|
store := state.NewMemKVStore()
|
||||||
|
ctx := stack.NewContext(chainID, height, log.NewNopLogger())
|
||||||
|
|
||||||
|
// Build the stack
|
||||||
|
app := stack.
|
||||||
|
New(Multiplexer{}).
|
||||||
|
Dispatch(
|
||||||
|
stack.WrapHandler(stack.OKHandler{Log: msg}),
|
||||||
|
stack.WrapHandler(PriceHandler{}),
|
||||||
|
)
|
||||||
|
|
||||||
|
raw := stack.NewRawTx([]byte{1, 2, 3, 4})
|
||||||
|
fail := stack.NewFailTx()
|
||||||
|
price1 := NewPriceShowTx(123, 456)
|
||||||
|
price2 := NewPriceShowTx(1000, 2000)
|
||||||
|
price3 := NewPriceShowTx(11, 0)
|
||||||
|
|
||||||
|
join := func(data ...[]byte) []byte {
|
||||||
|
return wire.BinaryBytes(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := [...]struct {
|
||||||
|
tx basecoin.Tx
|
||||||
|
valid bool
|
||||||
|
gasAllocated uint64
|
||||||
|
gasPayment uint64
|
||||||
|
log string
|
||||||
|
data data.Bytes
|
||||||
|
}{
|
||||||
|
// test the components without multiplexer (no effect)
|
||||||
|
0: {raw, true, 0, 0, msg, nil},
|
||||||
|
1: {price1, true, 123, 456, "", PriceData},
|
||||||
|
2: {fail, false, 0, 0, "", nil},
|
||||||
|
// test multiplexer on error
|
||||||
|
3: {NewMultiTx(raw, fail, price1), false, 0, 0, "", nil},
|
||||||
|
// test combining info on multiplexer
|
||||||
|
4: {NewMultiTx(price1, raw), true, 123, 456, "\n" + msg, join(PriceData, nil)},
|
||||||
|
// add lots of prices
|
||||||
|
5: {NewMultiTx(price1, price2, price3), true, 1134, 2456, "\n\n", join(PriceData, PriceData, PriceData)},
|
||||||
|
// combine multiple logs
|
||||||
|
6: {NewMultiTx(raw, price3, raw), true, 11, 0, msg + "\n\n" + msg, join(nil, PriceData, nil)},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range cases {
|
||||||
|
cres, err := app.CheckTx(ctx, store, tc.tx)
|
||||||
|
if tc.valid {
|
||||||
|
assert.Nil(err, "%d: %+v", i, err)
|
||||||
|
assert.Equal(tc.log, cres.Log, "%d", i)
|
||||||
|
assert.Equal(tc.data, cres.Data, "%d", i)
|
||||||
|
assert.Equal(tc.gasAllocated, cres.GasAllocated, "%d", i)
|
||||||
|
assert.Equal(tc.gasPayment, cres.GasPayment, "%d", i)
|
||||||
|
} else {
|
||||||
|
assert.NotNil(err, "%d", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure deliver returns error, not a panic crash
|
||||||
|
dres, err := app.DeliverTx(ctx, store, tc.tx)
|
||||||
|
if tc.valid {
|
||||||
|
assert.Nil(err, "%d: %+v", i, err)
|
||||||
|
assert.Equal(tc.log, dres.Log, "%d", i)
|
||||||
|
assert.Equal(tc.data, dres.Data, "%d", i)
|
||||||
|
} else {
|
||||||
|
assert.NotNil(err, "%d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,13 +18,13 @@ func TestEncoding(t *testing.T) {
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
raw := stack.NewRawTx([]byte{0x34, 0xa7})
|
raw := stack.NewRawTx([]byte{0x34, 0xa7})
|
||||||
raw2 := stack.NewRawTx([]byte{0x73, 0x86, 0x22})
|
// raw2 := stack.NewRawTx([]byte{0x73, 0x86, 0x22})
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
Tx basecoin.Tx
|
Tx basecoin.Tx
|
||||||
}{
|
}{
|
||||||
{raw},
|
{raw},
|
||||||
{NewMultiTx(raw, raw2)},
|
// {NewMultiTx(raw, raw2)},
|
||||||
{NewChainTx("foobar", 0, raw)},
|
{NewChainTx("foobar", 0, raw)},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ func BenchmarkSimpleTransfer(b *testing.B) {
|
||||||
|
|
||||||
// set the initial account
|
// set the initial account
|
||||||
acct := NewAccountWithKey(Coins{{"mycoin", 1234567890}})
|
acct := NewAccountWithKey(Coins{{"mycoin", 1234567890}})
|
||||||
h.SetOption(logger, store, NameCoin, "account", acct.MakeOption(), nil)
|
h.InitState(logger, store, NameCoin, "account", acct.MakeOption(), nil)
|
||||||
sender := acct.Actor()
|
sender := acct.Actor()
|
||||||
receiver := basecoin.Actor{App: "foo", Address: cmn.RandBytes(20)}
|
receiver := basecoin.Actor{App: "foo", Address: cmn.RandBytes(20)}
|
||||||
|
|
||||||
|
|
|
@ -12,11 +12,19 @@ import (
|
||||||
"github.com/tendermint/basecoin/state"
|
"github.com/tendermint/basecoin/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
//NameCoin - name space of the coin module
|
const (
|
||||||
const NameCoin = "coin"
|
//NameCoin - name space of the coin module
|
||||||
|
NameCoin = "coin"
|
||||||
|
// CostSend is GasAllocation per input/output
|
||||||
|
CostSend = uint64(10)
|
||||||
|
// CostCredit is GasAllocation of a credit allocation
|
||||||
|
CostCredit = uint64(20)
|
||||||
|
)
|
||||||
|
|
||||||
// Handler includes an accountant
|
// Handler includes an accountant
|
||||||
type Handler struct{}
|
type Handler struct {
|
||||||
|
stack.PassInitValidate
|
||||||
|
}
|
||||||
|
|
||||||
var _ stack.Dispatchable = Handler{}
|
var _ stack.Dispatchable = Handler{}
|
||||||
|
|
||||||
|
@ -35,7 +43,7 @@ func (Handler) AssertDispatcher() {}
|
||||||
|
|
||||||
// CheckTx checks if there is enough money in the account
|
// CheckTx checks if there is enough money in the account
|
||||||
func (h Handler) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
func (h Handler) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, _ basecoin.Checker) (res basecoin.Result, err error) {
|
tx basecoin.Tx, _ basecoin.Checker) (res basecoin.CheckResult, err error) {
|
||||||
|
|
||||||
err = tx.ValidateBasic()
|
err = tx.ValidateBasic()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -44,16 +52,19 @@ func (h Handler) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
|
|
||||||
switch t := tx.Unwrap().(type) {
|
switch t := tx.Unwrap().(type) {
|
||||||
case SendTx:
|
case SendTx:
|
||||||
return res, h.checkSendTx(ctx, store, t)
|
// price based on inputs and outputs
|
||||||
|
used := uint64(len(t.Inputs) + len(t.Outputs))
|
||||||
|
return basecoin.NewCheck(used*CostSend, ""), h.checkSendTx(ctx, store, t)
|
||||||
case CreditTx:
|
case CreditTx:
|
||||||
return h.creditTx(ctx, store, t)
|
// default price of 20, constant work
|
||||||
|
return basecoin.NewCheck(CostCredit, ""), h.creditTx(ctx, store, t)
|
||||||
}
|
}
|
||||||
return res, errors.ErrUnknownTxType(tx.Unwrap())
|
return res, errors.ErrUnknownTxType(tx.Unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx moves the money
|
// DeliverTx moves the money
|
||||||
func (h Handler) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
func (h Handler) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, cb basecoin.Deliver) (res basecoin.Result, err error) {
|
tx basecoin.Tx, cb basecoin.Deliver) (res basecoin.DeliverResult, err error) {
|
||||||
|
|
||||||
err = tx.ValidateBasic()
|
err = tx.ValidateBasic()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -62,16 +73,16 @@ func (h Handler) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
|
|
||||||
switch t := tx.Unwrap().(type) {
|
switch t := tx.Unwrap().(type) {
|
||||||
case SendTx:
|
case SendTx:
|
||||||
return h.sendTx(ctx, store, t, cb)
|
return res, h.sendTx(ctx, store, t, cb)
|
||||||
case CreditTx:
|
case CreditTx:
|
||||||
return h.creditTx(ctx, store, t)
|
return res, h.creditTx(ctx, store, t)
|
||||||
}
|
}
|
||||||
return res, errors.ErrUnknownTxType(tx.Unwrap())
|
return res, errors.ErrUnknownTxType(tx.Unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOption - sets the genesis account balance
|
// InitState - sets the genesis account balance
|
||||||
func (h Handler) SetOption(l log.Logger, store state.SimpleDB,
|
func (h Handler) InitState(l log.Logger, store state.SimpleDB,
|
||||||
module, key, value string, cb basecoin.SetOptioner) (log string, err error) {
|
module, key, value string, cb basecoin.InitStater) (log string, err error) {
|
||||||
if module != NameCoin {
|
if module != NameCoin {
|
||||||
return "", errors.ErrUnknownModule(module)
|
return "", errors.ErrUnknownModule(module)
|
||||||
}
|
}
|
||||||
|
@ -85,11 +96,11 @@ func (h Handler) SetOption(l log.Logger, store state.SimpleDB,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h Handler) sendTx(ctx basecoin.Context, store state.SimpleDB,
|
func (h Handler) sendTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
send SendTx, cb basecoin.Deliver) (res basecoin.Result, err error) {
|
send SendTx, cb basecoin.Deliver) error {
|
||||||
|
|
||||||
err = checkTx(ctx, send)
|
err := checkTx(ctx, send)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// deduct from all input accounts
|
// deduct from all input accounts
|
||||||
|
@ -97,7 +108,7 @@ func (h Handler) sendTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
for _, in := range send.Inputs {
|
for _, in := range send.Inputs {
|
||||||
_, err = ChangeCoins(store, in.Address, in.Coins.Negative())
|
_, err = ChangeCoins(store, in.Address, in.Coins.Negative())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return err
|
||||||
}
|
}
|
||||||
senders = append(senders, in.Address)
|
senders = append(senders, in.Address)
|
||||||
}
|
}
|
||||||
|
@ -112,7 +123,7 @@ func (h Handler) sendTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
|
|
||||||
_, err = ChangeCoins(store, out.Address, out.Coins)
|
_, err = ChangeCoins(store, out.Address, out.Coins)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return err
|
||||||
}
|
}
|
||||||
// now send ibc packet if needed...
|
// now send ibc packet if needed...
|
||||||
if out.Address.ChainID != "" {
|
if out.Address.ChainID != "" {
|
||||||
|
@ -133,46 +144,46 @@ func (h Handler) sendTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
ibcCtx := ctx.WithPermissions(ibc.AllowIBC(NameCoin))
|
ibcCtx := ctx.WithPermissions(ibc.AllowIBC(NameCoin))
|
||||||
_, err := cb.DeliverTx(ibcCtx, store, packet.Wrap())
|
_, err := cb.DeliverTx(ibcCtx, store, packet.Wrap())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// a-ok!
|
// a-ok!
|
||||||
return res, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h Handler) creditTx(ctx basecoin.Context, store state.SimpleDB,
|
func (h Handler) creditTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
credit CreditTx) (res basecoin.Result, err error) {
|
credit CreditTx) error {
|
||||||
|
|
||||||
// first check permissions!!
|
// first check permissions!!
|
||||||
info, err := loadHandlerInfo(store)
|
info, err := loadHandlerInfo(store)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return err
|
||||||
}
|
}
|
||||||
if info.Issuer.Empty() || !ctx.HasPermission(info.Issuer) {
|
if info.Issuer.Empty() || !ctx.HasPermission(info.Issuer) {
|
||||||
return res, errors.ErrUnauthorized()
|
return errors.ErrUnauthorized()
|
||||||
}
|
}
|
||||||
|
|
||||||
// load up the account
|
// load up the account
|
||||||
addr := ChainAddr(credit.Debitor)
|
addr := ChainAddr(credit.Debitor)
|
||||||
acct, err := GetAccount(store, addr)
|
acct, err := GetAccount(store, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// make and check changes
|
// make and check changes
|
||||||
acct.Coins = acct.Coins.Plus(credit.Credit)
|
acct.Coins = acct.Coins.Plus(credit.Credit)
|
||||||
if !acct.Coins.IsNonnegative() {
|
if !acct.Coins.IsNonnegative() {
|
||||||
return res, ErrInsufficientFunds()
|
return ErrInsufficientFunds()
|
||||||
}
|
}
|
||||||
acct.Credit = acct.Credit.Plus(credit.Credit)
|
acct.Credit = acct.Credit.Plus(credit.Credit)
|
||||||
if !acct.Credit.IsNonnegative() {
|
if !acct.Credit.IsNonnegative() {
|
||||||
return res, ErrInsufficientCredit()
|
return ErrInsufficientCredit()
|
||||||
}
|
}
|
||||||
|
|
||||||
err = storeAccount(store, addr.Bytes(), acct)
|
err = storeAccount(store, addr.Bytes(), acct)
|
||||||
return res, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkTx(ctx basecoin.Context, send SendTx) error {
|
func checkTx(ctx basecoin.Context, send SendTx) error {
|
||||||
|
|
|
@ -85,7 +85,7 @@ func TestHandlerValidation(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeliverSendTx(t *testing.T) {
|
func TestCheckDeliverSendTx(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
|
@ -110,6 +110,7 @@ func TestDeliverSendTx(t *testing.T) {
|
||||||
tx basecoin.Tx
|
tx basecoin.Tx
|
||||||
perms []basecoin.Actor
|
perms []basecoin.Actor
|
||||||
final []money // nil for error
|
final []money // nil for error
|
||||||
|
cost uint64 // gas allocated (if not error)
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
[]money{{addr1, moreCoins}},
|
[]money{{addr1, moreCoins}},
|
||||||
|
@ -118,6 +119,7 @@ func TestDeliverSendTx(t *testing.T) {
|
||||||
[]TxOutput{NewTxOutput(addr2, someCoins)}),
|
[]TxOutput{NewTxOutput(addr2, someCoins)}),
|
||||||
[]basecoin.Actor{addr1},
|
[]basecoin.Actor{addr1},
|
||||||
[]money{{addr1, diffCoins}, {addr2, someCoins}},
|
[]money{{addr1, diffCoins}, {addr2, someCoins}},
|
||||||
|
20,
|
||||||
},
|
},
|
||||||
// simple multi-sig 2 accounts to 1
|
// simple multi-sig 2 accounts to 1
|
||||||
{
|
{
|
||||||
|
@ -127,6 +129,7 @@ func TestDeliverSendTx(t *testing.T) {
|
||||||
[]TxOutput{NewTxOutput(addr3, mixedCoins)}),
|
[]TxOutput{NewTxOutput(addr3, mixedCoins)}),
|
||||||
[]basecoin.Actor{addr1, addr2},
|
[]basecoin.Actor{addr1, addr2},
|
||||||
[]money{{addr1, someCoins}, {addr2, diffCoins}, {addr3, mixedCoins}},
|
[]money{{addr1, someCoins}, {addr2, diffCoins}, {addr3, mixedCoins}},
|
||||||
|
30,
|
||||||
},
|
},
|
||||||
// multi-sig with one account sending many times
|
// multi-sig with one account sending many times
|
||||||
{
|
{
|
||||||
|
@ -136,6 +139,17 @@ func TestDeliverSendTx(t *testing.T) {
|
||||||
[]TxOutput{NewTxOutput(addr2, mixedCoins)}),
|
[]TxOutput{NewTxOutput(addr2, mixedCoins)}),
|
||||||
[]basecoin.Actor{addr1},
|
[]basecoin.Actor{addr1},
|
||||||
[]money{{addr1, diffCoins}, {addr2, mixedCoins}},
|
[]money{{addr1, diffCoins}, {addr2, mixedCoins}},
|
||||||
|
30,
|
||||||
|
},
|
||||||
|
// invalid send (not enough money )
|
||||||
|
{
|
||||||
|
[]money{{addr1, moreCoins}, {addr2, someCoins}},
|
||||||
|
NewSendTx(
|
||||||
|
[]TxInput{NewTxInput(addr2, moreCoins)},
|
||||||
|
[]TxOutput{NewTxOutput(addr1, moreCoins)}),
|
||||||
|
[]basecoin.Actor{addr1, addr2},
|
||||||
|
nil,
|
||||||
|
0,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,9 +164,19 @@ func TestDeliverSendTx(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := stack.MockContext("base-chain", 100).WithPermissions(tc.perms...)
|
ctx := stack.MockContext("base-chain", 100).WithPermissions(tc.perms...)
|
||||||
_, err := h.DeliverTx(ctx, store, tc.tx, nil)
|
|
||||||
|
// throw-away state for checktx
|
||||||
|
cache := store.Checkpoint()
|
||||||
|
cres, err := h.CheckTx(ctx, cache, tc.tx, nil)
|
||||||
|
// real store for delivertx
|
||||||
|
_, err2 := h.DeliverTx(ctx, store, tc.tx, nil)
|
||||||
|
|
||||||
if len(tc.final) > 0 { // valid
|
if len(tc.final) > 0 { // valid
|
||||||
assert.Nil(err, "%d: %+v", i, err)
|
assert.Nil(err, "%d: %+v", i, err)
|
||||||
|
assert.Nil(err2, "%d: %+v", i, err2)
|
||||||
|
// make sure proper gas is set
|
||||||
|
assert.Equal(uint64(0), cres.GasPayment, "%d", i)
|
||||||
|
assert.Equal(tc.cost, cres.GasAllocated, "%d", i)
|
||||||
// make sure the final balances are correct
|
// make sure the final balances are correct
|
||||||
for _, f := range tc.final {
|
for _, f := range tc.final {
|
||||||
acct, err := loadAccount(store, f.addr.Bytes())
|
acct, err := loadAccount(store, f.addr.Bytes())
|
||||||
|
@ -160,14 +184,15 @@ func TestDeliverSendTx(t *testing.T) {
|
||||||
assert.Equal(f.coins, acct.Coins)
|
assert.Equal(f.coins, acct.Coins)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// both check and deliver should fail
|
||||||
assert.NotNil(err, "%d", i)
|
assert.NotNil(err, "%d", i)
|
||||||
// TODO: make sure balances unchanged!
|
assert.NotNil(err2, "%d", i)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetOption(t *testing.T) {
|
func TestInitState(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
|
@ -205,7 +230,7 @@ func TestSetOption(t *testing.T) {
|
||||||
for j, gen := range tc.init {
|
for j, gen := range tc.init {
|
||||||
value, err := json.Marshal(gen)
|
value, err := json.Marshal(gen)
|
||||||
require.Nil(err, "%d,%d: %+v", i, j, err)
|
require.Nil(err, "%d,%d: %+v", i, j, err)
|
||||||
_, err = h.SetOption(l, store, NameCoin, key, string(value), nil)
|
_, err = h.InitState(l, store, NameCoin, key, string(value), nil)
|
||||||
require.Nil(err)
|
require.Nil(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,7 +264,7 @@ func TestSetIssuer(t *testing.T) {
|
||||||
|
|
||||||
value, err := json.Marshal(tc.issuer)
|
value, err := json.Marshal(tc.issuer)
|
||||||
require.Nil(err, "%d,%d: %+v", i, err)
|
require.Nil(err, "%d,%d: %+v", i, err)
|
||||||
_, err = h.SetOption(l, store, NameCoin, key, string(value), nil)
|
_, err = h.InitState(l, store, NameCoin, key, string(value), nil)
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
|
|
||||||
// check state is proper
|
// check state is proper
|
||||||
|
@ -274,11 +299,11 @@ func TestDeliverCreditTx(t *testing.T) {
|
||||||
// set the owner who can issue credit
|
// set the owner who can issue credit
|
||||||
js, err := json.Marshal(owner)
|
js, err := json.Marshal(owner)
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
_, err = h.SetOption(log.NewNopLogger(), store, "coin", "issuer", string(js), nil)
|
_, err = h.InitState(log.NewNopLogger(), store, "coin", "issuer", string(js), nil)
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
|
|
||||||
// give addr2 some coins to start
|
// give addr2 some coins to start
|
||||||
_, err = h.SetOption(log.NewNopLogger(), store, "coin", "account", key.MakeOption(), nil)
|
_, err = h.InitState(log.NewNopLogger(), store, "coin", "account", key.MakeOption(), nil)
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
|
|
|
@ -41,7 +41,7 @@ func (a *AccountWithKey) NextSequence() uint32 {
|
||||||
return a.Sequence
|
return a.Sequence
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeOption returns a string to use with SetOption to initialize this account
|
// MakeOption returns a string to use with InitState to initialize this account
|
||||||
//
|
//
|
||||||
// This is intended for use in test cases
|
// This is intended for use in test cases
|
||||||
func (a *AccountWithKey) MakeOption() string {
|
func (a *AccountWithKey) MakeOption() string {
|
||||||
|
|
|
@ -44,7 +44,7 @@ func TestIBCPostPacket(t *testing.T) {
|
||||||
// set up a rich guy on this chain
|
// set up a rich guy on this chain
|
||||||
wealth := Coins{{"btc", 300}, {"eth", 2000}, {"ltc", 5000}}
|
wealth := Coins{{"btc", 300}, {"eth", 2000}, {"ltc", 5000}}
|
||||||
rich := NewAccountWithKey(wealth)
|
rich := NewAccountWithKey(wealth)
|
||||||
_, err = ourChain.SetOption("coin", "account", rich.MakeOption())
|
_, err = ourChain.InitState("coin", "account", rich.MakeOption())
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
|
|
||||||
// sends money to another guy on a different chain, now other chain has credit
|
// sends money to another guy on a different chain, now other chain has credit
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
var (
|
var (
|
||||||
errInsufficientFees = fmt.Errorf("Insufficient fees")
|
errInsufficientFees = fmt.Errorf("Insufficient fees")
|
||||||
errWrongFeeDenom = fmt.Errorf("Required fee denomination")
|
errWrongFeeDenom = fmt.Errorf("Required fee denomination")
|
||||||
|
errSkipFees = fmt.Errorf("Skip fees")
|
||||||
|
|
||||||
invalidInput = abci.CodeType_BaseInvalidInput
|
invalidInput = abci.CodeType_BaseInvalidInput
|
||||||
)
|
)
|
||||||
|
@ -29,3 +30,10 @@ func ErrWrongFeeDenom(denom string) errors.TMError {
|
||||||
func IsWrongFeeDenomErr(err error) bool {
|
func IsWrongFeeDenomErr(err error) bool {
|
||||||
return errors.IsSameError(errWrongFeeDenom, err)
|
return errors.IsSameError(errWrongFeeDenom, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ErrSkipFees() errors.TMError {
|
||||||
|
return errors.WithCode(errSkipFees, invalidInput)
|
||||||
|
}
|
||||||
|
func IsSkipFeesErr(err error) bool {
|
||||||
|
return errors.IsSameError(errSkipFees, err)
|
||||||
|
}
|
||||||
|
|
|
@ -24,7 +24,8 @@ type SimpleFeeMiddleware struct {
|
||||||
// all fees go here, which could be a dump (Bank) or something reachable
|
// all fees go here, which could be a dump (Bank) or something reachable
|
||||||
// by other app logic
|
// by other app logic
|
||||||
Collector basecoin.Actor
|
Collector basecoin.Actor
|
||||||
stack.PassOption
|
stack.PassInitState
|
||||||
|
stack.PassInitValidate
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ stack.Middleware = SimpleFeeMiddleware{}
|
var _ stack.Middleware = SimpleFeeMiddleware{}
|
||||||
|
@ -45,40 +46,72 @@ func (SimpleFeeMiddleware) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTx - check the transaction
|
// CheckTx - check the transaction
|
||||||
func (h SimpleFeeMiddleware) CheckTx(ctx basecoin.Context, store state.SimpleDB, 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.CheckResult, err error) {
|
||||||
return h.doTx(ctx, store, tx, next.CheckTx)
|
fee, err := h.verifyFee(ctx, tx)
|
||||||
|
if err != nil {
|
||||||
|
if IsSkipFeesErr(err) {
|
||||||
|
return next.CheckTx(ctx, store, tx)
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var paid, used uint64
|
||||||
|
if !fee.Fee.IsZero() { // now, try to make a IPC call to coins...
|
||||||
|
send := coin.NewSendOneTx(fee.Payer, h.Collector, coin.Coins{fee.Fee})
|
||||||
|
sendRes, err := next.CheckTx(ctx, store, send)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
paid = uint64(fee.Fee.Amount)
|
||||||
|
used = sendRes.GasAllocated
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err = next.CheckTx(ctx, store, fee.Tx)
|
||||||
|
// add the given fee to the price for gas, plus one query
|
||||||
|
if err == nil {
|
||||||
|
res.GasPayment += paid
|
||||||
|
res.GasAllocated += used
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx - send the fee handler transaction
|
// DeliverTx - send the fee handler transaction
|
||||||
func (h SimpleFeeMiddleware) DeliverTx(ctx basecoin.Context, store state.SimpleDB, 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.DeliverResult, err error) {
|
||||||
return h.doTx(ctx, store, tx, next.DeliverTx)
|
fee, err := h.verifyFee(ctx, tx)
|
||||||
|
if IsSkipFeesErr(err) {
|
||||||
|
return next.DeliverTx(ctx, store, tx)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !fee.Fee.IsZero() { // now, try to make a IPC call to coins...
|
||||||
|
send := coin.NewSendOneTx(fee.Payer, h.Collector, coin.Coins{fee.Fee})
|
||||||
|
_, err = next.DeliverTx(ctx, store, send)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return next.DeliverTx(ctx, store, fee.Tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h SimpleFeeMiddleware) doTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.CheckerFunc) (res basecoin.Result, err error) {
|
func (h SimpleFeeMiddleware) verifyFee(ctx basecoin.Context, tx basecoin.Tx) (Fee, error) {
|
||||||
feeTx, ok := tx.Unwrap().(Fee)
|
feeTx, ok := tx.Unwrap().(Fee)
|
||||||
if !ok {
|
if !ok {
|
||||||
// the fee wrapper is not required if there is no minimum
|
// the fee wrapper is not required if there is no minimum
|
||||||
if h.MinFee.IsZero() {
|
if h.MinFee.IsZero() {
|
||||||
return next(ctx, store, tx)
|
return feeTx, ErrSkipFees()
|
||||||
}
|
}
|
||||||
return res, errors.ErrInvalidFormat(TypeFees, tx)
|
return feeTx, errors.ErrInvalidFormat(TypeFees, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// see if it is the proper denom and big enough
|
// see if it is the proper denom and big enough
|
||||||
fee := feeTx.Fee
|
fee := feeTx.Fee
|
||||||
if fee.Denom != h.MinFee.Denom {
|
if fee.Denom != h.MinFee.Denom {
|
||||||
return res, ErrWrongFeeDenom(h.MinFee.Denom)
|
return feeTx, ErrWrongFeeDenom(h.MinFee.Denom)
|
||||||
}
|
}
|
||||||
if !fee.IsGTE(h.MinFee) {
|
if !fee.IsGTE(h.MinFee) {
|
||||||
return res, ErrInsufficientFees()
|
return feeTx, ErrInsufficientFees()
|
||||||
}
|
}
|
||||||
|
return feeTx, nil
|
||||||
// now, try to make a IPC call to coins...
|
|
||||||
send := coin.NewSendOneTx(feeTx.Payer, h.Collector, coin.Coins{fee})
|
|
||||||
_, err = next(ctx, store, send)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return next(ctx, store, feeTx.Tx)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,11 +50,13 @@ func TestFeeChecks(t *testing.T) {
|
||||||
// set up the store and init the accounts
|
// set up the store and init the accounts
|
||||||
store := state.NewMemKVStore()
|
store := state.NewMemKVStore()
|
||||||
l := log.NewNopLogger()
|
l := log.NewNopLogger()
|
||||||
_, err := app1.SetOption(l, store, "coin", "account", key1.MakeOption())
|
_, err := app1.InitState(l, store, "coin", "account", key1.MakeOption())
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
_, err = app2.SetOption(l, store, "coin", "account", key2.MakeOption())
|
_, err = app2.InitState(l, store, "coin", "account", key2.MakeOption())
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
|
|
||||||
|
// feeCost is what we expect if the fee is properly paid
|
||||||
|
feeCost := coin.CostSend * 2
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
valid bool
|
valid bool
|
||||||
// this is the middleware stack to test
|
// this is the middleware stack to test
|
||||||
|
@ -68,30 +70,32 @@ func TestFeeChecks(t *testing.T) {
|
||||||
// expected balance after the tx
|
// expected balance after the tx
|
||||||
left coin.Coins
|
left coin.Coins
|
||||||
collected coin.Coins
|
collected coin.Coins
|
||||||
|
// expected gas allocated
|
||||||
|
expectedCost uint64
|
||||||
}{
|
}{
|
||||||
// make sure it works with no fee (control group)
|
// make sure it works with no fee (control group)
|
||||||
{true, app1, act1, false, act1, zero, mixed, nil},
|
{true, app1, act1, false, act1, zero, mixed, nil, 0},
|
||||||
{true, app1, act2, false, act2, zero, pure, nil},
|
{true, app1, act2, false, act2, zero, pure, nil, 0},
|
||||||
|
|
||||||
// no fee or too low is rejected
|
// no fee or too low is rejected
|
||||||
{false, app2, act1, false, act1, zero, mixed, nil},
|
{false, app2, act1, false, act1, zero, mixed, nil, 0},
|
||||||
{false, app2, act2, false, act2, zero, pure, nil},
|
{false, app2, act2, false, act2, zero, pure, nil, 0},
|
||||||
{false, app2, act1, true, act1, zero, mixed, nil},
|
{false, app2, act1, true, act1, zero, mixed, nil, 0},
|
||||||
{false, app2, act2, true, act2, atom(1), pure, nil},
|
{false, app2, act2, true, act2, atom(1), pure, nil, 0},
|
||||||
|
|
||||||
// proper fees are transfered in both cases
|
// proper fees are transfered in both cases
|
||||||
{true, app1, act1, true, act1, atom(1), wallet(1199, 55), atoms(1)},
|
{true, app1, act1, true, act1, atom(1), wallet(1199, 55), atoms(1), feeCost},
|
||||||
{true, app2, act2, true, act2, atom(57), atoms(46600), atoms(58)},
|
{true, app2, act2, true, act2, atom(57), atoms(46600), atoms(58), feeCost},
|
||||||
|
|
||||||
// // fee must be the proper type...
|
// // fee must be the proper type...
|
||||||
{false, app1, act1, true, act1, eth(5), wallet(1199, 55), atoms(58)},
|
{false, app1, act1, true, act1, eth(5), wallet(1199, 55), atoms(58), 0},
|
||||||
|
|
||||||
// signature must match
|
// signature must match
|
||||||
{false, app2, act1, true, act2, atom(5), atoms(46600), atoms(58)},
|
{false, app2, act1, true, act2, atom(5), atoms(46600), atoms(58), 0},
|
||||||
|
|
||||||
// send only works within limits
|
// send only works within limits
|
||||||
{true, app2, act1, true, act1, atom(1100), wallet(99, 55), atoms(1158)},
|
{true, app2, act1, true, act1, atom(1100), wallet(99, 55), atoms(1158), feeCost},
|
||||||
{false, app2, act1, true, act1, atom(500), wallet(99, 55), atoms(1158)},
|
{false, app2, act1, true, act1, atom(500), wallet(99, 55), atoms(1158), 0},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tc := range cases {
|
for i, tc := range cases {
|
||||||
|
@ -105,9 +109,11 @@ func TestFeeChecks(t *testing.T) {
|
||||||
ctx := stack.MockContext("x", 1).WithPermissions(tc.signer)
|
ctx := stack.MockContext("x", 1).WithPermissions(tc.signer)
|
||||||
|
|
||||||
// call checktx...
|
// call checktx...
|
||||||
_, err := tc.app.CheckTx(ctx, store, tx)
|
cres, err := tc.app.CheckTx(ctx, store, tx)
|
||||||
if tc.valid {
|
if tc.valid {
|
||||||
assert.Nil(err, "%d: %+v", i, err)
|
assert.Nil(err, "%d: %+v", i, err)
|
||||||
|
assert.EqualValues(tc.fee.Amount, cres.GasPayment)
|
||||||
|
assert.EqualValues(tc.expectedCost, cres.GasAllocated)
|
||||||
} else {
|
} else {
|
||||||
assert.NotNil(err, "%d", i)
|
assert.NotNil(err, "%d", i)
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,12 +34,14 @@ func AllowIBC(app string) basecoin.Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handler updates the chain state or creates an ibc packet
|
// Handler updates the chain state or creates an ibc packet
|
||||||
type Handler struct{}
|
type Handler struct {
|
||||||
|
basecoin.NopInitValidate
|
||||||
|
}
|
||||||
|
|
||||||
var _ basecoin.Handler = Handler{}
|
var _ basecoin.Handler = Handler{}
|
||||||
|
|
||||||
// NewHandler returns a Handler that allows all chains to connect via IBC.
|
// NewHandler returns a Handler that allows all chains to connect via IBC.
|
||||||
// Set a Registrar via SetOption to restrict it.
|
// Set a Registrar via InitState to restrict it.
|
||||||
func NewHandler() Handler {
|
func NewHandler() Handler {
|
||||||
return Handler{}
|
return Handler{}
|
||||||
}
|
}
|
||||||
|
@ -49,8 +51,8 @@ func (Handler) Name() string {
|
||||||
return NameIBC
|
return NameIBC
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOption sets the registrar for IBC
|
// InitState sets the registrar for IBC
|
||||||
func (h Handler) SetOption(l log.Logger, store state.SimpleDB, module, key, value string) (log string, err error) {
|
func (h Handler) InitState(l log.Logger, store state.SimpleDB, module, key, value string) (log string, err error) {
|
||||||
if module != NameIBC {
|
if module != NameIBC {
|
||||||
return "", errors.ErrUnknownModule(module)
|
return "", errors.ErrUnknownModule(module)
|
||||||
}
|
}
|
||||||
|
@ -70,26 +72,27 @@ func (h Handler) SetOption(l log.Logger, store state.SimpleDB, module, key, valu
|
||||||
|
|
||||||
// CheckTx verifies the packet is formated correctly, and has the proper sequence
|
// CheckTx verifies the packet is formated correctly, and has the proper sequence
|
||||||
// for a registered chain
|
// for a registered chain
|
||||||
func (h Handler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (h Handler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.CheckResult, err error) {
|
||||||
err = tx.ValidateBasic()
|
err = tx.ValidateBasic()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch t := tx.Unwrap().(type) {
|
// TODO: better fee calculation (don't do complex crypto)
|
||||||
|
switch tx.Unwrap().(type) {
|
||||||
case RegisterChainTx:
|
case RegisterChainTx:
|
||||||
return h.initSeed(ctx, store, t)
|
return res, nil
|
||||||
case UpdateChainTx:
|
case UpdateChainTx:
|
||||||
return h.updateSeed(ctx, store, t)
|
return res, nil
|
||||||
case CreatePacketTx:
|
case CreatePacketTx:
|
||||||
return h.createPacket(ctx, store, t)
|
return res, nil
|
||||||
}
|
}
|
||||||
return res, errors.ErrUnknownTxType(tx.Unwrap())
|
return res, errors.ErrUnknownTxType(tx.Unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx verifies all signatures on the tx and updates the chain state
|
// DeliverTx verifies all signatures on the tx and updates the chain state
|
||||||
// apropriately
|
// apropriately
|
||||||
func (h Handler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (h Handler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.DeliverResult, err error) {
|
||||||
err = tx.ValidateBasic()
|
err = tx.ValidateBasic()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
@ -111,7 +114,7 @@ func (h Handler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx baseco
|
||||||
//
|
//
|
||||||
// only the registrar, if set, is allowed to do this
|
// only the registrar, if set, is allowed to do this
|
||||||
func (h Handler) initSeed(ctx basecoin.Context, store state.SimpleDB,
|
func (h Handler) initSeed(ctx basecoin.Context, store state.SimpleDB,
|
||||||
t RegisterChainTx) (res basecoin.Result, err error) {
|
t RegisterChainTx) (res basecoin.DeliverResult, err error) {
|
||||||
|
|
||||||
info := LoadInfo(store)
|
info := LoadInfo(store)
|
||||||
if !info.Registrar.Empty() && !ctx.HasPermission(info.Registrar) {
|
if !info.Registrar.Empty() && !ctx.HasPermission(info.Registrar) {
|
||||||
|
@ -135,7 +138,7 @@ func (h Handler) initSeed(ctx basecoin.Context, store state.SimpleDB,
|
||||||
// updateSeed checks the seed against the existing chain data and rejects it if it
|
// updateSeed checks the seed against the existing chain data and rejects it if it
|
||||||
// doesn't fit (or no chain data)
|
// doesn't fit (or no chain data)
|
||||||
func (h Handler) updateSeed(ctx basecoin.Context, store state.SimpleDB,
|
func (h Handler) updateSeed(ctx basecoin.Context, store state.SimpleDB,
|
||||||
t UpdateChainTx) (res basecoin.Result, err error) {
|
t UpdateChainTx) (res basecoin.DeliverResult, err error) {
|
||||||
|
|
||||||
chainID := t.ChainID()
|
chainID := t.ChainID()
|
||||||
s := NewChainSet(store)
|
s := NewChainSet(store)
|
||||||
|
@ -165,7 +168,7 @@ func (h Handler) updateSeed(ctx basecoin.Context, store state.SimpleDB,
|
||||||
// createPacket makes sure all permissions are good and the destination
|
// createPacket makes sure all permissions are good and the destination
|
||||||
// chain is registed. If so, it appends it to the outgoing queue
|
// chain is registed. If so, it appends it to the outgoing queue
|
||||||
func (h Handler) createPacket(ctx basecoin.Context, store state.SimpleDB,
|
func (h Handler) createPacket(ctx basecoin.Context, store state.SimpleDB,
|
||||||
t CreatePacketTx) (res basecoin.Result, err error) {
|
t CreatePacketTx) (res basecoin.DeliverResult, err error) {
|
||||||
|
|
||||||
// make sure the chain is registed
|
// make sure the chain is registed
|
||||||
dest := t.DestChain
|
dest := t.DestChain
|
||||||
|
@ -203,6 +206,6 @@ func (h Handler) createPacket(ctx basecoin.Context, store state.SimpleDB,
|
||||||
packet.Sequence = q.Tail()
|
packet.Sequence = q.Tail()
|
||||||
q.Push(packet.Bytes())
|
q.Push(packet.Bytes())
|
||||||
|
|
||||||
res = basecoin.Result{Log: fmt.Sprintf("Packet %s %d", dest, packet.Sequence)}
|
res = basecoin.DeliverResult{Log: fmt.Sprintf("Packet %s %d", dest, packet.Sequence)}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,7 +130,7 @@ func TestIBCRegisterPermissions(t *testing.T) {
|
||||||
// set option specifies the registrar
|
// set option specifies the registrar
|
||||||
msg, err := json.Marshal(tc.registrar)
|
msg, err := json.Marshal(tc.registrar)
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
_, err = app.SetOption(log.NewNopLogger(), store,
|
_, err = app.InitState(log.NewNopLogger(), store,
|
||||||
NameIBC, OptionRegistrar, string(msg))
|
NameIBC, OptionRegistrar, string(msg))
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,8 @@ import (
|
||||||
// Middleware allows us to verify the IBC proof on a packet and
|
// Middleware allows us to verify the IBC proof on a packet and
|
||||||
// and if valid, attach this permission to the wrapped packet
|
// and if valid, attach this permission to the wrapped packet
|
||||||
type Middleware struct {
|
type Middleware struct {
|
||||||
stack.PassOption
|
stack.PassInitState
|
||||||
|
stack.PassInitValidate
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ stack.Middleware = Middleware{}
|
var _ stack.Middleware = Middleware{}
|
||||||
|
@ -26,7 +27,7 @@ func (Middleware) Name() string {
|
||||||
|
|
||||||
// CheckTx verifies the named chain and height is present, and verifies
|
// CheckTx verifies the named chain and height is present, and verifies
|
||||||
// the merkle proof in the packet
|
// the merkle proof in the packet
|
||||||
func (m Middleware) CheckTx(ctx basecoin.Context, store state.SimpleDB, 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.CheckResult, err error) {
|
||||||
// if it is not a PostPacket, just let it go through
|
// if it is not a PostPacket, just let it go through
|
||||||
post, ok := tx.Unwrap().(PostPacketTx)
|
post, ok := tx.Unwrap().(PostPacketTx)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -43,7 +44,7 @@ func (m Middleware) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basec
|
||||||
|
|
||||||
// DeliverTx verifies the named chain and height is present, and verifies
|
// DeliverTx verifies the named chain and height is present, and verifies
|
||||||
// the merkle proof in the packet
|
// the merkle proof in the packet
|
||||||
func (m Middleware) DeliverTx(ctx basecoin.Context, store state.SimpleDB, 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.DeliverResult, err error) {
|
||||||
// if it is not a PostPacket, just let it go through
|
// if it is not a PostPacket, just let it go through
|
||||||
post, ok := tx.Unwrap().(PostPacketTx)
|
post, ok := tx.Unwrap().(PostPacketTx)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
@ -100,7 +100,7 @@ func (a *AppChain) IncrementHeight(delta int) int {
|
||||||
|
|
||||||
// DeliverTx runs the tx and commits the new tree, incrementing height
|
// DeliverTx runs the tx and commits the new tree, incrementing height
|
||||||
// by one.
|
// by one.
|
||||||
func (a *AppChain) DeliverTx(tx basecoin.Tx, perms ...basecoin.Actor) (basecoin.Result, error) {
|
func (a *AppChain) DeliverTx(tx basecoin.Tx, perms ...basecoin.Actor) (basecoin.DeliverResult, error) {
|
||||||
ctx := stack.MockContext(a.chainID, uint64(a.height)).WithPermissions(perms...)
|
ctx := stack.MockContext(a.chainID, uint64(a.height)).WithPermissions(perms...)
|
||||||
store := a.store.Checkpoint()
|
store := a.store.Checkpoint()
|
||||||
res, err := a.app.DeliverTx(ctx, store, tx)
|
res, err := a.app.DeliverTx(ctx, store, tx)
|
||||||
|
@ -118,9 +118,9 @@ func (a *AppChain) Update(tx UpdateChainTx) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOption sets the option on our app
|
// InitState sets the option on our app
|
||||||
func (a *AppChain) SetOption(mod, key, value string) (string, error) {
|
func (a *AppChain) InitState(mod, key, value string) (string, error) {
|
||||||
return a.app.SetOption(log.NewNopLogger(), a.store, mod, key, value)
|
return a.app.InitState(log.NewNopLogger(), a.store, mod, key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStore is used to get the app-specific sub-store
|
// GetStore is used to get the app-specific sub-store
|
||||||
|
|
|
@ -9,11 +9,13 @@ import (
|
||||||
//nolint
|
//nolint
|
||||||
const (
|
const (
|
||||||
NameNonce = "nonce"
|
NameNonce = "nonce"
|
||||||
|
CostNonce = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReplayCheck uses the sequence to check for replay attacks
|
// ReplayCheck uses the sequence to check for replay attacks
|
||||||
type ReplayCheck struct {
|
type ReplayCheck struct {
|
||||||
stack.PassOption
|
stack.PassInitState
|
||||||
|
stack.PassInitValidate
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name of the module - fulfills Middleware interface
|
// Name of the module - fulfills Middleware interface
|
||||||
|
@ -25,21 +27,23 @@ var _ stack.Middleware = ReplayCheck{}
|
||||||
|
|
||||||
// CheckTx verifies tx is not being replayed - fulfills Middlware interface
|
// CheckTx verifies tx is not being replayed - fulfills Middlware interface
|
||||||
func (r ReplayCheck) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
func (r ReplayCheck) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, next basecoin.Checker) (res basecoin.Result, err error) {
|
tx basecoin.Tx, next basecoin.Checker) (res basecoin.CheckResult, err error) {
|
||||||
|
|
||||||
stx, err := r.checkIncrementNonceTx(ctx, store, tx)
|
stx, err := r.checkIncrementNonceTx(ctx, store, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return next.CheckTx(ctx, store, stx)
|
res, err = next.CheckTx(ctx, store, stx)
|
||||||
|
res.GasAllocated += CostNonce
|
||||||
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx verifies tx is not being replayed - fulfills Middlware interface
|
// DeliverTx verifies tx is not being replayed - fulfills Middlware interface
|
||||||
// NOTE It is okay to modify the sequence before running the wrapped TX because if the
|
// NOTE It is okay to modify the sequence before running the wrapped TX because if the
|
||||||
// wrapped Tx fails, the state changes are not applied
|
// wrapped Tx fails, the state changes are not applied
|
||||||
func (r ReplayCheck) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
func (r ReplayCheck) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, next basecoin.Deliver) (res basecoin.Result, err error) {
|
tx basecoin.Tx, next basecoin.Deliver) (res basecoin.DeliverResult, err error) {
|
||||||
|
|
||||||
stx, err := r.checkIncrementNonceTx(ctx, store, tx)
|
stx, err := r.checkIncrementNonceTx(ctx, store, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -6,12 +6,19 @@ import (
|
||||||
"github.com/tendermint/basecoin/state"
|
"github.com/tendermint/basecoin/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
//NameRole - name space of the roles module
|
const (
|
||||||
const NameRole = "role"
|
//NameRole - name space of the roles module
|
||||||
|
NameRole = "role"
|
||||||
|
// CostCreate is the cost to create a new role
|
||||||
|
CostCreate = uint64(40)
|
||||||
|
// CostAssume is the cost to assume a role as part of a tx
|
||||||
|
CostAssume = uint64(5)
|
||||||
|
)
|
||||||
|
|
||||||
// Handler allows us to create new roles
|
// Handler allows us to create new roles
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
basecoin.NopOption
|
basecoin.NopInitState
|
||||||
|
basecoin.NopInitValidate
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ basecoin.Handler = Handler{}
|
var _ basecoin.Handler = Handler{}
|
||||||
|
@ -27,12 +34,13 @@ func (Handler) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTx verifies if the transaction is properly formated
|
// CheckTx verifies if the transaction is properly formated
|
||||||
func (h Handler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (h Handler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.CheckResult, err error) {
|
||||||
var cr CreateRoleTx
|
var cr CreateRoleTx
|
||||||
cr, err = checkTx(ctx, tx)
|
cr, err = checkTx(ctx, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
res = basecoin.NewCheck(CostCreate, "")
|
||||||
err = checkNoRole(store, cr.Role)
|
err = checkNoRole(store, cr.Role)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -40,7 +48,7 @@ func (h Handler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin
|
||||||
// DeliverTx tries to create a new role.
|
// DeliverTx tries to create a new role.
|
||||||
//
|
//
|
||||||
// Returns an error if the role already exists
|
// Returns an error if the role already exists
|
||||||
func (h Handler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (h Handler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.DeliverResult, err error) {
|
||||||
create, err := checkTx(ctx, tx)
|
create, err := checkTx(ctx, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
|
|
@ -38,11 +38,13 @@ func TestCreateRole(t *testing.T) {
|
||||||
store := state.NewMemKVStore()
|
store := state.NewMemKVStore()
|
||||||
for i, tc := range cases {
|
for i, tc := range cases {
|
||||||
tx := roles.NewCreateRoleTx([]byte(tc.role), tc.min, tc.sigs)
|
tx := roles.NewCreateRoleTx([]byte(tc.role), tc.min, tc.sigs)
|
||||||
_, err := h.CheckTx(ctx, store, tx)
|
cres, err := h.CheckTx(ctx, store, tx)
|
||||||
_, err2 := h.DeliverTx(ctx, store, tx)
|
_, err2 := h.DeliverTx(ctx, store, tx)
|
||||||
if tc.valid {
|
if tc.valid {
|
||||||
assert.Nil(err, "%d/%s: %+v", i, tc.role, err)
|
assert.Nil(err, "%d/%s: %+v", i, tc.role, err)
|
||||||
assert.Nil(err2, "%d/%s: %+v", i, tc.role, err2)
|
assert.Nil(err2, "%d/%s: %+v", i, tc.role, err2)
|
||||||
|
assert.Equal(roles.CostCreate, cres.GasAllocated)
|
||||||
|
assert.Equal(uint64(0), cres.GasPayment)
|
||||||
} else {
|
} else {
|
||||||
assert.NotNil(err, "%d/%s", i, tc.role)
|
assert.NotNil(err, "%d/%s", i, tc.role)
|
||||||
assert.NotNil(err2, "%d/%s", i, tc.role)
|
assert.NotNil(err2, "%d/%s", i, tc.role)
|
||||||
|
|
|
@ -9,7 +9,8 @@ import (
|
||||||
// Middleware allows us to add a requested role as a permission
|
// Middleware allows us to add a requested role as a permission
|
||||||
// if the tx requests it and has sufficient authority
|
// if the tx requests it and has sufficient authority
|
||||||
type Middleware struct {
|
type Middleware struct {
|
||||||
stack.PassOption
|
stack.PassInitState
|
||||||
|
stack.PassInitValidate
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ stack.Middleware = Middleware{}
|
var _ stack.Middleware = Middleware{}
|
||||||
|
@ -27,7 +28,7 @@ func (Middleware) Name() string {
|
||||||
// CheckTx tries to assume the named role if requested.
|
// CheckTx tries to assume the named role if requested.
|
||||||
// If no role is requested, do nothing.
|
// If no role is requested, do nothing.
|
||||||
// If insufficient authority to assume the role, return error.
|
// If insufficient authority to assume the role, return error.
|
||||||
func (m Middleware) CheckTx(ctx basecoin.Context, store state.SimpleDB, 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.CheckResult, err error) {
|
||||||
// if this is not an AssumeRoleTx, then continue
|
// if this is not an AssumeRoleTx, then continue
|
||||||
assume, ok := tx.Unwrap().(AssumeRoleTx)
|
assume, ok := tx.Unwrap().(AssumeRoleTx)
|
||||||
if !ok { // this also breaks the recursion below
|
if !ok { // this also breaks the recursion below
|
||||||
|
@ -40,13 +41,16 @@ func (m Middleware) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basec
|
||||||
}
|
}
|
||||||
|
|
||||||
// one could add multiple role statements, repeat as needed
|
// one could add multiple role statements, repeat as needed
|
||||||
return m.CheckTx(ctx, store, assume.Tx, next)
|
// charging for each level
|
||||||
|
res, err = m.CheckTx(ctx, store, assume.Tx, next)
|
||||||
|
res.GasAllocated += CostAssume
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx tries to assume the named role if requested.
|
// DeliverTx tries to assume the named role if requested.
|
||||||
// If no role is requested, do nothing.
|
// If no role is requested, do nothing.
|
||||||
// If insufficient authority to assume the role, return error.
|
// If insufficient authority to assume the role, return error.
|
||||||
func (m Middleware) DeliverTx(ctx basecoin.Context, store state.SimpleDB, 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.DeliverResult, err error) {
|
||||||
// if this is not an AssumeRoleTx, then continue
|
// if this is not an AssumeRoleTx, then continue
|
||||||
assume, ok := tx.Unwrap().(AssumeRoleTx)
|
assume, ok := tx.Unwrap().(AssumeRoleTx)
|
||||||
if !ok { // this also breaks the recursion below
|
if !ok { // this also breaks the recursion below
|
||||||
|
|
|
@ -93,11 +93,14 @@ func TestAssumeRole(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// try CheckTx and DeliverTx and make sure they both assert permissions
|
// try CheckTx and DeliverTx and make sure they both assert permissions
|
||||||
_, err := app.CheckTx(myCtx, store, tx)
|
cres, err := app.CheckTx(myCtx, store, tx)
|
||||||
_, err2 := app.DeliverTx(myCtx, store, tx)
|
_, err2 := app.DeliverTx(myCtx, store, tx)
|
||||||
if tc.valid {
|
if tc.valid {
|
||||||
assert.Nil(err, "%d: %+v", i, err)
|
assert.Nil(err, "%d: %+v", i, err)
|
||||||
assert.Nil(err2, "%d: %+v", i, err2)
|
assert.Nil(err2, "%d: %+v", i, err2)
|
||||||
|
// make sure we charge for each role
|
||||||
|
assert.Equal(roles.CostAssume*uint64(len(tc.roles)), cres.GasAllocated)
|
||||||
|
assert.Equal(uint64(0), cres.GasPayment)
|
||||||
} else {
|
} else {
|
||||||
assert.NotNil(err, "%d", i)
|
assert.NotNil(err, "%d", i)
|
||||||
assert.NotNil(err2, "%d", i)
|
assert.NotNil(err2, "%d", i)
|
||||||
|
|
|
@ -14,7 +14,8 @@ const (
|
||||||
type Checkpoint struct {
|
type Checkpoint struct {
|
||||||
OnCheck bool
|
OnCheck bool
|
||||||
OnDeliver bool
|
OnDeliver bool
|
||||||
PassOption
|
PassInitState
|
||||||
|
PassInitValidate
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name of the module - fulfills Middleware interface
|
// Name of the module - fulfills Middleware interface
|
||||||
|
@ -25,7 +26,7 @@ func (Checkpoint) Name() string {
|
||||||
var _ Middleware = Checkpoint{}
|
var _ Middleware = Checkpoint{}
|
||||||
|
|
||||||
// CheckTx reverts all data changes if there was an error
|
// 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) {
|
func (c Checkpoint) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Checker) (res basecoin.CheckResult, err error) {
|
||||||
if !c.OnCheck {
|
if !c.OnCheck {
|
||||||
return next.CheckTx(ctx, store, tx)
|
return next.CheckTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
@ -38,7 +39,7 @@ func (c Checkpoint) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basec
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx reverts all data changes if there was an error
|
// 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) {
|
func (c Checkpoint) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx, next basecoin.Deliver) (res basecoin.DeliverResult, err error) {
|
||||||
if !c.OnDeliver {
|
if !c.OnDeliver {
|
||||||
return next.DeliverTx(ctx, store, tx)
|
return next.DeliverTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,12 +32,12 @@ func makeState() state.SimpleDB {
|
||||||
func TestCheckpointer(t *testing.T) {
|
func TestCheckpointer(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
|
||||||
good := writerHand{"foo", []byte{1, 2}, []byte("bar")}
|
good := writerHand{name: "foo", key: []byte{1, 2}, value: []byte("bar")}
|
||||||
bad := FailHandler{Err: errors.New("no go")}
|
bad := FailHandler{Err: errors.New("no go")}
|
||||||
|
|
||||||
app := New(
|
app := New(
|
||||||
Checkpoint{OnCheck: true},
|
Checkpoint{OnCheck: true},
|
||||||
writerMid{"bing", []byte{1, 2}, []byte("bang")},
|
writerMid{name: "bing", key: []byte{1, 2}, value: []byte("bang")},
|
||||||
Checkpoint{OnDeliver: true},
|
Checkpoint{OnDeliver: true},
|
||||||
).Use(
|
).Use(
|
||||||
NewDispatcher(
|
NewDispatcher(
|
||||||
|
|
|
@ -104,7 +104,7 @@ func withIBC(ctx basecoin.Context) basecoin.Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
func secureCheck(h basecoin.Checker, parent basecoin.Context) basecoin.Checker {
|
func secureCheck(h basecoin.Checker, parent basecoin.Context) basecoin.Checker {
|
||||||
next := func(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
next := func(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.CheckResult, err error) {
|
||||||
if !parent.IsParent(ctx) {
|
if !parent.IsParent(ctx) {
|
||||||
return res, errors.New("Passing in non-child Context")
|
return res, errors.New("Passing in non-child Context")
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ func secureCheck(h basecoin.Checker, parent basecoin.Context) basecoin.Checker {
|
||||||
}
|
}
|
||||||
|
|
||||||
func secureDeliver(h basecoin.Deliver, parent basecoin.Context) basecoin.Deliver {
|
func secureDeliver(h basecoin.Deliver, parent basecoin.Context) basecoin.Deliver {
|
||||||
next := func(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
next := func(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.DeliverResult, err error) {
|
||||||
if !parent.IsParent(ctx) {
|
if !parent.IsParent(ctx) {
|
||||||
return res, errors.New("Passing in non-child Context")
|
return res, errors.New("Passing in non-child Context")
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,10 @@ package stack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin"
|
"github.com/tendermint/basecoin"
|
||||||
|
@ -64,7 +66,7 @@ func (d *Dispatcher) Name() string {
|
||||||
// Tries to find a registered module (Dispatchable) based on the name of the tx.
|
// Tries to find a registered module (Dispatchable) based on the name of the tx.
|
||||||
// The tx name (as registered with go-data) should be in the form `<module name>/XXXX`,
|
// The tx name (as registered with go-data) should be in the form `<module name>/XXXX`,
|
||||||
// where `module name` must match the name of a dispatchable and XXX can be any string.
|
// where `module name` must match the name of a dispatchable and XXX can be any string.
|
||||||
func (d *Dispatcher) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (d *Dispatcher) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.CheckResult, err error) {
|
||||||
r, err := d.lookupTx(tx)
|
r, err := d.lookupTx(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
@ -85,7 +87,7 @@ func (d *Dispatcher) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx base
|
||||||
// Tries to find a registered module (Dispatchable) based on the name of the tx.
|
// Tries to find a registered module (Dispatchable) based on the name of the tx.
|
||||||
// The tx name (as registered with go-data) should be in the form `<module name>/XXXX`,
|
// The tx name (as registered with go-data) should be in the form `<module name>/XXXX`,
|
||||||
// where `module name` must match the name of a dispatchable and XXX can be any string.
|
// where `module name` must match the name of a dispatchable and XXX can be any string.
|
||||||
func (d *Dispatcher) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (d *Dispatcher) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.DeliverResult, err error) {
|
||||||
r, err := d.lookupTx(tx)
|
r, err := d.lookupTx(tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
@ -101,11 +103,11 @@ func (d *Dispatcher) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx ba
|
||||||
return r.DeliverTx(ctx, store, tx, cb)
|
return r.DeliverTx(ctx, store, tx, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOption - implements Handler interface
|
// InitState - implements Handler interface
|
||||||
//
|
//
|
||||||
// Tries to find a registered module (Dispatchable) based on the
|
// Tries to find a registered module (Dispatchable) based on the
|
||||||
// module name from SetOption of the tx.
|
// module name from InitState of the tx.
|
||||||
func (d *Dispatcher) SetOption(l log.Logger, store state.SimpleDB, module, key, value string) (string, error) {
|
func (d *Dispatcher) InitState(l log.Logger, store state.SimpleDB, module, key, value string) (string, error) {
|
||||||
r, err := d.lookupModule(module)
|
r, err := d.lookupModule(module)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -116,7 +118,17 @@ func (d *Dispatcher) SetOption(l log.Logger, store state.SimpleDB, module, key,
|
||||||
// but isolate data space
|
// but isolate data space
|
||||||
store = stateSpace(store, r.Name())
|
store = stateSpace(store, r.Name())
|
||||||
|
|
||||||
return r.SetOption(l, store, module, key, value, cb)
|
return r.InitState(l, store, module, key, value, cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitValidate makes sure all modules are informed
|
||||||
|
func (d *Dispatcher) InitValidate(log log.Logger, store state.SimpleDB, vals []*abci.Validator) {
|
||||||
|
for _, mod := range d.sortedModules() {
|
||||||
|
// no ctx, so secureCheck not needed
|
||||||
|
cb := d
|
||||||
|
space := stateSpace(store, mod.Name())
|
||||||
|
mod.InitValidate(log, space, vals, cb)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Dispatcher) lookupTx(tx basecoin.Tx) (Dispatchable, error) {
|
func (d *Dispatcher) lookupTx(tx basecoin.Tx) (Dispatchable, error) {
|
||||||
|
@ -140,3 +152,19 @@ func (d *Dispatcher) lookupModule(name string) (Dispatchable, error) {
|
||||||
}
|
}
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *Dispatcher) sortedModules() []Dispatchable {
|
||||||
|
// order all routes names
|
||||||
|
size := len(d.routes)
|
||||||
|
names := make([]string, 0, size)
|
||||||
|
for k := range d.routes {
|
||||||
|
names = append(names, k)
|
||||||
|
}
|
||||||
|
sort.Strings(names)
|
||||||
|
|
||||||
|
res := make([]Dispatchable, size)
|
||||||
|
for i, k := range names {
|
||||||
|
res[i] = d.routes[k]
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
|
@ -94,7 +94,8 @@ func (r FailTx) ValidateBasic() error {
|
||||||
// OKHandler just used to return okay to everything
|
// OKHandler just used to return okay to everything
|
||||||
type OKHandler struct {
|
type OKHandler struct {
|
||||||
Log string
|
Log string
|
||||||
basecoin.NopOption
|
basecoin.NopInitState
|
||||||
|
basecoin.NopInitValidate
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ basecoin.Handler = OKHandler{}
|
var _ basecoin.Handler = OKHandler{}
|
||||||
|
@ -105,18 +106,19 @@ func (OKHandler) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTx always returns an empty success tx
|
// CheckTx always returns an empty success tx
|
||||||
func (ok OKHandler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (ok OKHandler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.CheckResult, err error) {
|
||||||
return basecoin.Result{Log: ok.Log}, nil
|
return basecoin.CheckResult{Log: ok.Log}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx always returns an empty success tx
|
// DeliverTx always returns an empty success tx
|
||||||
func (ok OKHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (ok OKHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.DeliverResult, err error) {
|
||||||
return basecoin.Result{Log: ok.Log}, nil
|
return basecoin.DeliverResult{Log: ok.Log}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EchoHandler returns success, echoing res.Data = tx bytes
|
// EchoHandler returns success, echoing res.Data = tx bytes
|
||||||
type EchoHandler struct {
|
type EchoHandler struct {
|
||||||
basecoin.NopOption
|
basecoin.NopInitState
|
||||||
|
basecoin.NopInitValidate
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ basecoin.Handler = EchoHandler{}
|
var _ basecoin.Handler = EchoHandler{}
|
||||||
|
@ -127,21 +129,22 @@ func (EchoHandler) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTx always returns an empty success tx
|
// CheckTx always returns an empty success tx
|
||||||
func (EchoHandler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (EchoHandler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.CheckResult, err error) {
|
||||||
data, err := data.ToWire(tx)
|
data, err := data.ToWire(tx)
|
||||||
return basecoin.Result{Data: data}, err
|
return basecoin.CheckResult{Data: data}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx always returns an empty success tx
|
// DeliverTx always returns an empty success tx
|
||||||
func (EchoHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (EchoHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.DeliverResult, err error) {
|
||||||
data, err := data.ToWire(tx)
|
data, err := data.ToWire(tx)
|
||||||
return basecoin.Result{Data: data}, err
|
return basecoin.DeliverResult{Data: data}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// FailHandler always returns an error
|
// FailHandler always returns an error
|
||||||
type FailHandler struct {
|
type FailHandler struct {
|
||||||
Err error
|
Err error
|
||||||
basecoin.NopOption
|
basecoin.NopInitState
|
||||||
|
basecoin.NopInitValidate
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ basecoin.Handler = FailHandler{}
|
var _ basecoin.Handler = FailHandler{}
|
||||||
|
@ -152,12 +155,12 @@ func (FailHandler) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTx always returns the given error
|
// CheckTx always returns the given error
|
||||||
func (f FailHandler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (f FailHandler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.CheckResult, err error) {
|
||||||
return res, errors.Wrap(f.Err)
|
return res, errors.Wrap(f.Err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx always returns the given error
|
// DeliverTx always returns the given error
|
||||||
func (f FailHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (f FailHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.DeliverResult, err error) {
|
||||||
return res, errors.Wrap(f.Err)
|
return res, errors.Wrap(f.Err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +168,8 @@ func (f FailHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx ba
|
||||||
type PanicHandler struct {
|
type PanicHandler struct {
|
||||||
Msg string
|
Msg string
|
||||||
Err error
|
Err error
|
||||||
basecoin.NopOption
|
basecoin.NopInitState
|
||||||
|
basecoin.NopInitValidate
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ basecoin.Handler = PanicHandler{}
|
var _ basecoin.Handler = PanicHandler{}
|
||||||
|
@ -176,7 +180,7 @@ func (PanicHandler) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTx always panics
|
// CheckTx always panics
|
||||||
func (p PanicHandler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (p PanicHandler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.CheckResult, err error) {
|
||||||
if p.Err != nil {
|
if p.Err != nil {
|
||||||
panic(p.Err)
|
panic(p.Err)
|
||||||
}
|
}
|
||||||
|
@ -184,7 +188,7 @@ func (p PanicHandler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx bas
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx always panics
|
// DeliverTx always panics
|
||||||
func (p PanicHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (p PanicHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.DeliverResult, err error) {
|
||||||
if p.Err != nil {
|
if p.Err != nil {
|
||||||
panic(p.Err)
|
panic(p.Err)
|
||||||
}
|
}
|
||||||
|
@ -193,7 +197,8 @@ func (p PanicHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx b
|
||||||
|
|
||||||
// CheckHandler accepts CheckTx and verifies the permissions
|
// CheckHandler accepts CheckTx and verifies the permissions
|
||||||
type CheckHandler struct {
|
type CheckHandler struct {
|
||||||
basecoin.NopOption
|
basecoin.NopInitState
|
||||||
|
basecoin.NopInitValidate
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ basecoin.Handler = CheckHandler{}
|
var _ basecoin.Handler = CheckHandler{}
|
||||||
|
@ -204,7 +209,7 @@ func (CheckHandler) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTx verifies the permissions
|
// CheckTx verifies the permissions
|
||||||
func (c CheckHandler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (c CheckHandler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.CheckResult, err error) {
|
||||||
check, ok := tx.Unwrap().(CheckTx)
|
check, ok := tx.Unwrap().(CheckTx)
|
||||||
if !ok {
|
if !ok {
|
||||||
return res, errors.ErrUnknownTxType(tx)
|
return res, errors.ErrUnknownTxType(tx)
|
||||||
|
@ -219,7 +224,16 @@ func (c CheckHandler) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx bas
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx verifies the permissions
|
// DeliverTx verifies the permissions
|
||||||
func (c CheckHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (c CheckHandler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.DeliverResult, err error) {
|
||||||
// until something changes, just do the same as check
|
check, ok := tx.Unwrap().(CheckTx)
|
||||||
return c.CheckTx(ctx, store, tx)
|
if !ok {
|
||||||
|
return res, errors.ErrUnknownTxType(tx)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, perm := range check.Required {
|
||||||
|
if !ctx.HasPermission(perm) {
|
||||||
|
return res, errors.ErrUnauthorized()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,9 +25,9 @@ func TestOK(t *testing.T) {
|
||||||
assert.Nil(err, "%+v", err)
|
assert.Nil(err, "%+v", err)
|
||||||
assert.Equal(data, res.Log)
|
assert.Equal(data, res.Log)
|
||||||
|
|
||||||
res, err = ok.DeliverTx(ctx, store, tx)
|
dres, err := ok.DeliverTx(ctx, store, tx)
|
||||||
assert.Nil(err, "%+v", err)
|
assert.Nil(err, "%+v", err)
|
||||||
assert.Equal(data, res.Log)
|
assert.Equal(data, dres.Log)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFail(t *testing.T) {
|
func TestFail(t *testing.T) {
|
||||||
|
|
|
@ -16,7 +16,8 @@ const (
|
||||||
// Required Actor, otherwise passes along the call untouched
|
// Required Actor, otherwise passes along the call untouched
|
||||||
type CheckMiddleware struct {
|
type CheckMiddleware struct {
|
||||||
Required basecoin.Actor
|
Required basecoin.Actor
|
||||||
PassOption
|
PassInitState
|
||||||
|
PassInitValidate
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Middleware = CheckMiddleware{}
|
var _ Middleware = CheckMiddleware{}
|
||||||
|
@ -25,14 +26,14 @@ func (_ CheckMiddleware) Name() string {
|
||||||
return NameCheck
|
return NameCheck
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p CheckMiddleware) CheckTx(ctx basecoin.Context, store state.SimpleDB, 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.CheckResult, err error) {
|
||||||
if !ctx.HasPermission(p.Required) {
|
if !ctx.HasPermission(p.Required) {
|
||||||
return res, errors.ErrUnauthorized()
|
return res, errors.ErrUnauthorized()
|
||||||
}
|
}
|
||||||
return next.CheckTx(ctx, store, tx)
|
return next.CheckTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p CheckMiddleware) DeliverTx(ctx basecoin.Context, store state.SimpleDB, 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.DeliverResult, err error) {
|
||||||
if !ctx.HasPermission(p.Required) {
|
if !ctx.HasPermission(p.Required) {
|
||||||
return res, errors.ErrUnauthorized()
|
return res, errors.ErrUnauthorized()
|
||||||
}
|
}
|
||||||
|
@ -42,7 +43,8 @@ func (p CheckMiddleware) DeliverTx(ctx basecoin.Context, store state.SimpleDB, t
|
||||||
// GrantMiddleware tries to set the permission to this Actor, which may be prohibited
|
// GrantMiddleware tries to set the permission to this Actor, which may be prohibited
|
||||||
type GrantMiddleware struct {
|
type GrantMiddleware struct {
|
||||||
Auth basecoin.Actor
|
Auth basecoin.Actor
|
||||||
PassOption
|
PassInitState
|
||||||
|
PassInitValidate
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Middleware = GrantMiddleware{}
|
var _ Middleware = GrantMiddleware{}
|
||||||
|
@ -51,12 +53,12 @@ func (_ GrantMiddleware) Name() string {
|
||||||
return NameGrant
|
return NameGrant
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g GrantMiddleware) CheckTx(ctx basecoin.Context, store state.SimpleDB, 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.CheckResult, err error) {
|
||||||
ctx = ctx.WithPermissions(g.Auth)
|
ctx = ctx.WithPermissions(g.Auth)
|
||||||
return next.CheckTx(ctx, store, tx)
|
return next.CheckTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g GrantMiddleware) DeliverTx(ctx basecoin.Context, store state.SimpleDB, 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.DeliverResult, err error) {
|
||||||
ctx = ctx.WithPermissions(g.Auth)
|
ctx = ctx.WithPermissions(g.Auth)
|
||||||
return next.DeliverTx(ctx, store, tx)
|
return next.DeliverTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
package stack
|
package stack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin"
|
"github.com/tendermint/basecoin"
|
||||||
|
@ -15,76 +16,89 @@ import (
|
||||||
type Middleware interface {
|
type Middleware interface {
|
||||||
CheckerMiddle
|
CheckerMiddle
|
||||||
DeliverMiddle
|
DeliverMiddle
|
||||||
SetOptionMiddle
|
InitStaterMiddle
|
||||||
|
InitValidaterMiddle
|
||||||
basecoin.Named
|
basecoin.Named
|
||||||
}
|
}
|
||||||
|
|
||||||
type CheckerMiddle interface {
|
type CheckerMiddle interface {
|
||||||
CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, next basecoin.Checker) (basecoin.Result, error)
|
tx basecoin.Tx, next basecoin.Checker) (basecoin.CheckResult, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type CheckerMiddleFunc func(basecoin.Context, state.SimpleDB,
|
type CheckerMiddleFunc func(basecoin.Context, state.SimpleDB,
|
||||||
basecoin.Tx, basecoin.Checker) (basecoin.Result, error)
|
basecoin.Tx, basecoin.Checker) (basecoin.CheckResult, error)
|
||||||
|
|
||||||
func (c CheckerMiddleFunc) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
func (c CheckerMiddleFunc) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, next basecoin.Checker) (basecoin.Result, error) {
|
tx basecoin.Tx, next basecoin.Checker) (basecoin.CheckResult, error) {
|
||||||
return c(ctx, store, tx, next)
|
return c(ctx, store, tx, next)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeliverMiddle interface {
|
type DeliverMiddle interface {
|
||||||
DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx,
|
DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx,
|
||||||
next basecoin.Deliver) (basecoin.Result, error)
|
next basecoin.Deliver) (basecoin.DeliverResult, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeliverMiddleFunc func(basecoin.Context, state.SimpleDB,
|
type DeliverMiddleFunc func(basecoin.Context, state.SimpleDB,
|
||||||
basecoin.Tx, basecoin.Deliver) (basecoin.Result, error)
|
basecoin.Tx, basecoin.Deliver) (basecoin.DeliverResult, error)
|
||||||
|
|
||||||
func (d DeliverMiddleFunc) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
func (d DeliverMiddleFunc) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, next basecoin.Deliver) (basecoin.Result, error) {
|
tx basecoin.Tx, next basecoin.Deliver) (basecoin.DeliverResult, error) {
|
||||||
return d(ctx, store, tx, next)
|
return d(ctx, store, tx, next)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetOptionMiddle interface {
|
type InitStaterMiddle interface {
|
||||||
SetOption(l log.Logger, store state.SimpleDB, module,
|
InitState(l log.Logger, store state.SimpleDB, module,
|
||||||
key, value string, next basecoin.SetOptioner) (string, error)
|
key, value string, next basecoin.InitStater) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SetOptionMiddleFunc func(log.Logger, state.SimpleDB,
|
type InitStaterMiddleFunc func(log.Logger, state.SimpleDB,
|
||||||
string, string, string, basecoin.SetOptioner) (string, error)
|
string, string, string, basecoin.InitStater) (string, error)
|
||||||
|
|
||||||
func (c SetOptionMiddleFunc) SetOption(l log.Logger, store state.SimpleDB,
|
func (c InitStaterMiddleFunc) InitState(l log.Logger, store state.SimpleDB,
|
||||||
module, key, value string, next basecoin.SetOptioner) (string, error) {
|
module, key, value string, next basecoin.InitStater) (string, error) {
|
||||||
return c(l, store, module, key, value, next)
|
return c(l, store, module, key, value, next)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InitValidaterMiddle interface {
|
||||||
|
InitValidate(l log.Logger, store state.SimpleDB, vals []*abci.Validator, next basecoin.InitValidater)
|
||||||
|
}
|
||||||
|
|
||||||
|
type InitValidaterMiddleFunc func(log.Logger, state.SimpleDB,
|
||||||
|
[]*abci.Validator, basecoin.InitValidater)
|
||||||
|
|
||||||
|
func (c InitValidaterMiddleFunc) InitValidate(l log.Logger, store state.SimpleDB,
|
||||||
|
vals []*abci.Validator, next basecoin.InitValidater) {
|
||||||
|
c(l, store, vals, next)
|
||||||
|
}
|
||||||
|
|
||||||
// holders
|
// holders
|
||||||
type PassCheck struct{}
|
type PassCheck struct{}
|
||||||
|
|
||||||
func (_ PassCheck) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
func (_ PassCheck) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, next basecoin.Checker) (basecoin.Result, error) {
|
tx basecoin.Tx, next basecoin.Checker) (basecoin.CheckResult, error) {
|
||||||
return next.CheckTx(ctx, store, tx)
|
return next.CheckTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
type PassDeliver struct{}
|
type PassDeliver struct{}
|
||||||
|
|
||||||
func (_ PassDeliver) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
func (_ PassDeliver) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, next basecoin.Deliver) (basecoin.Result, error) {
|
tx basecoin.Tx, next basecoin.Deliver) (basecoin.DeliverResult, error) {
|
||||||
return next.DeliverTx(ctx, store, tx)
|
return next.DeliverTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
type PassOption struct{}
|
type PassInitState struct{}
|
||||||
|
|
||||||
func (_ PassOption) SetOption(l log.Logger, store state.SimpleDB, module,
|
func (_ PassInitState) InitState(l log.Logger, store state.SimpleDB, module,
|
||||||
key, value string, next basecoin.SetOptioner) (string, error) {
|
key, value string, next basecoin.InitStater) (string, error) {
|
||||||
return next.SetOption(l, store, module, key, value)
|
return next.InitState(l, store, module, key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
type NopOption struct{}
|
type PassInitValidate struct{}
|
||||||
|
|
||||||
func (_ NopOption) SetOption(l log.Logger, store state.SimpleDB, module,
|
func (_ PassInitValidate) InitValidate(l log.Logger, store state.SimpleDB,
|
||||||
key, value string, next basecoin.SetOptioner) (string, error) {
|
vals []*abci.Validator, next basecoin.InitValidater) {
|
||||||
return "", nil
|
next.InitValidate(l, store, vals)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatchable is like middleware, except the meaning of "next" is different.
|
// Dispatchable is like middleware, except the meaning of "next" is different.
|
||||||
|
@ -113,16 +127,21 @@ func (w wrapped) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w wrapped) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
func (w wrapped) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, _ basecoin.Checker) (basecoin.Result, error) {
|
tx basecoin.Tx, _ basecoin.Checker) (basecoin.CheckResult, error) {
|
||||||
return w.h.CheckTx(ctx, store, tx)
|
return w.h.CheckTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w wrapped) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
func (w wrapped) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, _ basecoin.Deliver) (basecoin.Result, error) {
|
tx basecoin.Tx, _ basecoin.Deliver) (basecoin.DeliverResult, error) {
|
||||||
return w.h.DeliverTx(ctx, store, tx)
|
return w.h.DeliverTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w wrapped) SetOption(l log.Logger, store state.SimpleDB,
|
func (w wrapped) InitState(l log.Logger, store state.SimpleDB,
|
||||||
module, key, value string, _ basecoin.SetOptioner) (string, error) {
|
module, key, value string, _ basecoin.InitStater) (string, error) {
|
||||||
return w.h.SetOption(l, store, module, key, value)
|
return w.h.InitState(l, store, module, key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w wrapped) InitValidate(l log.Logger, store state.SimpleDB,
|
||||||
|
vals []*abci.Validator, next basecoin.InitValidater) {
|
||||||
|
w.h.InitValidate(l, store, vals)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package stack
|
package stack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin"
|
"github.com/tendermint/basecoin"
|
||||||
|
@ -31,7 +32,7 @@ func (m *middleware) wrapCtx(ctx basecoin.Context) basecoin.Context {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckTx always returns an empty success tx
|
// CheckTx always returns an empty success tx
|
||||||
func (m *middleware) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (basecoin.Result, error) {
|
func (m *middleware) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (basecoin.CheckResult, error) {
|
||||||
// make sure we pass in proper context to child
|
// make sure we pass in proper context to child
|
||||||
next := secureCheck(m.next, ctx)
|
next := secureCheck(m.next, ctx)
|
||||||
// set the permissions for this app
|
// set the permissions for this app
|
||||||
|
@ -42,7 +43,7 @@ func (m *middleware) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx base
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx always returns an empty success tx
|
// DeliverTx always returns an empty success tx
|
||||||
func (m *middleware) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.Result, err error) {
|
func (m *middleware) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.Tx) (res basecoin.DeliverResult, err error) {
|
||||||
// make sure we pass in proper context to child
|
// make sure we pass in proper context to child
|
||||||
next := secureDeliver(m.next, ctx)
|
next := secureDeliver(m.next, ctx)
|
||||||
// set the permissions for this app
|
// set the permissions for this app
|
||||||
|
@ -52,11 +53,17 @@ func (m *middleware) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx ba
|
||||||
return m.middleware.DeliverTx(ctx, store, tx, next)
|
return m.middleware.DeliverTx(ctx, store, tx, next)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *middleware) SetOption(l log.Logger, store state.SimpleDB, module, key, value string) (string, error) {
|
func (m *middleware) InitState(l log.Logger, store state.SimpleDB, module, key, value string) (string, error) {
|
||||||
// set the namespace for the app
|
// set the namespace for the app
|
||||||
store = stateSpace(store, m.space)
|
store = stateSpace(store, m.space)
|
||||||
|
|
||||||
return m.middleware.SetOption(l, store, module, key, value, m.next)
|
return m.middleware.InitState(l, store, module, key, value, m.next)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *middleware) InitValidate(l log.Logger, store state.SimpleDB, vals []*abci.Validator) {
|
||||||
|
// set the namespace for the app
|
||||||
|
store = stateSpace(store, m.space)
|
||||||
|
m.middleware.InitValidate(l, store, vals, m.next)
|
||||||
}
|
}
|
||||||
|
|
||||||
// builder is used to associate info with the middleware, so we can build
|
// builder is used to associate info with the middleware, so we can build
|
||||||
|
|
|
@ -72,11 +72,11 @@ func TestPermissionSandbox(t *testing.T) {
|
||||||
Apps(CheckMiddleware{Required: tc.require}).
|
Apps(CheckMiddleware{Required: tc.require}).
|
||||||
Use(EchoHandler{})
|
Use(EchoHandler{})
|
||||||
|
|
||||||
res, err := app.CheckTx(ctx, store, raw)
|
cres, err := app.CheckTx(ctx, store, raw)
|
||||||
checkPerm(t, i, tc.expectedRes, tc.expected, res, err)
|
checkPerm(t, i, tc.expectedRes, tc.expected, cres, err)
|
||||||
|
|
||||||
res, err = app.DeliverTx(ctx, store, raw)
|
dres, err := app.DeliverTx(ctx, store, raw)
|
||||||
checkPerm(t, i, tc.expectedRes, tc.expected, res, err)
|
checkPerm(t, i, tc.expectedRes, tc.expected, dres, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ func checkPerm(t *testing.T, idx int, data []byte, check func(error) bool, res b
|
||||||
|
|
||||||
if len(data) > 0 {
|
if len(data) > 0 {
|
||||||
assert.Nil(err, "%d: %+v", idx, err)
|
assert.Nil(err, "%d: %+v", idx, err)
|
||||||
assert.EqualValues(data, res.Data)
|
assert.EqualValues(data, res.GetData())
|
||||||
} else {
|
} else {
|
||||||
assert.NotNil(err, "%d", idx)
|
assert.NotNil(err, "%d", idx)
|
||||||
// check error code!
|
// check error code!
|
||||||
|
|
|
@ -3,6 +3,7 @@ package stack
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin"
|
"github.com/tendermint/basecoin"
|
||||||
|
@ -26,7 +27,7 @@ func (Recovery) Name() string {
|
||||||
var _ Middleware = Recovery{}
|
var _ Middleware = Recovery{}
|
||||||
|
|
||||||
// CheckTx catches any panic and converts to error - fulfills Middlware interface
|
// CheckTx catches any panic and converts to error - fulfills Middlware interface
|
||||||
func (Recovery) CheckTx(ctx basecoin.Context, store state.SimpleDB, 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.CheckResult, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
err = normalizePanic(r)
|
err = normalizePanic(r)
|
||||||
|
@ -36,7 +37,7 @@ func (Recovery) CheckTx(ctx basecoin.Context, store state.SimpleDB, tx basecoin.
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverTx catches any panic and converts to error - fulfills Middlware interface
|
// DeliverTx catches any panic and converts to error - fulfills Middlware interface
|
||||||
func (Recovery) DeliverTx(ctx basecoin.Context, store state.SimpleDB, 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.DeliverResult, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
err = normalizePanic(r)
|
err = normalizePanic(r)
|
||||||
|
@ -45,14 +46,29 @@ func (Recovery) DeliverTx(ctx basecoin.Context, store state.SimpleDB, tx basecoi
|
||||||
return next.DeliverTx(ctx, store, tx)
|
return next.DeliverTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOption catches any panic and converts to error - fulfills Middlware interface
|
// InitState catches any panic and converts to error - fulfills Middlware interface
|
||||||
func (Recovery) SetOption(l log.Logger, store state.SimpleDB, module, key, value string, next basecoin.SetOptioner) (log string, err error) {
|
func (Recovery) InitState(l log.Logger, store state.SimpleDB, module, key, value string, next basecoin.InitStater) (log string, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
err = normalizePanic(r)
|
err = normalizePanic(r)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return next.SetOption(l, store, module, key, value)
|
return next.InitState(l, store, module, key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitValidate catches any panic and logs it
|
||||||
|
// TODO: return an error???
|
||||||
|
func (Recovery) InitValidate(l log.Logger, store state.SimpleDB,
|
||||||
|
vals []*abci.Validator, next basecoin.InitValidater) {
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
// TODO: return an error???
|
||||||
|
err := normalizePanic(r)
|
||||||
|
l.With("err", err).Error(err.Error())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
next.InitValidate(l, store, vals)
|
||||||
}
|
}
|
||||||
|
|
||||||
// normalizePanic makes sure we can get a nice TMError (with stack) out of it
|
// normalizePanic makes sure we can get a nice TMError (with stack) out of it
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
type writerMid struct {
|
type writerMid struct {
|
||||||
name string
|
name string
|
||||||
key, value []byte
|
key, value []byte
|
||||||
|
PassInitValidate
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Middleware = writerMid{}
|
var _ Middleware = writerMid{}
|
||||||
|
@ -24,27 +25,28 @@ var _ Middleware = writerMid{}
|
||||||
func (w writerMid) Name() string { return w.name }
|
func (w writerMid) Name() string { return w.name }
|
||||||
|
|
||||||
func (w writerMid) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
func (w writerMid) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, next basecoin.Checker) (basecoin.Result, error) {
|
tx basecoin.Tx, next basecoin.Checker) (basecoin.CheckResult, error) {
|
||||||
store.Set(w.key, w.value)
|
store.Set(w.key, w.value)
|
||||||
return next.CheckTx(ctx, store, tx)
|
return next.CheckTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w writerMid) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
func (w writerMid) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx, next basecoin.Deliver) (basecoin.Result, error) {
|
tx basecoin.Tx, next basecoin.Deliver) (basecoin.DeliverResult, error) {
|
||||||
store.Set(w.key, w.value)
|
store.Set(w.key, w.value)
|
||||||
return next.DeliverTx(ctx, store, tx)
|
return next.DeliverTx(ctx, store, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w writerMid) SetOption(l log.Logger, store state.SimpleDB, module,
|
func (w writerMid) InitState(l log.Logger, store state.SimpleDB, module,
|
||||||
key, value string, next basecoin.SetOptioner) (string, error) {
|
key, value string, next basecoin.InitStater) (string, error) {
|
||||||
store.Set([]byte(key), []byte(value))
|
store.Set([]byte(key), []byte(value))
|
||||||
return next.SetOption(l, store, module, key, value)
|
return next.InitState(l, store, module, key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// writerHand is a middleware that writes the given bytes on CheckTx and DeliverTx
|
// writerHand is a handler that writes the given bytes on CheckTx and DeliverTx
|
||||||
type writerHand struct {
|
type writerHand struct {
|
||||||
name string
|
name string
|
||||||
key, value []byte
|
key, value []byte
|
||||||
|
basecoin.NopInitValidate
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ basecoin.Handler = writerHand{}
|
var _ basecoin.Handler = writerHand{}
|
||||||
|
@ -52,18 +54,18 @@ var _ basecoin.Handler = writerHand{}
|
||||||
func (w writerHand) Name() string { return w.name }
|
func (w writerHand) Name() string { return w.name }
|
||||||
|
|
||||||
func (w writerHand) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
func (w writerHand) CheckTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx) (basecoin.Result, error) {
|
tx basecoin.Tx) (basecoin.CheckResult, error) {
|
||||||
store.Set(w.key, w.value)
|
store.Set(w.key, w.value)
|
||||||
return basecoin.Result{}, nil
|
return basecoin.CheckResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w writerHand) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
func (w writerHand) DeliverTx(ctx basecoin.Context, store state.SimpleDB,
|
||||||
tx basecoin.Tx) (basecoin.Result, error) {
|
tx basecoin.Tx) (basecoin.DeliverResult, error) {
|
||||||
store.Set(w.key, w.value)
|
store.Set(w.key, w.value)
|
||||||
return basecoin.Result{}, nil
|
return basecoin.DeliverResult{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w writerHand) SetOption(l log.Logger, store state.SimpleDB, module,
|
func (w writerHand) InitState(l log.Logger, store state.SimpleDB, module,
|
||||||
key, value string) (string, error) {
|
key, value string) (string, error) {
|
||||||
store.Set([]byte(key), []byte(value))
|
store.Set([]byte(key), []byte(value))
|
||||||
return "Success", nil
|
return "Success", nil
|
||||||
|
@ -76,9 +78,9 @@ func TestStateSpace(t *testing.T) {
|
||||||
expected []data.Bytes
|
expected []data.Bytes
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
writerHand{"foo", []byte{1, 2}, []byte("bar")},
|
writerHand{name: "foo", key: []byte{1, 2}, value: []byte("bar")},
|
||||||
[]Middleware{
|
[]Middleware{
|
||||||
writerMid{"bing", []byte{1, 2}, []byte("bang")},
|
writerMid{name: "bing", key: []byte{1, 2}, value: []byte("bang")},
|
||||||
},
|
},
|
||||||
[]data.Bytes{
|
[]data.Bytes{
|
||||||
{'f', 'o', 'o', 0, 1, 2},
|
{'f', 'o', 'o', 0, 1, 2},
|
||||||
|
|
Loading…
Reference in New Issue