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