Skip to content
Merged
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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ path = "tests/memory.rs"
name = "mempool"
path = "tests/mempool.rs"

[[test]]
name = "bootstrap"
path = "tests/bootstrap.rs"

[features]
strict = ["dolos-cardano/strict"]
mithril = ["mithril-client"]
Expand Down
95 changes: 95 additions & 0 deletions crates/cardano/src/indexes/delta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,101 @@ impl CardanoIndexDeltaBuilder {
.push(Tag::new(archive::METADATA, label.to_be_bytes().to_vec()));
}

/// Index all archive entries for a single block.
///
/// Calls `start_block`, then iterates all transactions adding
/// tx hashes, metadata, inputs (with resolved UTxO lookups),
/// outputs (with script refs), witness scripts/datums, certs, and redeemers.
pub fn index_block(
&mut self,
block: &pallas::ledger::traverse::MultiEraBlock<'_>,
resolved_inputs: &std::collections::HashMap<TxoRef, crate::OwnedMultiEraOutput>,
) {
use pallas::ledger::{
primitives::conway::ScriptRef,
traverse::{ComputeHash as _, OriginalHash as _},
};

self.start_block(block.slot(), block.hash().to_vec(), Some(block.number()));

for tx in block.txs() {
self.add_tx_hash(tx.hash().to_vec());

for (label, _) in tx.metadata().collect::<Vec<_>>() {
self.add_metadata_label(label);
}

for input in tx.inputs() {
self.add_spent_input(&input);

let txo_ref: TxoRef = (&input).into();
if let Some(resolved) = resolved_inputs.get(&txo_ref) {
resolved.with_dependent(|_, output| {
if let Ok(addr) = output.address() {
self.add_address(&addr);
}
self.add_assets(&output.value());
if let Some(datum) = output.datum() {
self.add_datum(&datum);
}
});
}
}

for (_, output) in tx.produces() {
if let Ok(addr) = output.address() {
self.add_address(&addr);
}
self.add_assets(&output.value());
if let Some(datum) = output.datum() {
self.add_datum(&datum);
}

if let Some(script_ref) = output.script_ref() {
match script_ref {
ScriptRef::NativeScript(script) => {
self.add_script_hash(script.original_hash().to_vec());
}
ScriptRef::PlutusV1Script(script) => {
self.add_script_hash(script.compute_hash().to_vec());
}
ScriptRef::PlutusV2Script(script) => {
self.add_script_hash(script.compute_hash().to_vec());
}
ScriptRef::PlutusV3Script(script) => {
self.add_script_hash(script.compute_hash().to_vec());
}
}
}
}

for script in tx.native_scripts() {
self.add_script_hash(script.original_hash().to_vec());
}
for script in tx.plutus_v1_scripts() {
self.add_script_hash(script.compute_hash().to_vec());
}
for script in tx.plutus_v2_scripts() {
self.add_script_hash(script.compute_hash().to_vec());
}
for script in tx.plutus_v3_scripts() {
self.add_script_hash(script.compute_hash().to_vec());
}

for datum in tx.plutus_data() {
self.add_datum_hash(datum.original_hash().to_vec());
}

for cert in tx.certs() {
self.add_cert(&cert);
}

for redeemer in tx.redeemers() {
self.add_datum_hash(redeemer.data().compute_hash().to_vec());
}
}
}

/// Build the final `IndexDelta`.
pub fn build(self) -> IndexDelta {
self.delta
Expand Down
38 changes: 38 additions & 0 deletions crates/cardano/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,44 @@ impl dolos_core::ChainLogic for CardanoLogic {
})
}

fn compute_catchup(
block: &dolos_core::Cbor,
inputs: &std::collections::HashMap<dolos_core::TxoRef, Arc<EraCbor>>,
point: ChainPoint,
) -> Result<dolos_core::CatchUpBlockData, ChainError> {
let block_arc = Arc::new(block.clone());
let blockd = OwnedMultiEraBlock::decode(block_arc)?;
let blockv = blockd.view();

let decoded_inputs: std::collections::HashMap<_, _> = inputs
.iter()
.map(|(k, v)| {
let out = (k.clone(), OwnedMultiEraOutput::decode(v.clone())?);
Result::<_, ChainError>::Ok(out)
})
.collect::<Result<_, _>>()?;

let utxo_delta = crate::utxoset::compute_apply_delta(blockv, &decoded_inputs)
.map_err(ChainError::from)?;

let mut builder = crate::indexes::CardanoIndexDeltaBuilder::new(point);

// UTxO filter changes
builder.add_produced_utxos_from_delta(&utxo_delta);
builder.add_consumed_utxos_from_delta(&utxo_delta);

// Archive indexes (shared logic)
builder.index_block(blockv, &decoded_inputs);

let tx_hashes = blockv.txs().iter().map(|tx| tx.hash()).collect();

Ok(dolos_core::CatchUpBlockData {
utxo_delta,
index_delta: builder.build(),
tx_hashes,
})
}

