Skip to content

Commit 915c335

Browse files
prestwichclaude
andauthored
feat(cold): add get_receipt_with_context composite query (#19)
* feat(cold): add get_receipt_with_context composite query Add a single-roundtrip method to ColdStorage that returns everything needed to build an RPC receipt response: the header, transaction, receipt, confirmation metadata, and prior cumulative gas. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(cold-mdbx): use single MDBX transaction per read method Rewrite get_header, get_headers, get_transaction, get_receipt, and get_receipt_with_context to each open a single read transaction with inline cursor lookups instead of delegating to helpers that each open their own transaction. Removes 9 now-unused private helpers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(cold): simplify ReceiptContext with Confirmed<Receipt> Use Confirmed<Receipt> instead of separate receipt + meta fields, add default trait impl for get_receipt_with_context, and eliminate MDBX get_receipt_inner duplication by delegating to the composite method. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor(cold): remove tag-based block lookups BlockTag and HeaderSpecifier::Tag were dead code — the only real consumer of "latest block" uses the dedicated get_latest_block() method, and Finalized/Safe were never written. Removing them simplifies the trait contract and reduces implementation burden for backends. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 21821e4 commit 915c335

File tree

10 files changed

+314
-244
lines changed

10 files changed

+314
-244
lines changed

crates/cold-mdbx/src/backend.rs

Lines changed: 152 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use crate::{
1010
};
1111
use alloy::{consensus::Header, primitives::BlockNumber};
1212
use signet_cold::{
13-
BlockData, BlockTag, ColdResult, ColdStorage, Confirmed, HeaderSpecifier, ReceiptSpecifier,
14-
SignetEventsSpecifier, TransactionSpecifier, ZenithHeaderSpecifier,
13+
BlockData, ColdResult, ColdStorage, Confirmed, HeaderSpecifier, ReceiptContext,
14+
ReceiptSpecifier, SignetEventsSpecifier, TransactionSpecifier, ZenithHeaderSpecifier,
1515
};
1616
use signet_hot::{
1717
KeySer, MAX_KEY_SIZE, ValSer,
@@ -124,116 +124,111 @@ impl MdbxColdBackend {
124124
Ok(())
125125
}
126126

127-
fn resolve_tag(&self, tag: BlockTag) -> Result<Option<BlockNumber>, MdbxColdError> {
128-
let key = match tag {
129-
BlockTag::Latest => MetadataKey::LatestBlock,
130-
BlockTag::Finalized => MetadataKey::FinalizedBlock,
131-
BlockTag::Safe => MetadataKey::SafeBlock,
132-
BlockTag::Earliest => MetadataKey::EarliestBlock,
133-
};
134-
self.get_metadata(key)
135-
}
136-
137127
fn get_metadata(&self, key: MetadataKey) -> Result<Option<BlockNumber>, MdbxColdError> {
138128
let tx = self.env.tx()?;
139129
Ok(TableTraverse::<ColdMetadata, _>::exact(&mut tx.new_cursor::<ColdMetadata>()?, &key)?)
140130
}
141131

142-
fn get_block_by_hash(
143-
&self,
144-
hash: alloy::primitives::B256,
145-
) -> Result<Option<BlockNumber>, MdbxColdError> {
132+
fn get_header_inner(&self, spec: HeaderSpecifier) -> Result<Option<Header>, MdbxColdError> {
146133
let tx = self.env.tx()?;
147-
Ok(TableTraverse::<ColdBlockHashIndex, _>::exact(
148-
&mut tx.new_cursor::<ColdBlockHashIndex>()?,
149-
&hash,
134+
let block_num = match spec {
135+
HeaderSpecifier::Number(n) => n,
136+
HeaderSpecifier::Hash(h) => {
137+
let Some(n) = TableTraverse::<ColdBlockHashIndex, _>::exact(
138+
&mut tx.new_cursor::<ColdBlockHashIndex>()?,
139+
&h,
140+
)?
141+
else {
142+
return Ok(None);
143+
};
144+
n
145+
}
146+
};
147+
Ok(TableTraverse::<ColdHeaders, _>::exact(
148+
&mut tx.new_cursor::<ColdHeaders>()?,
149+
&block_num,
150150
)?)
151151
}
152152

153-
fn get_tx_location(
153+
fn get_headers_inner(
154154
&self,
155-
hash: alloy::primitives::B256,
156-
) -> Result<Option<TxLocation>, MdbxColdError> {
155+
specs: Vec<HeaderSpecifier>,
156+
) -> Result<Vec<Option<Header>>, MdbxColdError> {
157157
let tx = self.env.tx()?;
158-
Ok(TableTraverse::<ColdTxHashIndex, _>::exact(
159-
&mut tx.new_cursor::<ColdTxHashIndex>()?,
160-
&hash,
161-
)?)
162-
}
163-
164-
fn resolve_header_spec(
165-
&self,
166-
spec: HeaderSpecifier,
167-
) -> Result<Option<BlockNumber>, MdbxColdError> {
168-
match spec {
169-
HeaderSpecifier::Number(n) => Ok(Some(n)),
170-
HeaderSpecifier::Hash(h) => self.get_block_by_hash(h),
171-
HeaderSpecifier::Tag(tag) => self.resolve_tag(tag),
172-
}
158+
specs
159+
.into_iter()
160+
.map(|spec| {
161+
let block_num = match spec {
162+
HeaderSpecifier::Number(n) => Some(n),
163+
HeaderSpecifier::Hash(h) => TableTraverse::<ColdBlockHashIndex, _>::exact(
164+
&mut tx.new_cursor::<ColdBlockHashIndex>()?,
165+
&h,
166+
)?,
167+
};
168+
block_num
169+
.map(|n| {
170+
TableTraverse::<ColdHeaders, _>::exact(
171+
&mut tx.new_cursor::<ColdHeaders>()?,
172+
&n,
173+
)
174+
})
175+
.transpose()
176+
.map(Option::flatten)
177+
})
178+
.collect::<Result<_, _>>()
179+
.map_err(Into::into)
173180
}
174181

175-
fn resolve_tx_spec(
182+
fn get_transaction_inner(
176183
&self,
177184
spec: TransactionSpecifier,
178-
) -> Result<Option<(BlockNumber, u64)>, MdbxColdError> {
179-
match spec {
185+
) -> Result<Option<Confirmed<TransactionSigned>>, MdbxColdError> {
186+
let tx = self.env.tx()?;
187+
let (block, index) = match spec {
180188
TransactionSpecifier::Hash(h) => {
181-
self.get_tx_location(h).map(|opt| opt.map(|loc| (loc.block, loc.index)))
189+
let Some(loc) = TableTraverse::<ColdTxHashIndex, _>::exact(
190+
&mut tx.new_cursor::<ColdTxHashIndex>()?,
191+
&h,
192+
)?
193+
else {
194+
return Ok(None);
195+
};
196+
(loc.block, loc.index)
182197
}
183-
TransactionSpecifier::BlockAndIndex { block, index } => Ok(Some((block, index))),
198+
TransactionSpecifier::BlockAndIndex { block, index } => (block, index),
184199
TransactionSpecifier::BlockHashAndIndex { block_hash, index } => {
185-
self.get_block_by_hash(block_hash).map(|opt| opt.map(|b| (b, index)))
200+
let Some(block) = TableTraverse::<ColdBlockHashIndex, _>::exact(
201+
&mut tx.new_cursor::<ColdBlockHashIndex>()?,
202+
&block_hash,
203+
)?
204+
else {
205+
return Ok(None);
206+
};
207+
(block, index)
186208
}
187-
}
188-
}
189-
190-
fn resolve_receipt_spec(
191-
&self,
192-
spec: ReceiptSpecifier,
193-
) -> Result<Option<(BlockNumber, u64)>, MdbxColdError> {
194-
match spec {
195-
ReceiptSpecifier::TxHash(h) => {
196-
self.get_tx_location(h).map(|opt| opt.map(|loc| (loc.block, loc.index)))
197-
}
198-
ReceiptSpecifier::BlockAndIndex { block, index } => Ok(Some((block, index))),
199-
}
200-
}
201-
202-
fn get_header_by_number(
203-
&self,
204-
block_num: BlockNumber,
205-
) -> Result<Option<Header>, MdbxColdError> {
206-
let tx = self.env.tx()?;
207-
Ok(TableTraverse::<ColdHeaders, _>::exact(
208-
&mut tx.new_cursor::<ColdHeaders>()?,
209-
&block_num,
210-
)?)
211-
}
212-
213-
fn get_transaction_by_location(
214-
&self,
215-
block: BlockNumber,
216-
index: u64,
217-
) -> Result<Option<TransactionSigned>, MdbxColdError> {
218-
let tx = self.env.tx()?;
219-
Ok(DualTableTraverse::<ColdTransactions, _>::exact_dual(
209+
};
210+
let Some(signed_tx) = DualTableTraverse::<ColdTransactions, _>::exact_dual(
220211
&mut tx.new_cursor::<ColdTransactions>()?,
221212
&block,
222213
&index,
223-
)?)
214+
)?
215+
else {
216+
return Ok(None);
217+
};
218+
let Some(header) =
219+
TableTraverse::<ColdHeaders, _>::exact(&mut tx.new_cursor::<ColdHeaders>()?, &block)?
220+
else {
221+
return Ok(None);
222+
};
223+
let meta = ConfirmationMeta::new(block, header.hash_slow(), index);
224+
Ok(Some(Confirmed::new(signed_tx, meta)))
224225
}
225226

226-
fn get_receipt_by_location(
227+
fn get_receipt_inner(
227228
&self,
228-
block: BlockNumber,
229-
index: u64,
230-
) -> Result<Option<Receipt>, MdbxColdError> {
231-
let tx = self.env.tx()?;
232-
Ok(DualTableTraverse::<ColdReceipts, _>::exact_dual(
233-
&mut tx.new_cursor::<ColdReceipts>()?,
234-
&block,
235-
&index,
236-
)?)
229+
spec: ReceiptSpecifier,
230+
) -> Result<Option<Confirmed<Receipt>>, MdbxColdError> {
231+
Ok(self.get_receipt_with_context_inner(spec)?.map(|ctx| ctx.receipt))
237232
}
238233

239234
fn get_zenith_header_by_number(
@@ -374,15 +369,6 @@ impl MdbxColdBackend {
374369
&current_latest.map_or(block, |prev| prev.max(block)),
375370
)?;
376371

377-
let current_earliest: Option<BlockNumber> = TableTraverse::<ColdMetadata, _>::exact(
378-
&mut tx.new_cursor::<ColdMetadata>()?,
379-
&MetadataKey::EarliestBlock,
380-
)?;
381-
tx.queue_put::<ColdMetadata>(
382-
&MetadataKey::EarliestBlock,
383-
&current_earliest.map_or(block, |prev| prev.min(block)),
384-
)?;
385-
386372
tx.raw_commit()?;
387373
Ok(())
388374
}
@@ -453,44 +439,81 @@ impl MdbxColdBackend {
453439
tx.raw_commit()?;
454440
Ok(())
455441
}
442+
443+
fn get_receipt_with_context_inner(
444+
&self,
445+
spec: ReceiptSpecifier,
446+
) -> Result<Option<ReceiptContext>, MdbxColdError> {
447+
let tx = self.env.tx()?;
448+
let (block, index) = match spec {
449+
ReceiptSpecifier::TxHash(h) => {
450+
let Some(loc) = TableTraverse::<ColdTxHashIndex, _>::exact(
451+
&mut tx.new_cursor::<ColdTxHashIndex>()?,
452+
&h,
453+
)?
454+
else {
455+
return Ok(None);
456+
};
457+
(loc.block, loc.index)
458+
}
459+
ReceiptSpecifier::BlockAndIndex { block, index } => (block, index),
460+
};
461+
let Some(header) =
462+
TableTraverse::<ColdHeaders, _>::exact(&mut tx.new_cursor::<ColdHeaders>()?, &block)?
463+
else {
464+
return Ok(None);
465+
};
466+
let Some(receipt) = DualTableTraverse::<ColdReceipts, _>::exact_dual(
467+
&mut tx.new_cursor::<ColdReceipts>()?,
468+
&block,
469+
&index,
470+
)?
471+
else {
472+
return Ok(None);
473+
};
474+
let Some(transaction) = DualTableTraverse::<ColdTransactions, _>::exact_dual(
475+
&mut tx.new_cursor::<ColdTransactions>()?,
476+
&block,
477+
&index,
478+
)?
479+
else {
480+
return Ok(None);
481+
};
482+
483+
let prior_cumulative_gas = index
484+
.checked_sub(1)
485+
.map(|prev| {
486+
DualTableTraverse::<ColdReceipts, _>::exact_dual(
487+
&mut tx.new_cursor::<ColdReceipts>()?,
488+
&block,
489+
&prev,
490+
)
491+
})
492+
.transpose()?
493+
.flatten()
494+
.map(|r: Receipt| r.inner.cumulative_gas_used)
495+
.unwrap_or(0);
496+
497+
let meta = ConfirmationMeta::new(block, header.hash_slow(), index);
498+
let confirmed_receipt = Confirmed::new(receipt, meta);
499+
Ok(Some(ReceiptContext::new(header, transaction, confirmed_receipt, prior_cumulative_gas)))
500+
}
456501
}
457502

458503
impl ColdStorage for MdbxColdBackend {
459504
async fn get_header(&self, spec: HeaderSpecifier) -> ColdResult<Option<Header>> {
460-
let Some(block_num) = self.resolve_header_spec(spec)? else {
461-
return Ok(None);
462-
};
463-
Ok(self.get_header_by_number(block_num)?)
505+
Ok(self.get_header_inner(spec)?)
464506
}
465507

466508
async fn get_headers(&self, specs: Vec<HeaderSpecifier>) -> ColdResult<Vec<Option<Header>>> {
467-
specs
468-
.into_iter()
469-
.map(|spec| {
470-
self.resolve_header_spec(spec)?
471-
.map(|n| self.get_header_by_number(n))
472-
.transpose()
473-
.map(Option::flatten)
474-
})
475-
.collect::<Result<_, MdbxColdError>>()
476-
.map_err(Into::into)
509+
Ok(self.get_headers_inner(specs)?)
477510
}
478511

479512
async fn get_transaction(
480513
&self,
481514
spec: TransactionSpecifier,
482515
) -> ColdResult<Option<Confirmed<TransactionSigned>>> {
483-
let Some((block, index)) = self.resolve_tx_spec(spec)? else {
484-
return Ok(None);
485-
};
486-
let Some(tx) = self.get_transaction_by_location(block, index)? else {
487-
return Ok(None);
488-
};
489-
let Some(header) = self.get_header_by_number(block)? else {
490-
return Ok(None);
491-
};
492-
let meta = ConfirmationMeta::new(block, header.hash_slow(), index);
493-
Ok(Some(Confirmed::new(tx, meta)))
516+
Ok(self.get_transaction_inner(spec)?)
494517
}
495518

496519
async fn get_transactions_in_block(
@@ -505,17 +528,7 @@ impl ColdStorage for MdbxColdBackend {
505528
}
506529

507530
async fn get_receipt(&self, spec: ReceiptSpecifier) -> ColdResult<Option<Confirmed<Receipt>>> {
508-
let Some((block, index)) = self.resolve_receipt_spec(spec)? else {
509-
return Ok(None);
510-
};
511-
let Some(receipt) = self.get_receipt_by_location(block, index)? else {
512-
return Ok(None);
513-
};
514-
let Some(header) = self.get_header_by_number(block)? else {
515-
return Ok(None);
516-
};
517-
let meta = ConfirmationMeta::new(block, header.hash_slow(), index);
518-
Ok(Some(Confirmed::new(receipt, meta)))
531+
Ok(self.get_receipt_inner(spec)?)
519532
}
520533

521534
async fn get_receipts_in_block(&self, block: BlockNumber) -> ColdResult<Vec<Receipt>> {
@@ -565,6 +578,13 @@ impl ColdStorage for MdbxColdBackend {
565578
Ok(self.get_metadata(MetadataKey::LatestBlock)?)
566579
}
567580

581+
async fn get_receipt_with_context(
582+
&self,
583+
spec: ReceiptSpecifier,
584+
) -> ColdResult<Option<ReceiptContext>> {
585+
Ok(self.get_receipt_with_context_inner(spec)?)
586+
}
587+
568588
async fn append_block(&self, data: BlockData) -> ColdResult<()> {
569589
Ok(self.append_block_inner(data)?)
570590
}

0 commit comments

Comments
 (0)