Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .changelog/6289.breaking.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
go/consensus/cometbft/abci: Include all events in the events root

The events root in block metadata system transaction now includes all events,
not just provable ones.
4 changes: 4 additions & 0 deletions .changelog/6289.breaking.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
go/consensus/cometbft/abci: Add results hash to block metadata tx

The results hash was added to the block metadata system transaction so that
stateless clients can partially verify the latest block’s results.
7 changes: 6 additions & 1 deletion go/consensus/api/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,20 @@ const BlockMetadataMaxSize = 16_384
type BlockMetadata struct {
// StateRoot is the state root after executing all logic in the block.
StateRoot hash.Hash `json:"state_root"`
// EventsRoot is the provable events root.
// EventsRoot is the root hash of all events emitted in the block.
EventsRoot []byte `json:"events_root"`
// ResultsHash is the hash of transaction results in the block.
ResultsHash []byte `json:"results_hash,omitempty"`
}

// ValidateBasic performs basic block metadata structure validation.
func (bm *BlockMetadata) ValidateBasic() error {
if len(bm.EventsRoot) != 32 {
return fmt.Errorf("malformed events root")
}
if bm.ResultsHash != nil && len(bm.ResultsHash) != 32 {
return fmt.Errorf("malformed results hash")
}
return nil
}

Expand Down
78 changes: 46 additions & 32 deletions go/consensus/cometbft/abci/mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -433,8 +433,15 @@ func (mux *abciMux) PrepareProposal(req types.RequestPrepareProposal) types.Resp
return types.ResponsePrepareProposal{}
}

// Inject system transactions at the end of the block.
systemTxs, systemTxResults, err := mux.prepareSystemTxs()
// Inject system transactions and their results at the end of the block.
//
// It is important to inject the results first, so that the results hash
// in the block metadata transaction includes all results, including results
// from system transactions.
systemTxResults := mux.prepareSystemTxResults()
mux.state.proposal.resultsDeliverTx = append(mux.state.proposal.resultsDeliverTx, systemTxResults...)

