Skip to content

Commit

Permalink
feat: integrate Pallas phase-1 validation function (#127)
Browse files Browse the repository at this point in the history
Co-authored-by: Maico Leberle <[email protected]>
  • Loading branch information
scarmuega and MaicoLeberle authored Dec 12, 2023
1 parent bebee58 commit 77f172c
Show file tree
Hide file tree
Showing 17 changed files with 572 additions and 265 deletions.
446 changes: 223 additions & 223 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ authors = ["Santiago Carmuega <[email protected]>"]


[dependencies]
# pallas = "0.19.0"
# pallas = { path = "../pallas/pallas" }
pallas = { git = "https://github.com/txpipe/pallas.git", features = ["unstable"] }

gasket = { version = "^0.5", features = ["derive"] }
Expand Down
1 change: 1 addition & 0 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- [Sync](./running/sync.md)
- [Serve](./running/serve.md)
- [Daemon](./running/daemon.md)
- [Eval](./running/eval.md)
- [API](./api/README.md)
- [gRPC](./api/grpc.md)
- [Ouroboros](./api/ouroboros.md)
Expand Down
18 changes: 18 additions & 0 deletions book/src/running/eval.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# `eval` command

The `eval` is an utility for evaluating transactions. It takes tx data from an external source and uses the current ledger state to evaluate phase-1 validation rules.

## Usage

To execute the evaluation, run the following command from your terminal:

```bash
dolos eval --file <FILE> --era <ERA> --magic <MAGIC> --slot <SLOT>
```

The args should be interpreted as:

- `--file <FILE>`: the path to the file containing the tx data as hex-encoded cbor.
- `--era <ERA>`: the id of the era that should be used to interpret the transaction data.
- `--magic <MAGIC>`: the protocol magic of the network.
- `--slot <SLOT>`: the slot that should be used for retrieving protocol parameters.
1 change: 1 addition & 0 deletions examples/sync-mainnet/dolos.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[upstream]
peer_address = "relays-new.cardano-mainnet.iohk.io:3001"
network_magic = 764824073
network_id = 1

[rolldb]
path = "./tmp/rolldb"
Expand Down
1 change: 1 addition & 0 deletions examples/sync-preprod/dolos.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[upstream]
peer_address = "preprod-node.world.dev.cardano.org:30000"
network_magic = 1
network_id = 0

[rolldb]
path = "./tmp/rolldb"
Expand Down
1 change: 1 addition & 0 deletions examples/sync-preview/dolos.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[upstream]
peer_address = "preview-node.world.dev.cardano.org:30002"
network_magic = 2
network_id = 0

[rolldb]
path = "./tmp/rolldb"
Expand Down
2 changes: 1 addition & 1 deletion src/bin/dolos/bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ pub fn run(config: &super::Config, args: &Args) -> miette::Result<()> {
.context("adding chain entry")?;

ledger
.apply_block(&block)
.apply_block(&blockd)
.into_diagnostic()
.context("applyting ledger block")?;
}
Expand Down
7 changes: 6 additions & 1 deletion src/bin/dolos/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ pub fn open_data_stores(config: &crate::Config) -> Result<Stores, Error> {

let chain = chain::Store::open(rolldb_path.join("chain")).map_err(Error::storage)?;

let ledger = ApplyDB::open(rolldb_path.join("ledger")).map_err(Error::storage)?;
let ledger = ApplyDB::open(
rolldb_path.join("ledger"),
config.upstream.network_magic as u32,
config.upstream.network_id,
)
.map_err(Error::storage)?;

Ok((wal, chain, ledger))
}
Expand Down
30 changes: 30 additions & 0 deletions src/bin/dolos/data.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
use std::path::Path;

use miette::IntoDiagnostic;

use pallas::{ledger::traverse::MultiEraBlock, storage::rolldb::chain};

#[allow(dead_code)]
fn dump_txs(chain: &chain::Store) -> miette::Result<()> {
for header in chain.crawl() {
let (slot, hash) = header.into_diagnostic()?;
println!("dumping {slot}");

let block = chain.get_block(hash).into_diagnostic()?.unwrap();
let block = MultiEraBlock::decode(&block).into_diagnostic()?;

for tx in block.txs() {
let cbor = hex::encode(tx.encode());
let path = format!("{}.tx", tx.hash());
std::fs::write(Path::new(&path), cbor).into_diagnostic()?;
}
}

Ok(())
}

#[derive(Debug, clap::Args)]
pub struct Args {}

Expand Down Expand Up @@ -31,5 +56,10 @@ pub fn run(config: &super::Config, _args: &Args) -> miette::Result<()> {
println!("chain is empty");
}

// WIP utility to dump tx data for debugging purposes. Should be implemented as
// a subcommand.

// dump_txs(&chain)?;

Ok(())
}
75 changes: 75 additions & 0 deletions src/bin/dolos/eval.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use miette::{Context, IntoDiagnostic};
use pallas::{
applying::{validate, Environment, UTxOs},
ledger::traverse::{Era, MultiEraInput, MultiEraOutput},
};
use std::{borrow::Cow, collections::HashMap, path::PathBuf};

