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

Missing crypto wasm api #292

Merged
merged 3 commits into from
Dec 29, 2023
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
36 changes: 35 additions & 1 deletion chain/rust/src/crypto/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use cml_crypto::{
use crate::{
auxdata::AuxiliaryData,
plutus::{CostModels, Language, PlutusData, Redeemer},
transaction::{cbor_encodings::TransactionWitnessSetEncoding, TransactionBody},
transaction::{
cbor_encodings::TransactionWitnessSetEncoding, TransactionBody, TransactionWitnessSet,
},
};

pub fn hash_auxiliary_data(auxiliary_data: &AuxiliaryData) -> AuxiliaryDataHash {
Expand All @@ -22,6 +24,11 @@ pub fn hash_plutus_data(plutus_data: &PlutusData) -> DatumHash {
DatumHash::from(blake2b256(&plutus_data.to_cbor_bytes()))
}

/// Calculates the hash for script data (no plutus scripts) if it is necessary.
/// Returns None if it was not necessary (no datums/redeemers) to include.
///
/// Most users will not directly need this as when using the builders
/// it will be invoked for you.
pub fn hash_script_data(
redeemers: &[Redeemer],
cost_models: &CostModels,
Expand Down Expand Up @@ -114,6 +121,11 @@ pub enum ScriptDataHashError {
MissingCostModel(Language),
}

/// Calculates the hash for script data (with plutus scripts) if it is necessary.
/// Returns None if it was not necessary (no datums/redeemers) to include.
///
/// Most users will not directly need this as when using the builders
/// it will be invoked for you.
pub fn calc_script_data_hash(
redeemers: &[Redeemer],
datums: &[PlutusData],
Expand Down Expand Up @@ -170,6 +182,28 @@ pub fn calc_script_data_hash(
}
}

/// Calculates the hash for script data from a witness if it is necessary.
/// Returns None if it was not necessary (no datums/redeemers) to include.
///
/// Most users will not directly need this as when using the builders
/// it will be invoked for you.
pub fn calc_script_data_hash_from_witness(
witnesses: &TransactionWitnessSet,
cost_models: &CostModels,
) -> Result<Option<ScriptDataHash>, ScriptDataHashError> {
if let (Some(redeemers), Some(datums)) = (&witnesses.redeemers, &witnesses.plutus_datums) {
calc_script_data_hash(
redeemers,
datums,
cost_models,
witnesses.languages().as_ref(),
witnesses.encodings.as_ref(),
)
} else {
Ok(None)
}
}

/// Each new language uses a different namespace for hashing its script
/// This is because you could have a language where the same bytes have different semantics
/// So this avoids scripts in different languages mapping to the same hash
Expand Down
2 changes: 1 addition & 1 deletion chain/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// possibly impacting PartialOrd performance on top of being unnecessary and occuring in generated code.
// Possibly the derivative crate could get updated to suppress this lint
// from within their proc macros itself. Issue: https://github.com/mcarton/rust-derivative/issues/115
#![allow(clippy::incorrect_partial_ord_impl_on_ord_type)]
#![allow(clippy::non_canonical_partial_ord_impl)]

pub mod address;
pub mod assets;
Expand Down
15 changes: 15 additions & 0 deletions chain/rust/src/transaction/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::collections::BTreeSet;

use crate::{
address::Address,
plutus::Language,
transaction::{DatumOption, ScriptRef, TransactionOutput},
Value,
};
Expand Down Expand Up @@ -191,4 +192,18 @@ impl TransactionWitnessSet {
}
}
}

pub fn languages(&self) -> Vec<Language> {
let mut used_langs = vec![];
if self.plutus_v1_scripts.is_some() {
used_langs.push(Language::PlutusV1);
}
if self.plutus_v2_scripts.is_some() {
used_langs.push(Language::PlutusV2);
}
if self.plutus_v3_scripts.is_some() {
used_langs.push(Language::PlutusV3);
}
used_langs
}
}
99 changes: 99 additions & 0 deletions chain/wasm/src/crypto/hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use wasm_bindgen::prelude::{wasm_bindgen, JsError};

use crate::{
auxdata::AuxiliaryData,
plutus::{CostModels, PlutusData},
transaction::{TransactionBody, TransactionWitnessSet},
utils::LanguageList,
PlutusDataList, RedeemerList,
};

use cml_crypto_wasm::{AuxiliaryDataHash, DatumHash, ScriptDataHash, TransactionHash};

#[wasm_bindgen]
pub fn hash_auxiliary_data(auxiliary_data: &AuxiliaryData) -> AuxiliaryDataHash {
cml_chain::crypto::hash::hash_auxiliary_data(auxiliary_data.as_ref()).into()
}

#[wasm_bindgen]
pub fn hash_transaction(tx_body: &TransactionBody) -> TransactionHash {
cml_chain::crypto::hash::hash_transaction(tx_body.as_ref()).into()
}

#[wasm_bindgen]
pub fn hash_plutus_data(plutus_data: &PlutusData) -> DatumHash {
cml_chain::crypto::hash::hash_plutus_data(plutus_data.as_ref()).into()
}