fn decode_utxo(&self, utxo: Arc<EraCbor>) -> Result<Self::Utxo, ChainError> {
let out = OwnedMultiEraOutput::decode(utxo)?;

Expand Down
98 changes: 2 additions & 96 deletions crates/cardano/src/roll/batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@ use dolos_core::{
NsKey, RawBlock, RawUtxoMap, StateError, StateStore as _, StateWriter as _, TxoRef,
UtxoSetDelta, WalStore as _,
};
use pallas::ledger::{
primitives::conway::ScriptRef,
traverse::{ComputeHash as _, OriginalHash as _},
};

use crate::indexes::CardanoIndexDeltaBuilder;
use crate::{CardanoDelta, CardanoEntity, CardanoLogic, OwnedMultiEraBlock, OwnedMultiEraOutput};

Expand Down Expand Up @@ -325,17 +320,13 @@ impl WorkBatch {
let mut builder = CardanoIndexDeltaBuilder::new(self.last_point());

for work_block in self.blocks.iter() {
let point = work_block.point();
let raw = work_block.raw();

// Decode block for tag extraction
let Ok(block) = MultiEraBlock::decode(&raw) else {
continue;
};

// Start archive delta for this block
builder.start_block(point.slot(), block.hash().to_vec(), Some(block.number()));

// Process UTxO delta for filter indexes
if let Some(utxo_delta) = &work_block.utxo_delta {
// Produced UTxOs
Expand Down Expand Up @@ -367,93 +358,8 @@ impl WorkBatch {
}
}

// Process transactions for archive indexes
for tx in block.txs() {
builder.add_tx_hash(tx.hash().to_vec());

// Metadata labels
for (label, _) in tx.metadata().collect::<Vec<_>>() {
builder.add_metadata_label(label);
}

// Inputs (spent UTxOs)
for input in tx.inputs() {
builder.add_spent_input(&input);

// Try to get resolved input for address/asset tags
let txo_ref: TxoRef = (&input).into();
if let Some(resolved) = self.utxos_decoded.get(&txo_ref) {
resolved.with_dependent(|_, output| {
if let Ok(addr) = output.address() {
builder.add_address(&addr);
}
builder.add_assets(&output.value());
if let Some(datum) = output.datum() {
builder.add_datum(&datum);
}
});
}
}

// Outputs
for (_, output) in tx.produces() {
if let Ok(addr) = output.address() {
builder.add_address(&addr);
}
builder.add_assets(&output.value());
if let Some(datum) = output.datum() {
builder.add_datum(&datum);
}

if let Some(script_ref) = output.script_ref() {
match script_ref {
ScriptRef::NativeScript(script) => {
builder.add_script_hash(script.original_hash().to_vec());
}
ScriptRef::PlutusV1Script(script) => {
builder.add_script_hash(script.compute_hash().to_vec());
}
ScriptRef::PlutusV2Script(script) => {
builder.add_script_hash(script.compute_hash().to_vec());
}
ScriptRef::PlutusV3Script(script) => {
builder.add_script_hash(script.compute_hash().to_vec());
}
}
}
}

// Witness scripts
{
for script in tx.native_scripts() {
builder.add_script_hash(script.original_hash().to_vec());
}
for script in tx.plutus_v1_scripts() {
builder.add_script_hash(script.compute_hash().to_vec());
}
for script in tx.plutus_v2_scripts() {
builder.add_script_hash(script.compute_hash().to_vec());
}
for script in tx.plutus_v3_scripts() {
builder.add_script_hash(script.compute_hash().to_vec());
}
}

// Witness datums
for datum in tx.plutus_data() {
builder.add_datum_hash(datum.original_hash().to_vec());
}

// Certificates
for cert in tx.certs() {
builder.add_cert(&cert);
}

// Redeemers
for redeemer in tx.redeemers() {
builder.add_datum_hash(redeemer.data().compute_hash().to_vec());
}
}
// Archive indexes (shared logic)
builder.index_block(&block, &self.utxos_decoded);
}

builder.build()
Expand Down
Loading
Loading