From 28d4a112628c94fda2801dd14bb5c99751925944 Mon Sep 17 00:00:00 2001 From: sara Date: Mon, 8 Apr 2024 16:03:12 -0300 Subject: [PATCH] feat: babbage validations added and era displayed on front --- napi-pallas/Cargo.toml | 2 +- napi-pallas/index.d.ts | 1 + napi-pallas/src/lib.rs | 8 + napi-pallas/src/validations/alonzo.rs | 2 +- napi-pallas/src/validations/babbage.rs | 309 +++++++++++++++++++++- napi-pallas/src/validations/byron.rs | 2 +- napi-pallas/src/validations/conway.rs | 2 +- napi-pallas/src/validations/shelley_ma.rs | 2 +- napi-pallas/src/validations/validate.rs | 2 +- web/app/components.tsx | 5 +- web/app/routes/tx.tsx | 10 +- 11 files changed, 334 insertions(+), 11 deletions(-) diff --git a/napi-pallas/Cargo.toml b/napi-pallas/Cargo.toml index cf2d72f..8b53243 100644 --- a/napi-pallas/Cargo.toml +++ b/napi-pallas/Cargo.toml @@ -12,7 +12,7 @@ hex = "0.4.3" # Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix napi = { version = "2.12.2", default-features = false, features = ["napi4"] } napi-derive = "2.12.2" -pallas = { git = "https://github.com/alegadea/pallas.git", rev = "54ffc77" , features = ["unstable"]} +pallas = { git = "https://github.com/txpipe/pallas.git", rev = "2f97cbe" , features = ["unstable"]} [build-dependencies] napi-build = "2.0.1" diff --git a/napi-pallas/index.d.ts b/napi-pallas/index.d.ts index 7254742..21e5cf8 100644 --- a/napi-pallas/index.d.ts +++ b/napi-pallas/index.d.ts @@ -46,4 +46,5 @@ export interface Validation { } export interface Validations { validations: Array + era: string } diff --git a/napi-pallas/src/lib.rs b/napi-pallas/src/lib.rs index af1cc3a..5f175ff 100644 --- a/napi-pallas/src/lib.rs +++ b/napi-pallas/src/lib.rs @@ -212,6 +212,7 @@ impl Validation { #[napi(object)] pub struct Validations { pub validations: Vec, + pub era: String, } impl Validations { @@ -224,4 +225,11 @@ impl Validations { self } + + pub fn with_era(self, era: impl ToString) -> Self { + Self { + era: era.to_string(), + ..self + } + } } diff --git a/napi-pallas/src/validations/alonzo.rs b/napi-pallas/src/validations/alonzo.rs index 8393406..dace7cf 100644 --- a/napi-pallas/src/validations/alonzo.rs +++ b/napi-pallas/src/validations/alonzo.rs @@ -3,6 +3,6 @@ use pallas::ledger::primitives::alonzo::MintedTx; use crate::Validations; pub fn validate_alonzo(mtx_a: &MintedTx) -> Validations { - let out = Validations::new(); + let out = Validations::new().with_era("Alonzo".to_string()); out } diff --git a/napi-pallas/src/validations/babbage.rs b/napi-pallas/src/validations/babbage.rs index 9006454..cf44dae 100644 --- a/napi-pallas/src/validations/babbage.rs +++ b/napi-pallas/src/validations/babbage.rs @@ -1,22 +1,325 @@ use crate::{Validation, Validations}; use pallas::{ - applying::babbage::check_ins_not_empty, + applying::{ + babbage::{ + check_all_ins_in_utxos, check_auxiliary_data, check_fee, check_ins_not_empty, + check_languages, check_min_lovelace, check_minting, check_network_id, check_output_val_size, + check_preservation_of_value, check_script_data_hash, check_tx_ex_units, check_tx_size, + check_tx_validity_interval, check_well_formedness, check_witness_set, + }, + utils::{get_babbage_tx_size, BabbageProtParams, FeePolicy}, + Environment, MultiEraProtParams, UTxOs, + }, ledger::primitives::babbage::{MintedTransactionBody, MintedTx as BabbageMintedTx}, }; use super::validate::set_description; +// &The following validations only require the tx fn validate_babbage_ins_not_empty(mtx: &BabbageMintedTx) -> Validation { let tx_body: &MintedTransactionBody = &mtx.transaction_body.clone(); let res = check_ins_not_empty(tx_body); - let description = set_description(&res, "Inputs are not empty".to_string()); + let description = set_description( + &res, + "The set of transaction inputs is not empty.".to_string(), + ); return Validation::new() .with_name("Non empty inputs".to_string()) .with_value(res.is_ok()) .with_description(description); } +fn validate_babbage_minting(mtx: &BabbageMintedTx) -> Validation { + let tx_body: &MintedTransactionBody = &mtx.transaction_body.clone(); + let res = check_minting(tx_body, mtx); + let description = set_description( + &res, + "Each minted / burned asset is paired with an appropriate native script or Plutus script" + .to_string(), + ); + return Validation::new() + .with_name("Minting policy".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +fn validate_babbage_well_formed(mtx: &BabbageMintedTx) -> Validation { + let tx_body: &MintedTransactionBody = &mtx.transaction_body.clone(); + let res = check_well_formedness(tx_body, mtx); + let description = set_description(&res, "The transaction is well-formed".to_string()); + return Validation::new() + .with_name("Well formedness".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +fn validate_babbage_auxiliary_data(mtx: &BabbageMintedTx) -> Validation { + let tx_body = &mtx.transaction_body.clone(); + let res = check_auxiliary_data(tx_body, mtx); + let description = set_description( + &res, + "The metadata of the transaction is valid.".to_string(), + ); + return Validation::new() + .with_name("Auxiliary data".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +// &The following validations also require the protocol parameters +fn validate_babbage_min_lovelace( + mtx: &BabbageMintedTx, + prot_pps: &BabbageProtParams, +) -> Validation { + let tx_body: &MintedTransactionBody = &mtx.transaction_body.clone(); + + let res = check_min_lovelace(tx_body, prot_pps); + let description = set_description( + &res, + "All transaction outputs (regular outputs and collateral outputs) contains at least the minimum lovelace.".to_string(), + ); + return Validation::new() + .with_name("Minimum lovelace".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +fn validate_babbage_output_val_size( + mtx: &BabbageMintedTx, + prot_pps: &BabbageProtParams, +) -> Validation { + let tx_body: &MintedTransactionBody = &mtx.transaction_body.clone(); + let res = check_output_val_size(tx_body, prot_pps); + let description = set_description( + &res, + "The size of the value in each of the outputs is not greater than the maximum allowed." + .to_string(), + ); + return Validation::new() + .with_name("Output value size".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +fn validate_babbage_tx_ex_units(mtx: &BabbageMintedTx, prot_pps: &BabbageProtParams) -> Validation { + let res = check_tx_ex_units(mtx, prot_pps); + let description = set_description( + &res, + "The number of execution units of the transaction does not exceed the maximum allowed." + .to_string(), + ); + return Validation::new() + .with_name("Transaction execution units".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +// &The following validation also requires the tx size +fn validate_babbage_tx_size(size: &Option, prot_pps: &BabbageProtParams) -> Validation { + match size { + Some(size_value) => { + let res = check_tx_size(size_value, prot_pps); + let description = set_description( + &res, + "The size of the transaction does not exceed the maximum allowed.".to_string(), + ); + Validation::new() + .with_name("Transaction size".to_string()) + .with_value(res.is_ok()) + .with_description(description) + } + None => { + // Handle the case where size is None + // For example, return a specific validation result indicating that the size is not provided + Validation::new() + .with_name("Transaction size".to_string()) + .with_value(false) + .with_description("The transaction size could not be obtained.".to_string()) + } + } +} + +// &The following validation also requires the tx utxos +fn validate_babbage_fee( + mtx: &BabbageMintedTx, + size: &Option, + utxos: &UTxOs, + prot_pps: &BabbageProtParams, +) -> Validation { + match size { + Some(size_value) => { + let tx_body = &mtx.transaction_body.clone(); + let res = check_fee(tx_body, size_value, mtx, utxos, prot_pps); + let description = set_description(&res, "The fee of the transaction is valid.".to_string()); + return Validation::new() + .with_name("Fee".to_string()) + .with_value(res.is_ok()) + .with_description(description); + } + None => { + // Handle the case where size is None + // For example, return a specific validation result indicating that the size is not provided + Validation::new() + .with_name("Fee".to_string()) + .with_value(false) + .with_description("The transaction size could not be obtained.".to_string()) + } + } +} + +// &The following validations require the transaction and its utxos +fn validate_babbage_witness_set(mtx: &BabbageMintedTx, utxos: &UTxOs) -> Validation { + let res = check_witness_set(mtx, utxos); + let description = set_description( + &res, + "The witness set of the transaction is valid.".to_string(), + ); + return Validation::new() + .with_name("Witness set".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +fn validate_babbage_all_ins_in_utxos(mtx: &BabbageMintedTx, utxos: &UTxOs) -> Validation { + let tx_body: &MintedTransactionBody = &mtx.transaction_body.clone(); + let res = check_all_ins_in_utxos(tx_body, utxos); + let description = set_description( + &res, + "All transaction inputs, collateral inputs and reference inputs are in the UTxO".to_string(), + ); + return Validation::new() + .with_name("All inputs in UTxOs".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +fn validate_babbage_preservation_of_value(mtx: &BabbageMintedTx, utxos: &UTxOs) -> Validation { + let tx_body: &MintedTransactionBody = &mtx.transaction_body.clone(); + let res = check_preservation_of_value(tx_body, utxos); + let description = set_description( + &res, + "The preservation of value property holds.".to_string(), + ); + return Validation::new() + .with_name("Preservation of value".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +// &The following validation also require the network and the block slot +fn validate_babbage_languages( + mtx: &BabbageMintedTx, + utxos: &UTxOs, + network_magic: &u32, + network_id: &u8, + block_slot: u64, +) -> Validation { + let res = check_languages(mtx, utxos, &network_magic, &network_id, &block_slot); + let description = set_description( + &res, + "The Plutus scripts and native scripts of the transaction are valid.".to_string(), + ); + return Validation::new() + .with_name("Languages".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +fn validate_babbage_script_data_hash( + mtx: &BabbageMintedTx, + utxos: &UTxOs, + network_magic: &u32, + network_id: &u8, + block_slot: u64, +) -> Validation { + let tx_body = &mtx.transaction_body.clone(); + let res = check_script_data_hash( + tx_body, + mtx, + utxos, + &network_magic, + &network_id, + &block_slot, + ); + let description = set_description( + &res, + "The Plutus scripts and native scripts of the transaction are valid.".to_string(), + ); + return Validation::new() + .with_name("Languages".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +// &The following validation requires the tx and the block slot +fn validate_babbage_tx_validity_interval(mtx: &BabbageMintedTx, block_slot: u64) -> Validation { + let tx_body: &MintedTransactionBody = &mtx.transaction_body.clone(); + let res = check_tx_validity_interval(tx_body, &block_slot); + let description = set_description( + &res, + "The block slot is contained in the transaction validity interval.".to_string(), + ); + return Validation::new() + .with_name("Validity interval".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +// &The following validation requires the tx and its network id +fn validate_babbage_network_id(mtx: &BabbageMintedTx, network_id: u8) -> Validation { + let tx_body: &MintedTransactionBody = &mtx.transaction_body.clone(); + let res = check_network_id(tx_body, &network_id); + let description = set_description( + &res, + "The network ID of each regular output as well as that of the collateral output match the global network ID." + .to_string(), + ); + return Validation::new() + .with_name("Network id".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + pub fn validate_babbage(mtx_b: &BabbageMintedTx) -> Validations { - let out = Validations::new().add_new_validation(validate_babbage_ins_not_empty(&mtx_b)); + let tx_body: &MintedTransactionBody = &mtx_b.transaction_body.clone(); + let size: &Option = &get_babbage_tx_size(tx_body); + let prot_params = BabbageProtParams { + fee_policy: FeePolicy { + summand: 155381, + multiplier: 44, + }, + max_tx_size: 16384, + max_block_ex_mem: 62000000, + max_block_ex_steps: 20000000000, + max_tx_ex_mem: 14000000, + max_tx_ex_steps: 10000000000, + max_val_size: 5000, + collateral_percent: 150, + max_collateral_inputs: 3, + coins_per_utxo_word: 4310, + }; + + let env: Environment = Environment { + prot_params: MultiEraProtParams::Babbage(prot_params.clone()), + prot_magic: 764824073, // Mainnet. For preprod: 1. For preview: 2 + block_slot: 72316896, // TODO: Must be an input + network_id: 1, // Mainnet. For preprod: 0. For preview: 0 + }; + + let out = Validations::new() + .with_era("Babbage".to_string()) + .add_new_validation(validate_babbage_ins_not_empty(&mtx_b)) + .add_new_validation(validate_babbage_minting(&mtx_b)) + .add_new_validation(validate_babbage_well_formed(&mtx_b)) + .add_new_validation(validate_babbage_auxiliary_data(&mtx_b)) + .add_new_validation(validate_babbage_min_lovelace(&mtx_b, &prot_params)) + .add_new_validation(validate_babbage_output_val_size(&mtx_b, &prot_params)) + .add_new_validation(validate_babbage_tx_ex_units(&mtx_b, &prot_params)) + .add_new_validation(validate_babbage_tx_size(&size, &prot_params)) + .add_new_validation(validate_babbage_tx_validity_interval( + &mtx_b, + env.block_slot, + )) + .add_new_validation(validate_babbage_network_id(&mtx_b, env.network_id)); out } diff --git a/napi-pallas/src/validations/byron.rs b/napi-pallas/src/validations/byron.rs index 78cd641..d7bbe94 100644 --- a/napi-pallas/src/validations/byron.rs +++ b/napi-pallas/src/validations/byron.rs @@ -1,6 +1,6 @@ use crate::Validations; use pallas::ledger::primitives::byron::MintedTxPayload; pub fn validate_byron(mtxp: &MintedTxPayload) -> Validations { - let out = Validations::new(); + let out = Validations::new().with_era("Byron".to_string()); out } diff --git a/napi-pallas/src/validations/conway.rs b/napi-pallas/src/validations/conway.rs index fa1ca30..1a4a002 100644 --- a/napi-pallas/src/validations/conway.rs +++ b/napi-pallas/src/validations/conway.rs @@ -2,6 +2,6 @@ use pallas::ledger::primitives::conway::MintedTx; use crate::Validations; pub fn validate_conway(mtx_c: &MintedTx) -> Validations { - let out = Validations::new(); + let out = Validations::new().with_era("Conway".to_string()); out } diff --git a/napi-pallas/src/validations/shelley_ma.rs b/napi-pallas/src/validations/shelley_ma.rs index 3778f67..e8c134c 100644 --- a/napi-pallas/src/validations/shelley_ma.rs +++ b/napi-pallas/src/validations/shelley_ma.rs @@ -3,6 +3,6 @@ use pallas::ledger::primitives::alonzo::MintedTx; use crate::Validations; pub fn validate_shelley_ma(mtx_sma: &MintedTx) -> Validations { - let out = Validations::new(); + let out = Validations::new().with_era("Shelley Mary Allegra".to_string()); out } diff --git a/napi-pallas/src/validations/validate.rs b/napi-pallas/src/validations/validate.rs index 8a43c6e..9cfb99a 100644 --- a/napi-pallas/src/validations/validate.rs +++ b/napi-pallas/src/validations/validate.rs @@ -12,7 +12,7 @@ use super::shelley_ma::validate_shelley_ma; pub fn set_description(res: &Result<(), ValidationError>, success: String) -> String { match res { Ok(_) => success, - Err(e) => format!("Error {:?}", e), + Err(e) => format!("Error: {:?}", e), } } diff --git a/web/app/components.tsx b/web/app/components.tsx index d305930..cb73df9 100644 --- a/web/app/components.tsx +++ b/web/app/components.tsx @@ -54,6 +54,7 @@ export function RootSection(props: { data: Section; topics: Record; validations: IValidation[]; + era: string; }) { const [open, setOpen] = useState(false); const handleClick = () => setOpen(!open); @@ -79,7 +80,9 @@ export function RootSection(props: { > {open ? "▼" : "▶"} -

Tx Validations

+
+

Tx Validations - {props.era}

+
{open && } diff --git a/web/app/routes/tx.tsx b/web/app/routes/tx.tsx index f274231..c872628 100644 --- a/web/app/routes/tx.tsx +++ b/web/app/routes/tx.tsx @@ -12,10 +12,12 @@ export interface IValidation { export interface IValidations { validations: IValidation[]; + era: string; } export interface DataProps extends server.Section { validations: IValidation[]; + era: string; raw?: string; } @@ -65,6 +67,7 @@ export default function Index() { if (data) logCuriosity(data); const validations: IValidation[] = data?.validations || []; + const era = data?.era || ""; return (
@@ -104,7 +107,12 @@ export default function Index() { )} {!!data && ( - + )}
);