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 (
-
- );
-}
-
-export function logCuriosity(data: DataProps) {
- if (data) {
- console.group("CURIOUS FELLOW, EH?");
- console.log("hello there! want to learn how we parse the data?");
- console.log(
- "we use the Pallas library on the backend. Pallas is a Rust library for Cardano. We compile it to native code and use FFI to trigger the logic from a NodeJS process."
- );
- console.log("https://github.com/txpipe/pallas");
- console.log(
- "here's the json payload for the data you see rendered on-screen:"
- );
- console.log(data);
- console.groupEnd();
- }
-}
-
-export function AccordionItem({ validation }: { validation: IValidation }) {
- const [open, setOpen] = useState(false);
- const handleClick = () => setOpen(!open);
- return (
-
-
-
-
- +
-
-
-
- {open && (
-
{validation.description}
- )}
-
-
- );
-}
-
-export function ValidationAccordion(props: { validations: IValidation[] }) {
- return (
-
- {props.validations.map((v) => (
-
- ))}
-
- );
-}
diff --git a/web/app/components/Button.tsx b/web/app/components/Button.tsx
new file mode 100644
index 0000000..fa7775e
--- /dev/null
+++ b/web/app/components/Button.tsx
@@ -0,0 +1,33 @@
+import { ButtonHTMLAttributes } from "react";
+
+interface ButtonProps extends ButtonHTMLAttributes {
+ type: "submit" | "button";
+ className?: string;
+ color?: "blue" | "pink";
+}
+
+export const Button = ({
+ type,
+ children,
+ className: customClassName,
+ color = "blue",
+ onClick: onClickFn,
+}: ButtonProps) => {
+ return (
+
+ );
+};
diff --git a/web/app/components/DataSection.tsx b/web/app/components/DataSection.tsx
new file mode 100644
index 0000000..9d72d54
--- /dev/null
+++ b/web/app/components/DataSection.tsx
@@ -0,0 +1,54 @@
+import { Section } from "napi-pallas";
+import { useState } from "react";
+import { getTopicMeta } from "../utils";
+import { EmptyBlock, HexBlock, PropBlock, TopicMeta } from "./constructors";
+
+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 && (
+
+ )}
+ >
+ )}
+
+ );
+}
diff --git a/web/app/components/Input.tsx b/web/app/components/Input.tsx
new file mode 100644
index 0000000..b5825e3
--- /dev/null
+++ b/web/app/components/Input.tsx
@@ -0,0 +1,31 @@
+import { InputHTMLAttributes } from "react";
+
+interface InputProps extends InputHTMLAttributes {
+ name: string;
+ className?: string;
+}
+
+export const Input = ({
+ name,
+ disabled,
+ id,
+ type,
+ value,
+ onChange,
+ placeholder,
+ className: customClassName,
+}: InputProps) => {
+ return (
+
+ );
+};
diff --git a/web/app/components/RootSection.tsx b/web/app/components/RootSection.tsx
new file mode 100644
index 0000000..1b571b9
--- /dev/null
+++ b/web/app/components/RootSection.tsx
@@ -0,0 +1,56 @@
+import { Section } from "napi-pallas";
+import { useState } from "react";
+import { IValidation } from "../interfaces";
+import { getTopicMeta } from "../utils";
+import { HexBlock, PropBlock, TopicMeta } from "./constructors";
+import { DataSection, ValidationAccordion } from "./index";
+
+export function RootSection(props: {
+ data: Section;
+ topics: Record;
+ validations: IValidation[];
+ era: string;
+}) {
+ 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) => (
+
+ ))}
+
+ );
+}
diff --git a/web/app/components/Validations/Configurations/Configurations.tsx b/web/app/components/Validations/Configurations/Configurations.tsx
new file mode 100644
index 0000000..e46178e
--- /dev/null
+++ b/web/app/components/Validations/Configurations/Configurations.tsx
@@ -0,0 +1,60 @@
+import { useEffect } from "react";
+import { Button } from "../../../components";
+import { IContext, ProtocolType } from "../../../interfaces";
+import { Tabs } from "./Tabs";
+
+export function ConfigsModal({
+ closeModal,
+ protocolParams,
+ changeParam,
+ otherContext,
+ setOtherContext,
+}: {
+ closeModal: () => void;
+ protocolParams: ProtocolType[];
+ changeParam: (
+ index: number
+ ) => (e: React.ChangeEvent) => void;
+ otherContext: IContext;
+ setOtherContext: (c: IContext) => void;
+}) {
+ // To close config modal on esc press
+ useEffect(() => {
+ const handleKeyDown = (event: KeyboardEvent) => {
+ if (event.key === "Escape") {
+ closeModal();
+ }
+ };
+ document.addEventListener("keydown", handleKeyDown);
+
+ return () => {
+ document.removeEventListener("keydown", handleKeyDown);
+ };
+ }, [closeModal]);
+
+ return (
+
+
+
+
+
Tx Validation Configurations
+
+
+
+
+
+
+ );
+}
diff --git a/web/app/components/Validations/Configurations/Tabs.tsx b/web/app/components/Validations/Configurations/Tabs.tsx
new file mode 100644
index 0000000..00bde60
--- /dev/null
+++ b/web/app/components/Validations/Configurations/Tabs.tsx
@@ -0,0 +1,167 @@
+import { useState } from "react";
+import { Button, Input } from "../../../components";
+import {
+ EraType,
+ Eras,
+ IContext,
+ NetworkType,
+ Networks,
+ ProtocolType,
+ TabNames,
+ TabType,
+} from "../../../interfaces";
+import { ByronPptParams } from "../../../utils";
+
+export const Tabs = ({
+ otherContext,
+ protocolParams,
+ changeParam,
+ setOtherContext,
+}: {
+ otherContext: IContext;
+ protocolParams: ProtocolType[];
+ changeParam: (
+ index: number
+ ) => (e: React.ChangeEvent) => void;
+ setOtherContext: (c: IContext) => void;
+}) => {
+ const tabs: TabType[] = [TabNames.ProtocolParameters, TabNames.Others];
+ const networks: NetworkType[] = [
+ Networks.Mainnet,
+ Networks.Preprod,
+ Networks.Preview,
+ ];
+ const eras = ["Byron", "Shelley MA", "Alonzo", "Babbage", "Conway"];
+ const [selected, setSelected] = useState(
+ TabNames.ProtocolParameters
+ );
+
+ const changeSelected = (tab: TabType) => () => setSelected(tab);
+ const changeNetwork = (value: NetworkType) => () =>
+ setOtherContext({ ...otherContext, selectedNetwork: value });
+ const changeEra = (value: EraType) => () =>
+ setOtherContext({ ...otherContext, selectedEra: value });
+ const changeBlockSlot = (value: string) => {
+ setOtherContext({ ...otherContext, blockSlot: Number(value) });
+ };
+
+ const isByron = otherContext.selectedEra === Eras.Byron;
+ const paramsList = isByron ? ByronPptParams : protocolParams;
+
+ return (
+
+
+ {tabs.map((tab) => (
+
+ ))}
+
+
+
+ {paramsList.map((param, index) => (
+
+
+
+
+ ))}
+
+
+
+
+
Select a Network
+ {/* To get the network from the form*/}
+
+
+ {networks.map((net) => (
+
+ ))}
+
+
+
+
Select an Era
+ {/* To get the era from the form*/}
+
+
+ {eras.map((era) => (
+
+ ))}
+
+
+
+
Select a Block Slot
+
changeBlockSlot(e.target.value)}
+ className="focus:bg-pink-200"
+ />
+
+
+
+
+ );
+};
diff --git a/web/app/components/Validations/Information.tsx b/web/app/components/Validations/Information.tsx
new file mode 100644
index 0000000..906b97d
--- /dev/null
+++ b/web/app/components/Validations/Information.tsx
@@ -0,0 +1,73 @@
+import { useState } from "react";
+import { IValidation } from "../../interfaces";
+
+function AccordionItem({ validation }: { validation: IValidation }) {
+ const [open, setOpen] = useState(false);
+ const handleClick = () => setOpen(!open);
+ return (
+
+
+
+
{validation.description}
+
+
+ );
+}
+
+export function ValidationAccordion(props: { validations: IValidation[] }) {
+ return (
+
+ {props.validations.map((v) => (
+
+ ))}
+
+ );
+}
diff --git a/web/app/components/constructors.tsx b/web/app/components/constructors.tsx
new file mode 100644
index 0000000..c53f6b0
--- /dev/null
+++ b/web/app/components/constructors.tsx
@@ -0,0 +1,56 @@
+import { Attribute } from "napi-pallas";
+import { PropsWithChildren } from "react";
+import { getTopicMeta } from "../utils";
+
+export type TopicMeta = {
+ title: string;
+ description?: React.ReactElement;
+};
+
+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 function EmptyBlock() {
+ return (
+
+ Empty
+
+ );
+}
+
+export function TextArea(props: { name: string; placeholder?: string }) {
+ return (
+
+ );
+}
diff --git a/web/app/components/index.tsx b/web/app/components/index.tsx
new file mode 100644
index 0000000..aeb3225
--- /dev/null
+++ b/web/app/components/index.tsx
@@ -0,0 +1,13 @@
+export { Button } from "./Button";
+export { DataSection } from "./DataSection";
+export { Input } from "./Input";
+export { RootSection } from "./RootSection";
+export { ConfigsModal } from "./Validations/Configurations/Configurations";
+export { ValidationAccordion } from "./Validations/Information";
+export {
+ EmptyBlock,
+ HexBlock,
+ Paragraph as P1,
+ PropBlock,
+ TextArea,
+} from "./constructors";
diff --git a/web/app/interfaces.ts b/web/app/interfaces.ts
new file mode 100644
index 0000000..43895fb
--- /dev/null
+++ b/web/app/interfaces.ts
@@ -0,0 +1,54 @@
+import * as server from "./routes/tx.server";
+
+export interface IValidation {
+ name: string;
+ value: boolean;
+ description: string;
+}
+
+export interface IValidations {
+ validations: IValidation[];
+ era: string;
+}
+
+export interface DataProps extends server.Section {
+ validations: IValidation[];
+ era: string;
+ raw?: string;
+}
+
+export interface ProtocolType {
+ name: string;
+ value: number;
+}
+
+export const TabNames = {
+ ProtocolParameters: "Protocol Parameters",
+ Others: "Other Context",
+} as const;
+
+export type TabType = (typeof TabNames)[keyof typeof TabNames];
+
+export const Networks = {
+ Mainnet: "Mainnet",
+ Preprod: "Preprod",
+ Preview: "Preview",
+} as const;
+
+export type NetworkType = (typeof Networks)[keyof typeof Networks];
+
+export const Eras = {
+ Byron: "Byron",
+ ShelleyMA: "Shelley MA",
+ Alonzo: "Alonzo",
+ Babbage: "Babbage",
+ Conway: "Conway",
+} as const;
+
+export type EraType = (typeof Eras)[keyof typeof Eras];
+
+export interface IContext {
+ blockSlot: number;
+ selectedEra: EraType;
+ selectedNetwork: NetworkType;
+}
diff --git a/web/app/routes/address.tsx b/web/app/routes/address.tsx
index ec23474..fe2d497 100644
--- a/web/app/routes/address.tsx
+++ b/web/app/routes/address.tsx
@@ -1,13 +1,9 @@
-import {
- ActionFunctionArgs,
- json,
- LoaderFunctionArgs,
- type MetaFunction,
-} from "@remix-run/node";
-import { parseAddress } from "./address.server";
+import { ActionFunctionArgs, json, type MetaFunction } from "@remix-run/node";
import { Form, useActionData } from "@remix-run/react";
-import { Button, EmptyBlock, HexBlock, logCuriosity } from "~/components";
import { PropsWithChildren } from "react";
+import { Button, EmptyBlock, HexBlock } from "../components";
+import { logCuriosity } from "../utils";
+import { parseAddress } from "./address.server";
export const meta: MetaFunction = () => {
return [
diff --git a/web/app/routes/block-wip.tsx b/web/app/routes/block-wip.tsx
index 9b37051..f26e19b 100644
--- a/web/app/routes/block-wip.tsx
+++ b/web/app/routes/block-wip.tsx
@@ -1,14 +1,8 @@
import { ActionFunctionArgs, json, type MetaFunction } from "@remix-run/node";
-import { Form, useActionData, useSubmit } from "@remix-run/react";
+import { Form, useActionData } from "@remix-run/react";
+import { Button, RootSection, TextArea } from "../components";
+import { logCuriosity } from "../utils";
import * as server from "./block.server";
-import {
- Button,
- DataSection,
- logCuriosity,
- RootSection,
- TextArea,
-} from "~/components";
-import { KeyboardEventHandler } from "react";
import TOPICS from "./block.topics";
export const meta: MetaFunction = () => {
diff --git a/web/app/routes/tx.tsx b/web/app/routes/tx.tsx
index 1d1b4ac..35babf5 100644
--- a/web/app/routes/tx.tsx
+++ b/web/app/routes/tx.tsx
@@ -1,24 +1,20 @@
import { ActionFunctionArgs, json, type MetaFunction } from "@remix-run/node";
import { Form, useActionData } from "@remix-run/react";
-import { Button, logCuriosity, RootSection } from "../components";
+import { useState } from "react";
+import SettingsIcon from "../../public/settings.svg";
+import { Button, ConfigsModal, Input, RootSection } from "../components";
+import {
+ DataProps,
+ Eras,
+ IContext,
+ IValidation,
+ Networks,
+ ProtocolType,
+} from "../interfaces";
+import { decimalToFraction, initialProtPps, logCuriosity } from "../utils";
import * as server from "./tx.server";
import TOPICS from "./tx.topics";
-export interface IValidation {
- name: string;
- value: boolean;
- description: string;
-}
-
-export interface IValidations {
- validations: IValidation[];
-}
-
-export interface DataProps extends server.Section {
- validations: IValidation[];
- raw?: string;
-}
-
export const meta: MetaFunction = () => {
return [
{ title: "Cardano Tx - Lovelace Anatomy" },
@@ -29,9 +25,72 @@ export const meta: MetaFunction = () => {
export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
const raw = formData.get("raw");
+ const era = formData.get("Era");
+ const net = formData.get("Network");
+ const [a0Numerator, a0Denominator] = decimalToFraction(
+ Number(formData.get("A0"))
+ );
+ const [rhoNumerator, rhoDenominator] = decimalToFraction(
+ Number(formData.get("Rho"))
+ );
+ const [tauNumerator, tauDenominator] = decimalToFraction(
+ Number(formData.get("Tau"))
+ );
+ const [decentralisationParamNumerator, decentralisationParamDenominator] =
+ decimalToFraction(Number(formData.get("Decentralisation_param")));
+ const [extraEntropyNumerator, extraEntropyDenominator] = decimalToFraction(
+ Number(formData.get("Extra_entropy"))
+ );
+ const [priceMemNumerator, priceMemDenominator] = decimalToFraction(
+ Number(formData.get("Price_mem"))
+ );
+ const [priceStepNumerator, priceStepDenominator] = decimalToFraction(
+ Number(formData.get("Price_step"))
+ );
if (raw) {
- const { section, validations } = server.safeParseTx(raw.toString());
+ const { section, validations } = server.safeParseTx(raw.toString(), {
+ epoch: Number(formData.get("Epoch")),
+ minFeeA: Number(formData.get("Min_fee_a")),
+ minFeeB: Number(formData.get("Min_fee_b")),
+ maxBlockSize: Number(formData.get("Max_block_size")),
+ maxTxSize: Number(formData.get("Max_tx_size")),
+ maxBlockHeaderSize: Number(formData.get("Max_block_header_size")),
+ keyDeposit: Number(formData.get("Key_deposit")),
+ poolDeposit: Number(formData.get("Pool_deposit")),
+ eMax: Number(formData.get("E_max")),
+ nOpt: Number(formData.get("N_opt")),
+ a0Numerator: a0Numerator,
+ a0Denominator: a0Denominator,
+ rhoNumerator: rhoNumerator,
+ rhoDenominator: rhoDenominator,
+ tauNumerator: tauNumerator,
+ tauDenominator: tauDenominator,
+ decentralisationParamNumerator: decentralisationParamNumerator,
+ decentralisationParamDenominator: decentralisationParamDenominator,
+ extraEntropyNumerator: extraEntropyNumerator,
+ extraEntropyDenominator: extraEntropyDenominator,
+ protocolMajorVer: Number(formData.get("Protocol_major_ver")),
+ protocolMinorVer: Number(formData.get("Protocol_minor_ver")),
+ minUtxo: Number(formData.get("Min_utxo")),
+ minPoolCost: Number(formData.get("Min_pool_cost")),
+ priceMemNumerator: priceMemNumerator,
+ priceMemDenominator: priceMemDenominator,
+ priceStepNumerator: priceStepNumerator,
+ priceStepDenominator: priceStepDenominator,
+ maxTxExMem: Number(formData.get("Max_tx_ex_mem")),
+ maxTxExSteps: Number(formData.get("Max_tx_ex_steps")),
+ maxBlockExMem: Number(formData.get("Max_block_ex_mem")),
+ maxBlockExSteps: Number(formData.get("Max_block_ex_steps")),
+ maxValSize: Number(formData.get("Max_val_size")),
+ collateralPercent: Number(formData.get("Collateral_percent")),
+ maxCollateralInputs: Number(formData.get("Max_collateral_inputs")),
+ coinsPerUtxoSize: Number(formData.get("Coins_per_utxo_size")),
+ coinsPerUtxoWord: Number(formData.get("Coins_per_utxo_word")),
+ blockSlot: Number(formData.get("Block_slot")),
+ era: era?.toString() ?? "Babbage",
+ network: net?.toString() ?? "Mainnet",
+ });
return json({
...section,
raw,
@@ -50,7 +109,19 @@ function ExampleCard(props: { title: string; address: string }) {
className="border-2 rounded-lg p-4 shadow bg-gray-100 cursor-pointer flex flex-col w-full h-full text-left"
>
{props.title}
-
+
+
+
+
+ {initialProtPps.map((param) => (
+
+ ))}
{props.address.substring(0, 30)}...
@@ -61,54 +132,37 @@ function ExampleCard(props: { title: string; address: string }) {
export default function Index() {
const data: DataProps | undefined = useActionData();
+ const [modalOpen, setModalOpen] = useState(false);
+ const [protocolParams, setProtocolParams] =
+ useState(initialProtPps);
+
+ const [otherContext, setOtherContext] = useState({
+ blockSlot: 72316896,
+ selectedEra: Eras.Babbage,
+ selectedNetwork: Networks.Mainnet,
+ });
if (data) logCuriosity(data);
const validations: IValidation[] = data?.validations || [];
+ const era = data?.era || "";
- const validations: IValidation[] = [
- { name: "Non empty inputs", value: true, description: "Sucessful" },
- {
- name: "All inputs in utxos",
- value: false,
- description:
- "Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro id maiores exercitationem asperiores molestias assumenda doloremque magnam fugit. Iure dolorum fugit facilis autem incidunt vero necessitatibus consectetur ducimus recusandae blanditiis!",
- },
- { name: "Validity interval", value: true, description: "Sucessful" },
- { name: "Fee", value: true, description: "Sucessful" },
- {
- name: "Preservation of value",
- value: false,
- description:
- "Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro id maiores exercitationem asperiores molestias assumenda doloremque magnam fugit. Iure dolorum fugit facilis autem incidunt vero necessitatibus consectetur ducimus recusandae blanditiis!",
- },
- {
- name: "Min lovelace per UTxO",
- value: false,
- description:
- "Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro id maiores exercitationem asperiores molestias assumenda doloremque magnam fugit. Iure dolorum fugit facilis autem incidunt vero necessitatibus consectetur ducimus recusandae blanditiis!",
- },
- { name: "Output value size", value: true, description: "Successful" },
- { name: "Network Id", value: true, description: "Successful" },
- { name: "Tx size", value: true, description: "Successful" },
- { name: "Tx execution units", value: true, description: "Successful" },
- { name: "Minting", value: true, description: "Successful" },
- {
- name: "Well formed",
- value: false,
- description:
- "Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro id maiores exercitationem asperiores molestias assumenda doloremque magnam fugit. Iure dolorum fugit facilis autem incidunt vero necessitatibus consectetur ducimus recusandae blanditiis!",
- },
- { name: "Script witness", value: true, description: "Successful" },
- { name: "Languages", value: true, description: "Successful" },
- {
- name: "Auxiliary data hash",
- value: false,
- description:
- "Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro id maiores exercitationem asperiores molestias assumenda doloremque magnam fugit. Iure dolorum fugit facilis autem incidunt vero necessitatibus consectetur ducimus recusandae blanditiis!",
- },
- { name: "Script data hash", value: true, description: "Successful" },
- ];
+ const handleModal = () => setModalOpen((prev) => !prev);
+
+ const changeParam =
+ (index: number) => (e: React.ChangeEvent) => {
+ setProtocolParams((prev: ProtocolType[]) => {
+ const updatedParams = [...prev];
+ updatedParams[index] = {
+ ...updatedParams[index],
+ value:
+ Number(e.target.value) >= 0
+ ? Number(e.target.value)
+ : updatedParams[index].value,
+ };
+ return updatedParams;
+ });
+ };
return (
@@ -118,18 +172,39 @@ export default function Index() {
inspect its contents.