Skip to content

Commit

Permalink
feat: protocol parameters can be obtained with blockfrost
Browse files Browse the repository at this point in the history
  • Loading branch information
mikarasv committed May 6, 2024
1 parent e45685e commit 5b074dc
Show file tree
Hide file tree
Showing 26 changed files with 1,217 additions and 433 deletions.
6 changes: 6 additions & 0 deletions napi-pallas/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ hex = "0.4.3"
napi = { version = "2.12.2", default-features = false, features = ["napi4"] }
napi-derive = "2.12.2"
pallas = { git = "https://github.com/txpipe/pallas.git", rev = "9f5713c" , features = ["unstable"]}
tokio = { version = "1", features = ["full"] }
blockfrost = "1.0.1"
blockfrost-openapi = "0.0.3"
num-rational = "0.4.0"
num-traits = "0.2.14"
dotenv = "0.15.0"

[build-dependencies]
napi-build = "2.0.1"
Expand Down
9 changes: 7 additions & 2 deletions napi-pallas/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export interface Output {
bytes?: string
address?: AddressDiagnostic
}
export interface ValidationContext {
export interface ProtocolParams {
epoch: number
minFeeA: number
minFeeB: number
Expand Down Expand Up @@ -58,6 +58,10 @@ export interface ValidationContext {
maxCollateralInputs: number
coinsPerUtxoSize: number
coinsPerUtxoWord: number
error?: string
}
export interface ValidationContext {
protocolParams: ProtocolParams
network: string
era: string
blockSlot: number
Expand All @@ -75,12 +79,13 @@ export interface Section {
children: Array<Section>
}
export function parseAddress(raw: string): Output
export function safeParseBlock(raw: string): Section
export interface SectionValidation {
section: Section
validations: Validations
}
export function safeParseTx(raw: string, context: ValidationContext): SectionValidation
export function safeParseBlock(raw: string): Section
export function getLatestParams(network: string): ProtocolParams
export interface Validation {
name: string
value: boolean
Expand Down
5 changes: 3 additions & 2 deletions napi-pallas/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,9 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}

const { parseAddress, safeParseTx, safeParseBlock } = nativeBinding
const { parseAddress, safeParseBlock, safeParseTx, getLatestParams } = nativeBinding

module.exports.parseAddress = parseAddress
module.exports.safeParseTx = safeParseTx
module.exports.safeParseBlock = safeParseBlock
module.exports.safeParseTx = safeParseTx
module.exports.getLatestParams = getLatestParams
38 changes: 30 additions & 8 deletions napi-pallas/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ mod validations;

#[derive(Default)]
#[napi(object)]
pub struct ValidationContext {
pub struct ProtocolParams {
pub epoch: u32,
pub min_fee_a: u32,
pub min_fee_b: u32,
Expand Down Expand Up @@ -50,6 +50,19 @@ pub struct ValidationContext {
pub max_collateral_inputs: u32,
pub coins_per_utxo_size: i64,
pub coins_per_utxo_word: i64,
pub error: Option<String>,
}

impl ProtocolParams {
fn new() -> Self {
Default::default()
}
}

#[derive(Default)]
#[napi(object)]
pub struct ValidationContext {
pub protocol_params: ProtocolParams,

pub network: String,
pub era: String,
Expand Down Expand Up @@ -190,6 +203,14 @@ pub fn parse_address(raw: String) -> address::Output {
}
}

#[napi]
pub fn safe_parse_block(raw: String) -> Section {
match block::parse(raw) {
Ok(x) => x,
Err(x) => x,
}
}

#[derive(Default)]
#[napi(object)]
pub struct SectionValidation {
Expand All @@ -204,7 +225,7 @@ pub fn safe_parse_tx(raw: String, context: ValidationContext) -> SectionValidati
let (section, validations) = x;
SectionValidation {
section,
validations: validations,
validations,
}
}
Err(x) => SectionValidation {
Expand All @@ -214,15 +235,16 @@ pub fn safe_parse_tx(raw: String, context: ValidationContext) -> SectionValidati
}
}

#[tokio::main]
#[napi]
pub fn safe_parse_block(raw: String) -> Section {
match block::parse(raw) {
Ok(x) => x,
Err(x) => x,
pub async fn get_latest_params(network: String) -> ProtocolParams {
match tx::get_epochs_latest_parameters(network).await {
Ok(params) => params,
Err(_) => ProtocolParams::new(),
}
}

#[derive(Default, Debug)]
#[derive(Default)]
#[napi(object)]
pub struct Validation {
pub name: String,
Expand Down Expand Up @@ -254,7 +276,7 @@ impl Validation {
}
}

#[derive(Debug, Default)]
#[derive(Default)]
#[napi(object)]
pub struct Validations {
pub validations: Vec<Validation>,
Expand Down
131 changes: 130 additions & 1 deletion napi-pallas/src/tx.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use crate::Validations;
use crate::{validations::validate::validate, ValidationContext};
use crate::{ProtocolParams, Validations};

use super::Section;
use blockfrost::{BlockFrostSettings, BlockfrostAPI};
use dotenv::dotenv;
use num_rational::Rational32;
use num_traits::FromPrimitive;
use pallas::ledger::traverse::Era;
use pallas::{
codec::utils::KeepRaw,
Expand All @@ -15,6 +19,7 @@ use pallas::{
traverse::{ComputeHash, MultiEraInput, MultiEraOutput, MultiEraTx},
},
};
use std::env;

fn generic_tx_input_section(topic: &str, i: &MultiEraInput) -> Section {
Section::new()
Expand Down Expand Up @@ -267,3 +272,127 @@ pub fn parse(raw: String, context: ValidationContext) -> Result<(Section, Valida
}
}
}

fn to_fraction(value: f32) -> (i32, i32) {
let rational = Rational32::from_f32(value).unwrap_or_else(|| Rational32::new(0, 1));
let (numerator, denominator) = rational.into();

(numerator, denominator)
}

fn parse_param_to_i64(value: &str) -> i64 {
match value.parse::<i64>() {
Ok(num) => num,
Err(_) => 0,
}
}

fn parse_option_param_to_i64(value: Option<String>) -> i64 {
match value {
Some(value) => match value.parse::<i64>() {
Ok(num) => num,
Err(_) => 0,
},
None => 0,
}
}

fn parse_option_param_to_u32(value: Option<String>) -> u32 {
match value {
Some(value) => match value.parse::<u32>() {
Ok(num) => num,
Err(_) => 0,
},
None => 0,
}
}

pub async fn get_epochs_latest_parameters(
network: String,
) -> Result<ProtocolParams, ProtocolParams> {
let settings = BlockFrostSettings::new();
dotenv().ok();
let mut project_id = env::var("MAINNET_PROJECT_ID").expect("MAINNET_PROJECT_ID must be set.");
if network == "Preprod" {
project_id = env::var("PREPROD_PROJECT_ID").expect("PREPROD_PROJECT_ID must be set.");
} else if network == "Preview" {
project_id = env::var("PREVIEW_PROJECT_ID").expect("PREVIEW_PROJECT_ID must be set.");
}

let api = BlockfrostAPI::new(&project_id, settings);
let epochs_latest_parameters = api.epochs_latest_parameters().await;
match epochs_latest_parameters {
Ok(params) => {
let mut out = ProtocolParams::new();
let parsed_key_deposit: i64 = parse_param_to_i64(&params.key_deposit);
let parsed_pool_deposit: i64 = parse_param_to_i64(&params.pool_deposit);
let parsed_extra_entropy: f32 = match params.extra_entropy {
Some(value) => match value.parse::<f32>() {
Ok(num) => num,
Err(_) => 0.0,
},
None => 0.0,
};
let parsed_max_tx_ex_mem: u32 = parse_option_param_to_u32(params.max_tx_ex_mem);
let parsed_max_tx_ex_steps: i64 = parse_option_param_to_i64(params.max_tx_ex_steps);
let parsed_max_block_ex_mem: u32 = parse_option_param_to_u32(params.max_block_ex_mem);
let parsed_max_block_ex_steps: i64 = parse_option_param_to_i64(params.max_block_ex_steps);

let parsed_max_val_size: u32 = parse_option_param_to_u32(params.max_val_size);
let parsed_collateral_percent = match params.collateral_percent {
Some(value) => value as u32,
None => 0,
};
let parsed_max_collateral_inputs: u32 = match params.max_collateral_inputs {
Some(value) => value as u32,
None => 0,
};
let parsed_coins_per_utxo_size: i64 = parse_option_param_to_i64(params.coins_per_utxo_size);
let parsed_coins_per_utxo_word: i64 = parse_option_param_to_i64(params.coins_per_utxo_word);

let (a0_numerator, a0_denominator) = to_fraction(params.a0);
let (rho_numerator, rho_denominator) = to_fraction(params.rho);
let (tau_numerator, tau_denominator) = to_fraction(params.tau);
let (decentralisation_param_numerator, decentralisation_param_denominator) =
to_fraction(params.decentralisation_param);
let (extra_entropy_numerator, extra_entropy_denominator) = to_fraction(parsed_extra_entropy);

out.epoch = params.epoch as u32;
out.min_fee_a = params.min_fee_a as u32;
out.min_fee_b = params.min_fee_b as u32;
out.max_block_size = params.max_block_size as u32;
out.max_tx_size = params.max_tx_size as u32;
out.max_block_header_size = params.max_block_header_size as u32;
out.key_deposit = parsed_key_deposit;
out.pool_deposit = parsed_pool_deposit;
out.e_max = params.e_max as i64;
out.n_opt = params.n_opt as u32;
out.a0_numerator = a0_numerator as i64;
out.a0_denominator = a0_denominator as i64;
out.rho_numerator = rho_numerator as i64;
out.rho_denominator = rho_denominator as i64;
out.tau_numerator = tau_numerator as i64;
out.tau_denominator = tau_denominator as i64;
out.decentralisation_param_numerator = decentralisation_param_numerator as i64;
out.decentralisation_param_denominator = decentralisation_param_denominator as i64;
out.extra_entropy_numerator = extra_entropy_numerator as u32;
out.extra_entropy_denominator = extra_entropy_denominator as u32;
out.max_tx_ex_mem = parsed_max_tx_ex_mem;
out.max_tx_ex_steps = parsed_max_tx_ex_steps;
out.max_block_ex_mem = parsed_max_block_ex_mem;
out.max_block_ex_steps = parsed_max_block_ex_steps;
out.max_val_size = parsed_max_val_size;
out.collateral_percent = parsed_collateral_percent;
out.max_collateral_inputs = parsed_max_collateral_inputs;
out.coins_per_utxo_size = parsed_coins_per_utxo_size;
out.coins_per_utxo_word = parsed_coins_per_utxo_word;

Ok(out)
}
Err(_) => {
let mut out = ProtocolParams::new();
out.epoch = 0;
Ok(out)
}
}
}
65 changes: 33 additions & 32 deletions napi-pallas/src/validations/alonzo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,65 +235,66 @@ fn validate_alonzo_fee(mtx_a: &MintedTx, utxos: &UTxOs, prot_pps: &AlonzoProtPar

pub fn validate_alonzo(mtx_a: &MintedTx, context: ValidationContext) -> Validations {
let tx_body: &TransactionBody = &mtx_a.transaction_body;
let ppt_params = context.protocol_params;
let size: &Option<u32> = &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,
minfee_a: ppt_params.min_fee_a,
minfee_b: ppt_params.min_fee_b,
max_block_body_size: ppt_params.max_block_size,
max_transaction_size: ppt_params.max_tx_size,
max_block_header_size: ppt_params.max_block_header_size,
key_deposit: ppt_params.key_deposit as u64,
pool_deposit: ppt_params.pool_deposit as u64,
maximum_epoch: ppt_params.e_max as u64,
desired_number_of_stake_pools: ppt_params.n_opt,
pool_pledge_influence: RationalNumber {
numerator: context.a0_numerator as u64,
denominator: context.a0_denominator as u64,
numerator: ppt_params.a0_numerator as u64,
denominator: ppt_params.a0_denominator as u64,
},
expansion_rate: RationalNumber {
numerator: context.rho_numerator as u64,
denominator: context.rho_denominator as u64,
numerator: ppt_params.rho_numerator as u64,
denominator: ppt_params.rho_denominator as u64,
},
treasury_growth_rate: RationalNumber {
numerator: context.tau_numerator as u64,
denominator: context.tau_denominator as u64,
numerator: ppt_params.tau_numerator as u64,
denominator: ppt_params.tau_denominator as u64,
},
decentralization_constant: RationalNumber {
numerator: context.decentralisation_param_numerator as u64,
denominator: context.decentralisation_param_denominator as u64,
numerator: ppt_params.decentralisation_param_numerator as u64,
denominator: ppt_params.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,
ppt_params.protocol_minor_ver as u64,
ppt_params.protocol_major_ver as u64,
),
min_pool_cost: context.min_pool_cost as u64,
min_pool_cost: ppt_params.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,
ada_per_utxo_byte: ppt_params.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,
numerator: ppt_params.price_mem_numerator as u64,
denominator: ppt_params.price_mem_denominator as u64,
},
step_price: RationalNumber {
numerator: context.price_step_numerator as u64,
denominator: context.price_step_denominator as u64,
numerator: ppt_params.price_step_numerator as u64,
denominator: ppt_params.price_step_denominator as u64,
},
},
max_tx_ex_units: ExUnits {
mem: context.max_tx_ex_mem,
steps: context.max_tx_ex_steps as u64,
mem: ppt_params.max_tx_ex_mem,
steps: ppt_params.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,
mem: ppt_params.max_block_ex_mem,
steps: ppt_params.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,
max_value_size: ppt_params.max_val_size,
collateral_percentage: ppt_params.collateral_percent,
max_collateral_inputs: ppt_params.max_collateral_inputs,
};

let mut magic = 764824073; // For mainnet
Expand Down
Loading

0 comments on commit 5b074dc

Please sign in to comment.