Skip to content

Commit 4023dbf

Browse files
committed
go/consensus/cometbft/abci/system: Add results hash to system tx
The results hash was added to the system transaction so that stateless clients can partially verify the latest block’s results.
1 parent 3ca9d84 commit 4023dbf

File tree

6 files changed

+71
-23
lines changed

6 files changed

+71
-23
lines changed

.changelog/6289.breaking.1.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
go/consensus/cometbft/abci/system: Add results hash to system tx
2+
3+
The results hash was added to the system transaction so that stateless
4+
clients can partially verify the latest block’s results.

go/consensus/api/meta.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,18 @@ type BlockMetadata struct {
2525
StateRoot hash.Hash `json:"state_root"`
2626
// EventsRoot is the root hash of all events emitted in the block.
2727
EventsRoot []byte `json:"events_root"`
28+
// ResultsHash is the hash of transaction results in the block.
29+
ResultsHash []byte `json:"results_hash"`
2830
}
2931

3032
// ValidateBasic performs basic block metadata structure validation.
3133
func (bm *BlockMetadata) ValidateBasic() error {
3234
if len(bm.EventsRoot) != 32 {
3335
return fmt.Errorf("malformed events root")
3436
}
37+
if bm.ResultsHash != nil && len(bm.ResultsHash) != 32 {
38+
return fmt.Errorf("malformed results hash")
39+
}
3540
return nil
3641
}
3742

go/consensus/cometbft/abci/mux.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,15 @@ func (mux *abciMux) PrepareProposal(req types.RequestPrepareProposal) types.Resp
433433
return types.ResponsePrepareProposal{}
434434
}
435435

