Skip to content

Commit

Permalink
feat: introduce eval command
Browse files Browse the repository at this point in the history
  • Loading branch information
scarmuega committed Nov 19, 2023
1 parent 7566ce6 commit d85a7fd
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 46 deletions.
32 changes: 32 additions & 0 deletions src/bin/dolos/data.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
use std::path::Path;

use miette::IntoDiagnostic;

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

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 +58,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(())
}
95 changes: 95 additions & 0 deletions src/bin/dolos/eval.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use miette::{Context, IntoDiagnostic};
use pallas::{
applying::{validate, Environment, UTxOs},
ledger::{
primitives::byron::TxIn,
traverse::{Era, MultiEraInput, MultiEraOutput, MultiEraTx},
},
};
use std::path::PathBuf;

use dolos::storage::applydb::ApplyDB;

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

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

#[arg(long, short)]
magic: u32,

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

type ResolveInputs = Vec<(TxIn, Vec<u8>)>;

pub fn resolve_inputs(tx: &MultiEraTx<'_>, ledger: &ApplyDB) -> miette::Result<ResolveInputs> {
let mut set = vec![];

for input in tx.inputs() {
let hash = input.hash();
let idx = input.index();

let bytes = ledger
.get_utxo(*hash, idx)
.into_diagnostic()
.context("fetching utxo from ledger")?
.ok_or(miette::miette!("utxo not found"))?;

//TODO: allow to pass extra utxos manually, to mimic what happens when
// consuming utxos from the same block;

let txin = pallas::ledger::primitives::byron::TxIn::Variant0(
pallas::codec::utils::CborWrap((*hash, idx as u32)),
);

set.push((txin, bytes));
}

Ok(set)
}

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: UTxOs = UTxOs::new();
let resolved = resolve_inputs(&tx, &ledger)?;

for (input, output) in resolved.iter() {
let key = MultiEraInput::from_byron(&input);

let value = MultiEraOutput::decode(Era::Byron, &output)
.into_diagnostic()
.context("decoding utxo cbor")?;

utxos.insert(key, value);
}

let env: Environment = ApplyDB::mk_environment(args.slot, args.magic)
.into_diagnostic()
.context("resolving pparams")?;

