diff --git a/crates/cardano/src/indexes/delta.rs b/crates/cardano/src/indexes/delta.rs index 046feb35..babb9880 100644 --- a/crates/cardano/src/indexes/delta.rs +++ b/crates/cardano/src/indexes/delta.rs @@ -208,6 +208,13 @@ impl CardanoIndexDeltaBuilder { .push(Tag::new(archive::SPENT_TXO, bytes)); } + /// Add a script hash to the current block. + pub fn add_script_hash(&mut self, hash: Vec) { + self.current_block() + .tags + .push(Tag::new(archive::SCRIPT, hash)); + } + /// Add certificate tags to the current block. pub fn add_cert(&mut self, cert: &MultiEraCert) { if let Some(cred) = pallas_extras::cert_as_stake_registration(cert) { diff --git a/crates/cardano/src/indexes/mod.rs b/crates/cardano/src/indexes/mod.rs index 34864765..237576d4 100644 --- a/crates/cardano/src/indexes/mod.rs +++ b/crates/cardano/src/indexes/mod.rs @@ -15,4 +15,4 @@ mod query; pub use delta::{index_delta_from_utxo_delta, CardanoIndexDeltaBuilder}; pub use dimensions::{archive as archive_dimensions, utxo as utxo_dimensions}; pub use ext::CardanoIndexExt; -pub use query::{AsyncCardanoQueryExt, SlotOrder}; +pub use query::{AsyncCardanoQueryExt, ScriptData, ScriptLanguage, SlotOrder}; diff --git a/crates/cardano/src/indexes/query.rs b/crates/cardano/src/indexes/query.rs index b7533ef3..df3224ca 100644 --- a/crates/cardano/src/indexes/query.rs +++ b/crates/cardano/src/indexes/query.rs @@ -4,7 +4,7 @@ use pallas::{ crypto::hash::Hash, ledger::{ - primitives::conway::{DatumOption, PlutusData}, + primitives::conway::{DatumOption, PlutusData, ScriptRef}, traverse::{ComputeHash, MultiEraBlock, OriginalHash}, }, }; @@ -28,6 +28,20 @@ pub enum SlotOrder { Desc, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ScriptLanguage { + Native, + PlutusV1, + PlutusV2, + PlutusV3, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ScriptData { + pub language: ScriptLanguage, + pub script: Vec, +} + #[async_trait::async_trait] pub trait AsyncCardanoQueryExt { fn blocks_by_address_stream( @@ -124,6 +138,11 @@ pub trait AsyncCardanoQueryExt { async fn get_datum(&self, datum_hash: &Hash<32>) -> Result>, DomainError>; + async fn script_by_hash( + &self, + script_hash: &Hash<28>, + ) -> Result, DomainError>; + async fn tx_by_spent_txo(&self, spent_txo: &[u8]) -> Result, DomainError>; } @@ -356,6 +375,110 @@ where .await } + async fn script_by_hash( + &self, + script_hash: &Hash<28>, + ) -> Result, DomainError> { + let end_slot = self + .run_blocking(move |domain| { + Ok(domain + .archive() + .get_tip()? + .map(|(slot, _)| slot) + .unwrap_or_default()) + }) + .await?; + + let script_hash = *script_hash; + find_first_by_tag( + self, + archive::SCRIPT, + script_hash.as_slice().to_vec(), + 0, + end_slot, + |block| { + for tx in block.txs() { + for script in tx.native_scripts() { + if script.original_hash() == script_hash { + return Some(ScriptData { + language: ScriptLanguage::Native, + script: script.raw_cbor().to_vec(), + }); + } + } + + for script in tx.plutus_v1_scripts() { + if script.compute_hash() == script_hash { + return Some(ScriptData { + language: ScriptLanguage::PlutusV1, + script: script.as_ref().to_vec(), + }); + } + } + + for script in tx.plutus_v2_scripts() { + if script.compute_hash() == script_hash { + return Some(ScriptData { + language: ScriptLanguage::PlutusV2, + script: script.as_ref().to_vec(), + }); + } + } + + for script in tx.plutus_v3_scripts() { + if script.compute_hash() == script_hash { + return Some(ScriptData { + language: ScriptLanguage::PlutusV3, + script: script.as_ref().to_vec(), + }); + } + } + + for (_, output) in tx.produces() { + if let Some(script_ref) = output.script_ref() { + match script_ref { + ScriptRef::NativeScript(script) => { + if script.original_hash() == script_hash { + return Some(ScriptData { + language: ScriptLanguage::Native, + script: script.raw_cbor().to_vec(), + }); + } + } + ScriptRef::PlutusV1Script(script) => { + if script.compute_hash() == script_hash { + return Some(ScriptData { + language: ScriptLanguage::PlutusV1, + script: script.as_ref().to_vec(), + }); + } + } + ScriptRef::PlutusV2Script(script) => { + if script.compute_hash() == script_hash { + return Some(ScriptData { + language: ScriptLanguage::PlutusV2, + script: script.as_ref().to_vec(), + }); + } + } + ScriptRef::PlutusV3Script(script) => { + if script.compute_hash() == script_hash { + return Some(ScriptData { + language: ScriptLanguage::PlutusV3, + script: script.as_ref().to_vec(), + }); + } + } + } + } + } + } + None + }, + ) + .await + } + async fn tx_by_spent_txo(&self, spent_txo: &[u8]) -> Result, DomainError> { let spent = spent_txo.to_vec(); diff --git a/crates/cardano/src/roll/batch.rs b/crates/cardano/src/roll/batch.rs index da8d0132..d098ba6f 100644 --- a/crates/cardano/src/roll/batch.rs +++ b/crates/cardano/src/roll/batch.rs @@ -14,6 +14,10 @@ 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}; @@ -396,11 +400,43 @@ impl WorkBatch { 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() { - use pallas::ledger::traverse::OriginalHash; builder.add_datum_hash(datum.original_hash().to_vec()); } @@ -411,7 +447,6 @@ impl WorkBatch { // Redeemers for redeemer in tx.redeemers() { - use pallas::ledger::traverse::ComputeHash; builder.add_datum_hash(redeemer.data().compute_hash().to_vec()); } }