436-
// Inject system transactions at the end of the block.
437-
systemTxs, systemTxResults, err := mux.prepareSystemTxs()
436+
// Inject system transactions and their results at the end of the block.
437+
//
438+
// It is important to inject the results first, so that the results hash
439+
// in the block metadata transaction includes all results, including results
440+
// from system transactions.
441+
systemTxResults := mux.prepareSystemTxResults()
442+
mux.state.proposal.resultsDeliverTx = append(mux.state.proposal.resultsDeliverTx, systemTxResults...)
443+
444+
systemTxs, err := mux.prepareSystemTxs()
438445
if err != nil {
439446
mux.logger.Error("failed to prepare system transactions",
440447
"height", req.Height,
@@ -444,7 +451,6 @@ func (mux *abciMux) PrepareProposal(req types.RequestPrepareProposal) types.Resp
444451
return types.ResponsePrepareProposal{}
445452
}
446453
txs = append(txs, systemTxs...)
447-
mux.state.proposal.resultsDeliverTx = append(mux.state.proposal.resultsDeliverTx, systemTxResults...)
448454

449455
// Record proposal inputs so we can compare in ProcessProposal.
450456
p := mux.state.proposal

go/consensus/cometbft/abci/system.go

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77

88
"github.com/cometbft/cometbft/abci/types"
9+
cmttypes "github.com/cometbft/cometbft/types"
910

1011
"github.com/oasisprotocol/oasis-core/go/common/cbor"
1112
consensus "github.com/oasisprotocol/oasis-core/go/consensus/api"
@@ -16,47 +17,62 @@ import (
1617
"github.com/oasisprotocol/oasis-core/go/upgrade/migrations"
1718
)
1819

19-
// prepareSystemTxs prepares a list of system transactions to be included in a proposed block in
20-
// case where the node is currently the block proposer.
21-
func (mux *abciMux) prepareSystemTxs() ([][]byte, []*types.ResponseDeliverTx, error) {
22-
var (
23-
systemTxs [][]byte
24-
systemTxResults []*types.ResponseDeliverTx
25-
)
20+
// prepareSystemTxs prepares a list of system transactions to be included
21+
// in a proposed block in case where the node is currently the block proposer.
22+
func (mux *abciMux) prepareSystemTxs() ([][]byte, error) {
23+
blockMetaTx, err := mux.prepareBlockMetaTx()
24+
if err != nil {
25+
return nil, fmt.Errorf("failed to prepare block metadata transaction: %w", err)
26+
}
27+
return [][]byte{blockMetaTx}, nil
28+
}
29+
30+
// prepareSystemTxResults prepares a list of system transaction results to be included
31+
// in a proposed block in case where the node is currently the block proposer.
32+
func (mux *abciMux) prepareSystemTxResults() []*types.ResponseDeliverTx {
33+
blockMetaResults := mux.prepareBlockMetaTxResults()
34+
return []*types.ResponseDeliverTx{blockMetaResults}
35+
}
2636

27-
// Append block metadata as a system transaction.
37+
// prepareBlockMetaTx prepares block metadata system transaction.
38+
func (mux *abciMux) prepareBlockMetaTx() ([]byte, error) {
2839
stateRoot, err := mux.state.workingStateRoot()
2940
if err != nil {
30-
return nil, nil, fmt.Errorf("failed to compute working state root: %w", err)
41+
return nil, fmt.Errorf("failed to compute working state root: %w", err)
3142
}
3243
eventsRoot, err := mux.computeEventsRoot()
3344
if err != nil {
34-
return nil, nil, fmt.Errorf("failed to compute events root: %w", err)
45+
return nil, fmt.Errorf("failed to compute events root: %w", err)
3546
}
47+
resultsHash := mux.computeResultsHash()
3648

3749
blockMeta := consensus.NewBlockMetadataTx(&consensus.BlockMetadata{
38-
StateRoot: stateRoot,
39-
EventsRoot: eventsRoot,
50+
StateRoot: stateRoot,
51+
EventsRoot: eventsRoot,
52+
ResultsHash: resultsHash,
4053
})
4154
sigBlockMeta, err := transaction.Sign(mux.state.identity.ConsensusSigner, blockMeta)
4255
if err != nil {
43-
return nil, nil, fmt.Errorf("failed to sign block metadata transaction: %w", err)
56+
return nil, fmt.Errorf("failed to sign block metadata transaction: %w", err)
4457
}
4558
sigBlockMetaRaw := cbor.Marshal(sigBlockMeta)
4659
if l := len(sigBlockMetaRaw); l > consensus.BlockMetadataMaxSize {
4760
mux.logger.Error("serialized block metadata larger than maximum allowed size",
4861
"meta_size", l,
4962
"max_meta_size", consensus.BlockMetadataMaxSize,
5063
)
51-
return nil, nil, fmt.Errorf("serialized block metadata would be oversized")
64+
return nil, fmt.Errorf("serialized block metadata would be oversized")
5265
}
53-
systemTxs = append(systemTxs, sigBlockMetaRaw)
54-
systemTxResults = append(systemTxResults, &types.ResponseDeliverTx{
66+
67+
return sigBlockMetaRaw, nil
68+
}
69+
70+
// prepareBlockMetaTxResults prepares block metadata system transaction results.
71+
func (mux *abciMux) prepareBlockMetaTxResults() *types.ResponseDeliverTx {
72+
return &types.ResponseDeliverTx{
5573
Code: types.CodeTypeOK,
5674
Data: cbor.Marshal(nil),
57-
})
58-
59-
return systemTxs, systemTxResults, nil
75+
}
6076
}
6177

6278
// processSystemTx processes a system transaction in DeliverTx context.
@@ -97,7 +113,7 @@ func (mux *abciMux) validateSystemTxs() error {
97113
for _, tx := range mux.state.blockCtx.SystemTransactions {
98114
switch tx.Method {
99115
case consensus.MethodMeta:
100-
// Block metadata, verify state root.
116+
// Decode block metadata.
101117
if hasBlockMetadata {
102118
return fmt.Errorf("duplicate block metadata in block")
103119
}
@@ -129,9 +145,16 @@ func (mux *abciMux) validateSystemTxs() error {
129145
return fmt.Errorf("invalid events root in block metadata (expected: %x got: %x)", eventsRoot, meta.EventsRoot)
130146
}
131147

148+
// Verify results hash.
149+
resultsHash := mux.computeResultsHash()
150+
if !bytes.Equal(resultsHash, meta.ResultsHash) {
151+
return fmt.Errorf("invalid results hash in block metadata (expected: %x got: %x)", resultsHash, meta.ResultsHash)
152+
}
153+
132154
mux.logger.Debug("validated block metadata",
133155
"state_root", meta.StateRoot,
134156
"events_root", hex.EncodeToString(eventsRoot),
157+
"results_hash", hex.EncodeToString(resultsHash),
135158
)
136159
default:
137160
return fmt.Errorf("unknown system method: %s", tx.Method)
@@ -171,3 +194,10 @@ func (mux *abciMux) computeEventsRoot() ([]byte, error) {
171194

172195
return merkle.RootHash(events), nil
173196
}
197+
198+
func (mux *abciMux) computeResultsHash() []byte {
199+
if !mux.state.ConsensusParameters().IsFeatureVersion(migrations.Version256) {
200+
return nil
201+
}
202+
return cmttypes.NewResults(mux.state.proposal.resultsDeliverTx).Hash()
203+
}

go/upgrade/migrations/consensus_256.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
// are allowed to query runtime key shares.
1717
// - An updated events root in the block metadata system transaction to capture all events
1818
// emitted in the block.
19+
// - A results hash in the block metadata system transaction.
1920
const Consensus256 = "consensus256"
2021

2122
// Version256 is the Oasis Core 25.6 version.

runtime/src/consensus/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,6 @@ pub struct BlockMetadata {
4949
pub state_root: Hash,
5050
// Root hash of all events emitted in the block.
5151
pub events_root: Vec<u8>,
52+
// Hash of transaction results in the block.
53+
pub results_hash: Vec<u8>,
5254
}

0 commit comments

Comments
 (0)