Skip to content

Commit

Permalink
Governance Builders
Browse files Browse the repository at this point in the history
Adds `VoteBuilder` and `ProposalBuilder` to facilitate creation of votes
and voting proposals to transactions.
  • Loading branch information
rooooooooob committed Jun 11, 2024
1 parent 255500b commit 8475c99
Show file tree
Hide file tree
Showing 16 changed files with 711 additions and 18 deletions.
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;
164 changes: 164 additions & 0 deletions chain/rust/src/builders/proposal_builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
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("Voter is script. Call with_plutus_vote() instead.")]
ProposalIsScript,
#[error("Voter is key hash. Call with_vote() instead.")]
ProposalIsKeyHash,
#[error("Vote already exists")]
VoteAlreayExists,
#[error("Missing the following witnesses for the input: {0:?}")]
MissingWitnesses(Box<RequiredWitnessSet>),
}

#[derive(Clone, Debug, Default)]
pub struct ProposalBuilderResult {
proposals: Vec<ProposalProcedure>,
required_wits: RequiredWitnessSet,
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_vote(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_vote(
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_vote(
self,
proposal: ProposalProcedure,
partial_witness: PartialPlutusWitness,
required_signers: RequiredSigners,
datum: PlutusData,
) -> Result<Self, ProposalBuilderError> {
self.with_plutus_vote_impl(proposal, partial_witness, required_signers, Some(datum))
}

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

fn with_plutus_vote_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
}
}
5 changes: 2 additions & 3 deletions chain/rust/src/builders/tx_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -720,13 +720,12 @@ impl TransactionBuilder {

match &script_witness.script {
PlutusScriptWitness::Ref(ref_script) => {
if self
if !self
.witness_builders
.witness_set_builder
.required_wits
.script_refs
.get(ref_script)
.is_none()
.contains(ref_script)
{
Err(TxBuilderError::RefScriptNotFound(
*ref_script,
Expand Down
14 changes: 14 additions & 0 deletions chain/rust/src/builders/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use crate::RequiredSigners;

use super::witness_builder::RequiredWitnessSet;

pub(crate) fn required_wits_from_required_signers(
required_signers: &RequiredSigners,
) -> RequiredWitnessSet {
let mut required_wits = RequiredWitnessSet::default();
required_signers
.as_ref()
.iter()
.for_each(|required_signer| required_wits.add_vkey_key_hash(*required_signer));
required_wits
}
Loading

0 comments on commit 8475c99

Please sign in to comment.