diff --git a/napi-pallas/Cargo.toml b/napi-pallas/Cargo.toml index cf2d72f..3d38211 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 = "9f5713c" , 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..947a5d1 100644 --- a/napi-pallas/index.d.ts +++ b/napi-pallas/index.d.ts @@ -20,6 +20,48 @@ export interface Output { bytes?: string address?: AddressDiagnostic } +export interface ValidationContext { + epoch: number + minFeeA: number + minFeeB: number + maxBlockSize: number + maxTxSize: number + maxBlockHeaderSize: number + keyDeposit: number + poolDeposit: number + eMax: number + nOpt: number + a0Numerator: number + a0Denominator: number + rhoNumerator: number + rhoDenominator: number + tauNumerator: number + tauDenominator: number + decentralisationParamNumerator: number + decentralisationParamDenominator: number + extraEntropyNumerator: number + extraEntropyDenominator: number + protocolMajorVer: number + protocolMinorVer: number + minUtxo: number + minPoolCost: number + priceMemNumerator: number + priceMemDenominator: number + priceStepNumerator: number + priceStepDenominator: number + maxTxExMem: number + maxTxExSteps: number + maxBlockExMem: number + maxBlockExSteps: number + maxValSize: number + collateralPercent: number + maxCollateralInputs: number + coinsPerUtxoSize: number + coinsPerUtxoWord: number + network: string + era: string + blockSlot: number +} export interface Attribute { topic?: string value?: string @@ -37,7 +79,7 @@ export interface SectionValidation { section: Section validations: Validations } -export function safeParseTx(raw: string): SectionValidation +export function safeParseTx(raw: string, context: ValidationContext): SectionValidation export function safeParseBlock(raw: string): Section export interface Validation { name: string @@ -46,4 +88,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..09b7a8a 100644 --- a/napi-pallas/src/lib.rs +++ b/napi-pallas/src/lib.rs @@ -10,6 +10,52 @@ mod block; mod tx; mod validations; +#[derive(Default)] +#[napi(object)] +pub struct ValidationContext { + pub epoch: u32, + pub min_fee_a: u32, + pub min_fee_b: u32, + pub max_block_size: u32, + pub max_tx_size: u32, + pub max_block_header_size: u32, + pub key_deposit: i64, + pub pool_deposit: i64, + pub e_max: i64, + pub n_opt: u32, + pub a0_numerator: i64, + pub a0_denominator: i64, + pub rho_numerator: i64, + pub rho_denominator: i64, + pub tau_numerator: i64, + pub tau_denominator: i64, + pub decentralisation_param_numerator: i64, + pub decentralisation_param_denominator: i64, + pub extra_entropy_numerator: u32, + pub extra_entropy_denominator: u32, + pub protocol_major_ver: i64, + pub protocol_minor_ver: i64, + pub min_utxo: i64, + pub min_pool_cost: i64, + pub price_mem_numerator: i64, + pub price_mem_denominator: i64, + pub price_step_numerator: i64, + pub price_step_denominator: i64, + pub max_tx_ex_mem: u32, + pub max_tx_ex_steps: i64, + pub max_block_ex_mem: u32, + pub max_block_ex_steps: i64, + pub max_val_size: u32, + pub collateral_percent: u32, + pub max_collateral_inputs: u32, + pub coins_per_utxo_size: i64, + pub coins_per_utxo_word: i64, + + pub network: String, + pub era: String, + pub block_slot: u32, +} + #[derive(Default)] #[napi(object)] pub struct Attribute { @@ -152,8 +198,8 @@ pub struct SectionValidation { } #[napi] -pub fn safe_parse_tx(raw: String) -> SectionValidation { - match tx::parse(raw) { +pub fn safe_parse_tx(raw: String, context: ValidationContext) -> SectionValidation { + match tx::parse(raw, context) { Ok(x) => { let (section, validations) = x; SectionValidation { @@ -212,6 +258,7 @@ impl Validation { #[napi(object)] pub struct Validations { pub validations: Vec, + pub era: String, } impl Validations { @@ -224,4 +271,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/tx.rs b/napi-pallas/src/tx.rs index 2be1295..950f830 100644 --- a/napi-pallas/src/tx.rs +++ b/napi-pallas/src/tx.rs @@ -1,7 +1,8 @@ -use crate::validations::validate::validate; use crate::Validations; +use crate::{validations::validate::validate, ValidationContext}; use super::Section; +use pallas::ledger::traverse::Era; use pallas::{ codec::utils::KeepRaw, crypto::hash::Hasher, @@ -235,13 +236,23 @@ pub fn create_cbor_structure(tx: &MultiEraTx<'_>) -> Section { out } -pub fn parse(raw: String) -> Result<(Section, Validations), Section> { +pub fn parse(raw: String, context: ValidationContext) -> Result<(Section, Validations), Section> { let res_cbor = hex::decode(raw); + let mut era_decode = Era::Babbage; + match context.era.as_str() { + "Alonzo" => era_decode = Era::Alonzo, + "Babbage" => era_decode = Era::Babbage, + "Byron" => era_decode = Era::Byron, + "Conway" => era_decode = Era::Conway, + "Shelley MA" => era_decode = Era::Shelley, + // This case should never happen + _ => {} + } match res_cbor { Ok(cbor) => { - let res_mtx = MultiEraTx::decode(&cbor); + let res_mtx = MultiEraTx::decode_for_era(era_decode, &cbor); match res_mtx { - Ok(mtx) => Ok((create_cbor_structure(&mtx), validate(&mtx))), + Ok(mtx) => Ok((create_cbor_structure(&mtx), validate(&mtx, context))), Err(e) => { let mut err = Section::new(); err.error = Some(e.to_string()); diff --git a/napi-pallas/src/validations/alonzo.rs b/napi-pallas/src/validations/alonzo.rs index 8393406..2fff5de 100644 --- a/napi-pallas/src/validations/alonzo.rs +++ b/napi-pallas/src/validations/alonzo.rs @@ -1,8 +1,331 @@ -use pallas::ledger::primitives::alonzo::MintedTx; +use pallas::{ + applying::{ + alonzo::{ + check_auxiliary_data, check_fee, check_ins_and_collateral_in_utxos, 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_witness_set, + }, + utils::{get_alonzo_comp_tx_size, AlonzoProtParams}, + Environment, MultiEraProtocolParameters, UTxOs, + }, + ledger::primitives::{ + alonzo::{ExUnitPrices, Language, MintedTx, TransactionBody}, + conway::{ExUnits, Nonce, NonceVariant, RationalNumber}, + }, +}; -use crate::Validations; +use crate::{Validation, ValidationContext, Validations}; -pub fn validate_alonzo(mtx_a: &MintedTx) -> Validations { - let out = Validations::new(); +use super::validate::set_description; +use pallas::codec::utils::KeyValuePairs; + +// & The following validation requires the size and the protocol params +fn validate_alonzo_tx_size(size: &Option, prot_pps: &AlonzoProtParams) -> Validation { + match size { + Some(size_value) => { + let res = check_tx_size(&size_value, &prot_pps); + let description = set_description( + &res, + "The transaction size does not exceed the protocol limit.".to_string(), + ); + return Validation::new() + .with_name("Transaction size".to_string()) + .with_value(res.is_ok()) + .with_description(description); + } + None => { + return Validation::new() + .with_name("Transaction size".to_string()) + .with_value(false) + .with_description("The transaction size could not be obtained.".to_string()); + } + } +} + +// & The following validations require the transaction +fn validate_alonzo_ins_not_empty(mtx_a: &MintedTx) -> Validation { + let res = check_ins_not_empty(&mtx_a.transaction_body); + 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_alonzo_auxiliary_data(mtx_a: &MintedTx) -> Validation { + let tx_body: &TransactionBody = &mtx_a.transaction_body; + let res = check_auxiliary_data(&tx_body, mtx_a); + let description = set_description( + &res, + "The auxiliary data of the transaction is valid.".to_string(), + ); + return Validation::new() + .with_name("Auxiliary data".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +fn validate_alonzo_script_data_hash(mtx_a: &MintedTx) -> Validation { + let tx_body: &TransactionBody = &mtx_a.transaction_body; + let res = check_script_data_hash(&tx_body, mtx_a); + let description = set_description( + &res, + "The script data integrity hash matches the hash of the redeemers, languages and datums of the transaction witness set.".to_string(), + ); + return Validation::new() + .with_name("Script data hash".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +fn validate_alonzo_minting(mtx_a: &MintedTx) -> Validation { + let tx_body: &TransactionBody = &mtx_a.transaction_body; + let res = check_minting(&tx_body, mtx_a); + 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".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +// & The following validations require the transaction and the protocol params +fn validate_alonzo_min_lovelace(mtx_a: &MintedTx, prot_pps: &AlonzoProtParams) -> Validation { + let tx_body: &TransactionBody = &mtx_a.transaction_body; + let res = check_min_lovelace(&tx_body, &prot_pps); + let description = set_description( + &res, + "All transaction outputs (regular outputs and collateral outputs) should contain 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_alonzo_output_val_size(mtx_a: &MintedTx, prot_pps: &AlonzoProtParams) -> Validation { + let tx_body: &TransactionBody = &mtx_a.transaction_body; + let res = check_output_val_size(&tx_body, &prot_pps); + let description = set_description( + &res, + "The size of the value in each of the transaction outputs (regular outputs and collateral outputs) is not greater than the maximum allowed".to_string(), + ); + return Validation::new() + .with_name("Minimum lovelace".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +fn validate_alonzo_tx_ex_units(mtx_a: &MintedTx, prot_pps: &AlonzoProtParams) -> Validation { + let res = check_tx_ex_units(mtx_a, &prot_pps); + let description = set_description( + &res, + "The size of the value in each of the transaction outputs (regular outputs and collateral outputs) is not greater than the maximum allowed".to_string(), + ); + return Validation::new() + .with_name("Minimum lovelace".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +fn validate_alonzo_languages(mtx_a: &MintedTx, prot_pps: &AlonzoProtParams) -> Validation { + let res = check_languages(mtx_a, &prot_pps); + let description = set_description( + &res, + "The required script languages are included in the protocol parameters.".to_string(), + ); + return Validation::new() + .with_name("Minimum lovelace".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +// & The following validation requires the transaction and the network id +fn validate_alonzo_network_id(mtx_a: &MintedTx, network_id: &u8) -> Validation { + let res = check_network_id(&mtx_a.transaction_body, network_id); + let description = set_description( + &res, + "The network ID of each output matches the global network ID.".to_string(), + ); + return Validation::new() + .with_name("Network ID".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +// & The following validations require the transaction and the block slot +fn validate_alonzo_tx_validity_interval(mtx_a: &MintedTx, block_slot: &u64) -> Validation { + let res = check_tx_validity_interval(&mtx_a.transaction_body, mtx_a, block_slot); + let description = set_description( + &res, + "The upper bound of the validity time interval is suitable for script execution: if there are minting policies, native scripts or Plutus scripts involved in the transaction, and if the upper bound of its validity interval is a finite number, then it can be translated to system time.".to_string(), + ); + return Validation::new() + .with_name("Transaction validity interval".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +// & The following validations require the transaction and its utxos +fn validate_alonzo_ins_and_collateral_in_utxos(mtx_a: &MintedTx, utxos: &UTxOs) -> Validation { + let res = check_ins_and_collateral_in_utxos(&mtx_a.transaction_body, utxos); + let description = set_description( + &res, + "All transaction inputs and collateral inputs are in the set of (yet) unspent transaction outputs.".to_string(), + ); + return Validation::new() + .with_name("Inputs and collateral in UTxOs".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +fn validate_alonzo_preservation_of_value(mtx_a: &MintedTx, utxos: &UTxOs) -> Validation { + let res = check_preservation_of_value(&mtx_a.transaction_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); +} + +fn validate_alonzo_witness_set(mtx_a: &MintedTx, utxos: &UTxOs) -> Validation { + let res = check_witness_set(mtx_a, utxos); + let description = set_description(&res, "The transaction witness set is valid.".to_string()); + return Validation::new() + .with_name("Witness set".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +// & The following validation requires the transaction, its utxos and the protocol params +fn validate_alonzo_fee(mtx_a: &MintedTx, utxos: &UTxOs, prot_pps: &AlonzoProtParams) -> Validation { + let tx_body: &TransactionBody = &mtx_a.transaction_body; + let size: &Option = &get_alonzo_comp_tx_size(tx_body); + match size { + Some(size_value) => { + let res = check_fee(tx_body, size_value, mtx_a, utxos, prot_pps); + let description = set_description( + &res, + "The fee paid by the transaction should be greater than or equal to the minimum fee." + .to_string(), + ); + return Validation::new() + .with_name("Fee".to_string()) + .with_value(res.is_ok()) + .with_description(description); + } + None => { + return Validation::new() + .with_name("Fee".to_string()) + .with_value(false) + .with_description("The size could not be obtained.".to_string()); + } + } +} + +pub fn validate_alonzo(mtx_a: &MintedTx, context: ValidationContext) -> Validations { + let tx_body: &TransactionBody = &mtx_a.transaction_body; + let size: &Option = &get_alonzo_comp_tx_size(tx_body); + let prot_params = AlonzoProtParams { + minfee_a: context.min_fee_a, + minfee_b: context.min_fee_b, + max_block_body_size: context.max_block_size, + max_transaction_size: context.max_tx_size, + max_block_header_size: context.max_block_header_size, + key_deposit: context.key_deposit as u64, + pool_deposit: context.pool_deposit as u64, + maximum_epoch: context.e_max as u64, + desired_number_of_stake_pools: context.n_opt, + pool_pledge_influence: RationalNumber { + numerator: context.a0_numerator as u64, + denominator: context.a0_denominator as u64, + }, + expansion_rate: RationalNumber { + numerator: context.rho_numerator as u64, + denominator: context.rho_denominator as u64, + }, + treasury_growth_rate: RationalNumber { + numerator: context.tau_numerator as u64, + denominator: context.tau_denominator as u64, + }, + decentralization_constant: RationalNumber { + numerator: context.decentralisation_param_numerator as u64, + denominator: context.decentralisation_param_denominator as u64, + }, + extra_entropy: Nonce { + variant: NonceVariant::NeutralNonce, + hash: None, + }, + protocol_version: ( + context.protocol_minor_ver as u64, + context.protocol_major_ver as u64, + ), + min_pool_cost: context.min_pool_cost as u64, + cost_models_for_script_languages: KeyValuePairs::Def(vec![(Language::PlutusV1, vec![])]), + ada_per_utxo_byte: context.coins_per_utxo_size as u64, + execution_costs: ExUnitPrices { + mem_price: RationalNumber { + numerator: context.price_mem_numerator as u64, + denominator: context.price_mem_denominator as u64, + }, + step_price: RationalNumber { + numerator: context.price_step_numerator as u64, + denominator: context.price_step_denominator as u64, + }, + }, + max_tx_ex_units: ExUnits { + mem: context.max_tx_ex_mem, + steps: context.max_tx_ex_steps as u64, + }, + max_block_ex_units: ExUnits { + mem: context.max_block_ex_mem, + steps: context.max_block_ex_steps as u64, + }, + max_value_size: context.max_val_size, + collateral_percentage: context.collateral_percent, + max_collateral_inputs: context.max_collateral_inputs, + }; + + let mut magic = 764824073; // For mainnet + if context.network == "Preprod" { + magic = 1; + } else if context.network == "Preview" { + magic = 2; + } + + let mut net_id = 1; // For mainnet + if context.network == "Preprod" || context.network == "Preview" { + net_id = 0; + } + + let env: Environment = Environment { + prot_params: MultiEraProtocolParameters::Alonzo(prot_params.clone()), + prot_magic: magic, + block_slot: context.block_slot as u64, + network_id: net_id, + }; + let out = Validations::new() + .with_era("Alonzo".to_string()) + .add_new_validation(validate_alonzo_tx_size(size, &prot_params)) + .add_new_validation(validate_alonzo_ins_not_empty(mtx_a)) + .add_new_validation(validate_alonzo_auxiliary_data(mtx_a)) + .add_new_validation(validate_alonzo_script_data_hash(mtx_a)) + .add_new_validation(validate_alonzo_minting(mtx_a)) + .add_new_validation(validate_alonzo_min_lovelace(mtx_a, &prot_params)) + .add_new_validation(validate_alonzo_output_val_size(mtx_a, &prot_params)) + .add_new_validation(validate_alonzo_tx_ex_units(mtx_a, &prot_params)) + .add_new_validation(validate_alonzo_languages(mtx_a, &prot_params)) + .add_new_validation(validate_alonzo_network_id(mtx_a, &env.network_id)) + .add_new_validation(validate_alonzo_tx_validity_interval(mtx_a, &env.block_slot)); out } diff --git a/napi-pallas/src/validations/babbage.rs b/napi-pallas/src/validations/babbage.rs index 9006454..e75ce8c 100644 --- a/napi-pallas/src/validations/babbage.rs +++ b/napi-pallas/src/validations/babbage.rs @@ -1,22 +1,391 @@ -use crate::{Validation, Validations}; +use crate::{Validation, ValidationContext, Validations}; use pallas::{ - applying::babbage::check_ins_not_empty, - ledger::primitives::babbage::{MintedTransactionBody, MintedTx as BabbageMintedTx}, + 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}, + Environment, MultiEraProtocolParameters, UTxOs, + }, + ledger::{ + primitives::{ + alonzo::ExUnitPrices, + babbage::{CostMdls, MintedTransactionBody, MintedTx as BabbageMintedTx}, + conway::{Nonce, NonceVariant, RationalNumber}, + }, + traverse::update::ExUnits, + }, }; 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); } -pub fn validate_babbage(mtx_b: &BabbageMintedTx) -> Validations { - let out = Validations::new().add_new_validation(validate_babbage_ins_not_empty(&mtx_b)); +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, context: ValidationContext) -> Validations { + let tx_body: &MintedTransactionBody = &mtx_b.transaction_body.clone(); + let size: &Option = &get_babbage_tx_size(tx_body); + let prot_params = BabbageProtParams { + minfee_a: context.min_fee_a, + minfee_b: context.min_fee_b, + max_block_body_size: context.max_block_size, + max_transaction_size: context.max_tx_size, + max_block_header_size: context.max_block_header_size, + key_deposit: context.key_deposit as u64, + pool_deposit: context.pool_deposit as u64, + maximum_epoch: context.e_max as u64, + desired_number_of_stake_pools: context.n_opt, + pool_pledge_influence: RationalNumber { + numerator: context.a0_numerator as u64, + denominator: context.a0_denominator as u64, + }, + expansion_rate: RationalNumber { + numerator: context.rho_numerator as u64, + denominator: context.rho_denominator as u64, + }, + treasury_growth_rate: RationalNumber { + numerator: context.tau_numerator as u64, + denominator: context.tau_denominator as u64, + }, + decentralization_constant: RationalNumber { + numerator: context.decentralisation_param_numerator as u64, + denominator: context.decentralisation_param_denominator as u64, + }, + extra_entropy: Nonce { + variant: NonceVariant::NeutralNonce, + hash: None, + }, + protocol_version: ( + context.protocol_minor_ver as u64, + context.protocol_major_ver as u64, + ), + min_pool_cost: context.min_pool_cost as u64, + cost_models_for_script_languages: CostMdls { + plutus_v1: None, + plutus_v2: None, + }, + ada_per_utxo_byte: context.coins_per_utxo_size as u64, + execution_costs: ExUnitPrices { + mem_price: RationalNumber { + numerator: context.price_mem_numerator as u64, + denominator: context.price_mem_denominator as u64, + }, + step_price: RationalNumber { + numerator: context.price_step_numerator as u64, + denominator: context.price_step_denominator as u64, + }, + }, + max_tx_ex_units: ExUnits { + mem: context.max_tx_ex_mem, + steps: context.max_tx_ex_steps as u64, + }, + max_block_ex_units: ExUnits { + mem: context.max_block_ex_mem, + steps: context.max_block_ex_steps as u64, + }, + max_value_size: context.max_val_size, + collateral_percentage: context.collateral_percent, + max_collateral_inputs: context.max_collateral_inputs, + }; + + let mut magic = 764824073; // For mainnet + if context.network == "Preprod" { + magic = 1; + } else if context.network == "Preview" { + magic = 2; + } + + let mut net_id = 1; // For mainnet + if context.network == "Preprod" || context.network == "Preview" { + net_id = 0; + } + + let env: Environment = Environment { + prot_params: MultiEraProtocolParameters::Babbage(prot_params.clone()), + prot_magic: magic, + block_slot: context.block_slot as u64, + network_id: net_id, + }; + + let out = Validations::new() + .with_era(context.era.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..bee436d 100644 --- a/napi-pallas/src/validations/byron.rs +++ b/napi-pallas/src/validations/byron.rs @@ -1,6 +1,151 @@ -use crate::Validations; -use pallas::ledger::primitives::byron::MintedTxPayload; +use crate::{Validation, Validations}; +use pallas::{ + applying::{ + byron::{ + check_fees, check_ins_in_utxos, check_ins_not_empty, check_outs_have_lovelace, + check_outs_not_empty, check_size, check_witnesses, + }, + utils::ByronProtParams, + UTxOs, + }, + codec::minicbor::encode, + ledger::primitives::byron::{MintedTxPayload, Tx}, +}; + +use super::validate::set_description; + +fn get_tx_size(tx: &Tx) -> u64 { + let mut buff: Vec = Vec::new(); + if encode(tx, &mut buff).is_ok() { + return buff.len() as u64; + } else { + return 0; + } +} + +// & The following validation requires the size and the protocol parameters +fn validate_byron_size(size: &u64, prot_pps: &ByronProtParams) -> Validation { + if size == &0 { + return Validation::new() + .with_name("Transaction size".to_string()) + .with_value(false) + .with_description("The transaction size could not be obtained.".to_string()); + } + let res = check_size(&size, &prot_pps); + let description = set_description( + &res, + "The transaction size does not exceed the protocol limit.".to_string(), + ); + return Validation::new() + .with_name("Transaction size".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +// & The following validations require the transaction +fn validate_byron_ins_not_empty(tx: &Tx) -> Validation { + let res = check_ins_not_empty(tx); + 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_byron_outs_not_empty(tx: &Tx) -> Validation { + let res = check_outs_not_empty(tx); + let description = set_description( + &res, + "The set of transaction outputs is not empty.".to_string(), + ); + return Validation::new() + .with_name("Non empty outputs".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +fn validate_byron_outs_have_lovelace(tx: &Tx) -> Validation { + let res = check_outs_have_lovelace(tx); + let description = set_description( + &res, + "All transaction outputs contain non-null Lovelace values.".to_string(), + ); + return Validation::new() + .with_name("Outputs have lovelace".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +// & The following validations require the transaction and the UTXOs +fn validate_byron_ins_in_utxos(tx: &Tx, utxos: &UTxOs) -> Validation { + let res = check_ins_in_utxos(tx, utxos); + let description = set_description( + &res, + "All transaction inputs are in the set of (yet) unspent transaction outputs.".to_string(), + ); + return Validation::new() + .with_name("Inputs in UTXOs".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +// & The following validations require the transaction, the UTXOs and the protocol magic +fn validate_byron_witnesses(tx: &MintedTxPayload, utxos: &UTxOs, prot_magic: u32) -> Validation { + let res = check_witnesses(&tx, &utxos, &prot_magic); + let description = set_description(&res, "All transaction witnesses are valid.".to_string()); + return Validation::new() + .with_name("Witnesses".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +// & The following validations require the transaction, the size, the UTXOs and the protocol parameters +fn validate_byron_fees( + tx: &Tx, + size: &u64, + utxos: &UTxOs, + prot_pps: &ByronProtParams, +) -> Validation { + let res = check_fees(&tx, &size, &utxos, &prot_pps); + let description = set_description( + &res, + "Fees are not less than what is determined by the protocol.".to_string(), + ); + return Validation::new() + .with_name("Fees".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + pub fn validate_byron(mtxp: &MintedTxPayload) -> Validations { - let out = Validations::new(); + let tx: &Tx = &mtxp.transaction; + let size: &u64 = &get_tx_size(&tx); + let prot_pps: ByronProtParams = ByronProtParams { + script_version: 0, + slot_duration: 20000, + max_block_size: 2000000, + max_header_size: 2000000, + max_tx_size: 4096, + max_proposal_size: 700, + mpc_thd: 20000000000000, + heavy_del_thd: 300000000000, + update_vote_thd: 1000000000000, + update_proposal_thd: 100000000000000, + update_implicit: 10000, + soft_fork_rule: (900000000000000, 600000000000000, 50000000000000), + summand: 155381, + multiplier: 44, + unlock_stake_epoch: 18446744073709551615, + }; + + let out = Validations::new() + .with_era("Byron".to_string()) + .add_new_validation(validate_byron_size(&size, &prot_pps)) + .add_new_validation(validate_byron_ins_not_empty(&tx)) + .add_new_validation(validate_byron_outs_not_empty(&tx)) + .add_new_validation(validate_byron_outs_have_lovelace(&tx)); out } diff --git a/napi-pallas/src/validations/conway.rs b/napi-pallas/src/validations/conway.rs index fa1ca30..e92306d 100644 --- a/napi-pallas/src/validations/conway.rs +++ b/napi-pallas/src/validations/conway.rs @@ -2,6 +2,12 @@ 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()) + .add_new_validation(crate::Validation { + name: "Coming soon...".to_string(), + value: true, + description: "Coming soon...".to_string(), + }); out } diff --git a/napi-pallas/src/validations/shelley_ma.rs b/napi-pallas/src/validations/shelley_ma.rs index 3778f67..569e642 100644 --- a/napi-pallas/src/validations/shelley_ma.rs +++ b/napi-pallas/src/validations/shelley_ma.rs @@ -1,8 +1,280 @@ -use pallas::ledger::primitives::alonzo::MintedTx; +use pallas::{ + applying::{ + shelley_ma::{ + check_fees, check_ins_in_utxos, check_ins_not_empty, check_metadata, check_min_lovelace, + check_minting, check_network_id, check_preservation_of_value, check_ttl, check_tx_size, + check_witnesses, + }, + utils::{get_alonzo_comp_tx_size, ShelleyProtParams}, + Environment, MultiEraProtocolParameters, UTxOs, + }, + ledger::{ + primitives::{ + alonzo::{MintedTx, MintedWitnessSet, TransactionBody}, + conway::{NonceVariant, RationalNumber}, + }, + traverse::{update::Nonce, Era}, + }, +}; -use crate::Validations; +use crate::{Validation, ValidationContext, Validations}; -pub fn validate_shelley_ma(mtx_sma: &MintedTx) -> Validations { - let out = Validations::new(); +use super::validate::set_description; + +// & The following validation requires the size and the protocol parameters +fn validate_shelley_ma_tx_size(size: &Option, prot_pps: &ShelleyProtParams) -> Validation { + match size { + Some(size_value) => { + let res = check_tx_size(&size_value, &prot_pps); + let description = set_description( + &res, + "The transaction size does not exceed the protocol limit.".to_string(), + ); + return Validation::new() + .with_name("Transaction size".to_string()) + .with_value(res.is_ok()) + .with_description(description); + } + None => { + return Validation::new() + .with_name("Transaction size".to_string()) + .with_value(false) + .with_description("The transaction size could not be obtained.".to_string()); + } + } +} + +// & The following validations require the transaction +fn validate_shelley_ma_ins_not_empty(mtx_sma: &MintedTx) -> Validation { + let res = check_ins_not_empty(&mtx_sma.transaction_body); + 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_shelley_ma_metadata(mtx_sma: &MintedTx) -> Validation { + let tx_body = &mtx_sma.transaction_body; + let res = check_metadata(&tx_body, mtx_sma); + let description = set_description( + &res, + "The metadata of the transaction is valid.".to_string(), + ); + return Validation::new() + .with_name("Metadata".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +fn validate_shelley_ma_minting(mtx_sma: &MintedTx) -> Validation { + let tx_body = &mtx_sma.transaction_body; + let res = check_minting(&tx_body, mtx_sma); + let description = set_description(&res, "The minting of the transaction is valid.".to_string()); + return Validation::new() + .with_name("Minting".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +// & The following validations require the transaction the protocol parameters +fn validate_shelley_ma_min_lovelace( + mtx_sma: &MintedTx, + prot_pps: &ShelleyProtParams, +) -> Validation { + let tx_body = &mtx_sma.transaction_body; + let res = check_min_lovelace(&tx_body, &prot_pps); + let description = set_description( + &res, + "All transaction outputs contain Lovelace values not under the minimum.".to_string(), + ); + return Validation::new() + .with_name("Minimum lovelace".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +// & The following validations require the transaction the size and the protocol parameters +fn validate_shelley_ma_fees( + mtx_sma: &MintedTx, + size: &Option, + prot_pps: &ShelleyProtParams, +) -> Validation { + match size { + Some(size_value) => { + let res = check_fees(&mtx_sma.transaction_body, &size_value, &prot_pps); + let description = set_description( + &res, + "The fee paid by the transaction has to be greater than or equal to the minimum fee." + .to_string(), + ); + return Validation::new() + .with_name("Fees".to_string()) + .with_value(res.is_ok()) + .with_description(description); + } + None => { + return Validation::new() + .with_name("Fees".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_shelley_ma_ins_in_utxos(mtx_sma: &MintedTx, utxos: &UTxOs) -> Validation { + let tx_body = &mtx_sma.transaction_body; + let res = check_ins_in_utxos(tx_body, &utxos); + let description = set_description( + &res, + "All transaction inputs are in the set of (yet) unspent transaction outputs.".to_string(), + ); + return Validation::new() + .with_name("Inputs in UTxOs".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +// & The following validations require the transaction its utxos and the era +fn validate_shelley_ma_preservation_of_value( + mtx_sma: &MintedTx, + utxos: &UTxOs, + era: &Era, +) -> Validation { + let tx_body = &mtx_sma.transaction_body; + let res = check_preservation_of_value(tx_body, &utxos, era); + 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 validations require the transaction its witnesses set and its utxos +fn validate_shelley_ma_witnesses( + mtx_sma: &MintedTx, + tx_wits: &MintedWitnessSet, + utxos: &UTxOs, +) -> Validation { + let tx_body = &mtx_sma.transaction_body; + let res = check_witnesses(&tx_body, &tx_wits, &utxos); + let description = set_description( + &res, + " The owner of each transaction input signed the transaction.".to_string(), + ); + return Validation::new() + .with_name("Witnesses".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +// & The following validations require the transaction and the block slot +fn validate_shelley_ma_ttl(mtx_sma: &MintedTx, block_slot: &u64) -> Validation { + let tx_body = &mtx_sma.transaction_body; + let res = check_ttl(&tx_body, &block_slot); + let description = set_description( + &res, + "The TTL limit of the transaction has not been exceeded.".to_string(), + ); + return Validation::new() + .with_name("TTL".to_string()) + .with_value(res.is_ok()) + .with_description(description); +} + +// & The following validations require the transaction and the network id +fn validate_shelley_ma_network_id(mtx_sma: &MintedTx, network_id: &u8) -> Validation { + let tx_body = &mtx_sma.transaction_body; + let res = check_network_id(&tx_body, &network_id); + let description = set_description( + &res, + "The network ID of each output matches 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_shelley_ma( + mtx_sma: &MintedTx, + era: &Era, + context: ValidationContext, +) -> Validations { + let tx_body: &TransactionBody = &mtx_sma.transaction_body; + let tx_wits: &MintedWitnessSet = &mtx_sma.transaction_witness_set; + let size: &Option = &get_alonzo_comp_tx_size(tx_body); + let prot_params = ShelleyProtParams { + minfee_a: context.min_fee_a, + minfee_b: context.min_fee_b, + max_block_body_size: context.max_block_size, + max_transaction_size: context.max_tx_size, + max_block_header_size: context.max_block_header_size, + key_deposit: context.key_deposit as u64, + pool_deposit: context.pool_deposit as u64, + maximum_epoch: context.e_max as u64, + desired_number_of_stake_pools: context.n_opt, + pool_pledge_influence: RationalNumber { + numerator: context.a0_numerator as u64, + denominator: context.a0_denominator as u64, + }, + expansion_rate: RationalNumber { + numerator: context.rho_numerator as u64, + denominator: context.rho_denominator as u64, + }, + treasury_growth_rate: RationalNumber { + numerator: context.tau_numerator as u64, + denominator: context.tau_denominator as u64, + }, + decentralization_constant: RationalNumber { + numerator: context.decentralisation_param_numerator as u64, + denominator: context.decentralisation_param_denominator as u64, + }, + extra_entropy: Nonce { + variant: NonceVariant::NeutralNonce, + hash: None, + }, + protocol_version: ( + context.protocol_minor_ver as u64, + context.protocol_major_ver as u64, + ), + min_utxo_value: context.min_utxo as u64, + }; + + let mut magic = 764824073; // For mainnet + if context.network == "Preprod" { + magic = 1; + } else if context.network == "Preview" { + magic = 2; + } + + let mut net_id = 1; // For mainnet + if context.network == "Preprod" || context.network == "Preview" { + net_id = 0; + } + + let env: Environment = Environment { + prot_params: MultiEraProtocolParameters::Shelley(prot_params.clone()), + prot_magic: magic, + block_slot: context.block_slot as u64, + network_id: net_id, + }; + let out = Validations::new() + .with_era("Shelley Mary Allegra".to_string()) + .add_new_validation(validate_shelley_ma_tx_size(size, &prot_params)) + .add_new_validation(validate_shelley_ma_ins_not_empty(&mtx_sma)) + .add_new_validation(validate_shelley_ma_metadata(&mtx_sma)) + .add_new_validation(validate_shelley_ma_minting(&mtx_sma)) + .add_new_validation(validate_shelley_ma_min_lovelace(&mtx_sma, &prot_params)) + .add_new_validation(validate_shelley_ma_fees(&mtx_sma, &size, &prot_params)) + .add_new_validation(validate_shelley_ma_ttl(&mtx_sma, &env.block_slot)) + .add_new_validation(validate_shelley_ma_network_id(&mtx_sma, &env.network_id)); out } diff --git a/napi-pallas/src/validations/validate.rs b/napi-pallas/src/validations/validate.rs index 8a43c6e..026e47d 100644 --- a/napi-pallas/src/validations/validate.rs +++ b/napi-pallas/src/validations/validate.rs @@ -1,5 +1,5 @@ use crate::validations::babbage::validate_babbage; -use crate::Validations; +use crate::{ValidationContext, Validations}; use pallas::applying::utils::ValidationError; use pallas::ledger::traverse::{Era, MultiEraTx}; @@ -12,18 +12,24 @@ 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), } } -pub fn validate(mtx: &MultiEraTx<'_>) -> Validations { +pub fn validate(mtx: &MultiEraTx<'_>, context: ValidationContext) -> Validations { match &mtx { MultiEraTx::Byron(mtxp) => validate_byron(&mtxp), - MultiEraTx::AlonzoCompatible(mtx_sma, Era::Shelley) - | MultiEraTx::AlonzoCompatible(mtx_sma, Era::Allegra) - | MultiEraTx::AlonzoCompatible(mtx_sma, Era::Mary) => validate_shelley_ma(&mtx_sma), - MultiEraTx::AlonzoCompatible(mtx_a, Era::Alonzo) => validate_alonzo(&mtx_a), - MultiEraTx::Babbage(mtx_b) => validate_babbage(&mtx_b), + MultiEraTx::AlonzoCompatible(mtx_sma, Era::Shelley) => { + validate_shelley_ma(&mtx_sma, &Era::Shelley, context) + } + MultiEraTx::AlonzoCompatible(mtx_sma, Era::Allegra) => { + validate_shelley_ma(&mtx_sma, &Era::Allegra, context) + } + MultiEraTx::AlonzoCompatible(mtx_sma, Era::Mary) => { + validate_shelley_ma(&mtx_sma, &Era::Mary, context) + } + MultiEraTx::AlonzoCompatible(mtx_a, Era::Alonzo) => validate_alonzo(&mtx_a, context), + MultiEraTx::Babbage(mtx_b) => validate_babbage(&mtx_b, context), MultiEraTx::Conway(mtx_c) => validate_conway(&mtx_c), // This case is impossible. TODO: Handle error _ => Validations::new(), diff --git a/web/app/components.tsx b/web/app/components.tsx deleted file mode 100644 index d305930..0000000 --- a/web/app/components.tsx +++ /dev/null @@ -1,257 +0,0 @@ -import { Attribute, type Section } from "napi-pallas"; -import { PropsWithChildren, useState } from "react"; -import { DataProps, IValidation } from "./routes/tx"; - -export type TopicMeta = { - title: string; - description?: React.ReactElement; -}; - -function getTopicMeta( - key: string | undefined, - all: Record -): TopicMeta { - return ( - all[key!] || { - title: key, - } - ); -} - -export function PropBlock(props: { - data: Attribute; - topics: Record; -}) { - const topic = getTopicMeta(props.data.topic, props.topics); - - return ( -
- {topic.description} -
-
{topic.title}
- {props.data.value || "(empty)"} -
-
- ); -} - -export function HexBlock(props: { name: string; value: string }) { - return ( -
-
{props.name}
- {props.value} -
- ); -} - -export function Paragraph(props: PropsWithChildren) { - return

{props.children}

; -} - -export const P1 = Paragraph; - -export function RootSection(props: { - data: Section; - topics: Record; - validations: IValidation[]; -}) { - const [open, setOpen] = useState(false); - const handleClick = () => setOpen(!open); - const topic = getTopicMeta(props.data.topic, props.topics); - - if (props.data.error) - return ( -
-

{topic.description}

- {props.data.error} -
- ); - - return ( -
-
- - {open && } -
-

{topic.title}

- {!!props.data.bytes && ( - - )} - {props.data.attributes?.map((c) => ( - - ))} - {props.data.children?.map((c) => ( - - ))} -
- ); -} - -export function DataSection(props: { - data: Section; - topics: Record; -}) { - const topic = getTopicMeta(props.data.topic, props.topics); - const [open, setOpen] = useState(true); - const handleClick = () => setOpen(!open); - - return ( -
- - {open && ( - <> - {topic.description} - {!!props.data.error && ( -
- {props.data.error} -
- )} - {props.data.attributes?.map((c) => ( - - ))} - {props.data.children?.map((c) => ( - - ))} - {!props.data.attributes?.length && !props.data.children?.length && ( - - )} - {!!props.data.bytes && ( - - )} - - )} -
- ); -} - -export function EmptyBlock() { - return ( -
- Empty -
- ); -} - -export function Button( - props: PropsWithChildren<{ type: "submit" | "button" }> -) { - return ( - - ); -} - -export function TextArea(props: { name: string; placeholder?: string }) { - return ( -