systemTxs, err := mux.prepareSystemTxs()
if err != nil {
mux.logger.Error("failed to prepare system transactions",
"height", req.Height,
Expand All @@ -444,7 +451,6 @@ func (mux *abciMux) PrepareProposal(req types.RequestPrepareProposal) types.Resp
return types.ResponsePrepareProposal{}
}
txs = append(txs, systemTxs...)
mux.state.proposal.resultsDeliverTx = append(mux.state.proposal.resultsDeliverTx, systemTxResults...)

// Record proposal inputs so we can compare in ProcessProposal.
p := mux.state.proposal
Expand Down Expand Up @@ -531,28 +537,25 @@ func (mux *abciMux) executeProposal(
mux.state.resetProposal()
mux.state.proposal.hash = hash

resultsBeginBlock := mux.BeginBlock(types.RequestBeginBlock{
// The proposal is updated inside every call, as the end block phase
// requires these results immediately to validate system transactions.
mux.BeginBlock(types.RequestBeginBlock{
Hash: hash,
Header: header,
LastCommitInfo: lastCommit,
ByzantineValidators: misbehavior,
})

resultsDeliverTx := make([]*types.ResponseDeliverTx, 0, len(txs))
for _, tx := range txs {
resp := mux.DeliverTx(types.RequestDeliverTx{
mux.DeliverTx(types.RequestDeliverTx{
Tx: tx,
})
resultsDeliverTx = append(resultsDeliverTx, &resp)
}

resultsEndBlock := mux.EndBlock(types.RequestEndBlock{
mux.EndBlock(types.RequestEndBlock{
Height: header.Height,
})

// Update the proposal with results, marking the proposal as executed.
mux.state.proposal.setResults(&resultsBeginBlock, resultsDeliverTx, &resultsEndBlock)

return nil
}

Expand Down Expand Up @@ -638,19 +641,22 @@ func (mux *abciMux) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginB
}
}

response := mux.BaseApplication.BeginBlock(req)
result := mux.BaseApplication.BeginBlock(req)

// During the first block, also collect and prepend application events generated during
// InitChain to BeginBlock events.
if mux.state.BlockHeight() == 0 {
response.Events = append(response.Events, mux.state.initEvents...)
result.Events = append(result.Events, mux.state.initEvents...)
}

// Collect and return events from the application's BeginBlock calls.
response.Events = append(response.Events, ctx.GetEvents()...)
result.Events = append(result.Events, ctx.GetEvents()...)
mux.processProvableEvents(ctx)

return response
// Update the proposal.
mux.state.proposal.resultsBeginBlock = &result

return result
}

func (mux *abciMux) notifyInvalidatedCheckTx(txHash hash.Hash, err error) {
Expand Down Expand Up @@ -715,7 +721,17 @@ func (mux *abciMux) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverT
ctx := mux.state.NewContext(api.ContextDeliverTx)
defer ctx.Close()

if err := mux.executeTx(ctx, req.Tx); err != nil {
var results types.ResponseDeliverTx
switch err := mux.executeTx(ctx, req.Tx); err {
case nil:
results = types.ResponseDeliverTx{
Code: types.CodeTypeOK,
Data: cbor.Marshal(ctx.Data()),
Events: ctx.GetEvents(),
GasWanted: int64(ctx.Gas().GasWanted()),
GasUsed: int64(ctx.Gas().GasUsed()),
}
default:
if api.IsUnavailableStateError(err) {
// Make sure to not commit any transactions which include results based on unavailable
// and/or corrupted state -- doing so can further corrupt state.
Expand All @@ -726,9 +742,7 @@ func (mux *abciMux) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverT
}
module, code := errors.Code(err)

mux.processProvableEvents(ctx)

return types.ResponseDeliverTx{
results = types.ResponseDeliverTx{
Codespace: module,
Code: code,
Log: err.Error(),
Expand All @@ -740,13 +754,10 @@ func (mux *abciMux) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverT

mux.processProvableEvents(ctx)

return types.ResponseDeliverTx{
Code: types.CodeTypeOK,
Data: cbor.Marshal(ctx.Data()),
Events: ctx.GetEvents(),
GasWanted: int64(ctx.Gas().GasWanted()),
GasUsed: int64(ctx.Gas().GasUsed()),
}
// Update the proposal.
mux.state.proposal.resultsDeliverTx = append(mux.state.proposal.resultsDeliverTx, &results)

return results
}

func (mux *abciMux) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
Expand All @@ -768,9 +779,9 @@ func (mux *abciMux) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
defer ctx.Close()

// Dispatch EndBlock to all applications.
resp := mux.BaseApplication.EndBlock(req)
results := mux.BaseApplication.EndBlock(req)
for _, app := range mux.appsByLexOrder {
newResp, err := app.EndBlock(ctx)
newResults, err := app.EndBlock(ctx)
if err != nil {
mux.logger.Error("EndBlock: fatal error in application",
"err", err,
Expand All @@ -779,7 +790,7 @@ func (mux *abciMux) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
panic(fmt.Errorf("mux: EndBlock: fatal error in application: '%s': %w", app.Name(), err))
}
if app.Blessed() {
resp = newResp
results = newResults
}
}

Expand All @@ -798,22 +809,25 @@ func (mux *abciMux) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
}

// Collect and return events.
resp.Events = ctx.GetEvents()
results.Events = ctx.GetEvents()
mux.processProvableEvents(ctx)

// Update version to what we are actually running.
resp.ConsensusParamUpdates = &cmtproto.ConsensusParams{
results.ConsensusParamUpdates = &cmtproto.ConsensusParams{
Version: &cmtproto.VersionParams{
App: version.CometBFTAppVersion,
},
}

// Update the proposal.
mux.state.proposal.resultsEndBlock = &results

// Validate system transactions included by the proposer.
if err := mux.validateSystemTxs(); err != nil {
panic(fmt.Errorf("proposed block has invalid system transactions: %w", err))
}

return resp
return results
}

func (mux *abciMux) Commit() types.ResponseCommit {
Expand Down
11 changes: 0 additions & 11 deletions go/consensus/cometbft/abci/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,6 @@ func (ps *proposalState) needsExecution() bool {
return ps.resultsBeginBlock == nil || ps.resultsDeliverTx == nil || ps.resultsEndBlock == nil
}

// setResults sets the proposal execution results.
func (ps *proposalState) setResults(
resultsBeginBlock *types.ResponseBeginBlock,
resultsDeliverTx []*types.ResponseDeliverTx,
resultsEndBlock *types.ResponseEndBlock,
) {
ps.resultsBeginBlock = resultsBeginBlock
ps.resultsDeliverTx = resultsDeliverTx
ps.resultsEndBlock = resultsEndBlock
}

type applicationState struct { // nolint: maligned
logger *logging.Logger

Expand Down
Loading
Loading