Skip to content

Commit

Permalink
fix MintBuilder for multiple different redeemers, add ref input suppo…
Browse files Browse the repository at this point in the history
…rt for native scripts to MintBuilder
  • Loading branch information
lisicky committed May 7, 2024
1 parent 8aa9d9f commit d9aad56
Show file tree
Hide file tree
Showing 10 changed files with 737 additions and 466 deletions.
195 changes: 160 additions & 35 deletions rust/src/builders/mint_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::collections::BTreeMap;
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
enum MintWitnessEnum {
Plutus(PlutusScriptSourceEnum, Redeemer),
NativeScript(NativeScript),
NativeScript(NativeScriptSourceEnum),
}

#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
Expand All @@ -13,8 +13,8 @@ pub struct MintWitness(MintWitnessEnum);

#[wasm_bindgen]
impl MintWitness {
pub fn new_native_script(native_script: &NativeScript) -> MintWitness {
MintWitness(MintWitnessEnum::NativeScript(native_script.clone()))
pub fn new_native_script(native_script: &NativeScriptSource) -> MintWitness {
MintWitness(MintWitnessEnum::NativeScript(native_script.0.clone()))
}

pub fn new_plutus_script(
Expand All @@ -26,18 +26,49 @@ impl MintWitness {
redeemer.clone(),
))
}

pub(crate) fn script_hash(&self) -> ScriptHash {
match &self.0 {
MintWitnessEnum::NativeScript(native_script) => native_script.script_hash(),
MintWitnessEnum::Plutus(plutus_script, _) => plutus_script.script_hash(),
}
}
}

#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
struct NativeMints {
script: NativeScript,
script: NativeScriptSourceEnum,
mints: BTreeMap<AssetName, Int>,
}

impl NativeMints {
fn script_hash(&self) -> PolicyID {
match &self.script {
NativeScriptSourceEnum::NativeScript(script, _) => script.hash(),
NativeScriptSourceEnum::RefInput(_, script_hash, _) => script_hash.clone(),
}
}

fn ref_input(&self) -> Option<&TransactionInput> {
match &self.script {
NativeScriptSourceEnum::RefInput(input, _, _) => Some(input),
_ => None,
}
}

fn native_script(&self) -> Option<&NativeScript> {
match &self.script {
NativeScriptSourceEnum::NativeScript(script, _) => Some(script),
_ => None,
}
}
}

#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
struct PlutusMints {
script: PlutusScriptSourceEnum,
redeemer_mints: BTreeMap<Redeemer, BTreeMap<AssetName, Int>>,
redeemer: Redeemer,
mints: BTreeMap<AssetName, Int>,
}

