diff --git a/Cargo.lock b/Cargo.lock index cf5ce3db..7f42be87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1942,7 +1942,7 @@ checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" [[package]] name = "pallas-addresses" version = "0.29.0" -source = "git+https://github.com/KtorZ/pallas.git?rev=8ea5a1adc9919b70b213dfe597e920d6e113120c#8ea5a1adc9919b70b213dfe597e920d6e113120c" +source = "git+https://github.com/KtorZ/pallas.git?rev=0cdd6393bb3fbceb3b5209ad248877b381822e90#0cdd6393bb3fbceb3b5209ad248877b381822e90" dependencies = [ "base58", "bech32", @@ -1957,7 +1957,7 @@ dependencies = [ [[package]] name = "pallas-codec" version = "0.29.0" -source = "git+https://github.com/KtorZ/pallas.git?rev=8ea5a1adc9919b70b213dfe597e920d6e113120c#8ea5a1adc9919b70b213dfe597e920d6e113120c" +source = "git+https://github.com/KtorZ/pallas.git?rev=0cdd6393bb3fbceb3b5209ad248877b381822e90#0cdd6393bb3fbceb3b5209ad248877b381822e90" dependencies = [ "hex", "minicbor", @@ -1969,7 +1969,7 @@ dependencies = [ [[package]] name = "pallas-crypto" version = "0.29.0" -source = "git+https://github.com/KtorZ/pallas.git?rev=8ea5a1adc9919b70b213dfe597e920d6e113120c#8ea5a1adc9919b70b213dfe597e920d6e113120c" +source = "git+https://github.com/KtorZ/pallas.git?rev=0cdd6393bb3fbceb3b5209ad248877b381822e90#0cdd6393bb3fbceb3b5209ad248877b381822e90" dependencies = [ "cryptoxide", "hex", @@ -1982,7 +1982,7 @@ dependencies = [ [[package]] name = "pallas-primitives" version = "0.29.0" -source = "git+https://github.com/KtorZ/pallas.git?rev=8ea5a1adc9919b70b213dfe597e920d6e113120c#8ea5a1adc9919b70b213dfe597e920d6e113120c" +source = "git+https://github.com/KtorZ/pallas.git?rev=0cdd6393bb3fbceb3b5209ad248877b381822e90#0cdd6393bb3fbceb3b5209ad248877b381822e90" dependencies = [ "base58", "bech32", @@ -1997,7 +1997,7 @@ dependencies = [ [[package]] name = "pallas-traverse" version = "0.29.0" -source = "git+https://github.com/KtorZ/pallas.git?rev=8ea5a1adc9919b70b213dfe597e920d6e113120c#8ea5a1adc9919b70b213dfe597e920d6e113120c" +source = "git+https://github.com/KtorZ/pallas.git?rev=0cdd6393bb3fbceb3b5209ad248877b381822e90#0cdd6393bb3fbceb3b5209ad248877b381822e90" dependencies = [ "hex", "itertools 0.13.0", diff --git a/Cargo.toml b/Cargo.toml index 02633e06..27d4eee2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,11 +49,11 @@ x86_64-unknown-linux-gnu = "ubuntu-22.04" walkdir = "2.3.2" insta = { version = "1.30.0", features = ["yaml", "json", "redactions"] } miette = { version = "7.2.0", features = ["fancy"] } -pallas-addresses = { git = "https://github.com/KtorZ/pallas.git", rev = "8ea5a1adc9919b70b213dfe597e920d6e113120c" } -pallas-codec = { git = "https://github.com/KtorZ/pallas.git", rev = "8ea5a1adc9919b70b213dfe597e920d6e113120c", features = ["num-bigint"] } -pallas-crypto = { git = "https://github.com/KtorZ/pallas.git", rev = "8ea5a1adc9919b70b213dfe597e920d6e113120c" } -pallas-primitives = { git = "https://github.com/KtorZ/pallas.git", rev = "8ea5a1adc9919b70b213dfe597e920d6e113120c" } -pallas-traverse = { git = "https://github.com/KtorZ/pallas.git", rev = "8ea5a1adc9919b70b213dfe597e920d6e113120c" } +pallas-addresses = { git = "https://github.com/KtorZ/pallas.git", rev = "0cdd6393bb3fbceb3b5209ad248877b381822e90" } +pallas-codec = { git = "https://github.com/KtorZ/pallas.git", rev = "0cdd6393bb3fbceb3b5209ad248877b381822e90", features = ["num-bigint"] } +pallas-crypto = { git = "https://github.com/KtorZ/pallas.git", rev = "0cdd6393bb3fbceb3b5209ad248877b381822e90" } +pallas-primitives = { git = "https://github.com/KtorZ/pallas.git", rev = "0cdd6393bb3fbceb3b5209ad248877b381822e90" } +pallas-traverse = { git = "https://github.com/KtorZ/pallas.git", rev = "0cdd6393bb3fbceb3b5209ad248877b381822e90" } [profile.dev.package.insta] opt-level = 3 diff --git a/crates/uplc/src/tx/error.rs b/crates/uplc/src/tx/error.rs index 28275a60..48590354 100644 --- a/crates/uplc/src/tx/error.rs +++ b/crates/uplc/src/tx/error.rs @@ -67,6 +67,8 @@ pub enum Error { NonScriptWithdrawal, #[error("stake credential points to a non-script withdrawal")] NonScriptStakeCredential, + #[error("the designated procedure defines no guardrail script")] + NoGuardrailScriptForProcedure, #[error("cost model not found for language\n{:>13} {:?}", "Language", .0)] CostModelNotFound(Language), #[error("unsupported era, please use Conway\n{:>13} {0}", "Decoder error")] diff --git a/crates/uplc/src/tx/phase_one.rs b/crates/uplc/src/tx/phase_one.rs index 9d56f055..b0eb06eb 100644 --- a/crates/uplc/src/tx/phase_one.rs +++ b/crates/uplc/src/tx/phase_one.rs @@ -4,9 +4,10 @@ use super::{ }; use itertools::Itertools; use pallas_addresses::{Address, ScriptHash, ShelleyPaymentPart, StakePayload}; +use pallas_codec::utils::Nullable; use pallas_primitives::conway::{ - Certificate, MintedTx, PolicyId, RedeemerTag, RedeemersKey, RewardAccount, StakeCredential, - TransactionOutput, + Certificate, GovAction, MintedTx, PolicyId, RedeemerTag, RedeemersKey, RewardAccount, + StakeCredential, TransactionOutput, }; use std::collections::HashMap; @@ -106,14 +107,15 @@ pub fn scripts_needed(tx: &MintedTx, utxos: &[ResolvedInput]) -> Result { - Some((ScriptPurpose::Certifying(cert.clone()), *h)) + Some((ScriptPurpose::Certifying(ix, cert.clone()), *h)) } Certificate::StakeDelegation(StakeCredential::Scripthash(h), _) => { - Some((ScriptPurpose::Certifying(cert.clone()), *h)) + Some((ScriptPurpose::Certifying(ix, cert.clone()), *h)) } _ => None, } @@ -132,14 +134,39 @@ pub fn scripts_needed(tx: &MintedTx, utxos: &[ResolvedInput]) -> Result { + Some((ScriptPurpose::Proposing(ix, procedure.clone()), hash)) + } + GovAction::TreasuryWithdrawals(_, Nullable::Some(hash)) => { + Some((ScriptPurpose::Proposing(ix, procedure.clone()), hash)) + } + GovAction::HardForkInitiation(..) + | GovAction::Information + | GovAction::NewConstitution(..) + | GovAction::TreasuryWithdrawals(..) + | GovAction::ParameterChange(..) + | GovAction::NoConfidence(..) + | GovAction::UpdateCommittee(..) => None, + }) + .collect::() + }) + .unwrap_or_default(); + // TODO - assert!(txb.proposal_procedures.is_none()); assert!(txb.voting_procedures.is_none()); needed.append(&mut spend); needed.append(&mut reward); needed.append(&mut cert); needed.append(&mut mint); + needed.append(&mut propose); Ok(needed) } @@ -191,7 +218,7 @@ pub fn has_exact_set_of_redeemers( let extra: Vec<_> = wits_redeemer_keys .into_iter() .filter(|x| !needed_redeemer_keys.contains(x)) - .map(|x| format!("{x:?}")) + .map(|x| format!("{:?}[{:?}]", x.tag, x.index)) .collect(); if !missing.is_empty() || !extra.is_empty() { @@ -281,7 +308,7 @@ fn build_redeemer_key( Ok(redeemer_key) } - ScriptPurpose::Certifying(d) => { + ScriptPurpose::Certifying(_, d) => { let redeemer_key = tx_body .certificates .as_deref() @@ -294,5 +321,19 @@ fn build_redeemer_key( Ok(redeemer_key) } + + ScriptPurpose::Proposing(_, procedure) => { + let redeemer_key = tx_body + .proposal_procedures + .as_deref() + .map(|m| m.iter().position(|x| x == procedure)) + .unwrap_or_default() + .map(|index| RedeemersKey { + tag: RedeemerTag::Propose, + index: index as u32, + }); + + Ok(redeemer_key) + } } } diff --git a/crates/uplc/src/tx/script_context.rs b/crates/uplc/src/tx/script_context.rs index 8c6f2d28..f0ba3179 100644 --- a/crates/uplc/src/tx/script_context.rs +++ b/crates/uplc/src/tx/script_context.rs @@ -1,16 +1,17 @@ use super::{to_plutus_data::MintValue, Error}; use itertools::Itertools; use pallas_addresses::{Address, StakePayload}; -use pallas_codec::utils::{KeyValuePairs, NonEmptyKeyValuePairs, NonEmptySet}; +use pallas_codec::utils::{KeyValuePairs, NonEmptyKeyValuePairs, NonEmptySet, Nullable}; use pallas_crypto::hash::Hash; use pallas_primitives::{ alonzo, conway::{ - AddrKeyhash, Certificate, Coin, DatumHash, DatumOption, Mint, MintedTransactionBody, - MintedTransactionOutput, MintedTx, MintedWitnessSet, NativeScript, PlutusData, - PlutusV1Script, PlutusV2Script, PlutusV3Script, PolicyId, PostAlonzoTransactionOutput, - PseudoDatumOption, PseudoScript, Redeemer, RedeemerTag, RedeemersKey, RequiredSigners, - RewardAccount, ScriptHash, StakeCredential, TransactionInput, TransactionOutput, Value, + AddrKeyhash, Certificate, Coin, DatumHash, DatumOption, GovAction, Mint, + MintedTransactionBody, MintedTransactionOutput, MintedTx, MintedWitnessSet, NativeScript, + PlutusData, PlutusV1Script, PlutusV2Script, PlutusV3Script, PolicyId, + PostAlonzoTransactionOutput, ProposalProcedure, PseudoDatumOption, PseudoScript, Redeemer, + RedeemerTag, RedeemersKey, RequiredSigners, RewardAccount, ScriptHash, StakeCredential, + TransactionInput, TransactionOutput, Value, }, }; use pallas_traverse::{ComputeHash, OriginalHash}; @@ -49,7 +50,8 @@ pub enum ScriptInfo { Minting(PolicyId), Spending(TransactionInput, T), Rewarding(StakeCredential), - Certifying(Certificate), + Certifying(usize, Certificate), + Proposing(usize, ProposalProcedure), } pub type ScriptPurpose = ScriptInfo<()>; @@ -62,7 +64,8 @@ impl ScriptPurpose { ScriptInfo::Spending(transaction_output, datum) } Self::Rewarding(stake_credential) => ScriptInfo::Rewarding(stake_credential), - Self::Certifying(certificate) => ScriptInfo::Certifying(certificate), + Self::Certifying(ix, certificate) => ScriptInfo::Certifying(ix, certificate), + Self::Proposing(ix, procedure) => ScriptInfo::Proposing(ix, procedure), } } } @@ -215,7 +218,7 @@ impl TxInfoV1 { let redeemers = get_redeemers_info( &tx.transaction_witness_set, - script_purpose_builder(&inputs[..], &mint, &certificates, &withdrawals), + script_purpose_builder(&inputs[..], &mint, &certificates, &withdrawals, &[]), )?; Ok(TxInfo::V1(TxInfoV1 { @@ -264,7 +267,7 @@ impl TxInfoV2 { let redeemers = get_redeemers_info( &tx.transaction_witness_set, - script_purpose_builder(&inputs[..], &mint, &certificates, &withdrawals), + script_purpose_builder(&inputs[..], &mint, &certificates, &withdrawals, &[]), )?; let reference_inputs = tx @@ -305,12 +308,12 @@ pub struct TxInfoV3 { pub signatories: Vec, pub redeemers: KeyValuePairs, pub data: KeyValuePairs, + pub proposal_procedures: Vec, + pub id: Hash<32>, // TODO: // votes : KeyValuePairs> - // proposalProcedures : Vec // currentTreasuryAmount : Option // treasuryDonation : Option - pub id: Hash<32>, } impl TxInfoV3 { @@ -319,24 +322,52 @@ impl TxInfoV3 { utxos: &[ResolvedInput], slot_config: &SlotConfig, ) -> Result { - if let TxInfo::V2(tx_info_v2) = TxInfoV2::from_transaction(tx, utxos, slot_config)? { - Ok(TxInfo::V3(TxInfoV3 { - inputs: tx_info_v2.inputs, - reference_inputs: tx_info_v2.reference_inputs, - outputs: tx_info_v2.outputs, - fee: get_fee_info(&tx.transaction_body.fee), - mint: tx_info_v2.mint, - certificates: tx_info_v2.certificates, - withdrawals: tx_info_v2.withdrawals, - valid_range: tx_info_v2.valid_range, - signatories: tx_info_v2.signatories, - redeemers: tx_info_v2.redeemers, - data: tx_info_v2.data, - id: tx_info_v2.id, - })) - } else { - unreachable!() - } + let inputs = get_tx_in_info_v2(&tx.transaction_body.inputs, utxos)?; + + let certificates = get_certificates_info(&tx.transaction_body.certificates); + + let withdrawals = + KeyValuePairs::from(get_withdrawal_info(&tx.transaction_body.withdrawals)); + + let mint = get_mint_info(&tx.transaction_body.mint); + + let proposal_procedures = + get_proposal_procedures_info(&tx.transaction_body.proposal_procedures); + + let redeemers = get_redeemers_info( + &tx.transaction_witness_set, + script_purpose_builder( + &inputs[..], + &mint, + &certificates, + &withdrawals, + &proposal_procedures, + ), + )?; + + let reference_inputs = tx + .transaction_body + .reference_inputs + .clone() + .map(|refs| get_tx_in_info_v2(&refs[..], utxos)) + .transpose()? + .unwrap_or_default(); + + Ok(TxInfo::V3(TxInfoV3 { + inputs, + reference_inputs, + outputs: get_outputs_info(&tx.transaction_body.outputs[..]), + fee: get_fee_info(&tx.transaction_body.fee), + mint, + certificates, + withdrawals, + valid_range: get_validity_range_info(&tx.transaction_body, slot_config), + signatories: get_signatories_info(&tx.transaction_body.required_signers), + data: KeyValuePairs::from(get_data_info(&tx.transaction_witness_set)), + redeemers, + proposal_procedures, + id: tx.transaction_body.original_hash(), + })) } } @@ -567,6 +598,15 @@ pub fn get_certificates_info(certificates: &Option>) -> certificates.clone().map(|s| s.to_vec()).unwrap_or_default() } +pub fn get_proposal_procedures_info( + proposal_procedures: &Option>, +) -> Vec { + proposal_procedures + .clone() + .map(|s| s.to_vec()) + .unwrap_or_default() +} + pub fn get_withdrawal_info( withdrawals: &Option>, ) -> Vec<(Address, Coin)> { @@ -664,23 +704,28 @@ fn script_purpose_builder<'a>( mint: &'a MintValue, certificates: &'a [Certificate], withdrawals: &'a KeyValuePairs, + proposal_procedures: &'a [ProposalProcedure], ) -> impl Fn(&'a RedeemersKey) -> Result { move |redeemer: &'a RedeemersKey| { let tag = redeemer.tag; let index = redeemer.index as usize; + match tag { RedeemerTag::Mint => mint .mint_value .get(index) .map(|(policy_id, _)| ScriptPurpose::Minting(*policy_id)), + RedeemerTag::Spend => inputs .get(index) .cloned() .map(|i| ScriptPurpose::Spending(i.out_ref, ())), + RedeemerTag::Cert => certificates .get(index) .cloned() - .map(ScriptPurpose::Certifying), + .map(|c| ScriptPurpose::Certifying(index, c)), + RedeemerTag::Reward => withdrawals .get(index) .cloned() @@ -694,6 +739,12 @@ fn script_purpose_builder<'a>( _ => Err(Error::BadWithdrawalAddress), }) .transpose()?, + + RedeemerTag::Propose => proposal_procedures + .get(redeemer.index as usize) + .cloned() + .map(|p| ScriptPurpose::Proposing(index, p)), + tag => todo!("get_script_purpose for {tag:?}"), } .ok_or(Error::ExtraneousRedeemer) @@ -796,7 +847,23 @@ pub fn find_script( _ => Err(Error::NonScriptStakeCredential), }), - RedeemerTag::Propose => todo!("find_script: RedeemerTag::Propose"), + RedeemerTag::Propose => { + get_proposal_procedures_info(&tx.transaction_body.proposal_procedures) + .get(redeemer.index as usize) + .ok_or(Error::MissingScriptForRedeemer) + .and_then(|procedure| match procedure.gov_action { + GovAction::ParameterChange(_, _, Nullable::Some(ref hash)) => Ok(hash), + GovAction::TreasuryWithdrawals(_, Nullable::Some(ref hash)) => Ok(hash), + GovAction::HardForkInitiation(..) + | GovAction::Information + | GovAction::NewConstitution(..) + | GovAction::TreasuryWithdrawals(..) + | GovAction::ParameterChange(..) + | GovAction::NoConfidence(..) + | GovAction::UpdateCommittee(..) => Err(Error::NoGuardrailScriptForProcedure), + }) + .and_then(lookup_script) + } RedeemerTag::Vote => todo!("find_script: RedeemerTag::Vote"), } @@ -1065,4 +1132,1914 @@ mod tests { // from the Haskell ledger / cardano node. insta::assert_debug_snapshot!(script_context.to_plutus_data()); } + + #[test] + fn script_context_propose_all_but_pparams() { + let redeemer = Redeemer { + tag: RedeemerTag::Propose, + index: 3, + data: Data::constr(0, vec![]), + ex_units: ExUnits { + mem: 1000000, + steps: 100000000, + }, + }; + + let script_context = fixture_tx_info( + "84a40081825820000000000000000000000000000000000000000000000000000000000000000000018002182a14d9010289841a001e8480581df0000000000000000000000000000000000000000000000000000000008301f6820a00827668747470733a2f2f61696b656e2d6c616e672e6f726758200000000000000000000000000000000000000000000000000000000000000000841a001e8480581df0000000000000000000000000000000000000000000000000000000008301825820000000000000000000000000000000000000000000000000000000000000000000820b00827668747470733a2f2f61696b656e2d6c616e672e6f726758200000000000000000000000000000000000000000000000000000000000000000841a001e8480581df0000000000000000000000000000000000000000000000000000000008302a1581de0111111111111111111111111111111111111111111111111111111111a000f4240f6827668747470733a2f2f61696b656e2d6c616e672e6f726758200000000000000000000000000000000000000000000000000000000000000000841a001e8480581df0000000000000000000000000000000000000000000000000000000008302a1581de0222222222222222222222222222222222222222222222222222222221a000f4240581c9b24324046544393443e1fb35c8b72c3c39e18a516a95df5f6654101827668747470733a2f2f61696b656e2d6c616e672e6f726758200000000000000000000000000000000000000000000000000000000000000000841a001e8480581df0000000000000000000000000000000000000000000000000000000008203f6827668747470733a2f2f61696b656e2d6c616e672e6f726758200000000000000000000000000000000000000000000000000000000000000000841a001e8480581df0000000000000000000000000000000000000000000000000000000008504f6818200581c00000000000000000000000000000000000000000000000000000000a18200581c000000000000000000000000000000000000000000000000000000001901f4d81e820102827668747470733a2f2f61696b656e2d6c616e672e6f726758200000000000000000000000000000000000000000000000000000000000000000841a001e8480581df0000000000000000000000000000000000000000000000000000000008305f68282782068747470733a2f2f636f6e737469747574696f6e2e63617264616e6f2e6f726758200000000000000000000000000000000000000000000000000000000000000000f6827668747470733a2f2f61696b656e2d6c616e672e6f726758200000000000000000000000000000000000000000000000000000000000000000841a001e8480581df0000000000000000000000000000000000000000000000000000000008305f68282782068747470733a2f2f636f6e737469747574696f6e2e63617264616e6f2e6f726758200000000000000000000000000000000000000000000000000000000000000000581c00000000000000000000000000000000000000000000000000000000827668747470733a2f2f61696b656e2d6c616e672e6f726758200000000000000000000000000000000000000000000000000000000000000000841a001e8480581de0000000000000000000000000000000000000000000000000000000008106827668747470733a2f2f61696b656e2d6c616e672e6f726758200000000000000000000000000000000000000000000000000000000000000000a20581840503d87980821a000f42401a05f5e1000781587d587b0101003232323232323225333333008001153330033370e900018029baa001153330073006375400224a66600894452615330054911856616c696461746f722072657475726e65642066616c73650013656002002002002002002153300249010b5f746d70313a20566f696400165734ae7155ceaab9e5573eae91f5f6", + "8182582000000000000000000000000000000000000000000000000000000000\ + 0000000000", + "81a200581d600000000000000000000000000000000000000000000000000000\ + 0000011a000f4240", + ) + .into_script_context(&redeemer, None) + .unwrap(); + + // NOTE: The initial snapshot has been generated using the Haskell + // implementation of the ledger library for that same serialized + // transactions. It is meant to control that our construction of the + // script context and its serialization matches exactly those + // from the Haskell ledger / cardano node. + insta::assert_debug_snapshot!(script_context.to_plutus_data(), @"Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + Array( + [ + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + ), + ), + BigInt( + Int( + Int( + Int { + neg: false, + val: 0, + }, + ), + ), + ), + ], + }, + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + ), + ), + ], + }, + ), + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [], + }, + ), + ], + }, + ), + Map( + Def( + [ + ( + BoundedBytes( + BoundedBytes( + [], + ), + ), + Map( + Def( + [ + ( + BoundedBytes( + BoundedBytes( + [], + ), + ), + BigInt( + Int( + Int( + Int { + neg: false, + val: 1000000, + }, + ), + ), + ), + ), + ], + ), + ), + ), + ], + ), + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [], + }, + ), + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [], + }, + ), + ], + }, + ), + ], + }, + ), + ], + ), + Array( + [], + ), + Array( + [], + ), + BigInt( + Int( + Int( + Int { + neg: false, + val: 42, + }, + ), + ), + ), + Map( + Def( + [], + ), + ), + Array( + [], + ), + Map( + Def( + [], + ), + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [], + }, + ), + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [], + }, + ), + ], + }, + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + Constr( + Constr { + tag: 123, + any_constructor: None, + fields: [], + }, + ), + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [], + }, + ), + ], + }, + ), + ], + }, + ), + Array( + [], + ), + Map( + Def( + [ + ( + Constr( + Constr { + tag: 126, + any_constructor: None, + fields: [ + BigInt( + Int( + Int( + Int { + neg: false, + val: 3, + }, + ), + ), + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BigInt( + Int( + Int( + Int { + neg: false, + val: 2000000, + }, + ), + ), + ), + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + ), + ), + ], + }, + ), + Constr( + Constr { + tag: 123, + any_constructor: None, + fields: [ + Map( + Def( + [ + ( + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + ], + ), + ), + ], + }, + ), + BigInt( + Int( + Int( + Int { + neg: false, + val: 1000000, + }, + ), + ), + ), + ), + ], + ), + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 155, + 36, + 50, + 64, + 70, + 84, + 67, + 147, + 68, + 62, + 31, + 179, + 92, + 139, + 114, + 195, + 195, + 158, + 24, + 165, + 22, + 169, + 93, + 245, + 246, + 101, + 65, + 1, + ], + ), + ), + ], + }, + ), + ], + }, + ), + ], + }, + ), + ], + }, + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [], + }, + ), + ), + ], + ), + ), + Map( + Def( + [], + ), + ), + BoundedBytes( + BoundedBytes( + [ + 100, + 76, + 84, + 18, + 140, + 45, + 90, + 9, + 167, + 181, + 207, + 31, + 83, + 63, + 250, + 186, + 198, + 73, + 117, + 34, + 75, + 214, + 232, + 246, + 68, + 61, + 41, + 50, + 186, + 97, + 252, + 205, + ], + ), + ), + Map( + Def( + [], + ), + ), + Array( + [ + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BigInt( + Int( + Int( + Int { + neg: false, + val: 2000000, + }, + ), + ), + ), + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + ), + ), + ], + }, + ), + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [ + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [], + }, + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BigInt( + Int( + Int( + Int { + neg: false, + val: 10, + }, + ), + ), + ), + BigInt( + Int( + Int( + Int { + neg: false, + val: 0, + }, + ), + ), + ), + ], + }, + ), + ], + }, + ), + ], + }, + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BigInt( + Int( + Int( + Int { + neg: false, + val: 2000000, + }, + ), + ), + ), + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + ), + ), + ], + }, + ), + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [ + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + ), + ), + BigInt( + Int( + Int( + Int { + neg: false, + val: 0, + }, + ), + ), + ), + ], + }, + ), + ], + }, + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BigInt( + Int( + Int( + Int { + neg: false, + val: 11, + }, + ), + ), + ), + BigInt( + Int( + Int( + Int { + neg: false, + val: 0, + }, + ), + ), + ), + ], + }, + ), + ], + }, + ), + ], + }, + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BigInt( + Int( + Int( + Int { + neg: false, + val: 2000000, + }, + ), + ), + ), + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + ), + ), + ], + }, + ), + Constr( + Constr { + tag: 123, + any_constructor: None, + fields: [ + Map( + Def( + [ + ( + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + 17, + ], + ), + ), + ], + }, + ), + BigInt( + Int( + Int( + Int { + neg: false, + val: 1000000, + }, + ), + ), + ), + ), + ], + ), + ), + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [], + }, + ), + ], + }, + ), + ], + }, + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BigInt( + Int( + Int( + Int { + neg: false, + val: 2000000, + }, + ), + ), + ), + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + ), + ), + ], + }, + ), + Constr( + Constr { + tag: 123, + any_constructor: None, + fields: [ + Map( + Def( + [ + ( + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + ], + ), + ), + ], + }, + ), + BigInt( + Int( + Int( + Int { + neg: false, + val: 1000000, + }, + ), + ), + ), + ), + ], + ), + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 155, + 36, + 50, + 64, + 70, + 84, + 67, + 147, + 68, + 62, + 31, + 179, + 92, + 139, + 114, + 195, + 195, + 158, + 24, + 165, + 22, + 169, + 93, + 245, + 246, + 101, + 65, + 1, + ], + ), + ), + ], + }, + ), + ], + }, + ), + ], + }, + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BigInt( + Int( + Int( + Int { + neg: false, + val: 2000000, + }, + ), + ), + ), + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + ), + ), + ], + }, + ), + Constr( + Constr { + tag: 124, + any_constructor: None, + fields: [ + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [], + }, + ), + ], + }, + ), + ], + }, + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BigInt( + Int( + Int( + Int { + neg: false, + val: 2000000, + }, + ), + ), + ), + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + ), + ), + ], + }, + ), + Constr( + Constr { + tag: 125, + any_constructor: None, + fields: [ + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [], + }, + ), + Array( + [ + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + ), + ), + ], + }, + ), + ], + ), + Map( + Def( + [ + ( + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + ), + ), + ], + }, + ), + BigInt( + Int( + Int( + Int { + neg: false, + val: 500, + }, + ), + ), + ), + ), + ], + ), + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BigInt( + Int( + Int( + Int { + neg: false, + val: 1, + }, + ), + ), + ), + BigInt( + Int( + Int( + Int { + neg: false, + val: 2, + }, + ), + ), + ), + ], + }, + ), + ], + }, + ), + ], + }, + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BigInt( + Int( + Int( + Int { + neg: false, + val: 2000000, + }, + ), + ), + ), + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + ), + ), + ], + }, + ), + Constr( + Constr { + tag: 126, + any_constructor: None, + fields: [ + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [], + }, + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [], + }, + ), + ], + }, + ), + ], + }, + ), + ], + }, + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BigInt( + Int( + Int( + Int { + neg: false, + val: 2000000, + }, + ), + ), + ), + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + ), + ), + ], + }, + ), + Constr( + Constr { + tag: 126, + any_constructor: None, + fields: [ + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [], + }, + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + ), + ), + ], + }, + ), + ], + }, + ), + ], + }, + ), + ], + }, + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BigInt( + Int( + Int( + Int { + neg: false, + val: 2000000, + }, + ), + ), + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + ), + ), + ], + }, + ), + Constr( + Constr { + tag: 127, + any_constructor: None, + fields: [], + }, + ), + ], + }, + ), + ], + ), + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [], + }, + ), + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [], + }, + ), + ], + }, + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [], + }, + ), + Constr( + Constr { + tag: 126, + any_constructor: None, + fields: [ + BigInt( + Int( + Int( + Int { + neg: false, + val: 3, + }, + ), + ), + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BigInt( + Int( + Int( + Int { + neg: false, + val: 2000000, + }, + ), + ), + ), + Constr( + Constr { + tag: 122, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + ), + ), + ], + }, + ), + Constr( + Constr { + tag: 123, + any_constructor: None, + fields: [ + Map( + Def( + [ + ( + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + ], + ), + ), + ], + }, + ), + BigInt( + Int( + Int( + Int { + neg: false, + val: 1000000, + }, + ), + ), + ), + ), + ], + ), + ), + Constr( + Constr { + tag: 121, + any_constructor: None, + fields: [ + BoundedBytes( + BoundedBytes( + [ + 155, + 36, + 50, + 64, + 70, + 84, + 67, + 147, + 68, + 62, + 31, + 179, + 92, + 139, + 114, + 195, + 195, + 158, + 24, + 165, + 22, + 169, + 93, + 245, + 246, + 101, + 65, + 1, + ], + ), + ), + ], + }, + ), + ], + }, + ), + ], + }, + ), + ], + }, + ), + ], + }, +)"); + } } diff --git a/crates/uplc/src/tx/to_plutus_data.rs b/crates/uplc/src/tx/to_plutus_data.rs index b04f620c..301c4006 100644 --- a/crates/uplc/src/tx/to_plutus_data.rs +++ b/crates/uplc/src/tx/to_plutus_data.rs @@ -6,12 +6,16 @@ use crate::{ machine::runtime::{convert_constr_to_tag, ANY_TAG}, tx::script_context::from_alonzo_output, }; -use pallas_addresses::{Address, ShelleyDelegationPart, ShelleyPaymentPart, StakePayload}; -use pallas_codec::utils::{AnyUInt, Bytes, Int, KeyValuePairs, NonEmptyKeyValuePairs}; +use pallas_addresses::{ + Address, ShelleyDelegationPart, ShelleyPaymentPart, StakeAddress, StakePayload, +}; +use pallas_codec::utils::{AnyUInt, Bytes, Int, KeyValuePairs, NonEmptyKeyValuePairs, Nullable}; use pallas_crypto::hash::Hash; use pallas_primitives::conway::{ - AssetName, BigInt, Certificate, Coin, Constr, DatumOption, Mint, PlutusData, PolicyId, - PseudoScript, Redeemer, ScriptRef, StakeCredential, TransactionInput, TransactionOutput, Value, + AssetName, BigInt, Certificate, Coin, Constitution, Constr, DatumOption, GovAction, + GovActionId, Mint, PlutusData, PolicyId, ProposalProcedure, ProtocolParamUpdate, PseudoScript, + RationalNumber, Redeemer, ScriptRef, StakeCredential, TransactionInput, TransactionOutput, + Value, }; use pallas_traverse::ComputeHash; @@ -34,6 +38,8 @@ fn empty_constr(index: u64) -> PlutusData { struct WithWrappedTransactionId<'a, T>(&'a T); +struct WithWrappedStakeCredential<'a, T>(&'a T); + struct WithZeroAdaAsset<'a, T>(&'a T); struct WithOptionDatum<'a, T>(&'a T); @@ -47,6 +53,23 @@ pub struct MintValue { pub mint_value: Mint, } +impl ToPlutusData for bool { + fn to_plutus_data(&self) -> PlutusData { + match self { + false => empty_constr(0), + true => empty_constr(1), + } + } +} +impl ToPlutusData for StakeAddress { + fn to_plutus_data(&self) -> PlutusData { + match self.payload() { + StakePayload::Stake(x) => wrap_with_constr(0, x.to_plutus_data()), + StakePayload::Script(x) => wrap_with_constr(1, x.to_plutus_data()), + } + } +} + impl ToPlutusData for Address { fn to_plutus_data(&self) -> PlutusData { match self { @@ -64,12 +87,16 @@ impl ToPlutusData for Address { }; let stake_part_plutus_data = match stake_part { - ShelleyDelegationPart::Key(stake_keyhash) => { - Some(StakeCredential::AddrKeyhash(*stake_keyhash)).to_plutus_data() - } - ShelleyDelegationPart::Script(script_hash) => { - Some(StakeCredential::Scripthash(*script_hash)).to_plutus_data() - } + ShelleyDelegationPart::Key(stake_keyhash) => Some(wrap_with_constr( + 0, + StakeCredential::AddrKeyhash(*stake_keyhash).to_plutus_data(), + )) + .to_plutus_data(), + ShelleyDelegationPart::Script(script_hash) => Some(wrap_with_constr( + 0, + StakeCredential::Scripthash(*script_hash).to_plutus_data(), + )) + .to_plutus_data(), ShelleyDelegationPart::Pointer(pointer) => Some(wrap_multiple_with_constr( 1, vec![ @@ -84,17 +111,7 @@ impl ToPlutusData for Address { wrap_multiple_with_constr(0, vec![payment_part_plutus_data, stake_part_plutus_data]) } - Address::Stake(stake_address) => { - // This is right now only used in Withdrawals (Reward account) - match stake_address.payload() { - StakePayload::Stake(stake_keyhash) => { - StakeCredential::AddrKeyhash(*stake_keyhash).to_plutus_data() - } - StakePayload::Script(script_hash) => { - StakeCredential::Scripthash(*script_hash).to_plutus_data() - } - } - } + Address::Stake(stake_address) => stake_address.to_plutus_data(), _ => unreachable!(), } } @@ -230,12 +247,24 @@ impl ToPlutusData for i64 { } } +impl ToPlutusData for u32 { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::BigInt(BigInt::Int(Int::try_from(*self as i128).unwrap())) + } +} + impl ToPlutusData for u64 { fn to_plutus_data(&self) -> PlutusData { PlutusData::BigInt(BigInt::Int(Int::try_from(*self as i128).unwrap())) } } +impl ToPlutusData for usize { + fn to_plutus_data(&self) -> PlutusData { + PlutusData::BigInt(BigInt::Int(Int::try_from(*self as i128).unwrap())) + } +} + impl<'a> ToPlutusData for WithZeroAdaAsset<'a, Value> { fn to_plutus_data(&self) -> PlutusData { match self.0 { @@ -443,16 +472,13 @@ impl ToPlutusData for TransactionOutput { } impl ToPlutusData for StakeCredential { - // Stake Credential needs to be wrapped inside another Constr, because we could have either a StakingHash or a StakingPtr - // The current implementation of StakeCredential doesn't capture the credential of a Pointer address. - // So a StakeCredential for a Pointer address needs to be converted separately fn to_plutus_data(&self) -> PlutusData { match self { StakeCredential::AddrKeyhash(addr_keyhas) => { - wrap_with_constr(0, wrap_with_constr(0, addr_keyhas.to_plutus_data())) + wrap_with_constr(0, addr_keyhas.to_plutus_data()) } StakeCredential::Scripthash(script_hash) => { - wrap_with_constr(0, wrap_with_constr(1, script_hash.to_plutus_data())) + wrap_with_constr(1, script_hash.to_plutus_data()) } } } @@ -722,6 +748,10 @@ impl<'a> ToPlutusData for WithWrappedTransactionId<'a, ScriptPurpose> { ScriptPurpose::Spending(out_ref, ()) => { wrap_with_constr(1, WithWrappedTransactionId(out_ref).to_plutus_data()) } + // NOTE: This is a _small_ abuse of the 'WithWrappedTransactionId'. We know the wrapped + // is needed for V1 and V2, and it also appears that for V1 and V2, the certifying + // purpose mustn't include the certificate index. So, we also short-circuit it here. + ScriptPurpose::Certifying(_, dcert) => wrap_with_constr(3, dcert.to_plutus_data()), otherwise => otherwise.to_plutus_data(), } } @@ -735,11 +765,167 @@ impl ToPlutusData for ScriptPurpose { ScriptPurpose::Rewarding(stake_credential) => { wrap_with_constr(2, stake_credential.to_plutus_data()) } - ScriptPurpose::Certifying(dcert) => wrap_with_constr(3, dcert.to_plutus_data()), + ScriptPurpose::Certifying(ix, dcert) => { + wrap_multiple_with_constr(3, vec![ix.to_plutus_data(), dcert.to_plutus_data()]) + } + ScriptPurpose::Proposing(ix, procedure) => { + wrap_multiple_with_constr(5, vec![ix.to_plutus_data(), procedure.to_plutus_data()]) + } } } } +impl ToPlutusData for ProposalProcedure { + fn to_plutus_data(&self) -> PlutusData { + wrap_multiple_with_constr( + 0, + vec![ + self.deposit.to_plutus_data(), + Address::from_bytes(&self.reward_account) + .unwrap() + .to_plutus_data(), + self.gov_action.to_plutus_data(), + ], + ) + } +} + +impl ToPlutusData for Nullable +where + T: ToPlutusData + Clone, +{ + fn to_plutus_data(&self) -> PlutusData { + match self { + Nullable::Some(t) => wrap_with_constr(0, t.to_plutus_data()), + Nullable::Null | Nullable::Undefined => empty_constr(1), + } + } +} + +impl ToPlutusData for GovActionId { + fn to_plutus_data(&self) -> PlutusData { + wrap_multiple_with_constr( + 0, + vec![ + self.transaction_id.to_plutus_data(), + self.action_index.to_plutus_data(), + ], + ) + } +} + +impl ToPlutusData for ProtocolParamUpdate { + fn to_plutus_data(&self) -> PlutusData { + todo!("ToPlutusData for ProtocolParamUpdate") + } +} + +impl ToPlutusData for GovAction { + fn to_plutus_data(&self) -> PlutusData { + match self { + GovAction::ParameterChange(previous_action, params, guardrail) => { + wrap_multiple_with_constr( + 0, + vec![ + previous_action.to_plutus_data(), + params.as_ref().to_plutus_data(), + guardrail.to_plutus_data(), + ], + ) + } + GovAction::HardForkInitiation(previous_action, version) => wrap_multiple_with_constr( + 1, + vec![previous_action.to_plutus_data(), version.to_plutus_data()], + ), + GovAction::TreasuryWithdrawals(withdrawals, guardrail) => wrap_multiple_with_constr( + 2, + vec![ + KeyValuePairs::from( + withdrawals + .iter() + .map(|(reward_account, amount)| { + ( + Address::from_bytes(reward_account) + .expect("Invalid stake address in treasury withdrawal?"), + *amount, + ) + }) + .collect::>(), + ) + .to_plutus_data(), + guardrail.to_plutus_data(), + ], + ), + GovAction::NoConfidence(previous_action) => { + wrap_with_constr(3, previous_action.to_plutus_data()) + } + GovAction::UpdateCommittee(previous_action, removed, added, quorum) => { + wrap_multiple_with_constr( + 4, + vec![ + previous_action.to_plutus_data(), + removed.to_plutus_data(), + added.to_plutus_data(), + quorum.to_plutus_data(), + ], + ) + } + GovAction::NewConstitution(previous_action, constitution) => wrap_multiple_with_constr( + 5, + vec![ + previous_action.to_plutus_data(), + constitution.to_plutus_data(), + ], + ), + GovAction::Information => empty_constr(6), + } + } +} + +impl ToPlutusData for Constitution { + fn to_plutus_data(&self) -> PlutusData { + wrap_with_constr(0, self.guardrail_script.to_plutus_data()) + } +} + +impl ToPlutusData for RationalNumber { + fn to_plutus_data(&self) -> PlutusData { + (self.numerator, self.denominator).to_plutus_data() + } +} + +impl<'a> ToPlutusData for WithWrappedStakeCredential<'a, Vec<(Address, Coin)>> { + fn to_plutus_data(&self) -> PlutusData { + self.0 + .iter() + .map(|(reward_account, amount)| { + ( + wrap_with_constr(0, reward_account.to_plutus_data()), + *amount, + ) + }) + .collect::>() + .to_plutus_data() + } +} + +impl<'a> ToPlutusData for WithWrappedStakeCredential<'a, KeyValuePairs> { + fn to_plutus_data(&self) -> PlutusData { + KeyValuePairs::from( + self.0 + .iter() + .map(|(reward_account, amount)| { + ( + wrap_with_constr(0, reward_account.to_plutus_data()), + *amount, + ) + }) + .collect::>(), + ) + .to_plutus_data() + } +} + impl ToPlutusData for ScriptInfo where T: ToPlutusData, @@ -753,7 +939,12 @@ where ScriptInfo::Rewarding(stake_credential) => { wrap_with_constr(2, stake_credential.to_plutus_data()) } - ScriptInfo::Certifying(dcert) => wrap_with_constr(3, dcert.to_plutus_data()), + ScriptInfo::Certifying(ix, dcert) => { + wrap_multiple_with_constr(3, vec![ix.to_plutus_data(), dcert.to_plutus_data()]) + } + ScriptInfo::Proposing(ix, procedure) => { + wrap_multiple_with_constr(5, vec![ix.to_plutus_data(), procedure.to_plutus_data()]) + } } } } @@ -772,7 +963,7 @@ impl ToPlutusData for TxInfo { WithZeroAdaAsset(&tx_info.fee).to_plutus_data(), WithZeroAdaAsset(&tx_info.mint).to_plutus_data(), tx_info.certificates.to_plutus_data(), - tx_info.withdrawals.to_plutus_data(), + WithWrappedStakeCredential(&tx_info.withdrawals).to_plutus_data(), tx_info.valid_range.to_plutus_data(), tx_info.signatories.to_plutus_data(), tx_info.data.to_plutus_data(), @@ -789,7 +980,7 @@ impl ToPlutusData for TxInfo { WithZeroAdaAsset(&tx_info.fee).to_plutus_data(), WithZeroAdaAsset(&tx_info.mint).to_plutus_data(), tx_info.certificates.to_plutus_data(), - tx_info.withdrawals.to_plutus_data(), + WithWrappedStakeCredential(&tx_info.withdrawals).to_plutus_data(), tx_info.valid_range.to_plutus_data(), tx_info.signatories.to_plutus_data(), WithWrappedTransactionId(&tx_info.redeemers).to_plutus_data(), @@ -813,7 +1004,7 @@ impl ToPlutusData for TxInfo { tx_info.data.to_plutus_data(), tx_info.id.to_plutus_data(), Data::map(vec![]), // TODO tx_info.votes :: Map Voter (Map GovernanceActionId Vote) - Data::list(vec![]), // TODO tx_info.proposal_procedures :: [ProposalProcedure] + tx_info.proposal_procedures.to_plutus_data(), empty_constr(1), // TODO tx_info.current_treasury_amount :: Haskell.Maybe V2.Lovelace empty_constr(1), // TODO tx_info.treasury_donation :: Haskell.Maybe V2.Lovelace ], @@ -847,12 +1038,3 @@ impl ToPlutusData for ScriptContext { } } } - -impl ToPlutusData for bool { - fn to_plutus_data(&self) -> PlutusData { - match self { - false => empty_constr(0), - true => empty_constr(1), - } - } -}