Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Governance Builders #333

Merged
merged 4 commits into from
Jul 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions chain/rust/src/builders/certificate_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,14 @@ pub fn add_cert_vkeys(
vkeys.insert(*hash);
}
},
Certificate::RegDrepCert(_cert) => {
// does not need a witness
}
Certificate::RegDrepCert(cert) => match &cert.drep_credential {
StakeCredential::Script { hash, .. } => {
return Err(CertBuilderError::ExpectedKeyHash(*hash))
}
StakeCredential::PubKey { hash, .. } => {
vkeys.insert(*hash);
}
},
Certificate::UnregDrepCert(cert) => match &cert.drep_credential {
StakeCredential::Script { hash, .. } => {
return Err(CertBuilderError::ExpectedKeyHash(*hash))
Expand Down
7 changes: 2 additions & 5 deletions chain/rust/src/builders/input_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::builders::witness_builder::{InputAggregateWitnessData, PartialPlutusW

use super::{
tx_builder::TransactionUnspentOutput,
utils::required_wits_from_required_signers,
witness_builder::{NativeScriptWitnessInfo, RequiredWitnessSet},
};

Expand Down Expand Up @@ -138,11 +139,7 @@ impl SingleInputBuilder {
required_signers: RequiredSigners,
datum: Option<PlutusData>,
) -> Result<InputBuilderResult, InputBuilderError> {
let mut required_wits = RequiredWitnessSet::default();
required_signers
.as_ref()
.iter()
.for_each(|required_signer| required_wits.add_vkey_key_hash(*required_signer));
let mut required_wits = required_wits_from_required_signers(&required_signers);
input_required_wits(&self.utxo_info, &mut required_wits);
let mut required_wits_left = required_wits.clone();

Expand Down
3 changes: 3 additions & 0 deletions chain/rust/src/builders/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ pub mod certificate_builder;
pub mod input_builder;
pub mod mint_builder;
pub mod output_builder;
pub mod proposal_builder;
pub mod redeemer_builder;
pub mod tx_builder;
mod utils;
pub mod vote_builder;
pub mod withdrawal_builder;
pub mod witness_builder;
165 changes: 165 additions & 0 deletions chain/rust/src/builders/proposal_builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
use crate::{
crypto::hash::hash_plutus_data, governance::ProposalProcedure, plutus::PlutusData,
transaction::NativeScript, RequiredSigners,
};

use super::{
utils::required_wits_from_required_signers,
witness_builder::{
InputAggregateWitnessData, NativeScriptWitnessInfo, PartialPlutusWitness,
RequiredWitnessSet,
},
};

#[derive(Debug, thiserror::Error)]
pub enum ProposalBuilderError {
#[error("Proposal uses script. Call with_plutus_proposal() instead.")]
ProposalIsScript,
#[error("Proposal uses key hash. Call with_proposal() instead.")]
ProposalIsKeyHash,
#[error("Missing the following witnesses for the input: {0:?}")]
MissingWitnesses(Box<RequiredWitnessSet>),
}

#[derive(Clone, Debug, Default)]
pub struct ProposalBuilderResult {
pub proposals: Vec<ProposalProcedure>,
pub required_wits: RequiredWitnessSet,
pub aggregate_witnesses: Vec<InputAggregateWitnessData>,
}

#[derive(Clone, Debug)]
pub struct ProposalBuilder {
result: ProposalBuilderResult,
}

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

impl ProposalBuilder {
pub fn new() -> Self {
Self {
result: ProposalBuilderResult::default(),
}
}

pub fn with_proposal(
mut self,
proposal: ProposalProcedure,
) -> Result<Self, ProposalBuilderError> {
if proposal.gov_action.script_hash().is_some() {
return Err(ProposalBuilderError::ProposalIsScript);
}

self.result.proposals.push(proposal.clone());

Ok(self)
}

pub fn with_native_script_proposal(
mut self,
proposal: ProposalProcedure,
native_script: NativeScript,
witness_info: NativeScriptWitnessInfo,
) -> Result<Self, ProposalBuilderError> {
if let Some(script_hash) = proposal.gov_action.script_hash() {
if *script_hash != native_script.hash() {
let mut err_req_wits = RequiredWitnessSet::new();
err_req_wits.add_script_hash(*script_hash);
return Err(ProposalBuilderError::MissingWitnesses(Box::new(
err_req_wits,
)));
}
self.result.required_wits.add_script_hash(*script_hash);
} else {
return Err(ProposalBuilderError::ProposalIsKeyHash);
}

self.result.proposals.push(proposal);

self.result
.aggregate_witnesses
.push(InputAggregateWitnessData::NativeScript(
native_script,
witness_info,
));

Ok(self)
}

pub fn with_plutus_proposal(
self,
proposal: ProposalProcedure,
partial_witness: PartialPlutusWitness,
required_signers: RequiredSigners,
datum: PlutusData,
) -> Result<Self, ProposalBuilderError> {
self.with_plutus_proposal_impl(proposal, partial_witness, required_signers, Some(datum))
}

pub fn with_plutus_proposal_inline_datum(
self,
proposal: ProposalProcedure,
partial_witness: PartialPlutusWitness,
required_signers: RequiredSigners,
) -> Result<Self, ProposalBuilderError> {
self.with_plutus_proposal_impl(proposal, partial_witness, required_signers, None)
}

fn with_plutus_proposal_impl(
mut self,
proposal: ProposalProcedure,
partial_witness: PartialPlutusWitness,
required_signers: RequiredSigners,
datum: Option<PlutusData>,
) -> Result<Self, ProposalBuilderError> {
let mut required_wits = required_wits_from_required_signers(&required_signers);
if let Some(script_hash) = proposal.gov_action.script_hash() {
required_wits.add_script_hash(*script_hash);
} else {
return Err(ProposalBuilderError::ProposalIsKeyHash);
}

let mut required_wits_left = required_wits.clone();

// no way to know these at this time
required_wits_left.vkeys.clear();

let script_hash = partial_witness.script.hash();

// check the user provided all the required witnesses
required_wits_left.scripts.remove(&script_hash);
if let Some(datum) = &datum {
required_wits_left
.plutus_data
.remove(&hash_plutus_data(datum));
}

if required_wits_left.len() > 0 {
return Err(ProposalBuilderError::MissingWitnesses(Box::new(
required_wits_left,
)));
}

self.result.proposals.push(proposal);

self.result.required_wits.add_all(required_wits);

self.result
.aggregate_witnesses
.push(InputAggregateWitnessData::PlutusScript(
partial_witness,
required_signers,
datum,
));

Ok(self)
}

pub fn build(self) -> ProposalBuilderResult {
self.result
}
}
53 changes: 50 additions & 3 deletions chain/rust/src/builders/redeemer_builder.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::{
certificate_builder::CertificateBuilderResult, input_builder::InputBuilderResult,
mint_builder::MintBuilderResult, withdrawal_builder::WithdrawalBuilderResult,
mint_builder::MintBuilderResult, proposal_builder::ProposalBuilderResult,
vote_builder::VoteBuilderResult, withdrawal_builder::WithdrawalBuilderResult,
};
use crate::{
address::RewardAddress,
Expand Down Expand Up @@ -97,6 +98,10 @@ pub struct RedeemerSetBuilder {
// certificates in the DCert list are indexed in the order in which they arranged in the (full, unfiltered)
// list of certificates inside the transaction
cert: Vec<Option<UntaggedRedeemerPlaceholder>>,

proposals: Vec<Option<UntaggedRedeemerPlaceholder>>,

votes: Vec<Option<UntaggedRedeemerPlaceholder>>,
}

impl RedeemerSetBuilder {
Expand Down Expand Up @@ -143,10 +148,18 @@ impl RedeemerSetBuilder {
)));
}
RedeemerTag::Proposing => {
todo!("https://github.com/dcSpark/cardano-multiplatform-lib/issues/323")
let entry = self.proposals.get_mut(key.index as usize).unwrap();
*entry = Some(UntaggedRedeemerPlaceholder::Full(UntaggedRedeemer::new(
entry.as_ref().unwrap().data().clone(),
ex_units,
)));
}
RedeemerTag::Voting => {
todo!("https://github.com/dcSpark/cardano-multiplatform-lib/issues/323")
let entry = self.votes.get_mut(key.index as usize).unwrap();
*entry = Some(UntaggedRedeemerPlaceholder::Full(UntaggedRedeemer::new(
entry.as_ref().unwrap().data().clone(),
ex_units,
)));
}
}
}
Expand Down Expand Up @@ -217,6 +230,28 @@ impl RedeemerSetBuilder {
}
}

