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
1,923 changes: 1,852 additions & 71 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ zeromq = ["zmq"]
u5c = ["utxorpc", "futures"]
mithril = ["mithril-client"]
hydra = ["tungstenite", "tokio-tungstenite", "futures-util", "bytes"]
eth = ["futures-util"]
# elasticsearch = auto feature flag
# kafka = auto feature flag

Expand Down Expand Up @@ -83,6 +84,9 @@ futures-util = { version = "0.3", optional = true }
bytes = { version = "1.7.2", optional = true }
zmq = { version = "0.10.0", optional = true }

# TODO(p): add feature
alloy = { version = "1.0.30", features = ["full"] }
Comment on lines +87 to +88
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make alloy dependency optional and feature-gated.

The alloy dependency is currently unconditional despite the TODO comment suggesting it should be feature-gated. This adds unnecessary dependencies and increases build times for users not using Ethereum features.

Apply this diff to make alloy optional and gated by the eth feature:

-# TODO(p): add feature
-alloy = { version = "1.0.30", features = ["full"] }
+alloy = { version = "1.0.30", features = ["full"], optional = true }

Then update the eth feature definition at line 23 to include alloy:

-eth = ["futures-util"]
+eth = ["futures-util", "alloy"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# TODO(p): add feature
alloy = { version = "1.0.30", features = ["full"] }
# In the [features] section (around line 23):
eth = ["futures-util", "alloy"]
# In the [dependencies] section (around lines 87–88):
alloy = { version = "1.0.30", features = ["full"], optional = true }
🤖 Prompt for AI Agents
In Cargo.toml around lines 87-88, the alloy dependency is unconditional; make it
optional and feature-gated by adding optional = true to the alloy dependency
entry and keeping its features = ["full"], then update the [features] section at
line 23 to include alloy by adding "alloy" to the eth feature list (i.e., make
eth = ["existing-items...", "alloy"]), so the crate is only pulled in when the
eth feature is enabled.


[dev-dependencies]
goldenfile = "1.7.3"
tempfile = "3.4"
Expand Down
21 changes: 11 additions & 10 deletions src/bin/oura/dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use clap::{Parser, ValueEnum};
use oura::{
daemon::{run_daemon, ConfigRoot},
filters,
framework::{ChainConfig, Error, IntersectConfig},
framework::{cardano, Chain, Error, IntersectConfig},
sinks, sources,
};
use tracing::{info, Level};
Expand All @@ -19,7 +19,7 @@ pub fn run(args: &Args) -> Result<(), Error> {
.with(env_filter)
.init();

let chain = args.magic.clone().unwrap_or_default().into();
let chain = Chain::Cardano(args.magic.clone().unwrap_or_default().into());
let intersect = parse_since(args.since.clone())?;
let bearer = args.bearer.clone().unwrap_or_default();
let source = match bearer {
Expand Down Expand Up @@ -95,7 +95,7 @@ pub struct Args {
bearer: Option<Bearer>,

#[arg(long)]
magic: Option<Chain>,
magic: Option<DumpChain>,

/// point in the chain to start reading from, expects format `slot,hex-hash`
#[arg(long)]
Expand All @@ -115,20 +115,21 @@ enum Bearer {
}

#[derive(ValueEnum, Clone, Default)]
enum Chain {
enum DumpChain {
#[default]
Mainnet,
Testnet,
PreProd,
Preview,
}
impl From<Chain> for ChainConfig {
fn from(value: Chain) -> Self {
// TODO(p): add support multchain
impl From<DumpChain> for cardano::ChainConfig {
fn from(value: DumpChain) -> Self {
match value {
Chain::Mainnet => ChainConfig::Mainnet,
Chain::Testnet => ChainConfig::Testnet,
Chain::PreProd => ChainConfig::PreProd,
Chain::Preview => ChainConfig::Preview,
DumpChain::Mainnet => cardano::ChainConfig::Mainnet,
DumpChain::Testnet => cardano::ChainConfig::Testnet,
DumpChain::PreProd => cardano::ChainConfig::PreProd,
DumpChain::Preview => cardano::ChainConfig::Preview,
}
}
}
20 changes: 10 additions & 10 deletions src/bin/oura/watch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use clap::{Parser, ValueEnum};
use oura::{
daemon::{run_daemon, ConfigRoot},
filters,
framework::{ChainConfig, Error, IntersectConfig},
framework::{cardano, Chain, Error, IntersectConfig},
sinks, sources,
};
use tracing::{info, Level};
Expand All @@ -19,7 +19,7 @@ pub fn run(args: &Args) -> Result<(), Error> {
.with(env_filter)
.init();

let chain = args.magic.clone().unwrap_or_default().into();
let chain = Chain::Cardano(args.magic.clone().unwrap_or_default().into());
let intersect = parse_since(args.since.clone())?;
let bearer = args.bearer.clone().unwrap_or_default();
let source = match bearer {
Expand Down Expand Up @@ -92,7 +92,7 @@ pub struct Args {
bearer: Option<Bearer>,

#[arg(long)]
magic: Option<Chain>,
magic: Option<WatchChain>,

/// point in the chain to start reading from, expects format `slot,hex-hash`
#[arg(long)]
Expand All @@ -116,20 +116,20 @@ pub enum Bearer {
}

#[derive(ValueEnum, Clone, Default)]
pub enum Chain {
pub enum WatchChain {
#[default]
Mainnet,
Testnet,
PreProd,
Preview,
}
impl From<Chain> for ChainConfig {
fn from(value: Chain) -> Self {
impl From<WatchChain> for cardano::ChainConfig {
fn from(value: WatchChain) -> Self {
match value {
Chain::Mainnet => ChainConfig::Mainnet,
Chain::Testnet => ChainConfig::Testnet,
Chain::PreProd => ChainConfig::PreProd,
Chain::Preview => ChainConfig::Preview,
WatchChain::Mainnet => cardano::ChainConfig::Mainnet,
WatchChain::Testnet => cardano::ChainConfig::Testnet,
WatchChain::PreProd => cardano::ChainConfig::PreProd,
WatchChain::Preview => cardano::ChainConfig::Preview,
}
}
}
2 changes: 1 addition & 1 deletion src/daemon/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub struct ConfigRoot {
pub sink: sinks::Config,
pub intersect: IntersectConfig,
pub finalize: Option<FinalizeConfig>,
pub chain: Option<ChainConfig>,
pub chain: Option<Chain>,
pub retries: Option<gasket::retries::Policy>,
pub cursor: Option<cursor::Config>,
pub metrics: Option<MetricsConfig>,
Expand Down
6 changes: 5 additions & 1 deletion src/filters/into_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ impl From<&Stage> for Worker {
}

gasket::impl_mapper!(|_worker: Worker, stage: Stage, unit: ChainEvent| => {
let out = unit.clone().map_record(|r| Record::GenericJson(JsonValue::from(r)));
let out = unit.clone().map_record(|r| match r {
Record::Cardano(record) => Record::GenericJson(JsonValue::from(record)),
x => x,
});

stage.ops_count.inc(1);
out
});
Expand Down
2 changes: 1 addition & 1 deletion src/filters/legacy_v1/cip15.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use serde_json::Value as JsonValue;
use pallas::ledger::primitives::alonzo::Metadatum;

use super::EventWriter;
use crate::framework::legacy_v1::*;
use crate::framework::cardano::legacy_v1::*;

fn extract_json_property<'a>(json: &'a JsonValue, key: &str) -> Option<&'a JsonValue> {
json.as_object().and_then(|x| x.get(key))
Expand Down
2 changes: 1 addition & 1 deletion src/filters/legacy_v1/cip25.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use pallas::ledger::primitives::alonzo::Metadatum;
use serde_json::Value as JsonValue;
use tracing::warn;

use crate::framework::legacy_v1::CIP25AssetRecord;
use crate::framework::cardano::legacy_v1::CIP25AssetRecord;

use super::EventWriter;

Expand Down
2 changes: 1 addition & 1 deletion src/filters/legacy_v1/crawl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use pallas::ledger::primitives::babbage::MintedDatumOption;
use pallas::ledger::traverse::{MultiEraBlock, MultiEraInput, MultiEraOutput, MultiEraTx};
use pallas::network::miniprotocols::Point;

use crate::framework::legacy_v1::*;
use crate::framework::cardano::legacy_v1::*;
use crate::framework::Error as OuraError;

use super::EventWriter;
Expand Down
2 changes: 1 addition & 1 deletion src/filters/legacy_v1/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use pallas::ledger::traverse::{
use pallas::network::miniprotocols::Point;
use pallas::{codec::utils::KeepRaw, crypto::hash::Hash};

use crate::framework::legacy_v1::*;
use crate::framework::cardano::legacy_v1::*;

use super::EventWriter;

Expand Down
8 changes: 6 additions & 2 deletions src/filters/legacy_v1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ gasket::impl_splitter!(|_worker: Worker, stage: Stage, unit: ChainEvent| => {
let mut buffer = Vec::new();

match unit {
ChainEvent::Apply(point, Record::CborBlock(cbor)) => {
ChainEvent::Apply(point, Record::Cardano(cardano::Record::CborBlock(cbor))) => {
let mut writer = EventWriter::new(
point.clone(),
&stage.output,
Expand Down Expand Up @@ -89,9 +89,13 @@ pub struct Config {

impl Config {
pub fn bootstrapper(self, ctx: &Context) -> Result<Stage, Error> {
let chain_config = match &ctx.chain {
Chain::Cardano(chain_config) => chain_config.clone(),
};
Comment on lines +92 to +94
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Non-exhaustive pattern matching will panic on non-Cardano chains.

The pattern match on ctx.chain only handles the Chain::Cardano variant. According to the PR summary, the Chain enum includes Ethereum, Bitcoin, and Substrate variants. This will panic at runtime if a non-Cardano chain is configured.

Add proper error handling for non-Cardano chains:

-        let chain_config = match &ctx.chain {
-            Chain::Cardano(chain_config) => chain_config.clone(),
-        };
+        let chain_config = match &ctx.chain {
+            Chain::Cardano(chain_config) => chain_config.clone(),
+            _ => return Err(Error::config("legacy_v1 filter only supports Cardano chain")),
+        };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let chain_config = match &ctx.chain {
Chain::Cardano(chain_config) => chain_config.clone(),
};
let chain_config = match &ctx.chain {
Chain::Cardano(chain_config) => chain_config.clone(),
_ => return Err(Error::config("legacy_v1 filter only supports Cardano chain")),
};
🤖 Prompt for AI Agents
In src/filters/legacy_v1/mod.rs around lines 92-94, the match on ctx.chain only
handles Chain::Cardano and will panic for other variants; change it to an
exhaustive match that either extracts the Cardano config or returns a proper
error for non-Cardano variants (include the actual variant in the message), e.g.
match on Chain::{Cardano => chain_config.clone(), other => return Err(/*
appropriate error type with descriptive message including other */)}; ensure the
function signature and error type are adjusted/propagated accordingly instead of
letting the code panic.


let stage = Stage {
config: self,
genesis: ctx.chain.clone().into(),
genesis: chain_config.into(),
ops_count: Default::default(),
input: Default::default(),
output: Default::default(),
Expand Down
7 changes: 5 additions & 2 deletions src/filters/legacy_v1/prelude.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::framework::legacy_v1::*;
use crate::framework::cardano::legacy_v1::*;
use crate::framework::*;

use gasket::framework::WorkerError;
Expand Down Expand Up @@ -42,7 +42,10 @@ impl<'a> EventWriter<'a> {
fingerprint: None,
};

let msg = ChainEvent::Apply(self.point.clone(), Record::OuraV1Event(evt));
let msg = ChainEvent::Apply(
self.point.clone(),
Record::Cardano(cardano::Record::OuraV1Event(evt)),
);
self.buffer.push(msg);

Ok(())
Expand Down
8 changes: 4 additions & 4 deletions src/filters/parse_cbor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ impl From<&Stage> for Worker {

gasket::impl_mapper!(|_worker: Worker, stage: Stage, unit: ChainEvent| => {
let output = unit.clone().try_map_record(|r| match r {
Record::CborBlock(cbor) => {
Record::Cardano(cardano::Record::CborBlock(cbor)) => {
let block = trv::MultiEraBlock::decode(&cbor).or_panic()?;
let block = stage.mapper.map_block(&block);
Ok(Record::ParsedBlock(block))
Ok(Record::Cardano(cardano::Record::ParsedBlock(block)))
}
Record::CborTx(cbor) => {
Record::Cardano(cardano::Record::CborTx(cbor)) => {
let tx = trv::MultiEraTx::decode(&cbor).or_panic()?;
let tx = stage.mapper.map_tx(&tx);
Ok(Record::ParsedTx(tx))
Ok(Record::Cardano(cardano::Record::ParsedTx(tx)))
}
x => Ok(x),
})?;
Expand Down
12 changes: 9 additions & 3 deletions src/filters/select/eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -605,10 +605,16 @@ fn eval_block(block: &ParsedBlock, predicate: &Predicate) -> MatchOutcome {

pub fn eval(record: &Record, predicate: &Predicate) -> MatchOutcome {
match record {
Record::ParsedTx(x) => eval_tx(x, predicate),
Record::ParsedBlock(x) => eval_block(x, predicate),
Record::Cardano(record) => match record {
cardano::Record::ParsedTx(x) => eval_tx(x, predicate),
cardano::Record::ParsedBlock(x) => eval_block(x, predicate),
_ => {
warn!("The select filter is valid only for ParsedTx / ParsedBlock records");
MatchOutcome::Uncertain
}
},
_ => {
warn!("The select filter is valid only with ParsedTx & ParsedBlock records");
warn!("The select filter is valid only for Cardano ParsedTx / ParsedBlock records");
MatchOutcome::Uncertain
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/filters/split_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ impl From<&Stage> for Worker {

gasket::impl_splitter!(|_worker: Worker, stage: Stage, unit: ChainEvent| => {
let output = unit.clone().try_map_record_to_many(|r| match r {
Record::CborBlock(cbor) => {
Record::Cardano(cardano::Record::CborBlock(cbor)) => {
let out = map_block_to_tx(Cow::Borrowed(&cbor))?
.into_iter()
.map(|tx| Record::CborTx(tx.into()))
.map(|tx| Record::Cardano(cardano::Record::CborTx(tx.into())))
.collect();

Ok(out)
Expand Down
26 changes: 16 additions & 10 deletions src/filters/wasm_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use serde::Deserialize;

use crate::framework::*;

pub type CardanoRecord = cardano::Record;

#[derive(Stage)]
#[stage(name = "filter-wasm", unit = "ChainEvent", worker = "Worker")]
pub struct Stage {
Expand All @@ -18,23 +20,20 @@ pub struct Stage {
}

impl Stage {
fn map_record(&mut self, r: Record) -> Result<Vec<Record>, Error> {
fn map_cardano_record(&mut self, r: CardanoRecord) -> Result<Vec<Record>, Error> {
let extism::convert::Json::<serde_json::Value>(output) = match r {
Record::CborBlock(x) => self.plugin.call("map_cbor_block", x).unwrap(),
Record::CborTx(x) => self.plugin.call("map_cbor_tx", x).unwrap(),
Record::ParsedTx(x) => self
CardanoRecord::CborBlock(x) => self.plugin.call("map_cbor_block", x).unwrap(),
CardanoRecord::CborTx(x) => self.plugin.call("map_cbor_tx", x).unwrap(),
CardanoRecord::ParsedTx(x) => self
.plugin
.call("map_u5c_tx", extism::convert::Json(x))
.unwrap(),
Record::ParsedBlock(x) => self
CardanoRecord::ParsedBlock(x) => self
.plugin
.call("map_u5c_block", extism::convert::Json(x))
.unwrap(),
Record::GenericJson(x) => self
.plugin
.call("map_json", extism::convert::Json(x))
.unwrap(),
Record::OuraV1Event(x) => self

CardanoRecord::OuraV1Event(x) => self
.plugin
.call("map_json", extism::convert::Json(x))
.unwrap(),
Expand All @@ -48,6 +47,13 @@ impl Stage {

Ok(output)
}

fn map_record(&mut self, r: Record) -> Result<Vec<Record>, Error> {
match r {
Record::Cardano(x) => self.map_cardano_record(x),
x => Ok(vec![x]),
}
}
}

#[derive(Default)]
Expand Down
17 changes: 17 additions & 0 deletions src/framework/bitcoin/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use serde_json::{json, Value as JsonValue};

#[derive(Debug, Clone)]
pub enum Record {
// Scaffold placeholder for now
ParsedBlock(()),
RawBlock(Vec<u8>),
}

impl From<Record> for JsonValue {
fn from(value: Record) -> Self {
match value {
Record::ParsedBlock(x) => json!(x),
Record::RawBlock(x) => json!({ "hex": hex::encode(x) }),
}
}
}
File renamed without changes.
Loading
Loading