validate(&tx, &utxos, &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 @@ -7,6 +7,7 @@ use std::path::PathBuf;
mod common;
mod daemon;
mod data;
mod eval;
mod serve;
mod sync;

Expand All @@ -16,6 +17,7 @@ enum Command {
Sync(sync::Args),
Data(data::Args),
Serve(serve::Args),
Eval(eval::Args),
}

#[derive(Debug, Parser)]
Expand Down Expand Up @@ -99,6 +101,7 @@ fn main() -> Result<()> {
Command::Sync(x) => sync::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
93 changes: 48 additions & 45 deletions src/storage/applydb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,12 @@ impl<'a> ApplyBatch<'a> {
self.utxo_inserts.contains_key(&UtxoRef(tx, output))
}

// Meant to be used to get the UTxO associated with a transaction input, assuming the current
// block has already been traversed, appropriately filling utxo_inserts and utxo_deletes.
// Meant to be used to get the UTxO associated with a transaction input,
// assuming the current block has already been traversed, appropriately
// filling utxo_inserts and utxo_deletes.
pub fn get_same_block_utxo(&self, tx_hash: TxHash, ind: OutputIndex) -> Option<UtxoBody> {
// utxo_inserts contains the UTxOs produced in the current block which haven't been spent.
// utxo_inserts contains the UTxOs produced in the current block which haven't
// been spent.
self.utxo_inserts
.get(&UtxoRef(tx_hash, ind))
// utxo_deletes contains UTxOs previously stored in the DB, which we don't care
Expand Down Expand Up @@ -299,7 +301,7 @@ impl ApplyDB {
Ok(dbval.map(|x| x.0))
}

pub fn apply_block(&mut self, cbor: &[u8], prot_magic: &u32) -> Result<(), Error> {
pub fn apply_block(&mut self, cbor: &[u8], prot_magic: u32) -> Result<(), Error> {
let block = MultiEraBlock::decode(cbor).map_err(|_| Error::Cbor)?;
let slot = block.slot();
let hash = block.hash();
Expand Down Expand Up @@ -332,12 +334,13 @@ impl ApplyDB {
}
}

// TODO: move out of storage
for tx in txs.iter() {
if tx.era() == Era::Byron {
match self.get_inputs(tx, &batch) {
Ok(inputs) => {
let utxos: UTxOs = Self::mk_utxo(&inputs);
let env: Environment = Self::mk_environment(&block, prot_magic)?;
let env: Environment = Self::mk_environment(block.slot(), prot_magic)?;
match validate(tx, &utxos, &env) {
Ok(()) => (),
Err(err) => warn!("Transaction validation failed ({:?})", err),
Expand All @@ -357,7 +360,8 @@ impl ApplyDB {
Ok(())
}

fn get_inputs(
// TODO: move out of storage
pub fn get_inputs(
&self,
metx: &MultiEraTx,
batch: &ApplyBatch,
Expand Down Expand Up @@ -390,7 +394,8 @@ impl ApplyDB {
Ok(res)
}

fn mk_utxo<'a>(entries: &'a [(TxIn, TxOut)]) -> UTxOs<'a> {
// TODO: move out of storage
pub fn mk_utxo<'a>(entries: &'a [(TxIn, TxOut)]) -> UTxOs<'a> {
let mut utxos: UTxOs<'a> = UTxOs::new();
for (input, output) in entries.iter() {
let multi_era_input: MultiEraInput = MultiEraInput::from_byron(input);
Expand All @@ -400,44 +405,42 @@ impl ApplyDB {
utxos
}

fn mk_environment(block: &MultiEraBlock, prot_magic: &u32) -> Result<Environment, Error> {
if block.era() == Era::Byron {
let slot: u64 = block.header().slot();
if slot <= 322876 {
// These are the genesis values.
Ok(Environment {
prot_params: MultiEraProtParams::Byron(ByronProtParams {
min_fees_const: 155381,
min_fees_factor: 44,
max_tx_size: 4096,
}),
prot_magic: *prot_magic,
})
} else if slot > 322876 && slot <= 1784895 {
// Block hash were the update proposal was submitted:
// 850805044e0df6c13ced2190db7b11489672b0225d478a35a6db71fbfb33afc0
Ok(Environment {
prot_params: MultiEraProtParams::Byron(ByronProtParams {
min_fees_const: 155381,
min_fees_factor: 44,
max_tx_size: 65536,
}),
prot_magic: *prot_magic,
})
} else {
// Block hash were the update proposal was submitted:
// d798a8d617b25fc6456ffe2d90895a2c15a7271b671dab2d18d46f3d0e4ef495
Ok(Environment {
prot_params: MultiEraProtParams::Byron(ByronProtParams {
min_fees_const: 155381,
min_fees_factor: 44,
max_tx_size: 8192,
}),
prot_magic: *prot_magic,
})
}
// TODO: move out of storage
pub fn mk_environment(slot: u64, prot_magic: u32) -> Result<Environment, Error> {
if slot <= 322876 {
// These are the genesis values.
Ok(Environment {
prot_params: MultiEraProtParams::Byron(ByronProtParams {
min_fees_const: 155381,
min_fees_factor: 44,
max_tx_size: 4096,
}),
prot_magic,
})
} else if slot > 322876 && slot <= 1784895 {
// Block hash were the update proposal was submitted:
// 850805044e0df6c13ced2190db7b11489672b0225d478a35a6db71fbfb33afc0
Ok(Environment {
prot_params: MultiEraProtParams::Byron(ByronProtParams {
min_fees_const: 155381,
min_fees_factor: 44,
max_tx_size: 65536,
}),
prot_magic,
})
} else if slot < 4492800 {
// Block hash were the update proposal was submitted:
// d798a8d617b25fc6456ffe2d90895a2c15a7271b671dab2d18d46f3d0e4ef495
Ok(Environment {
prot_params: MultiEraProtParams::Byron(ByronProtParams {
min_fees_const: 155381,
min_fees_factor: 44,
max_tx_size: 8192,
}),
prot_magic,
})
} else {
Err(Error::UnimplementedEra)
unimplemented!("we don't have pparams after byron")
}
}

Expand Down Expand Up @@ -564,7 +567,7 @@ mod tests {
}
}

db.apply_block(&cbor, &764824073).unwrap(); // This is mainnet's protocol magic number.
db.apply_block(&cbor, 764824073).unwrap(); // This is mainnet's protocol magic number.

for tx in block.txs() {
for input in tx.consumes() {
Expand Down
2 changes: 1 addition & 1 deletion src/sync/ledger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl gasket::framework::Worker<Stage> for Worker {
info!(slot, "applying block");
stage
.ledger
.apply_block(cbor, &stage.prot_magic)
.apply_block(cbor, stage.prot_magic)
.or_panic()?;
}
RollEvent::Undo(slot, _, cbor) => {
Expand Down

0 comments on commit d85a7fd

Please sign in to comment.