baseapp: ctxCheck and ctxDeliver, begin/endBlocker
This commit is contained in:
parent
8fc12a5265
commit
958a632eed
|
@ -20,25 +20,31 @@ var mainHeaderKey = []byte("header")
|
||||||
|
|
||||||
// The ABCI application
|
// The ABCI application
|
||||||
type BaseApp struct {
|
type BaseApp struct {
|
||||||
logger log.Logger
|
// initialized on creation
|
||||||
name string // application name from abci.Info
|
logger log.Logger
|
||||||
db dbm.DB // common DB backend
|
name string // application name from abci.Info
|
||||||
cms sdk.CommitMultiStore // Main (uncached) state
|
db dbm.DB // common DB backend
|
||||||
txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx
|
cms sdk.CommitMultiStore // Main (uncached) state
|
||||||
initChainer sdk.InitChainer //
|
router Router // handle any kind of message
|
||||||
anteHandler sdk.AnteHandler // ante handler for fee and auth
|
|
||||||
router Router // handle any kind of message
|
// may be nil
|
||||||
|
txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx
|
||||||
|
initChainer sdk.InitChainer // initialize state with validators and state blob
|
||||||
|
beginBlocker sdk.BeginBlocker // logic to run before any txs
|
||||||
|
endBlocker sdk.EndBlocker // logic to run after all txs, and to determine valset changes
|
||||||
|
anteHandler sdk.AnteHandler // ante handler for fee and auth
|
||||||
|
|
||||||
//--------------------
|
//--------------------
|
||||||
// Volatile
|
// Volatile
|
||||||
// .msCheck and .header are set on initialization.
|
// .msCheck and .ctxCheck are set on initialization and reset on Commit.
|
||||||
// .msDeliver is only set (and reset) in BeginBlock.
|
// .msDeliver and .ctxDeliver are (re-)set on BeginBlock.
|
||||||
// .header and .valUpdates are also reset in BeginBlock.
|
// .valUpdates accumulate in DeliverTx and reset in BeginBlock.
|
||||||
// .msCheck is only reset in Commit.
|
// QUESTION: should we put valUpdates in the ctxDeliver?
|
||||||
|
|
||||||
header abci.Header // current block header
|
|
||||||
msCheck sdk.CacheMultiStore // CheckTx state, a cache-wrap of `.cms`
|
msCheck sdk.CacheMultiStore // CheckTx state, a cache-wrap of `.cms`
|
||||||
msDeliver sdk.CacheMultiStore // DeliverTx state, a cache-wrap of `.cms`
|
msDeliver sdk.CacheMultiStore // DeliverTx state, a cache-wrap of `.cms`
|
||||||
|
ctxCheck sdk.Context // CheckTx context
|
||||||
|
ctxDeliver sdk.Context // DeliverTx context
|
||||||
valUpdates []abci.Validator // cached validator changes from DeliverTx
|
valUpdates []abci.Validator // cached validator changes from DeliverTx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,23 +149,19 @@ func (app *BaseApp) initFromStore(mainKey sdk.StoreKey) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set BaseApp state
|
// initialize Check state
|
||||||
app.header = header
|
|
||||||
app.msCheck = app.cms.CacheMultiStore()
|
app.msCheck = app.cms.CacheMultiStore()
|
||||||
app.msDeliver = nil
|
app.ctxCheck = app.NewContext(true, abci.Header{})
|
||||||
app.valUpdates = nil
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContext returns a new Context suitable for AnteHandler and Handler processing.
|
// NewContext returns a new Context with the correct store, the given header, and nil txBytes.
|
||||||
// NOTE: header is empty for checkTx
|
func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context {
|
||||||
// NOTE: txBytes may be nil, for instance in tests (using app.Check or app.Deliver directly).
|
if isCheckTx {
|
||||||
func (app *BaseApp) NewContext(isCheckTx bool, txBytes []byte) sdk.Context {
|
return sdk.NewContext(app.msCheck, header, true, nil)
|
||||||
store := app.getMultiStore(isCheckTx)
|
}
|
||||||
// XXX CheckTx can't safely get the header
|
return sdk.NewContext(app.msDeliver, header, false, nil)
|
||||||
header := abci.Header{}
|
|
||||||
return sdk.NewContext(store, header, isCheckTx, txBytes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
|
@ -195,11 +197,8 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC
|
||||||
// NOTE: we're writing to the cms directly, without a CacheWrap
|
// NOTE: we're writing to the cms directly, without a CacheWrap
|
||||||
ctx := sdk.NewContext(app.cms, abci.Header{}, false, nil)
|
ctx := sdk.NewContext(app.cms, abci.Header{}, false, nil)
|
||||||
|
|
||||||
err := app.initChainer(ctx, req)
|
res = app.initChainer(ctx, req)
|
||||||
if err != nil {
|
// TODO: handle error https://github.com/cosmos/cosmos-sdk/issues/468
|
||||||
// TODO: something better https://github.com/cosmos/cosmos-sdk/issues/468
|
|
||||||
cmn.Exit(fmt.Sprintf("error initializing application genesis state: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX this commits everything and bumps the version.
|
// XXX this commits everything and bumps the version.
|
||||||
// https://github.com/cosmos/cosmos-sdk/issues/442#issuecomment-366470148
|
// https://github.com/cosmos/cosmos-sdk/issues/442#issuecomment-366470148
|
||||||
|
@ -221,10 +220,12 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) {
|
||||||
|
|
||||||
// Implements ABCI
|
// Implements ABCI
|
||||||
func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) {
|
func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) {
|
||||||
// NOTE: For consistency we should unset these upon EndBlock.
|
|
||||||
app.header = req.Header
|
|
||||||
app.msDeliver = app.cms.CacheMultiStore()
|
app.msDeliver = app.cms.CacheMultiStore()
|
||||||
|
app.ctxDeliver = app.NewContext(false, req.Header)
|
||||||
app.valUpdates = nil
|
app.valUpdates = nil
|
||||||
|
if app.beginBlocker != nil {
|
||||||
|
res = app.beginBlocker(app.ctxDeliver, req)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,8 +318,13 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk
|
||||||
return err.Result()
|
return err.Result()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct a Context.
|
// Get the context
|
||||||
var ctx = app.NewContext(isCheckTx, txBytes)
|
var ctx sdk.Context
|
||||||
|
if isCheckTx {
|
||||||
|
ctx = app.ctxCheck.WithTxBytes(txBytes)
|
||||||
|
} else {
|
||||||
|
ctx = app.ctxDeliver.WithTxBytes(txBytes)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: override default ante handler w/ custom ante handler.
|
// TODO: override default ante handler w/ custom ante handler.
|
||||||
|
|
||||||
|
@ -332,7 +338,7 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk
|
||||||
}
|
}
|
||||||
|
|
||||||
// CacheWrap app.msDeliver in case it fails.
|
// CacheWrap app.msDeliver in case it fails.
|
||||||
msCache := app.getMultiStore(false).CacheMultiStore()
|
msCache := app.msDeliver.CacheMultiStore()
|
||||||
ctx = ctx.WithMultiStore(msCache)
|
ctx = ctx.WithMultiStore(msCache)
|
||||||
|
|
||||||
// Match and run route.
|
// Match and run route.
|
||||||
|
@ -350,19 +356,30 @@ func (app *BaseApp) runTx(isCheckTx bool, txBytes []byte, tx sdk.Tx) (result sdk
|
||||||
|
|
||||||
// Implements ABCI
|
// Implements ABCI
|
||||||
func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) {
|
func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) {
|
||||||
res.ValidatorUpdates = app.valUpdates
|
if app.endBlocker != nil {
|
||||||
app.valUpdates = nil
|
res = app.endBlocker(app.ctxDeliver, req)
|
||||||
app.msDeliver = nil
|
} else {
|
||||||
|
res.ValidatorUpdates = app.valUpdates
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements ABCI
|
// Implements ABCI
|
||||||
func (app *BaseApp) Commit() (res abci.ResponseCommit) {
|
func (app *BaseApp) Commit() (res abci.ResponseCommit) {
|
||||||
|
// Write the Deliver state and commit the MultiStore
|
||||||
app.msDeliver.Write()
|
app.msDeliver.Write()
|
||||||
commitID := app.cms.Commit()
|
commitID := app.cms.Commit()
|
||||||
app.logger.Debug("Commit synced",
|
app.logger.Debug("Commit synced",
|
||||||
"commit", commitID,
|
"commit", commitID,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Reset the Check state
|
||||||
|
// NOTE: safe because Tendermint holds a lock on the mempool for Commit.
|
||||||
|
// Use the header from this latest block.
|
||||||
|
header := app.ctxDeliver.BlockHeader()
|
||||||
|
app.msCheck = app.cms.CacheMultiStore()
|
||||||
|
app.ctxCheck = app.NewContext(true, header)
|
||||||
|
|
||||||
return abci.ResponseCommit{
|
return abci.ResponseCommit{
|
||||||
Data: commitID.Hash,
|
Data: commitID.Hash,
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,10 +65,10 @@ func TestInitChainer(t *testing.T) {
|
||||||
key, value := []byte("hello"), []byte("goodbye")
|
key, value := []byte("hello"), []byte("goodbye")
|
||||||
|
|
||||||
// initChainer sets a value in the store
|
// initChainer sets a value in the store
|
||||||
var initChainer sdk.InitChainer = func(ctx sdk.Context, req abci.RequestInitChain) sdk.Error {
|
var initChainer sdk.InitChainer = func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||||
store := ctx.KVStore(capKey)
|
store := ctx.KVStore(capKey)
|
||||||
store.Set(key, value)
|
store.Set(key, value)
|
||||||
return nil
|
return abci.ResponseInitChain{}
|
||||||
}
|
}
|
||||||
|
|
||||||
query := abci.RequestQuery{
|
query := abci.RequestQuery{
|
||||||
|
|
|
@ -57,8 +57,8 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
|
||||||
app.Router().AddRoute("sketchy", sketchy.NewHandler())
|
app.Router().AddRoute("sketchy", sketchy.NewHandler())
|
||||||
|
|
||||||
// initialize BaseApp
|
// initialize BaseApp
|
||||||
app.SetTxDecoder()
|
app.SetTxDecoder(app.txDecoder)
|
||||||
app.SetInitChainer()
|
app.SetInitChainer(app.initChainer)
|
||||||
app.MountStoresIAVL(app.capKeyMainStore, app.capKeyIBCStore)
|
app.MountStoresIAVL(app.capKeyMainStore, app.capKeyIBCStore)
|
||||||
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper))
|
app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper))
|
||||||
err := app.LoadLatestVersion(app.capKeyMainStore)
|
err := app.LoadLatestVersion(app.capKeyMainStore)
|
||||||
|
@ -78,37 +78,35 @@ func MakeTxCodec() *wire.Codec {
|
||||||
}
|
}
|
||||||
|
|
||||||
// custom logic for transaction decoding
|
// custom logic for transaction decoding
|
||||||
func (app *BasecoinApp) SetTxDecoder() {
|
func (app *BasecoinApp) txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
|
||||||
app.BaseApp.SetTxDecoder(func(txBytes []byte) (sdk.Tx, sdk.Error) {
|
var tx = sdk.StdTx{}
|
||||||
var tx = sdk.StdTx{}
|
// StdTx.Msg is an interface whose concrete
|
||||||
// StdTx.Msg is an interface whose concrete
|
// types are registered in app/msgs.go.
|
||||||
// types are registered in app/msgs.go.
|
err := app.cdc.UnmarshalBinary(txBytes, &tx)
|
||||||
err := app.cdc.UnmarshalBinary(txBytes, &tx)
|
if err != nil {
|
||||||
if err != nil {
|
return nil, sdk.ErrTxParse("").TraceCause(err, "")
|
||||||
return nil, sdk.ErrTxParse("").TraceCause(err, "")
|
}
|
||||||
}
|
return tx, nil
|
||||||
return tx, nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// custom logic for basecoin initialization
|
// custom logic for basecoin initialization
|
||||||
func (app *BasecoinApp) SetInitChainer() {
|
func (app *BasecoinApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
|
||||||
app.BaseApp.SetInitChainer(func(ctx sdk.Context, req abci.RequestInitChain) sdk.Error {
|
stateJSON := req.AppStateBytes
|
||||||
stateJSON := req.AppStateBytes
|
|
||||||
|
|
||||||
genesisState := new(types.GenesisState)
|
genesisState := new(types.GenesisState)
|
||||||
err := json.Unmarshal(stateJSON, genesisState)
|
err := json.Unmarshal(stateJSON, genesisState)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
||||||
|
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, gacc := range genesisState.Accounts {
|
||||||
|
acc, err := gacc.ToAppAccount()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sdk.ErrGenesisParse("").TraceCause(err, "")
|
panic(err) // TODO https://github.com/cosmos/cosmos-sdk/issues/468
|
||||||
|
// return sdk.ErrGenesisParse("").TraceCause(err, "")
|
||||||
}
|
}
|
||||||
|
app.accountMapper.SetAccount(ctx, acc)
|
||||||
for _, gacc := range genesisState.Accounts {
|
}
|
||||||
acc, err := gacc.ToAppAccount()
|
return abci.ResponseInitChain{}
|
||||||
if err != nil {
|
|
||||||
return sdk.ErrGenesisParse("").TraceCause(err, "")
|
|
||||||
}
|
|
||||||
app.accountMapper.SetAccount(ctx, acc)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@ func TestGenesis(t *testing.T) {
|
||||||
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
|
bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
|
||||||
|
|
||||||
// a checkTx context
|
// a checkTx context
|
||||||
ctx := bapp.BaseApp.NewContext(true, nil)
|
ctx := bapp.BaseApp.NewContext(true, abci.Header{})
|
||||||
|
|
||||||
res1 := bapp.accountMapper.GetAccount(ctx, baseAcc.Address)
|
res1 := bapp.accountMapper.GetAccount(ctx, baseAcc.Address)
|
||||||
assert.Equal(t, acc, res1)
|
assert.Equal(t, acc, res1)
|
||||||
|
|
|
@ -3,4 +3,10 @@ package types
|
||||||
import abci "github.com/tendermint/abci/types"
|
import abci "github.com/tendermint/abci/types"
|
||||||
|
|
||||||
// initialize application state at genesis
|
// initialize application state at genesis
|
||||||
type InitChainer func(ctx Context, req abci.RequestInitChain) Error
|
type InitChainer func(ctx Context, req abci.RequestInitChain) abci.ResponseInitChain
|
||||||
|
|
||||||
|
//
|
||||||
|
type BeginBlocker func(ctx Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock
|
||||||
|
|
||||||
|
//
|
||||||
|
type EndBlocker func(ctx Context, req abci.RequestEndBlock) abci.ResponseEndBlock
|
||||||
|
|
Loading…
Reference in New Issue