/// Calculates the hash for script data (no plutus scripts) if it is necessary.
/// Returns None if it was not necessary (no datums/redeemers) to include.
///
/// Most users will not directly need this as when using the builders
/// it will be invoked for you.
///
/// Note: This WASM binding does not work with non-standard witness set
/// encodings. If you created the witness set manually this is not an issue
/// but for constructing it from deserializing a transaction/witness then
/// please use calc_script_data_hash_from_witness()
#[wasm_bindgen]
pub fn hash_script_data(
redeemers: &RedeemerList,
cost_models: &CostModels,
datums: Option<PlutusDataList>,
// encoding: Option<TransactionWitnessSetEncoding>,
) -> ScriptDataHash {
cml_chain::crypto::hash::hash_script_data(
redeemers.as_ref(),
cost_models.as_ref(),
datums.as_ref().map(AsRef::as_ref),
None,
)
.into()
}

/// Calculates the hash for script data (with plutus scripts) if it is necessary.
/// Returns None if it was not necessary (no datums/redeemers) to include.
///
/// Most users will not directly need this as when using the builders
/// it will be invoked for you.
///
/// Note: This WASM binding does not work with non-standard witness set
/// encodings. If you created the witness set manually this is not an issue
/// but for constructing it from deserializing a transaction/witness then
/// please use calc_script_data_hash_from_witness()
#[wasm_bindgen]
pub fn calc_script_data_hash(
redeemers: &RedeemerList,
datums: &PlutusDataList,
cost_models: &CostModels,
used_langs: &LanguageList,
// encoding: Option<TransactionWitnessSetEncoding>,
) -> Result<Option<ScriptDataHash>, JsError> {
cml_chain::crypto::hash::calc_script_data_hash(
redeemers.as_ref(),
datums.as_ref(),
cost_models.as_ref(),
used_langs.as_ref(),
None,
)
.map(|sdh| sdh.map(Into::into))
.map_err(Into::into)
}

/// Calculates the hash for script data from a witness if it is necessary.
/// Returns None if it was not necessary (no datums/redeemers) to include.
///
/// Most users will not directly need this as when using the builders
/// it will be invoked for you.
#[wasm_bindgen]
pub fn calc_script_data_hash_from_witness(
witnesses: &TransactionWitnessSet,
cost_models: &CostModels,
) -> Result<Option<ScriptDataHash>, JsError> {
cml_chain::crypto::hash::calc_script_data_hash_from_witness(
witnesses.as_ref(),
cost_models.as_ref(),
)
.map(|sdh| sdh.map(Into::into))
.map_err(Into::into)
}
3 changes: 3 additions & 0 deletions chain/wasm/src/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ pub use cml_crypto_wasm::{
ScriptDataHash, ScriptHash, TransactionHash, VRFKeyHash, VRFVkey,
};

pub mod hash;
pub mod utils;

use wasm_bindgen::prelude::{wasm_bindgen, JsError, JsValue};

use cml_core_wasm::{impl_wasm_cbor_json_api, impl_wasm_conversions};
Expand Down
17 changes: 17 additions & 0 deletions chain/wasm/src/crypto/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use wasm_bindgen::prelude::{wasm_bindgen, JsError};

use crate::{byron::AddressContent, crypto::BootstrapWitness, Vkeywitness};

use cml_crypto_wasm::{PrivateKey, TransactionHash};

#[wasm_bindgen]
impl BootstrapWitness {
pub fn to_address(&self) -> Result<AddressContent, JsError> {
self.0.to_address().map(Into::into).map_err(Into::into)
}
}

#[wasm_bindgen]
pub fn make_vkey_witness(tx_body_hash: &TransactionHash, sk: &PrivateKey) -> Vkeywitness {
cml_chain::crypto::utils::make_vkey_witness(tx_body_hash.as_ref(), sk.as_ref()).into()
}
27 changes: 2 additions & 25 deletions chain/wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use ::wasm_bindgen::prelude::{wasm_bindgen, JsError, JsValue};
use cml_core_wasm::metadata::TransactionMetadatumList;
use cml_core_wasm::{impl_wasm_cbor_json_api, impl_wasm_conversions};
use cml_core_wasm::{impl_wasm_cbor_json_api, impl_wasm_conversions, impl_wasm_list};

pub use cml_core_wasm::Int;

Expand Down Expand Up @@ -642,30 +642,7 @@ impl NativeScriptList {
}
}

#[derive(Clone, Debug)]
#[wasm_bindgen]
pub struct PlutusDataList(Vec<cml_chain::plutus::PlutusData>);

impl_wasm_conversions!(Vec<cml_chain::plutus::PlutusData>, PlutusDataList);

#[wasm_bindgen]
impl PlutusDataList {
pub fn new() -> Self {
Self(Vec::new())
}

pub fn len(&self) -> usize {
self.0.len()
}

pub fn get(&self, index: usize) -> PlutusData {
self.0[index].clone().into()
}

pub fn add(&mut self, elem: &PlutusData) {
self.0.push(elem.clone().into());
}
}
impl_wasm_list!(cml_chain::plutus::PlutusData, PlutusData, PlutusDataList);