#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
Expand Down Expand Up @@ -88,19 +119,22 @@ impl MintBuilder {

fn update_mint_value(
&mut self,
mint: &MintWitness,
mint_witness: &MintWitness,
asset_name: &AssetName,
amount: &Int,
overwrite: bool,
) -> Result<(), JsError> {
if amount.0 == 0 {
return Err(JsError::from_str("Mint cannot be zero."));
}
match &mint.0 {
let script_mint = self.mints.get(&mint_witness.script_hash());
Self::validate_mint_witness(mint_witness, script_mint)?;

match &mint_witness.0 {
MintWitnessEnum::NativeScript(native_script) => {
let script_mint =
self.mints
.entry(native_script.hash())
.entry(native_script.script_hash())
.or_insert(ScriptMint::Native(NativeMints {
script: native_script.clone(),
mints: BTreeMap::new(),
Expand All @@ -126,15 +160,13 @@ impl MintBuilder {
.entry(plutus_script.script_hash())
.or_insert(ScriptMint::Plutus(PlutusMints {
script: plutus_script.clone(),
redeemer_mints: BTreeMap::new(),
redeemer: redeemer.clone(),
mints: BTreeMap::new(),
}));
match script_mint {
ScriptMint::Plutus(plutus_mints) => {
let redeemer_mints = plutus_mints
.redeemer_mints
.entry(redeemer.clone())
.or_insert(BTreeMap::new());
let mint = redeemer_mints
let mint = plutus_mints
.mints
.entry(asset_name.clone())
.or_insert(Int::new(&BigNum::zero()));
if overwrite {
Expand All @@ -150,27 +182,118 @@ impl MintBuilder {
Ok(())
}

fn validate_mint_witness(
mint_witness: &MintWitness,
current_script_mint: Option<&ScriptMint>,
) -> Result<(), JsError> {
if let Some(current_script_mint) = current_script_mint {
match &mint_witness.0 {
MintWitnessEnum::NativeScript(native_script) => {
if let ScriptMint::Native(native_mints) = current_script_mint {
Self::validate_native_source_type(&native_mints.script, &native_script)?;
} else {
return Err(JsError::from_str(
&BuilderError::MintBuilderDifferentScriptType.as_str(),
));
}
}
MintWitnessEnum::Plutus(plutus_script, redeemer) => {
if let ScriptMint::Plutus(plutus_mints) = current_script_mint {
Self::validate_plutus_script_source_type(
&plutus_mints.script,
&plutus_script,
)?;
if !plutus_mints.redeemer.partially_eq(redeemer) {
return Err(JsError::from_str(
&BuilderError::MintBuilderDifferentRedeemerDataAndExUnits(
plutus_mints.redeemer.to_json()?,
redeemer.to_json()?,
)
.as_str(),
));
}
} else {
return Err(JsError::from_str(
&BuilderError::MintBuilderDifferentScriptType.as_str(),
));
}
}
}
Ok(())
} else {
Ok(())
}
}

fn validate_native_source_type(
current_script_source: &NativeScriptSourceEnum,
input_script_source: &NativeScriptSourceEnum,
) -> Result<(), JsError> {
match current_script_source {
NativeScriptSourceEnum::NativeScript(_, _) => {
if let NativeScriptSourceEnum::NativeScript(_, _) = input_script_source {
Ok(())
} else {
Err(JsError::from_str(
&BuilderError::MintBuilderDifferentWitnessTypeNonRef.as_str(),
))
}
}
NativeScriptSourceEnum::RefInput(_, _, _) => {
if let NativeScriptSourceEnum::RefInput(_, _, _) = input_script_source {
Ok(())
} else {
Err(JsError::from_str(
&BuilderError::MintBuilderDifferentWitnessTypeRef.as_str(),
))
}
}
}
}

fn validate_plutus_script_source_type(
current_script_source: &PlutusScriptSourceEnum,
input_script_source: &PlutusScriptSourceEnum,
) -> Result<(), JsError> {
match current_script_source {
PlutusScriptSourceEnum::RefInput(_, _) => {
if let PlutusScriptSourceEnum::RefInput(_, _) = input_script_source {
Ok(())
} else {
Err(JsError::from_str(
&BuilderError::MintBuilderDifferentWitnessTypeRef.as_str(),
))
}
}
PlutusScriptSourceEnum::Script(_, _) => {
if let PlutusScriptSourceEnum::Script(_, _) = input_script_source {
Ok(())
} else {
Err(JsError::from_str(
&BuilderError::MintBuilderDifferentWitnessTypeNonRef.as_str(),
))
}
}
}
}

pub fn build(&self) -> Mint {
let mut mint = Mint::new();
for (_, script_mint) in self.mints.iter() {
for (policy, script_mint) in self.mints.iter() {
let mut mint_asset = MintAssets::new();
match script_mint {
ScriptMint::Native(native_mints) => {
let mut mint_asset = MintAssets::new();
for (asset_name, amount) in &native_mints.mints {
mint_asset.insert(asset_name, amount.clone());
}
mint.insert(&native_mints.script.hash(), &mint_asset);
}
ScriptMint::Plutus(plutus_mints) => {
for (_, redeemer_mints) in &plutus_mints.redeemer_mints {
let mut mint_asset = MintAssets::new();
for (asset_name, amount) in redeemer_mints {
mint_asset.insert(asset_name, amount.clone());
}
mint.insert(&plutus_mints.script.script_hash(), &mint_asset);
for (asset_name, amount) in &plutus_mints.mints {
mint_asset.insert(asset_name, amount.clone());
}
}
}
mint.insert(&policy, &mint_asset);
}
mint
}
Expand All @@ -180,7 +303,9 @@ impl MintBuilder {
for script_mint in self.mints.values() {
match script_mint {
ScriptMint::Native(native_mints) => {
native_scripts.push(native_mints.script.clone());
if let Some(script) = native_mints.native_script() {
native_scripts.push(script.clone());
}
}
_ => {}
}
Expand All @@ -193,12 +318,10 @@ impl MintBuilder {
for script_mint in self.mints.values() {
match script_mint {
ScriptMint::Plutus(plutus_mints) => {
for (redeemer, _) in &plutus_mints.redeemer_mints {
plutus_witnesses.push(PlutusWitness::new_with_ref_without_datum(
&PlutusScriptSource(plutus_mints.script.clone()),
redeemer,
));
}
plutus_witnesses.push(PlutusWitness::new_with_ref_without_datum(
&PlutusScriptSource(plutus_mints.script.clone()),
&plutus_mints.redeemer,
));
}
_ => {}
}
Expand All @@ -215,7 +338,11 @@ impl MintBuilder {
reference_inputs.push(ref_script.input_ref.clone());
}
}
_ => {}
ScriptMint::Native(native_mints) => {
if let Some(input) = native_mints.ref_input() {
reference_inputs.push(input.clone());
}
}
}
}
TransactionInputs(reference_inputs)
Expand All @@ -228,10 +355,8 @@ impl MintBuilder {
for (_, script_mint) in &self.mints {
match script_mint {
ScriptMint::Plutus(plutus_mints) => {
for (redeemer, _) in &plutus_mints.redeemer_mints {
redeeemers.push(redeemer.clone_with_index_and_tag(&index, &tag));
index = index.checked_add(&BigNum::one())?;
}
redeeemers.push(plutus_mints.redeemer.clone_with_index_and_tag(&index, &tag));
index = index.checked_add(&BigNum::one())?;
}
_ => {
index = index.checked_add(&BigNum::one())?;
Expand Down
4 changes: 4 additions & 0 deletions rust/src/builders/script_structs/native_script_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,8 @@ impl NativeScriptSource {
pub fn set_required_signers(&mut self, key_hashes: &Ed25519KeyHashes) {
self.0.set_required_signers(key_hashes)
}

pub(crate) fn script_hash(&self) -> ScriptHash {
self.0.script_hash()
}
}
14 changes: 9 additions & 5 deletions rust/src/builders/tx_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1207,7 +1207,8 @@ impl TransactionBuilder {
for (policy_id, asset_map) in &mint.0 {
for (asset_name, amount) in &asset_map.0 {
if let Some(script) = scripts_policies.get(policy_id) {
let mint_witness = MintWitness::new_native_script(script);
let native_script_source = NativeScriptSource::new(script);
let mint_witness = MintWitness::new_native_script(&native_script_source);
mint_builder.set_asset(&mint_witness, asset_name, amount);
} else {
return Err(JsError::from_str(
Expand Down Expand Up @@ -1254,7 +1255,8 @@ impl TransactionBuilder {
/// It will be securely added to existing or new Mint in this builder
/// It will replace any existing mint assets with the same PolicyID
pub fn set_mint_asset(&mut self, policy_script: &NativeScript, mint_assets: &MintAssets) {
let mint_witness = MintWitness::new_native_script(policy_script);
let native_script_source = NativeScriptSource::new(policy_script);
let mint_witness = MintWitness::new_native_script(&native_script_source);
if let Some(mint) = &mut self.mint {
for (asset, amount) in mint_assets.0.iter() {
mint.set_asset(&mint_witness, asset, amount);
Expand Down Expand Up @@ -1284,7 +1286,8 @@ impl TransactionBuilder {
asset_name: &AssetName,
amount: &Int,
) {
let mint_witness = MintWitness::new_native_script(policy_script);
let native_script_source = NativeScriptSource::new(policy_script);
let mint_witness = MintWitness::new_native_script(&native_script_source);
if let Some(mint) = &mut self.mint {
mint.add_asset(&mint_witness, asset_name, &amount);
} else {
Expand Down Expand Up @@ -1538,9 +1541,10 @@ impl TransactionBuilder {
self.mint
.as_ref()
.map(|m| {
let mint = m.build();
(
Value::new_from_assets(&m.build().as_positive_multiasset()),
Value::new_from_assets(&m.build().as_negative_multiasset()),
Value::new_from_assets(&mint.as_positive_multiasset()),
Value::new_from_assets(&mint.as_negative_multiasset()),
)
})
.unwrap_or((Value::zero(), Value::zero()))
Expand Down
10 changes: 5 additions & 5 deletions rust/src/builders/tx_inputs_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ impl TxInputsBuilder {
Ok(())
}
CredType::Script(_) => Err(JsError::from_str(
BuilderError::RegularInputIsScript.as_str(),
&BuilderError::RegularInputIsScript.as_str(),
)),
},
AddrType::Enterprise(ent_aaddr) => match &ent_aaddr.payment.0 {
Expand All @@ -137,7 +137,7 @@ impl TxInputsBuilder {
Ok(())
}
CredType::Script(_) => Err(JsError::from_str(
BuilderError::RegularInputIsScript.as_str(),
&BuilderError::RegularInputIsScript.as_str(),
)),
},
AddrType::Ptr(ptr_addr) => match &ptr_addr.payment.0 {
Expand All @@ -146,18 +146,18 @@ impl TxInputsBuilder {
Ok(())
}
CredType::Script(_) => Err(JsError::from_str(
BuilderError::RegularInputIsScript.as_str(),
&BuilderError::RegularInputIsScript.as_str(),
)),
},
AddrType::Byron(byron_addr) => {
self.add_bootstrap_input(byron_addr, input, amount);
Ok(())
}
AddrType::Reward(_) => Err(JsError::from_str(
BuilderError::RegularInputIsFromRewardAddress.as_str(),
&BuilderError::RegularInputIsFromRewardAddress.as_str(),
)),
AddrType::Malformed(_) => {
Err(JsError::from_str(BuilderError::MalformedAddress.as_str()))
Err(JsError::from_str(&BuilderError::MalformedAddress.as_str()))
}
}
}
Expand Down

0 comments on commit d9aad56

Please sign in to comment.