From 3736cf3b47089b1408988dfedd1908081e502c46 Mon Sep 17 00:00:00 2001 From: twwu123 Date: Wed, 5 Jun 2024 17:55:52 +0800 Subject: [PATCH 1/8] add support for withdrawals --- packages/sidan-csl-rs/src/builder/core.rs | 17 ++++ .../sidan-csl-rs/src/builder/interface.rs | 10 ++ packages/sidan-csl-rs/src/core/builder.rs | 92 +++++++++++++++++++ .../sidan-csl-rs/src/core/tx_parser/mod.rs | 1 + packages/sidan-csl-rs/src/model/mod.rs | 23 +++++ 5 files changed, 143 insertions(+) diff --git a/packages/sidan-csl-rs/src/builder/core.rs b/packages/sidan-csl-rs/src/builder/core.rs index 8335540..c1369e0 100644 --- a/packages/sidan-csl-rs/src/builder/core.rs +++ b/packages/sidan-csl-rs/src/builder/core.rs @@ -159,6 +159,7 @@ impl IMeshTxBuilderCore for MeshTxBuilderCore { collaterals: vec![], required_signatures: JsVecString::new(), reference_inputs: vec![], + withdrawals: vec![], mints: vec![], change_address: "".to_string(), change_datum: None, @@ -215,6 +216,22 @@ impl IMeshTxBuilderCore for MeshTxBuilderCore { } } + fn add_all_withdrawals(mesh_csl: &mut MeshCSL, withdrawals: Vec) { + for withdrawal in withdrawals { + match withdrawal { + Withdrawal::PubKeyWithdrawal(pub_key_withdrawal) => { + mesh_csl.add_pub_key_withdrawal(pub_key_withdrawal) + } + Withdrawal::PlutusScriptWithdrawal(plutus_script_withdrawal) => { + mesh_csl.add_plutus_withdrawal(plutus_script_withdrawal) + } + } + } + mesh_csl + .tx_builder + .set_withdrawals_builder(&mesh_csl.tx_withdrawals_builder); + } + fn add_all_mints(mesh_csl: &mut MeshCSL, mints: Vec) { let mut mint_builder = csl::MintBuilder::new(); for (index, mint) in mints.into_iter().enumerate() { diff --git a/packages/sidan-csl-rs/src/builder/interface.rs b/packages/sidan-csl-rs/src/builder/interface.rs index 2bd89b1..57f965e 100644 --- a/packages/sidan-csl-rs/src/builder/interface.rs +++ b/packages/sidan-csl-rs/src/builder/interface.rs @@ -76,6 +76,16 @@ pub trait IMeshTxBuilderCore { /// * `ref_inputs` - A vector of reference inputs fn add_all_reference_inputs(mesh_csl: &mut MeshCSL, ref_inputs: Vec); + /// ## Internal method + /// + /// Add multiple withdrawals to the MeshTxBuilder instance + /// + /// ## Arguments + /// + /// * `mesh_csl` - The MeshCSL instance + /// * `withdrawals` - A vector of withdrawals + fn add_all_withdrawals(mesh_csl: &mut MeshCSL, withdrawals: Vec); + /// ## Internal method /// /// Add multiple mints to the MeshTxBuilder instance diff --git a/packages/sidan-csl-rs/src/core/builder.rs b/packages/sidan-csl-rs/src/core/builder.rs index afd2d02..d4e1f23 100644 --- a/packages/sidan-csl-rs/src/core/builder.rs +++ b/packages/sidan-csl-rs/src/core/builder.rs @@ -13,6 +13,8 @@ pub trait IMeshCSL { collateral: PubKeyTxIn, ); fn add_reference_input(&mut self, ref_input: RefTxIn); + fn add_pub_key_withdrawal(&mut self, withdrawal: PubKeyWithdrawal); + fn add_plutus_withdrawal(&mut self, withdrawal: PlutusScriptWithdrawal); fn add_plutus_mint(&mut self, mint_builder: &mut csl::MintBuilder, mint: MintItem, index: u64); fn add_native_mint(&mut self, mint_builder: &mut csl::MintBuilder, mint: MintItem); fn add_invalid_before(&mut self, invalid_before: u64); @@ -29,6 +31,7 @@ pub struct MeshCSL { pub tx_hex: String, pub tx_builder: csl::TransactionBuilder, pub tx_inputs_builder: csl::TxInputsBuilder, + pub tx_withdrawals_builder: csl::WithdrawalsBuilder, } impl IMeshCSL for MeshCSL { @@ -37,6 +40,7 @@ impl IMeshCSL for MeshCSL { tx_hex: String::new(), tx_builder: build_tx_builder(params), tx_inputs_builder: csl::TxInputsBuilder::new(), + tx_withdrawals_builder: csl::WithdrawalsBuilder::new(), } } @@ -214,6 +218,94 @@ impl IMeshCSL for MeshCSL { self.tx_builder.add_reference_input(&csl_ref_input); } + fn add_pub_key_withdrawal(&mut self, withdrawal: PubKeyWithdrawal) { + let _ = self + .tx_withdrawals_builder + .add( + &csl::RewardAddress::from_address( + &csl::Address::from_bech32(&withdrawal.address).unwrap(), + ) + .unwrap(), + &csl::BigNum::from_str(&withdrawal.coin.to_string()).unwrap(), + ) + .unwrap(); + } + + fn add_plutus_withdrawal(&mut self, withdrawal: PlutusScriptWithdrawal) { + let datum_source = withdrawal.script_param.datum_source.unwrap(); + let script_source = withdrawal.script_param.script_source.unwrap(); + let redeemer = withdrawal.script_param.redeemer.unwrap(); + let csl_datum: csl::DatumSource = match datum_source { + DatumSource::ProvidedDatumSource(datum) => csl::DatumSource::new( + &csl::PlutusData::from_json(&datum.data, csl::PlutusDatumSchema::DetailedSchema) + .unwrap(), + ), + DatumSource::InlineDatumSource(datum) => { + let ref_input = csl::TransactionInput::new( + &csl::TransactionHash::from_hex(&datum.tx_hash).unwrap(), + datum.tx_index, + ); + csl::DatumSource::new_ref_input(&ref_input) + } + }; + + let csl_script: csl::PlutusScriptSource = match script_source { + ScriptSource::ProvidedScriptSource(script) => { + let language_version: csl::Language = match script.language_version { + LanguageVersion::V1 => csl::Language::new_plutus_v1(), + LanguageVersion::V2 => csl::Language::new_plutus_v2(), + LanguageVersion::V3 => csl::Language::new_plutus_v3(), + }; + csl::PlutusScriptSource::new( + &csl::PlutusScript::from_hex_with_version( + &script.script_cbor, + &language_version, + ) + .unwrap(), + ) + } + ScriptSource::InlineScriptSource(script) => { + let language_version: csl::Language = match script.language_version { + LanguageVersion::V1 => csl::Language::new_plutus_v1(), + LanguageVersion::V2 => csl::Language::new_plutus_v2(), + LanguageVersion::V3 => csl::Language::new_plutus_v3(), + }; + csl::PlutusScriptSource::new_ref_input( + &csl::ScriptHash::from_hex(&script.spending_script_hash).unwrap(), + &csl::TransactionInput::new( + &csl::TransactionHash::from_hex(&script.tx_hash).unwrap(), + script.tx_index, + ), + &language_version, + script.script_size, + ) + } + }; + + let csl_redeemer: csl::Redeemer = csl::Redeemer::new( + &csl::RedeemerTag::new_spend(), + &to_bignum(0), + &csl::PlutusData::from_json(&redeemer.data, csl::PlutusDatumSchema::DetailedSchema) + .unwrap(), + &csl::ExUnits::new( + &to_bignum(redeemer.ex_units.mem), + &to_bignum(redeemer.ex_units.steps), + ), + ); + + let _ = self + .tx_withdrawals_builder + .add_with_plutus_witness( + &csl::RewardAddress::from_address( + &csl::Address::from_bech32(&withdrawal.address).unwrap(), + ) + .unwrap(), + &csl::BigNum::from_str(&withdrawal.coin.to_string()).unwrap(), + &csl::PlutusWitness::new_with_ref(&csl_script, &csl_datum, &csl_redeemer), + ) + .unwrap(); + } + fn add_plutus_mint(&mut self, mint_builder: &mut csl::MintBuilder, mint: MintItem, index: u64) { let redeemer_info = mint.redeemer.unwrap(); let mint_redeemer = csl::Redeemer::new( diff --git a/packages/sidan-csl-rs/src/core/tx_parser/mod.rs b/packages/sidan-csl-rs/src/core/tx_parser/mod.rs index ea001de..89dbfcb 100644 --- a/packages/sidan-csl-rs/src/core/tx_parser/mod.rs +++ b/packages/sidan-csl-rs/src/core/tx_parser/mod.rs @@ -37,6 +37,7 @@ impl IMeshTxParser for MeshTxParser { collaterals: vec![], required_signatures: JsVecString::new(), reference_inputs: vec![], + withdrawals: vec![], mints: vec![], change_address: "".to_string(), change_datum: None, diff --git a/packages/sidan-csl-rs/src/model/mod.rs b/packages/sidan-csl-rs/src/model/mod.rs index ac90206..85ec554 100644 --- a/packages/sidan-csl-rs/src/model/mod.rs +++ b/packages/sidan-csl-rs/src/model/mod.rs @@ -22,6 +22,7 @@ pub struct MeshTxBuilderBody { pub collaterals: Vec, pub required_signatures: JsVecString, pub reference_inputs: Vec, + pub withdrawals: Vec, pub mints: Vec, pub change_address: String, pub change_datum: Option, @@ -151,6 +152,28 @@ pub struct ScriptSourceInfo { pub spending_script_hash: Option, } +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum Withdrawal { + PubKeyWithdrawal(PubKeyWithdrawal), + PlutusScriptWithdrawal(PlutusScriptWithdrawal), +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PubKeyWithdrawal { + pub address: String, + pub coin: u64, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PlutusScriptWithdrawal { + pub address: String, + pub coin: u64, + pub script_param: ScriptTxInParameter, +} + #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct MintItem { From ae18124963d315a461c497fe2503bf4099f8b5ab Mon Sep 17 00:00:00 2001 From: twwu123 Date: Fri, 7 Jun 2024 14:55:55 +0800 Subject: [PATCH 2/8] add withdrawal support to mesh tx builder body --- packages/sidan-csl-rs/src/builder/core.rs | 1 + packages/sidan-csl-rs/src/core/builder.rs | 20 +--- packages/sidan-csl-rs/src/model/mod.rs | 3 +- packages/whisky/src/builder/core.rs | 138 +++++++++++++++++++++- packages/whisky/src/builder/interface.rs | 100 +++++++++++++++- 5 files changed, 238 insertions(+), 24 deletions(-) diff --git a/packages/sidan-csl-rs/src/builder/core.rs b/packages/sidan-csl-rs/src/builder/core.rs index c1369e0..8f92832 100644 --- a/packages/sidan-csl-rs/src/builder/core.rs +++ b/packages/sidan-csl-rs/src/builder/core.rs @@ -65,6 +65,7 @@ pub fn serialize_tx_body( &mut mesh_csl, mesh_tx_builder_body.reference_inputs.clone(), ); + MeshTxBuilderCore::add_all_withdrawals(&mut mesh_csl, mesh_tx_builder_body.withdrawals.clone()); MeshTxBuilderCore::add_all_mints(&mut mesh_csl, mesh_tx_builder_body.mints.clone()); MeshTxBuilderCore::add_validity_range( &mut mesh_csl, diff --git a/packages/sidan-csl-rs/src/core/builder.rs b/packages/sidan-csl-rs/src/core/builder.rs index d4e1f23..dd2e804 100644 --- a/packages/sidan-csl-rs/src/core/builder.rs +++ b/packages/sidan-csl-rs/src/core/builder.rs @@ -232,22 +232,8 @@ impl IMeshCSL for MeshCSL { } fn add_plutus_withdrawal(&mut self, withdrawal: PlutusScriptWithdrawal) { - let datum_source = withdrawal.script_param.datum_source.unwrap(); - let script_source = withdrawal.script_param.script_source.unwrap(); - let redeemer = withdrawal.script_param.redeemer.unwrap(); - let csl_datum: csl::DatumSource = match datum_source { - DatumSource::ProvidedDatumSource(datum) => csl::DatumSource::new( - &csl::PlutusData::from_json(&datum.data, csl::PlutusDatumSchema::DetailedSchema) - .unwrap(), - ), - DatumSource::InlineDatumSource(datum) => { - let ref_input = csl::TransactionInput::new( - &csl::TransactionHash::from_hex(&datum.tx_hash).unwrap(), - datum.tx_index, - ); - csl::DatumSource::new_ref_input(&ref_input) - } - }; + let script_source = withdrawal.script_source.unwrap(); + let redeemer = withdrawal.redeemer.unwrap(); let csl_script: csl::PlutusScriptSource = match script_source { ScriptSource::ProvidedScriptSource(script) => { @@ -301,7 +287,7 @@ impl IMeshCSL for MeshCSL { ) .unwrap(), &csl::BigNum::from_str(&withdrawal.coin.to_string()).unwrap(), - &csl::PlutusWitness::new_with_ref(&csl_script, &csl_datum, &csl_redeemer), + &csl::PlutusWitness::new_with_ref_without_datum(&csl_script, &csl_redeemer), ) .unwrap(); } diff --git a/packages/sidan-csl-rs/src/model/mod.rs b/packages/sidan-csl-rs/src/model/mod.rs index 85ec554..252c542 100644 --- a/packages/sidan-csl-rs/src/model/mod.rs +++ b/packages/sidan-csl-rs/src/model/mod.rs @@ -171,7 +171,8 @@ pub struct PubKeyWithdrawal { pub struct PlutusScriptWithdrawal { pub address: String, pub coin: u64, - pub script_param: ScriptTxInParameter, + pub script_source: Option, + pub redeemer: Option, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] diff --git a/packages/whisky/src/builder/core.rs b/packages/whisky/src/builder/core.rs index aeebbc8..550a83f 100644 --- a/packages/whisky/src/builder/core.rs +++ b/packages/whisky/src/builder/core.rs @@ -5,9 +5,9 @@ use sidan_csl_rs::{ csl, model::{ Asset, Datum, DatumSource, InlineDatumSource, InlineScriptSource, LanguageVersion, - MeshTxBuilderBody, Metadata, MintItem, Output, ProvidedDatumSource, ProvidedScriptSource, - PubKeyTxIn, Redeemer, RefTxIn, ScriptSource, ScriptTxIn, ScriptTxInParameter, TxIn, - TxInParameter, UTxO, Value, + MeshTxBuilderBody, Metadata, MintItem, Output, PlutusScriptWithdrawal, ProvidedDatumSource, + ProvidedScriptSource, PubKeyTxIn, PubKeyWithdrawal, Redeemer, RefTxIn, ScriptSource, + ScriptTxIn, ScriptTxInParameter, TxIn, TxInParameter, UTxO, Value, Withdrawal, }, }; @@ -23,11 +23,13 @@ impl IMeshTxBuilder for MeshTxBuilder { tx_in_item: None, extra_inputs: vec![], selection_threshold: 5_000_000, + withdrawal_item: None, mint_item: None, collateral_item: None, tx_output: None, adding_script_input: false, adding_plutus_mint: false, + adding_plutus_withdrawal: false, fetcher: param.fetcher, evaluator: match param.evaluator { Some(evaluator) => Some(evaluator), @@ -69,8 +71,10 @@ impl IMeshTxBuilder for MeshTxBuilder { self.add_utxos_from(self.extra_inputs.clone(), self.selection_threshold); } } - let tx_hex = - serialize_tx_body(self.core.mesh_tx_builder_body.clone(), self.protocol_params.clone()); + let tx_hex = serialize_tx_body( + self.core.mesh_tx_builder_body.clone(), + self.protocol_params.clone(), + ); self.core.mesh_csl.tx_hex = tx_hex; self.core.mesh_csl.tx_builder = build_tx_builder(None); self.core.mesh_csl.tx_inputs_builder = csl::TxInputsBuilder::new(); @@ -318,6 +322,108 @@ impl IMeshTxBuilder for MeshTxBuilder { self } + fn withdrawal_plutus_script_v2(&mut self) -> &mut Self { + self.adding_plutus_withdrawal = true; + self + } + + fn withdrawal_tx_in_reference( + &mut self, + tx_hash: &str, + tx_index: u32, + withdrawal_script_hash: &str, + version: LanguageVersion, + script_size: usize, + ) -> &mut Self { + let withdrawal_item = self.withdrawal_item.take(); + if withdrawal_item.is_none() { + panic!("Undefined output") + } + let withdrawal_item = withdrawal_item.unwrap(); + match withdrawal_item { + Withdrawal::PubKeyWithdrawal(_) => panic!("Script reference cannot be defined for a pubkey withdrawal"), + Withdrawal::PlutusScriptWithdrawal(mut withdrawal) => { + withdrawal.script_source = + Some(ScriptSource::InlineScriptSource(InlineScriptSource { + tx_hash: tx_hash.to_string(), + tx_index, + spending_script_hash: withdrawal_script_hash.to_string(), + language_version: version, + script_size, + })); + self.withdrawal_item = Some(Withdrawal::PlutusScriptWithdrawal(withdrawal)); + } + } + self + } + + fn withdrawal(&mut self, stake_address: &str, coin: u64) -> &mut Self { + if self.withdrawal_item.is_some() { + self.queue_withdrawal(); + } + if !self.adding_plutus_withdrawal { + let withdrawal_item = Withdrawal::PubKeyWithdrawal(PubKeyWithdrawal { + address: stake_address.to_string(), + coin, + }); + self.withdrawal_item = Some(withdrawal_item); + } else { + let withdrawal_item = Withdrawal::PlutusScriptWithdrawal(PlutusScriptWithdrawal { + address: stake_address.to_string(), + coin, + script_source: None, + redeemer: None, + }); + self.withdrawal_item = Some(withdrawal_item); + } + self.adding_plutus_withdrawal = false; + self + } + + fn withdrawal_script(&mut self, script_cbor: &str, version: LanguageVersion) -> &mut Self { + let withdrawal_item = self.withdrawal_item.take(); + if withdrawal_item.is_none() { + panic!("Undefined withdrawal") + } + let withdrawal_item = withdrawal_item.unwrap(); + match withdrawal_item { + Withdrawal::PubKeyWithdrawal(_) => { + panic!("Script cannot be defined for a pubkey withdrawal") + } + Withdrawal::PlutusScriptWithdrawal(mut withdraw) => { + withdraw.script_source = + Some(ScriptSource::ProvidedScriptSource(ProvidedScriptSource { + script_cbor: script_cbor.to_string(), + language_version: version, + })); + self.withdrawal_item = Some(Withdrawal::PlutusScriptWithdrawal(withdraw)); + } + } + self + } + + fn withdrawal_redeemer_value(&mut self, redeemer: Redeemer) -> &mut Self { + let withdrawal_item = self.withdrawal_item.take(); + if withdrawal_item.is_none() { + panic!("Undefined input") + } + let withdrawal_item = withdrawal_item.unwrap(); + match withdrawal_item { + Withdrawal::PubKeyWithdrawal(_) => { + panic!("Script cannot be defined for a pubkey withdrawal") + } + Withdrawal::PlutusScriptWithdrawal(mut withdraw) => { + withdraw.redeemer = Some(redeemer); + self.withdrawal_item = Some(Withdrawal::PlutusScriptWithdrawal(withdraw)); + } + } + self + } + + fn withdrawal_reference_tx_in_redeemer_value(&mut self, redeemer: Redeemer) -> &mut Self { + self.withdrawal_redeemer_value(redeemer) + } + fn mint_plutus_script_v2(&mut self) -> &mut Self { self.adding_plutus_mint = true; self @@ -516,6 +622,25 @@ impl IMeshTxBuilder for MeshTxBuilder { self.tx_in_item = None } + fn queue_withdrawal(&mut self) { + let withdrawal_item = self.withdrawal_item.clone().unwrap(); + match withdrawal_item { + Withdrawal::PlutusScriptWithdrawal(withdrawal) => { + match (withdrawal.redeemer, withdrawal.script_source) { + (None, _) => panic!("Redeemer in script input cannot be None"), + (_, None) => panic!("Script source in script input cannot be None"), + _ => {} + } + } + Withdrawal::PubKeyWithdrawal(_) => {} + } + self.core + .mesh_tx_builder_body + .withdrawals + .push(self.withdrawal_item.clone().unwrap()); + self.withdrawal_item = None; + } + fn queue_mint(&mut self) { let mint_item = self.mint_item.clone().unwrap(); if mint_item.script_source.is_none() { @@ -543,6 +668,9 @@ impl IMeshTxBuilder for MeshTxBuilder { .push(self.collateral_item.clone().unwrap()); self.collateral_item = None; } + if self.withdrawal_item.is_some() { + self.queue_withdrawal(); + } if self.mint_item.is_some() { self.queue_mint(); } diff --git a/packages/whisky/src/builder/interface.rs b/packages/whisky/src/builder/interface.rs index 3025827..8f0a13d 100644 --- a/packages/whisky/src/builder/interface.rs +++ b/packages/whisky/src/builder/interface.rs @@ -3,7 +3,7 @@ use sidan_csl_rs::{ builder::MeshTxBuilderCore, model::{ Asset, LanguageVersion, MeshTxBuilderBody, MintItem, Output, Protocol, PubKeyTxIn, - Redeemer, TxIn, UTxO, + Redeemer, TxIn, UTxO, Withdrawal, }, }; @@ -15,11 +15,13 @@ pub struct MeshTxBuilder { pub tx_in_item: Option, pub extra_inputs: Vec, pub selection_threshold: u64, + pub withdrawal_item: Option, pub mint_item: Option, pub collateral_item: Option, pub tx_output: Option, pub adding_script_input: bool, pub adding_plutus_mint: bool, + pub adding_plutus_withdrawal: bool, pub fetcher: Option>, pub evaluator: Option>, pub submitter: Option>, @@ -284,6 +286,97 @@ pub trait IMeshTxBuilder { /// * `Self` - The MeshTxBuilder instance fn read_only_tx_in_reference(&mut self, tx_hash: &str, tx_index: u32) -> &mut Self; + + /// ## Transaction building method + /// + /// Indicate that the transaction is withdrawing using a plutus staking script in the MeshTxBuilder instance + /// + /// ### Returns + /// + /// * `Self` - The MeshTxBuilder instance + fn withdrawal_plutus_script_v2(&mut self) -> &mut Self; + + /// ## Transaction building method + /// + /// Add a withdrawal reference to the MeshTxBuilder instance + /// + /// ### Arguments + /// + /// * `tx_hash` - The transaction hash + /// * `tx_index` - The transaction index + /// * `withdrawal_script_hash` - The withdrawal script hash + /// * `version` - The language version + /// * `scrip_size` - Size of the script + /// + /// ### Returns + /// + /// * `Self` - The MeshTxBuilder instance + fn withdrawal_tx_in_reference( + &mut self, + tx_hash: &str, + tx_index: u32, + withdrawal_script_hash: &str, + version: LanguageVersion, + script_size: usize, + ) -> &mut Self; + + + /// ## Transaction building method + /// + /// Withdraw stake rewards in the MeshTxBuilder instance + /// + /// ### Arguments + /// + /// * `stake_address` - The address corresponding to the stake key + /// * `coin` - The amount of lovelaces in the withdrawal + /// + /// ### Returns + /// + /// * `Self` - The MeshTxBuilder instance + fn withdrawal(&mut self, stake_address: &str, coin: u64) -> &mut Self; + + /// ## Transaction building method + /// + /// Add a withdrawal script to the MeshTxBuilder instance + /// + /// ### Arguments + /// + /// * `script_cbor` - The script in CBOR format + /// * `version` - The language version + /// + /// ### Returns + /// + /// * `Self` - The MeshTxBuilder instance + fn withdrawal_script(&mut self, script_cbor: &str, version: LanguageVersion) -> &mut Self; + + + /// ## Transaction building method + /// + /// Set the transaction withdrawal redeemer value in the MeshTxBuilder instance + /// + /// ### Arguments + /// + /// * `redeemer` - The redeemer value + /// + /// ### Returns + /// + /// * `Self` - The MeshTxBuilder instance + fn withdrawal_redeemer_value(&mut self, redeemer: Redeemer) -> &mut Self; + + + /// ## Transaction building method + /// + /// Set the withdrawal reference redeemer value in the MeshTxBuilder instance + /// + /// ### Arguments + /// + /// * `redeemer` - The redeemer value + /// + /// ### Returns + /// + /// * `Self` - The MeshTxBuilder instance + fn withdrawal_reference_tx_in_redeemer_value(&mut self, redeemer: Redeemer) -> &mut Self; + /// ## Transaction building method /// /// Indicate that the transaction is minting a Plutus script v2 in the MeshTxBuilder instance @@ -531,6 +624,11 @@ pub trait IMeshTxBuilder { /// Queue an input in the MeshTxBuilder instance fn queue_input(&mut self); + /// ## Internal method + /// + /// Queue a withdrawal in the MeshTxBuilder instance + fn queue_withdrawal(&mut self); + /// ## Internal method /// /// Queue a mint in the MeshTxBuilder instance From f1b7b251fb7a52c39f62d6ae099806791ac6fdaa Mon Sep 17 00:00:00 2001 From: twwu123 Date: Fri, 7 Jun 2024 16:13:29 +0800 Subject: [PATCH 3/8] add simple withdraw test --- packages/whisky/tests/integration_tests.rs | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/packages/whisky/tests/integration_tests.rs b/packages/whisky/tests/integration_tests.rs index 1fbf7e1..624c5f3 100644 --- a/packages/whisky/tests/integration_tests.rs +++ b/packages/whisky/tests/integration_tests.rs @@ -184,4 +184,33 @@ mod int_tests { println!("{}", signed_tx); assert!(mesh.core.mesh_csl.tx_hex != *""); } + + #[test] + fn test_simple_withdraw() { + let mut mesh = MeshTxBuilder::new(MeshTxBuilderParam { + evaluator: None, + fetcher: None, + submitter: None, + params: None, + }); + + let signed_tx = mesh + .tx_in( + "fbd3e8091c9f0c5fb446be9e58d9235f548546a5a7d5f60ee56e389344db9c5e", + 0, + vec![Asset::new_from_str("lovelace", "9496607660")], + "addr_test1qpjfsrjdr8kk5ffj4jnw02ht3y3td0y0zkcm52rc6w7z7flmy7vplnvz6a7dncss4q5quqwt48tv9dewuvdxqssur9jqc4x459", + ) + .change_address("addr_test1qpjfsrjdr8kk5ffj4jnw02ht3y3td0y0zkcm52rc6w7z7flmy7vplnvz6a7dncss4q5quqwt48tv9dewuvdxqssur9jqc4x459") + .withdrawal("stake_test1uraj0xqlekpdwlxeugg2s2qwq896n4kzkuhwxxnqggwpjeqe9s9k2", 0) + .required_signer_hash("fb27981fcd82d77cd9e210a8280e01cba9d6c2b72ee31a60421c1964") + .required_signer_hash("64980e4d19ed6a2532aca6e7aaeb8922b6bc8f15b1ba2878d3bc2f27") + .signing_key("58208d4cfa90e8bd0c48c52d2fb62c77ba3f6f5eb46f640d5f997390012928d670f7") + .signing_key("5820ba73019f1239fa47f8d9c0c42c5d05bf34f2b2f6ebd1c556f8f86e5bee1aac66") + .complete_sync(None) + .complete_signing(); + + println!("{}", signed_tx); + assert!(mesh.core.mesh_csl.tx_hex != *""); + } } From a910731a53eec225ba57582fd4467e493c66ccd9 Mon Sep 17 00:00:00 2001 From: twwu123 Date: Fri, 7 Jun 2024 17:19:42 +0800 Subject: [PATCH 4/8] add plutus withdraw test --- packages/whisky/tests/integration_tests.rs | 46 ++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/packages/whisky/tests/integration_tests.rs b/packages/whisky/tests/integration_tests.rs index 624c5f3..526e7cf 100644 --- a/packages/whisky/tests/integration_tests.rs +++ b/packages/whisky/tests/integration_tests.rs @@ -213,4 +213,50 @@ mod int_tests { println!("{}", signed_tx); assert!(mesh.core.mesh_csl.tx_hex != *""); } + + #[test] + fn test_plutus_withdraw() { + let mut mesh = MeshTxBuilder::new(MeshTxBuilderParam { + evaluator: None, + fetcher: None, + submitter: None, + params: None, + }); + + let signed_tx = mesh + .tx_in( + "60b6a29a4c164bece283738abd57fa35c0b839f298f15836ee54a875ede87d37", + 0, + vec![Asset::new_from_str("lovelace", "9999639476")], + "addr_test1yp8ezxpltlrus89uz8g7e07795w0cxn3a7w7nxdac8s4aj7cjpk2t3a6zf9qgpar9k4n0vkg9vfm8hxezy0y99qde6jq58zjfw", + ) + .tx_in_collateral( + "60b6a29a4c164bece283738abd57fa35c0b839f298f15836ee54a875ede87d37", + 0, + vec![Asset::new_from_str("lovelace", "9999639476")], + "addr_test1yp8ezxpltlrus89uz8g7e07795w0cxn3a7w7nxdac8s4aj7cjpk2t3a6zf9qgpar9k4n0vkg9vfm8hxezy0y99qde6jq58zjfw", + ) + .change_address("addr_test1yp8ezxpltlrus89uz8g7e07795w0cxn3a7w7nxdac8s4aj7cjpk2t3a6zf9qgpar9k4n0vkg9vfm8hxezy0y99qde6jq58zjfw") + .withdrawal_plutus_script_v2() + .withdrawal("stake_test17rvfqm99c7apyjsyq73jm2ehktyzkyanmnv3z8jzjsxuafq5a6z2j", 0) + .withdrawal_script("5251010000322253330034a229309b2b2b9a01", LanguageVersion::V2) + .withdrawal_redeemer_value(Redeemer { + data: to_string(&json!({ + "constructor": 0, + "fields": [] + })) + .unwrap(), + ex_units: Budget { + mem: 2501, + steps: 617656, + }, + }) + .required_signer_hash("4f91183f5fc7c81cbc11d1ecbfde2d1cfc1a71ef9de999bdc1e15ecb") + .signing_key("5820c835cd2413c6330537c85e3d510b313dfdeee5708206e76ce8bd387cdd4b6bb2") + .complete_sync(None) + .complete_signing(); + + println!("{}", signed_tx); + assert!(mesh.core.mesh_csl.tx_hex != *""); + } } From 9a806b1915969422d3e3ca71a2ba331eeaf53cd9 Mon Sep 17 00:00:00 2001 From: twwu123 Date: Mon, 10 Jun 2024 15:23:04 +0800 Subject: [PATCH 5/8] add staking certificate building to core --- packages/sidan-csl-rs/src/builder/core.rs | 31 ++++ .../sidan-csl-rs/src/builder/interface.rs | 10 + packages/sidan-csl-rs/src/core/builder.rs | 171 ++++++++++++++++++ .../sidan-csl-rs/src/core/tx_parser/mod.rs | 1 + packages/sidan-csl-rs/src/model/mod.rs | 93 ++++++++++ 5 files changed, 306 insertions(+) diff --git a/packages/sidan-csl-rs/src/builder/core.rs b/packages/sidan-csl-rs/src/builder/core.rs index 8f92832..8de48ea 100644 --- a/packages/sidan-csl-rs/src/builder/core.rs +++ b/packages/sidan-csl-rs/src/builder/core.rs @@ -1,3 +1,5 @@ +use pallas_traverse::cert; + use crate::{ core::builder::{IMeshCSL, MeshCSL}, csl, @@ -67,6 +69,10 @@ pub fn serialize_tx_body( ); MeshTxBuilderCore::add_all_withdrawals(&mut mesh_csl, mesh_tx_builder_body.withdrawals.clone()); MeshTxBuilderCore::add_all_mints(&mut mesh_csl, mesh_tx_builder_body.mints.clone()); + MeshTxBuilderCore::add_all_certificates( + &mut mesh_csl, + mesh_tx_builder_body.certificates.clone(), + ); MeshTxBuilderCore::add_validity_range( &mut mesh_csl, mesh_tx_builder_body.validity_range.clone(), @@ -164,6 +170,7 @@ impl IMeshTxBuilderCore for MeshTxBuilderCore { mints: vec![], change_address: "".to_string(), change_datum: None, + certificates: vec![], metadata: vec![], validity_range: ValidityRange { invalid_before: None, @@ -245,6 +252,30 @@ impl IMeshTxBuilderCore for MeshTxBuilderCore { mesh_csl.tx_builder.set_mint_builder(&mint_builder) } + fn add_all_certificates(mesh_csl: &mut MeshCSL, certificates: Vec) { + let mut certificates_builder = csl::CertificatesBuilder::new(); + for cert in certificates { + match cert { + Certificate::RegisterPool(register_pool) => { + mesh_csl.add_register_pool_cert(&mut certificates_builder, register_pool) + } + Certificate::RegisterStake(register_stake) => { + mesh_csl.add_register_stake_cert(&mut certificates_builder, register_stake) + } + Certificate::DelegateStake(delegate_stake) => { + mesh_csl.add_delegate_stake_cert(&mut certificates_builder, delegate_stake) + } + Certificate::DeregisterStake(deregister_stake) => { + mesh_csl.add_deregister_stake_cert(&mut certificates_builder, deregister_stake) + } + Certificate::RetirePool(retire_pool) => { + mesh_csl.add_retire_pool_cert(&mut certificates_builder, retire_pool) + } + } + } + mesh_csl.tx_builder.set_certs_builder(&certificates_builder) + } + fn add_validity_range(mesh_csl: &mut MeshCSL, validity_range: ValidityRange) { if validity_range.invalid_before.is_some() { mesh_csl.add_invalid_before(validity_range.invalid_before.unwrap()) diff --git a/packages/sidan-csl-rs/src/builder/interface.rs b/packages/sidan-csl-rs/src/builder/interface.rs index 57f965e..578b1a0 100644 --- a/packages/sidan-csl-rs/src/builder/interface.rs +++ b/packages/sidan-csl-rs/src/builder/interface.rs @@ -96,6 +96,16 @@ pub trait IMeshTxBuilderCore { /// * `mints` - A vector of mints fn add_all_mints(mesh_csl: &mut MeshCSL, mints: Vec); + /// ## Internal method + /// + /// Add multiple certificates to the MeshTxBuilder instance + /// + /// ### Arguments + /// + /// * `mesh_csl` - The MeshCSL instance + /// * `certificates` - A vector of certificates + fn add_all_certificates(mesh_csl: &mut MeshCSL, certificates: Vec); + /// ## Internal method /// /// Add a validity range to the MeshTxBuilder instance diff --git a/packages/sidan-csl-rs/src/core/builder.rs b/packages/sidan-csl-rs/src/core/builder.rs index dd2e804..d5bff4e 100644 --- a/packages/sidan-csl-rs/src/core/builder.rs +++ b/packages/sidan-csl-rs/src/core/builder.rs @@ -1,3 +1,7 @@ +use std::net::{Ipv4Addr, Ipv6Addr}; + +use pallas_traverse::cert; + use crate::{csl, model::*}; use super::{utils::build_tx_builder, utils::sign_transaction, utils::to_bignum, utils::to_value}; @@ -17,6 +21,31 @@ pub trait IMeshCSL { fn add_plutus_withdrawal(&mut self, withdrawal: PlutusScriptWithdrawal); fn add_plutus_mint(&mut self, mint_builder: &mut csl::MintBuilder, mint: MintItem, index: u64); fn add_native_mint(&mut self, mint_builder: &mut csl::MintBuilder, mint: MintItem); + fn add_register_pool_cert( + &mut self, + certificate_builder: &mut csl::CertificatesBuilder, + register_pool: RegisterPool, + ); + fn add_register_stake_cert( + &mut self, + certificate_builder: &mut csl::CertificatesBuilder, + register_stake: RegisterStake, + ); + fn add_delegate_stake_cert( + &mut self, + certificates_builder: &mut csl::CertificatesBuilder, + delegate_stake: DelegateStake, + ); + fn add_deregister_stake_cert( + &mut self, + certificates_builder: &mut csl::CertificatesBuilder, + deregister_stake: DeregisterStake, + ); + fn add_retire_pool_cert( + &mut self, + certificates_builder: &mut csl::CertificatesBuilder, + retire_pool: RetirePool, + ); fn add_invalid_before(&mut self, invalid_before: u64); fn add_invalid_hereafter(&mut self, invalid_hereafter: u64); fn add_change(&mut self, change_address: String, change_datum: Option); @@ -364,6 +393,148 @@ impl IMeshCSL for MeshCSL { }; } + fn add_register_pool_cert( + &mut self, + certificate_builder: &mut csl::CertificatesBuilder, + register_pool: RegisterPool, + ) { + let mut relays = csl::Relays::new(); + for relay in register_pool.pool_params.relays { + match relay { + Relay::SingleHostAddr(single_host_address_relay) => { + let ipv4_bytes: Option = + single_host_address_relay.ipv4.map_or(None, |ipv4_str| { + let addr: Ipv4Addr = + ipv4_str.parse().expect("ipv4 address parse failed"); + + Some(csl::Ipv4::new(addr.octets().to_vec()).unwrap()) + }); + + let ipv6_bytes: Option = + single_host_address_relay.ipv6.map_or(None, |ipv6_str| { + let addr: Ipv6Addr = + ipv6_str.parse().expect("ipv6 address parse failed"); + + Some(csl::Ipv6::new(addr.octets().to_vec()).unwrap()) + }); + relays.add(&csl::Relay::new_single_host_addr( + &csl::SingleHostAddr::new( + single_host_address_relay.port, + ipv4_bytes, + ipv6_bytes, + ), + )); + } + Relay::SingleHostName(single_host_name_relay) => relays.add( + &csl::Relay::new_single_host_name(&csl::SingleHostName::new( + single_host_name_relay.port, + &csl::DNSRecordAorAAAA::new(single_host_name_relay.domain_name).unwrap(), + )), + ), + Relay::MultiHostName(multi_host_name_relay) => { + relays.add(&csl::Relay::new_multi_host_name(&csl::MultiHostName::new( + &csl::DNSRecordSRV::new(multi_host_name_relay.domain_name).unwrap(), + ))) + } + } + } + + let mut pool_owners = csl::Ed25519KeyHashes::new(); + for owner in register_pool.pool_params.owners { + pool_owners.add(&csl::Ed25519KeyHash::from_hex(&owner).unwrap()); + } + + certificate_builder + .add(&csl::Certificate::new_pool_registration( + &csl::PoolRegistration::new(&csl::PoolParams::new( + &csl::Ed25519KeyHash::from_hex(®ister_pool.pool_params.operator).unwrap(), + &csl::VRFKeyHash::from_hex(®ister_pool.pool_params.vrf_key_hash).unwrap(), + &csl::BigNum::from_str(®ister_pool.pool_params.pledge).unwrap(), + &csl::BigNum::from_str(®ister_pool.pool_params.cost).unwrap(), + &csl::UnitInterval::new( + &csl::BigNum::from_str(®ister_pool.pool_params.margin.0.to_string()) + .unwrap(), + &csl::BigNum::from_str(®ister_pool.pool_params.margin.1.to_string()) + .unwrap(), + ), + &csl::RewardAddress::from_address( + &csl::Address::from_bech32(®ister_pool.pool_params.reward_address) + .unwrap(), + ) + .unwrap(), + &pool_owners, + &relays, + register_pool.pool_params.metadata.map_or(None, |data| { + Some(csl::PoolMetadata::new( + &csl::URL::new(data.url).unwrap(), + &csl::PoolMetadataHash::from_hex(&data.hash).unwrap(), + )) + }), + )), + )) + .unwrap(); + } + + fn add_register_stake_cert( + &mut self, + certificates_builder: &mut csl::CertificatesBuilder, + register_stake: RegisterStake, + ) { + certificates_builder + .add(&csl::Certificate::new_stake_registration( + &csl::StakeRegistration::new(&csl::Credential::from_keyhash( + &csl::Ed25519KeyHash::from_hex(®ister_stake.stake_key_hash).unwrap(), + )), + )) + .unwrap(); + } + + fn add_delegate_stake_cert( + &mut self, + certificates_builder: &mut csl::CertificatesBuilder, + delegate_stake: DelegateStake, + ) { + certificates_builder + .add(&csl::Certificate::new_stake_delegation( + &csl::StakeDelegation::new( + &csl::Credential::from_keyhash( + &csl::Ed25519KeyHash::from_hex(&delegate_stake.stake_key_hash).unwrap(), + ), + &csl::Ed25519KeyHash::from_hex(&delegate_stake.pool_id).unwrap(), + ), + )) + .unwrap(); + } + + fn add_deregister_stake_cert( + &mut self, + certificates_builder: &mut csl::CertificatesBuilder, + deregister_stake: DeregisterStake, + ) { + certificates_builder + .add(&csl::Certificate::new_stake_deregistration( + &csl::StakeDeregistration::new(&csl::Credential::from_keyhash( + &csl::Ed25519KeyHash::from_hex(&deregister_stake.stake_key_hash).unwrap(), + )), + )) + .unwrap(); + } + + fn add_retire_pool_cert( + &mut self, + certificates_builder: &mut csl::CertificatesBuilder, + retire_pool: RetirePool, + ) { + certificates_builder + .add(&csl::Certificate::new_pool_retirement( + &csl::PoolRetirement::new( + &csl::Ed25519KeyHash::from_hex(&retire_pool.pool_id).unwrap(), + retire_pool.epoch, + ), + )) + .unwrap(); + } + fn add_invalid_before(&mut self, invalid_before: u64) { self.tx_builder .set_validity_start_interval_bignum(to_bignum(invalid_before)); diff --git a/packages/sidan-csl-rs/src/core/tx_parser/mod.rs b/packages/sidan-csl-rs/src/core/tx_parser/mod.rs index 89dbfcb..5808c93 100644 --- a/packages/sidan-csl-rs/src/core/tx_parser/mod.rs +++ b/packages/sidan-csl-rs/src/core/tx_parser/mod.rs @@ -41,6 +41,7 @@ impl IMeshTxParser for MeshTxParser { mints: vec![], change_address: "".to_string(), change_datum: None, + certificates: vec![], metadata: vec![], validity_range: ValidityRange { invalid_before: None, diff --git a/packages/sidan-csl-rs/src/model/mod.rs b/packages/sidan-csl-rs/src/model/mod.rs index 252c542..6341e2a 100644 --- a/packages/sidan-csl-rs/src/model/mod.rs +++ b/packages/sidan-csl-rs/src/model/mod.rs @@ -28,6 +28,7 @@ pub struct MeshTxBuilderBody { pub change_datum: Option, pub metadata: Vec, pub validity_range: ValidityRange, + pub certificates: Vec, pub signing_key: JsVecString, } @@ -207,6 +208,98 @@ pub struct Metadata { pub metadata: String, } +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum Certificate { + RegisterPool(RegisterPool), + RegisterStake(RegisterStake), + DelegateStake(DelegateStake), + DeregisterStake(DeregisterStake), + RetirePool(RetirePool), +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RegisterPool { + pub pool_params: PoolParams, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PoolParams { + pub vrf_key_hash: String, + pub operator: String, + pub pledge: String, + pub cost: String, + pub margin: (u64, u64), + pub relays: Vec, + pub owners: Vec, + pub reward_address: String, + pub metadata: Option, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub enum Relay { + SingleHostAddr(SingleHostAddr), + SingleHostName(SingleHostName), + MultiHostName(MultiHostName), +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SingleHostAddr { + pub ipv4: Option, + pub ipv6: Option, + pub port: Option, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SingleHostName { + pub domain_name: String, + pub port: Option, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct MultiHostName { + pub domain_name: String, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct PoolMetadata { + pub url: String, + pub hash: String, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RegisterStake { + pub stake_key_hash: String, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct DelegateStake { + pub stake_key_hash: String, + pub pool_id: String, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct DeregisterStake { + pub stake_key_hash: String, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RetirePool { + pub pool_id: String, + pub epoch: u32, +} + #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Datum { From 2ada9da63dc6ac68250c750e5eeaaf55b05fea5d Mon Sep 17 00:00:00 2001 From: twwu123 Date: Mon, 10 Jun 2024 15:39:22 +0800 Subject: [PATCH 6/8] add methods to build staking certificates to MeshTxBuilder --- packages/whisky/src/builder/core.rs | 61 +++++++++++++++++-- packages/whisky/src/builder/interface.rs | 74 ++++++++++++++++++++++-- 2 files changed, 125 insertions(+), 10 deletions(-) diff --git a/packages/whisky/src/builder/core.rs b/packages/whisky/src/builder/core.rs index 550a83f..905f8af 100644 --- a/packages/whisky/src/builder/core.rs +++ b/packages/whisky/src/builder/core.rs @@ -4,9 +4,10 @@ use sidan_csl_rs::{ core::{algo::select_utxos, builder::IMeshCSL, utils::build_tx_builder}, csl, model::{ - Asset, Datum, DatumSource, InlineDatumSource, InlineScriptSource, LanguageVersion, - MeshTxBuilderBody, Metadata, MintItem, Output, PlutusScriptWithdrawal, ProvidedDatumSource, - ProvidedScriptSource, PubKeyTxIn, PubKeyWithdrawal, Redeemer, RefTxIn, ScriptSource, + Asset, Certificate, Datum, DatumSource, DelegateStake, DeregisterStake, InlineDatumSource, + InlineScriptSource, LanguageVersion, MeshTxBuilderBody, Metadata, MintItem, Output, + PlutusScriptWithdrawal, PoolParams, ProvidedDatumSource, ProvidedScriptSource, PubKeyTxIn, + PubKeyWithdrawal, Redeemer, RefTxIn, RegisterPool, RegisterStake, RetirePool, ScriptSource, ScriptTxIn, ScriptTxInParameter, TxIn, TxInParameter, UTxO, Value, Withdrawal, }, }; @@ -341,7 +342,9 @@ impl IMeshTxBuilder for MeshTxBuilder { } let withdrawal_item = withdrawal_item.unwrap(); match withdrawal_item { - Withdrawal::PubKeyWithdrawal(_) => panic!("Script reference cannot be defined for a pubkey withdrawal"), + Withdrawal::PubKeyWithdrawal(_) => { + panic!("Script reference cannot be defined for a pubkey withdrawal") + } Withdrawal::PlutusScriptWithdrawal(mut withdrawal) => { withdrawal.script_source = Some(ScriptSource::InlineScriptSource(InlineScriptSource { @@ -540,6 +543,56 @@ impl IMeshTxBuilder for MeshTxBuilder { self } + fn register_pool_certificate(&mut self, pool_params: PoolParams) -> &mut Self { + self.core + .mesh_tx_builder_body + .certificates + .push(Certificate::RegisterPool(RegisterPool { pool_params })); + self + } + + fn register_stake_certificate(&mut self, stake_key_hash: &str) -> &mut Self { + self.core + .mesh_tx_builder_body + .certificates + .push(Certificate::RegisterStake(RegisterStake { + stake_key_hash: stake_key_hash.to_string(), + })); + self + } + + fn delegate_stake_certificate(&mut self, stake_key_hash: &str, pool_id: &str) -> &mut Self { + self.core + .mesh_tx_builder_body + .certificates + .push(Certificate::DelegateStake(DelegateStake { + stake_key_hash: stake_key_hash.to_string(), + pool_id: pool_id.to_string(), + })); + self + } + + fn deregister_stake_certificate(&mut self, stake_key_hash: &str) -> &mut Self { + self.core + .mesh_tx_builder_body + .certificates + .push(Certificate::DeregisterStake(DeregisterStake { + stake_key_hash: stake_key_hash.to_string(), + })); + self + } + + fn retire_pool_certificate(&mut self, pool_id: &str, epoch: u32) -> &mut Self { + self.core + .mesh_tx_builder_body + .certificates + .push(Certificate::RetirePool(RetirePool { + pool_id: pool_id.to_string(), + epoch, + })); + self + } + fn change_address(&mut self, address: &str) -> &mut Self { self.core.mesh_tx_builder_body.change_address = address.to_string(); self diff --git a/packages/whisky/src/builder/interface.rs b/packages/whisky/src/builder/interface.rs index 8f0a13d..96d3ec9 100644 --- a/packages/whisky/src/builder/interface.rs +++ b/packages/whisky/src/builder/interface.rs @@ -2,8 +2,7 @@ use async_trait::async_trait; use sidan_csl_rs::{ builder::MeshTxBuilderCore, model::{ - Asset, LanguageVersion, MeshTxBuilderBody, MintItem, Output, Protocol, PubKeyTxIn, - Redeemer, TxIn, UTxO, Withdrawal, + Asset, LanguageVersion, MeshTxBuilderBody, MintItem, Output, PoolParams, Protocol, PubKeyTxIn, Redeemer, TxIn, UTxO, Withdrawal }, }; @@ -286,7 +285,6 @@ pub trait IMeshTxBuilder { /// * `Self` - The MeshTxBuilder instance fn read_only_tx_in_reference(&mut self, tx_hash: &str, tx_index: u32) -> &mut Self; - /// ## Transaction building method /// /// Indicate that the transaction is withdrawing using a plutus staking script in the MeshTxBuilder instance @@ -320,7 +318,6 @@ pub trait IMeshTxBuilder { script_size: usize, ) -> &mut Self; - /// ## Transaction building method /// /// Withdraw stake rewards in the MeshTxBuilder instance @@ -349,7 +346,6 @@ pub trait IMeshTxBuilder { /// * `Self` - The MeshTxBuilder instance fn withdrawal_script(&mut self, script_cbor: &str, version: LanguageVersion) -> &mut Self; - /// ## Transaction building method /// /// Set the transaction withdrawal redeemer value in the MeshTxBuilder instance @@ -363,7 +359,6 @@ pub trait IMeshTxBuilder { /// * `Self` - The MeshTxBuilder instance fn withdrawal_redeemer_value(&mut self, redeemer: Redeemer) -> &mut Self; - /// ## Transaction building method /// /// Set the withdrawal reference redeemer value in the MeshTxBuilder instance @@ -500,6 +495,73 @@ pub trait IMeshTxBuilder { address: &str, ) -> &mut Self; + /// ## Transaction building method + /// + /// Add a pool registration certificate to the MeshTxBuilder instance + /// + /// ### Arguments + /// + /// * `pool_params` - Parameters of pool to be registered + /// + /// ### Returns + /// + /// * `Self` - The MeshTxBuilder instance + fn register_pool_certificate(&mut self, pool_params: PoolParams) -> &mut Self; + + /// ## Transaction building method + /// + /// Add a stake registration certificate to the MeshTxBuilder instance + /// + /// ### Arguments + /// + /// * `stake_key_hash` - Hash of the stake key + /// + /// ### Returns + /// + /// * `Self` - The MeshTxBuilder instance + fn register_stake_certificate(&mut self, stake_key_hash: &str) -> &mut Self; + + /// ## Transaction building method + /// + /// Add a stake delegation certificate to the MeshTxBuilder instance + /// + /// ### Arguments + /// + /// * `stake_key_hash` - Hash of the stake key + /// * `pool_id` - id of the pool that will be delegated to + /// + /// ### Returns + /// + /// * `Self` - The MeshTxBuilder instance + fn delegate_stake_certificate(&mut self, stake_key_hash: &str, pool_id: &str) -> &mut Self; + + /// ## Transaction building method + /// + /// Add a stake deregistration certificate to the MeshTxBuilder instance + /// + /// ### Arguments + /// + /// * `stake_key_hash` - Hash of the stake key + /// + /// ### Returns + /// + /// * `Self` - The MeshTxBuilder instance + fn deregister_stake_certificate(&mut self, stake_key_hash: &str) -> &mut Self; + + /// ## Transaction building method + /// + /// Add a pool retire certificate to the MeshTxBuilder instance + /// + /// ### Arguments + /// + /// * `pool_id` - id of the pool that will be retired + /// * `epoch` - The epoch that the pool will be retired from + /// + /// ### Returns + /// + /// * `Self` - The MeshTxBuilder instance + fn retire_pool_certificate(&mut self, pool_id: &str, epoch: u32) -> &mut Self; + /// ## Transaction building method /// /// Change the address in the MeshTxBuilder instance From a5c75b48ce8f62efa99e2ed36736fdb9a5f712d1 Mon Sep 17 00:00:00 2001 From: twwu123 Date: Wed, 12 Jun 2024 15:25:09 +0800 Subject: [PATCH 7/8] bug fixes for to value util --- packages/sidan-csl-rs/src/core/builder.rs | 78 ++++++++++--------- .../sidan-csl-rs/src/core/utils/ungroup.rs | 10 +-- 2 files changed, 45 insertions(+), 43 deletions(-) diff --git a/packages/sidan-csl-rs/src/core/builder.rs b/packages/sidan-csl-rs/src/core/builder.rs index d5bff4e..c12dc3b 100644 --- a/packages/sidan-csl-rs/src/core/builder.rs +++ b/packages/sidan-csl-rs/src/core/builder.rs @@ -74,14 +74,16 @@ impl IMeshCSL for MeshCSL { } fn add_tx_in(&mut self, input: PubKeyTxIn) { - let _ = self.tx_inputs_builder.add_regular_input( - &csl::Address::from_bech32(&input.tx_in.address.unwrap()).unwrap(), - &csl::TransactionInput::new( - &csl::TransactionHash::from_hex(&input.tx_in.tx_hash).unwrap(), - input.tx_in.tx_index, - ), - &to_value(&input.tx_in.amount.unwrap()), - ); + self.tx_inputs_builder + .add_regular_input( + &csl::Address::from_bech32(&input.tx_in.address.unwrap()).unwrap(), + &csl::TransactionInput::new( + &csl::TransactionHash::from_hex(&input.tx_in.tx_hash).unwrap(), + input.tx_in.tx_index, + ), + &to_value(&input.tx_in.amount.unwrap()), + ) + .unwrap(); } fn add_script_tx_in(&mut self, input: ScriptTxIn) { @@ -229,14 +231,16 @@ impl IMeshCSL for MeshCSL { collateral_builder: &mut csl::TxInputsBuilder, collateral: PubKeyTxIn, ) { - let _ = collateral_builder.add_regular_input( - &csl::Address::from_bech32(&collateral.tx_in.address.unwrap()).unwrap(), - &csl::TransactionInput::new( - &csl::TransactionHash::from_hex(&collateral.tx_in.tx_hash).unwrap(), - collateral.tx_in.tx_index, - ), - &to_value(&collateral.tx_in.amount.unwrap()), - ); + collateral_builder + .add_regular_input( + &csl::Address::from_bech32(&collateral.tx_in.address.unwrap()).unwrap(), + &csl::TransactionInput::new( + &csl::TransactionHash::from_hex(&collateral.tx_in.tx_hash).unwrap(), + collateral.tx_in.tx_index, + ), + &to_value(&collateral.tx_in.amount.unwrap()), + ) + .unwrap(); } fn add_reference_input(&mut self, ref_input: RefTxIn) { @@ -248,8 +252,7 @@ impl IMeshCSL for MeshCSL { } fn add_pub_key_withdrawal(&mut self, withdrawal: PubKeyWithdrawal) { - let _ = self - .tx_withdrawals_builder + self.tx_withdrawals_builder .add( &csl::RewardAddress::from_address( &csl::Address::from_bech32(&withdrawal.address).unwrap(), @@ -308,8 +311,7 @@ impl IMeshCSL for MeshCSL { ), ); - let _ = self - .tx_withdrawals_builder + self.tx_withdrawals_builder .add_with_plutus_witness( &csl::RewardAddress::from_address( &csl::Address::from_bech32(&withdrawal.address).unwrap(), @@ -370,26 +372,30 @@ impl IMeshCSL for MeshCSL { } }; - let _ = mint_builder.add_asset( - &csl::MintWitness::new_plutus_script(&mint_script, &mint_redeemer), - &csl::AssetName::new(hex::decode(mint.asset_name).unwrap()).unwrap(), - &csl::Int::new_i32(mint.amount.try_into().unwrap()), - ); + mint_builder + .add_asset( + &csl::MintWitness::new_plutus_script(&mint_script, &mint_redeemer), + &csl::AssetName::new(hex::decode(mint.asset_name).unwrap()).unwrap(), + &csl::Int::new_i32(mint.amount.try_into().unwrap()), + ) + .unwrap(); } fn add_native_mint(&mut self, mint_builder: &mut csl::MintBuilder, mint: MintItem) { let script_info = mint.script_source.unwrap(); - let _ = match script_info { - ScriptSource::ProvidedScriptSource(script) => mint_builder.add_asset( - &csl::MintWitness::new_native_script(&csl::NativeScriptSource::new( - &csl::NativeScript::from_hex(&script.script_cbor).unwrap(), - )), - &csl::AssetName::new(hex::decode(mint.asset_name).unwrap()).unwrap(), - &csl::Int::new_i32(mint.amount.try_into().unwrap()), - ), - ScriptSource::InlineScriptSource(_) => Err(csl::JsError::from_str( - "Native scripts cannot be referenced", - )), + match script_info { + ScriptSource::ProvidedScriptSource(script) => mint_builder + .add_asset( + &csl::MintWitness::new_native_script(&csl::NativeScriptSource::new( + &csl::NativeScript::from_hex(&script.script_cbor).unwrap(), + )), + &csl::AssetName::new(hex::decode(mint.asset_name).unwrap()).unwrap(), + &csl::Int::new_i32(mint.amount.try_into().unwrap()), + ) + .unwrap(), + ScriptSource::InlineScriptSource(_) => {} // Err(csl::JsError::from_str( + // "Native scripts cannot be referenced", + // )), }; } diff --git a/packages/sidan-csl-rs/src/core/utils/ungroup.rs b/packages/sidan-csl-rs/src/core/utils/ungroup.rs index 5a9d733..bd75dd6 100644 --- a/packages/sidan-csl-rs/src/core/utils/ungroup.rs +++ b/packages/sidan-csl-rs/src/core/utils/ungroup.rs @@ -47,17 +47,13 @@ pub fn to_value(assets: &Vec) -> csl::Value { if asset.unit() == "lovelace" { continue; } - let mut policy_assets = csl::Assets::new(); let name_bytes = Vec::::from_hex(&asset.unit()[56..]).expect("Failed to parse hex asset name"); - policy_assets.insert( - &csl::AssetName::new(name_bytes).unwrap(), - &csl::BigNum::from_str(&asset.quantity().to_string()).unwrap(), - ); - multi_asset.insert( + multi_asset.set_asset( &csl::ScriptHash::from_hex(&asset.unit()[0..56]).unwrap(), - &policy_assets, + &csl::AssetName::new(name_bytes).unwrap(), + csl::BigNum::from_str(&asset.quantity().to_string()).unwrap(), ); } From 6b8ed002b7239c9859b4291c541c835aee4ca2f2 Mon Sep 17 00:00:00 2001 From: SIDANWhatever Date: Wed, 12 Jun 2024 16:04:11 +0800 Subject: [PATCH 8/8] feat: bumping version --- packages/Cargo.lock | 4 ++-- packages/sidan-csl-rs/Cargo.toml | 2 +- packages/sidan-csl-rs/src/builder/core.rs | 2 -- packages/sidan-csl-rs/src/core/builder.rs | 18 +++++++----------- packages/whisky/Cargo.toml | 4 ++-- 5 files changed, 12 insertions(+), 18 deletions(-) diff --git a/packages/Cargo.lock b/packages/Cargo.lock index 274ae40..95646bc 100644 --- a/packages/Cargo.lock +++ b/packages/Cargo.lock @@ -1833,7 +1833,7 @@ dependencies = [ [[package]] name = "sidan-csl-rs" -version = "0.5.1" +version = "0.5.2" dependencies = [ "cardano-serialization-lib", "cryptoxide", @@ -2387,7 +2387,7 @@ dependencies = [ [[package]] name = "whisky" -version = "0.5.1" +version = "0.5.2" dependencies = [ "async-trait", "cardano-serialization-lib", diff --git a/packages/sidan-csl-rs/Cargo.toml b/packages/sidan-csl-rs/Cargo.toml index 9293d91..b4dc1d4 100644 --- a/packages/sidan-csl-rs/Cargo.toml +++ b/packages/sidan-csl-rs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sidan-csl-rs" -version = "0.5.1" +version = "0.5.2" edition = "2021" license = "Apache-2.0" description = "Wrapper around the cardano-serialization-lib for easier transaction building, heavily inspired by cardano-cli APIs" diff --git a/packages/sidan-csl-rs/src/builder/core.rs b/packages/sidan-csl-rs/src/builder/core.rs index dd05116..132662d 100644 --- a/packages/sidan-csl-rs/src/builder/core.rs +++ b/packages/sidan-csl-rs/src/builder/core.rs @@ -1,5 +1,3 @@ -use pallas_traverse::cert; - use crate::{ core::builder::{IMeshCSL, MeshCSL}, csl, diff --git a/packages/sidan-csl-rs/src/core/builder.rs b/packages/sidan-csl-rs/src/core/builder.rs index c12dc3b..0ac0efc 100644 --- a/packages/sidan-csl-rs/src/core/builder.rs +++ b/packages/sidan-csl-rs/src/core/builder.rs @@ -1,7 +1,5 @@ use std::net::{Ipv4Addr, Ipv6Addr}; -use pallas_traverse::cert; - use crate::{csl, model::*}; use super::{utils::build_tx_builder, utils::sign_transaction, utils::to_bignum, utils::to_value}; @@ -409,19 +407,17 @@ impl IMeshCSL for MeshCSL { match relay { Relay::SingleHostAddr(single_host_address_relay) => { let ipv4_bytes: Option = - single_host_address_relay.ipv4.map_or(None, |ipv4_str| { + single_host_address_relay.ipv4.map(|ipv4_str| { let addr: Ipv4Addr = ipv4_str.parse().expect("ipv4 address parse failed"); - - Some(csl::Ipv4::new(addr.octets().to_vec()).unwrap()) + csl::Ipv4::new(addr.octets().to_vec()).unwrap() }); let ipv6_bytes: Option = - single_host_address_relay.ipv6.map_or(None, |ipv6_str| { + single_host_address_relay.ipv6.map(|ipv6_str| { let addr: Ipv6Addr = ipv6_str.parse().expect("ipv6 address parse failed"); - - Some(csl::Ipv6::new(addr.octets().to_vec()).unwrap()) + csl::Ipv6::new(addr.octets().to_vec()).unwrap() }); relays.add(&csl::Relay::new_single_host_addr( &csl::SingleHostAddr::new( @@ -470,11 +466,11 @@ impl IMeshCSL for MeshCSL { .unwrap(), &pool_owners, &relays, - register_pool.pool_params.metadata.map_or(None, |data| { - Some(csl::PoolMetadata::new( + register_pool.pool_params.metadata.map(|data| { + csl::PoolMetadata::new( &csl::URL::new(data.url).unwrap(), &csl::PoolMetadataHash::from_hex(&data.hash).unwrap(), - )) + ) }), )), )) diff --git a/packages/whisky/Cargo.toml b/packages/whisky/Cargo.toml index 7fc4a15..2c6fa81 100644 --- a/packages/whisky/Cargo.toml +++ b/packages/whisky/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "whisky" -version = "0.5.1" +version = "0.5.2" edition = "2021" license = "Apache-2.0" description = "The Cardano Rust SDK, inspired by MeshJS" @@ -24,7 +24,7 @@ noop_proc_macro = "0.3.0" pallas-codec = "0.22.0" pallas-primitives = "0.22.0" pallas-traverse = "0.22.0" -sidan-csl-rs = { version = "=0.5.1", path = "../sidan-csl-rs" } +sidan-csl-rs = { version = "=0.5.2", path = "../sidan-csl-rs" } [profile.release] # Tell `rustc` to optimize for small code size.