#[derive(Clone, Debug)]
#[wasm_bindgen]
Expand Down
5 changes: 5 additions & 0 deletions chain/wasm/src/transaction/utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{
address::Address,
transaction::{DatumOption, ScriptRef, TransactionOutput},
utils::LanguageList,
Ed25519KeyHashList, NativeScript, Value,
};
use cml_crypto_wasm::{DatumHash, ScriptHash};
Expand Down Expand Up @@ -74,4 +75,8 @@ impl TransactionWitnessSet {
pub fn add_all_witnesses(&mut self, other: &TransactionWitnessSet) {
self.0.add_all_witnesses(other.clone().into());
}

pub fn languages(&self) -> LanguageList {
self.0.languages().into()
}
}
4 changes: 3 additions & 1 deletion chain/wasm/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use super::{Int, Script, ScriptHash};
use cml_chain::plutus::Language;
use wasm_bindgen::prelude::{wasm_bindgen, JsError, JsValue};

use cml_core_wasm::{impl_wasm_cbor_json_api, impl_wasm_conversions};
use cml_core_wasm::{impl_wasm_cbor_json_api, impl_wasm_conversions, impl_wasm_list};

impl_wasm_list!(Language, Language, LanguageList, true, true);

#[wasm_bindgen]
#[derive(Clone, Debug)]
Expand Down
2 changes: 1 addition & 1 deletion core/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
// possibly impacting PartialOrd performance on top of being unnecessary and occuring in generated code.
// Possibly the derivative crate could get updated to suppress this lint
// from within their proc macros itself. Issue: https://github.com/mcarton/rust-derivative/issues/115
#![allow(clippy::incorrect_partial_ord_impl_on_ord_type)]
#![allow(clippy::non_canonical_partial_ord_impl)]

pub use error::*;

Expand Down
6 changes: 6 additions & 0 deletions core/wasm/src/wasm_wrappers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ macro_rules! impl_wasm_list {
}
}

impl AsRef<[$rust_elem_name]> for $wasm_list_name {
fn as_ref(&self) -> &[$rust_elem_name] {
&self.0
}
}

$crate::impl_wasm_list_add!(
$rust_elem_name,
$wasm_elem_name,
Expand Down
28 changes: 22 additions & 6 deletions specs/conway/transaction.cddl
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ transaction_output = alonzo_format_tx_out / conway_format_tx_out
; Since this data does not exist in contiguous form inside a transaction, it needs
; to be independently constructed by each recipient.
;
; script data format:
; [ redeemers | datums | language views ]
; The bytestring which is hashed is the concatenation of three things:
; redeemers || datums || language views
; The redeemers are exactly the data present in the transaction witness set.
; Similarly for the datums, if present. If no datums are provided, the middle
; field is an empty string.
; field is omitted (i.e. it is the empty/null bytestring).
;
; language views CDDL:
; { * language => script_integrity_data }
Expand All @@ -83,21 +83,37 @@ transaction_output = alonzo_format_tx_out / conway_format_tx_out
; in (byte-wise) lexical order sorts earlier.
;
; For PlutusV1 (language id 0), the language view is the following:
; - the value of costmdls map at key 0 is encoded as an indefinite length
; list and the result is encoded as a bytestring. (our apologies)
; - the value of costmdls map at key 0 (in other words, the script_integrity_data)
; is encoded as an indefinite length list and the result is encoded as a bytestring.
; (our apologies)
; For example, the script_integrity_data corresponding to the all zero costmodel for V1
; would be encoded as (in hex):
; 58a89f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff
; - the language ID tag is also encoded twice. first as a uint then as
; a bytestring. (our apologies)
; Concretely, this means that the language version for V1 is encoded as
; 4100 in hex.
; For PlutusV2 (language id 1), the language view is the following:
; - the value of costmdls map at key 1 is encoded as an definite length list.
; For example, the script_integrity_data corresponding to the all zero costmodel for V2
; would be encoded as (in hex):
; 98af0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
; - the language ID tag is encoded as expected.
; Concretely, this means that the language version for V2 is encoded as
; 01 in hex.
; For PlutusV3 (language id 2), the language view is the following:
; - the value of costmdls map at key 2 is encoded as a definite length list.
;
; Note that each Plutus language represented inside a transaction must have
; a cost model in the costmdls protocol parameter in order to execute,
; regardless of what the script integrity data is.
;
; Finally, note that in the case that a transaction includes datums but does not
; include any redeemers, the script data format becomes (in hex):
; include the redeemers field, the script data format becomes (in hex):
; [ 80 | datums | A0 ]
; corresponding to a CBOR empty list and an empty map.
; Note that a transaction might include the redeemers field and it to the
; empty map, in which case the user supplied encoding of the empty map is used.

; data = #6.24(bytes .cbor plutus_data)

Expand Down
Loading