#[derive(Debug, clap::Args)]
pub struct Args {
#[arg(long, short)]
file: PathBuf,

#[arg(long, short)]
era: u16,

#[arg(long, short)]
slot: u64,
}

pub fn run(config: &super::Config, args: &Args) -> miette::Result<()> {
crate::common::setup_tracing(&config.logging)?;

let (_, _, ledger) = crate::common::open_data_stores(config)?;

let cbor = std::fs::read_to_string(&args.file)
.into_diagnostic()
.context("reading tx from file")?;

let cbor = hex::decode(&cbor)
.into_diagnostic()
.context("decoding hex content from file")?;

let era = Era::try_from(args.era).unwrap();

let tx = pallas::ledger::traverse::MultiEraTx::decode_for_era(era, &cbor)
.into_diagnostic()
.context("decoding tx cbor")?;

let mut utxos = HashMap::new();
ledger
.resolve_inputs_for_tx(&tx, &mut utxos)
.into_diagnostic()
.context("resolving tx inputs")?;

let mut utxos2 = UTxOs::new();

for (ref_, output) in utxos.iter() {
let txin = pallas::ledger::primitives::byron::TxIn::Variant0(
pallas::codec::utils::CborWrap((ref_.0.clone(), ref_.1 as u32)),
);

let key = MultiEraInput::Byron(
<Box<Cow<'_, pallas::ledger::primitives::byron::TxIn>>>::from(Cow::Owned(txin)),
);

let era = Era::try_from(output.0)
.into_diagnostic()
.context("parsing era")?;

let value = MultiEraOutput::decode(era, &output.1)
.into_diagnostic()
.context("decoding utxo")?;

utxos2.insert(key, value);
}

let env: Environment = ledger
.get_active_pparams(args.slot)
.into_diagnostic()
.context("resolving pparams")?;

validate(&tx, &utxos2, &env).unwrap();

Ok(())
}
3 changes: 3 additions & 0 deletions src/bin/dolos/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod bootstrap;
mod common;
mod daemon;
mod data;
mod eval;
mod serve;
mod sync;

Expand All @@ -18,6 +19,7 @@ enum Command {
Bootstrap(bootstrap::Args),
Data(data::Args),
Serve(serve::Args),
Eval(eval::Args),
}

#[derive(Debug, Parser)]
Expand Down Expand Up @@ -102,6 +104,7 @@ fn main() -> Result<()> {
Command::Bootstrap(x) => bootstrap::run(&config, &x)?,
Command::Data(x) => data::run(&config, &x)?,
Command::Serve(x) => serve::run(config, &x)?,
Command::Eval(x) => eval::run(&config, &x)?,
};

Ok(())
Expand Down
4 changes: 2 additions & 2 deletions src/model.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use pallas::{crypto::hash::Hash, ledger::traverse::Era, network::miniprotocols::Point};
use pallas::{crypto::hash::Hash, network::miniprotocols::Point};

pub type BlockSlot = u64;
pub type BlockHash = Hash<32>;
pub type RawBlock = Vec<u8>;
pub type TxHash = Hash<32>;
pub type OutputIdx = u64;
pub type UtxoBody = (Era, Vec<u8>);
pub type UtxoBody = (u16, Vec<u8>);

// #[derive(Debug, Clone)]
// pub struct BlockWithContext {
Expand Down
14 changes: 8 additions & 6 deletions src/storage/applydb/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use tracing::info;

use crate::{
prelude::*,
storage::kvtable::{DBBytes, DBSerde, KVTable},
storage::kvtable::{DBSerde, KVTable},
};

use super::{ApplyDB, UtxoKV, UtxoRef};
Expand All @@ -21,14 +21,14 @@ fn build_byron_txout(addr: ByronAddress, amount: u64) -> TxOut {
}
}

fn genesis_utxo_to_kv(utxo: GenesisUtxo) -> Result<(DBSerde<UtxoRef>, DBBytes), Error> {
fn genesis_utxo_to_kv(utxo: GenesisUtxo) -> Result<(DBSerde<UtxoRef>, DBSerde<UtxoBody>), Error> {
let (tx, addr, amount) = utxo;

let key = DBSerde(UtxoRef(tx, 0));

let txout = build_byron_txout(addr, amount);
let txout = pallas::codec::minicbor::to_vec(txout).map_err(Error::config)?;
let value = DBBytes(txout);
let value = DBSerde((0u16, txout));

Ok((key, value))
}
Expand Down Expand Up @@ -64,10 +64,12 @@ mod tests {
fn assert_genesis_utxo_exists(db: &ApplyDB, tx_hex: &str, addr_base58: &str, amount: u64) {
let tx = Hash::<32>::from_str(tx_hex).unwrap();

let cbor = db.get_utxo(tx, 0).unwrap();
let utxo_body = db.get_utxo(tx, 0).unwrap();

assert!(cbor.is_some(), "utxo not found");
let cbor = cbor.unwrap();
assert!(utxo_body.is_some(), "utxo not found");
let (era, cbor) = utxo_body.unwrap();

assert_eq!(era, 0);

let txout: Result<pallas::ledger::primitives::byron::TxOut, _> =
pallas::codec::minicbor::decode(&cbor);
Expand Down
Loading

0 comments on commit 77f172c

Please sign in to comment.