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

Extra datum #588

Merged
merged 14 commits into from
Aug 23, 2023
10 changes: 10 additions & 0 deletions rust/pkg/cardano_serialization_lib.js.flow
Original file line number Diff line number Diff line change
Expand Up @@ -8561,6 +8561,16 @@ declare export class TransactionBuilder {
output_builder: TransactionOutputAmountBuilder
): void;

/**
* @param {PlutusData} datum
*/
add_extra_datum(datum: PlutusData): void;
lisicky marked this conversation as resolved.
Show resolved Hide resolved

/**
* @returns {PlutusList | void}
*/
get_extra_datums(): PlutusList | void;

/**
* @param {TransactionBuilderConfig} cfg
* @returns {TransactionBuilder}
Expand Down
9 changes: 9 additions & 0 deletions rust/src/plutus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,15 @@ pub struct PlutusList {
pub(crate) definite_encoding: Option<bool>,
}

impl<'a> IntoIterator for &'a PlutusList {
type Item = &'a PlutusData;
type IntoIter = std::slice::Iter<'a, PlutusData>;

fn into_iter(self) -> std::slice::Iter<'a, PlutusData> {
self.elems.iter()
}
}

impl std::cmp::PartialEq<Self> for PlutusList {
fn eq(&self, other: &Self) -> bool {
self.elems.eq(&other.elems)
Expand Down
121 changes: 105 additions & 16 deletions rust/src/tx_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ pub struct TransactionBuilder {
collateral_return: Option<TransactionOutput>,
total_collateral: Option<Coin>,
reference_inputs: HashSet<TransactionInput>,
extra_datums: Option<PlutusList>
}

#[wasm_bindgen]
Expand Down Expand Up @@ -1226,6 +1227,20 @@ impl TransactionBuilder {
)
}

pub fn add_extra_datum(&mut self, datum: &PlutusData) {
if let Some(extra_datums) = &mut self.extra_datums {
extra_datums.add(datum);
} else {
let mut extra_datums = PlutusList::new();
extra_datums.add(datum);
self.extra_datums = Some(extra_datums);
}
}

pub fn get_extra_datums(&self) -> Option<PlutusList> {
self.extra_datums.clone()
}

pub fn new(cfg: &TransactionBuilderConfig) -> Self {
Self {
config: cfg.clone(),
Expand All @@ -1244,6 +1259,7 @@ impl TransactionBuilder {
collateral_return: None,
total_collateral: None,
reference_inputs: HashSet::new(),
extra_datums: None,
}
}

Expand Down Expand Up @@ -1734,27 +1750,41 @@ impl TransactionBuilder {
plutus_witnesses.0.append(&mut mint_builder.get_plutus_witnesses().0)
}

if plutus_witnesses.len() > 0 {
let (_scripts, datums, redeemers) = plutus_witnesses.collect();
for lang in used_langs {
match cost_models.get(&lang) {
Some(cost) => {
retained_cost_models.insert(&lang, &cost);
}
_ => {
return Err(JsError::from_str(&format!(
"Missing cost model for language version: {:?}",
lang
)))
}
let (_scripts, mut datums, redeemers) = plutus_witnesses.collect();
for lang in used_langs {
match cost_models.get(&lang) {
Some(cost) => {
retained_cost_models.insert(&lang, &cost);
}
_ => {
return Err(JsError::from_str(&format!(
"Missing cost model for language version: {:?}",
lang
)))
}
}
}

if let Some(extra_datum) = &self.extra_datums {
if datums.is_none() {
datums = Some(PlutusList::new());
}

for datum in extra_datum {
if let Some(datums) = &mut datums {
datums.add(datum);
}
}
}

if datums.is_some() || redeemers.len() > 0 || retained_cost_models.len() > 0 {
self.script_data_hash = Some(hash_script_data(
&redeemers,
&retained_cost_models,
datums,
));
}

Ok(())
}

Expand Down Expand Up @@ -1890,14 +1920,30 @@ impl TransactionBuilder {
if let Some(scripts) = self.get_combined_native_scripts() {
wit.set_native_scripts(&scripts);
}
let mut all_datums = None;
if let Some(pw) = self.get_combined_plutus_scripts() {
let (scripts, datums, redeemers) = pw.collect();
wit.set_plutus_scripts(&scripts);
if let Some(datums) = &datums {
wit.set_plutus_data(datums);
}
all_datums = datums;
wit.set_redeemers(&redeemers);
}

if let Some(extra_datum) = &self.extra_datums {
if all_datums.is_none() {
all_datums = Some(PlutusList::new());
}

for datum in extra_datum {
if let Some(datums) = &mut all_datums {
datums.add(datum);
}
}
}

if let Some(datums) = &all_datums {
wit.set_plutus_data(datums);
}

wit
}

Expand Down Expand Up @@ -8125,4 +8171,47 @@ mod tests {
assert_eq!(tx.witness_set.plutus_scripts.unwrap().len(), 1usize);
assert_eq!(tx.witness_set.redeemers.unwrap().len(), 2usize);
}

#[test]
pub fn test_extra_datum() {
let mut tx_builder = create_reallistic_tx_builder();
tx_builder.set_fee(&to_bignum(42));

let datum = PlutusData::new_bytes(fake_bytes_32(1));
tx_builder.add_extra_datum(&datum);

let mut inp = TxInputsBuilder::new();
inp.add_input(
&fake_base_address(0),
&fake_tx_input(0),
&Value::new(&to_bignum(1000000u64)),
);

tx_builder.set_inputs(&inp);
tx_builder
.calc_script_data_hash(&TxBuilderConstants::plutus_default_cost_models())
.unwrap();


let res = tx_builder.build_tx();
assert!(res.is_ok());

let tx = res.unwrap();

let data_hash = hash_script_data(
&Redeemers::new(),
&Costmdls::new(),
Some(PlutusList::from(vec![datum.clone()])),
);

let tx_builder_script_data_hash = tx_builder.script_data_hash.clone();
assert_eq!(tx_builder_script_data_hash.unwrap(), data_hash);

let extra_datums = tx_builder.get_extra_datums().unwrap();
assert_eq!(&extra_datums.get(0), &datum);
assert_eq!(extra_datums.len(), 1usize);
assert_eq!(tx_builder.get_witness_set().plutus_data().unwrap().len(), 1usize);
assert_eq!(tx.witness_set().plutus_data().unwrap().len(), 1usize);
assert_eq!(tx.witness_set().plutus_data().unwrap().get(0), datum);
}
}