pub fn add_proposal(&mut self, result: &ProposalBuilderResult) {
for aggregate_witness in &result.aggregate_witnesses {
if let Some(data) = aggregate_witness.redeemer_plutus_data() {
self.proposals
.push(Some(UntaggedRedeemerPlaceholder::JustData(data.clone())));
} else {
self.proposals.push(None);
}
}
}

pub fn add_vote(&mut self, result: &VoteBuilderResult) {
for aggregate_witness in &result.aggregate_witnesses {
if let Some(data) = aggregate_witness.redeemer_plutus_data() {
self.votes
.push(Some(UntaggedRedeemerPlaceholder::JustData(data.clone())));
} else {
self.votes.push(None);
}
}
}

pub fn build(&self, default_to_dummy_exunits: bool) -> Result<Redeemers, RedeemerBuilderError> {
let mut redeemers = Vec::new();
// Calling iter on a BTreeMap returns a list of sorted keys
Expand Down Expand Up @@ -244,6 +279,18 @@ impl RedeemerSetBuilder {
&mut self.cert.iter().map(|entry| (&(), entry)),
default_to_dummy_exunits,
)?;
self.remove_placeholders_and_tag(
&mut redeemers,
RedeemerTag::Proposing,
&mut self.proposals.iter().map(|entry| (&(), entry)),
default_to_dummy_exunits,
)?;
self.remove_placeholders_and_tag(
&mut redeemers,
RedeemerTag::Voting,
&mut self.votes.iter().map(|entry| (&(), entry)),
default_to_dummy_exunits,
)?;

Ok(Redeemers::new_arr_legacy_redeemer(redeemers))
}
Expand Down
Loading
Loading