Skip to content
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
1,644 changes: 978 additions & 666 deletions packages/Cargo.lock

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion packages/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,13 @@ members = [
"whisky-wallet",
"whisky",
"whisky-examples"
]
]

[workspace.dependencies]
# Downgrade uplc to version compatible with pallas 0.31.0
uplc = "=1.1.10"
# Keep at 0.31.0 for cquisitor-lib compatibility
pallas-codec = "0.31.0"
pallas-crypto = "0.31.0"
pallas-primitives = "0.31.0"
pallas-traverse = "0.31.0"
2 changes: 1 addition & 1 deletion packages/whisky-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ readme = "../../README.md"
authors = ["Wu Tsz Wai <tszwai@deltadefi.io>", "Hinson Wong <hinson.wong@deltadefi.io>"]

[dependencies]
uplc = "=1.1.10"
uplc.workspace = true
hex = "0.4"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.136"
Expand Down
16 changes: 8 additions & 8 deletions packages/whisky-csl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,26 @@ whisky-common = { version = "1.0.24", path = "../whisky-common" }

# non-wasm
[target.'cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))'.dependencies]
uplc = "=1.1.10"
uplc.workspace = true
wasm-bindgen = { version = "=0.2.92", features = ["serde-serialize"] }
rand_os = "0.1"
noop_proc_macro = "0.3.0"
pallas-codec = { version = "0.31.0", features = ["num-bigint"] }
pallas-primitives = "0.31.0"
pallas-traverse = "0.31.0"
pallas-codec = { workspace = true, features = ["num-bigint"] }
pallas-primitives.workspace = true
pallas-traverse.workspace = true
cquisitor-lib = "0.1.0-beta.25"

# wasm
[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies]
serde-wasm-bindgen = "0.4.5"
uplc = "=1.1.10"
uplc.workspace = true
wasm-bindgen = { version = "=0.2.92", features = ["serde-serialize"] }
rand_os = { version = "0.1", features = ["wasm-bindgen"] }
js-sys = "=0.3.69"
wasm-bindgen-futures = "=0.4.40"
pallas-codec = { version = "0.31.0", features = ["num-bigint"] }
pallas-primitives = "0.31.0"
pallas-traverse = "0.31.0"
pallas-codec = { workspace = true, features = ["num-bigint"] }
pallas-primitives.workspace = true
pallas-traverse.workspace = true
cquisitor-lib = "0.1.0-beta.25"

[profile.release]
Expand Down
4 changes: 2 additions & 2 deletions packages/whisky-js/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ whisky-common = { version = "1.0.24", path = "../whisky-common" }

# non-wasm
[target.'cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))'.dependencies]
uplc = "=1.1.10"
uplc.workspace = true
wasm-bindgen = { version = "=0.2.92", features = ["serde-serialize"] }
rand_os = "0.1"
noop_proc_macro = "0.3.0"
Expand All @@ -28,7 +28,7 @@ cquisitor-lib = "0.1.0-beta.25"
[target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies]
serde-wasm-bindgen = "0.4.5"
# uplc = { version = "=1.0.26-alpha", default-features = false, features = ["native-secp256k1"] }
uplc = "=1.1.10"
uplc.workspace = true
wasm-bindgen = { version = "=0.2.92", features = ["serde-serialize"] }
rand_os = { version = "0.1", features = ["wasm-bindgen"] }
js-sys = "=0.3.69"
Expand Down
5 changes: 5 additions & 0 deletions packages/whisky-pallas/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,8 @@ ouroboros = "0.18"
bech32 = "0.11.1"
serde_json = "1.0"
whisky-common = { version = "1.0.24", path = "../whisky-common" }
uplc = "1.1.10"
# Need pallas 0.31.0 for uplc compatibility
pallas-primitives = "0.31.0"
pallas-codec = "0.31.0"
pallas-traverse = "0.31.0"
95 changes: 92 additions & 3 deletions packages/whisky-pallas/src/tx_parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ mod context;
mod inputs;
mod metadata;
mod mints;
mod outputs;
pub mod outputs;
mod reference_inputs;
mod required_signers;
mod validity_range;
Expand All @@ -19,11 +19,12 @@ use crate::{
required_signers::extract_required_signers, validity_range::extract_validity_range,
votes::extract_votes, withdrawals::extract_withdrawals,
},
wrapper::transaction_body::Transaction,
wrapper::transaction_body::{ScriptRef, ScriptRefKind, Transaction},
};

use pallas::ledger::traverse::ComputeHash;
use pallas_crypto::key::ed25519::{PublicKey, Signature};
use whisky_common::{TxBuilderBody, UTxO, WError};
use whisky_common::{TxBuilderBody, UTxO, UtxoInput, UtxoOutput, WError};

pub fn parse(tx_hex: &str, resolved_utxos: &[UTxO]) -> Result<TxBuilderBody, WError> {
let bytes = hex::decode(tx_hex).map_err(|e| {
Expand Down Expand Up @@ -102,3 +103,91 @@ pub fn check_tx_required_signers(tx_hex: &str) -> Result<bool, WError> {
Ok(required_signers.is_empty())
}
}

pub fn extract_tx_utxos(tx_hex: &str) -> Result<Vec<whisky_common::UTxO>, WError> {
let bytes = hex::decode(tx_hex).map_err(|e| {
WError::new(
"WhiskyPallas - extract tx outputs:",
&format!("Hex decode error: {}", e),
)
})?;
let pallas_tx = Transaction::decode_bytes(&bytes)?;
extract_outputs(&pallas_tx.inner).and_then(|outputs| {
outputs
.into_iter()
.enumerate()
.map(|(index, output)| -> Result<UTxO, WError> {
let datum_cbor = match output.datum.clone() {
Some(datum) => match datum {
whisky_common::Datum::Inline(s) => Some(s),
whisky_common::Datum::Hash(_) => None,
whisky_common::Datum::Embedded(_) => None,
},
None => None,
};
let datum_hash = match output.datum {
Some(datum) => match datum {
whisky_common::Datum::Inline(_) => None,
whisky_common::Datum::Hash(s) => Some(s),
whisky_common::Datum::Embedded(s) => Some(s),
},
None => None,
};
let script_cbor: Option<String> = match output.reference_script {
Some(script) => match script {
whisky_common::OutputScriptSource::ProvidedSimpleScriptSource(
provided_simple_script_source,
) => Some(
ScriptRef::new(ScriptRefKind::NativeScript {
native_script_hex: provided_simple_script_source.script_cbor,
})
.unwrap()
.encode()?,
),

whisky_common::OutputScriptSource::ProvidedScriptSource(
provided_script_source,
) => match provided_script_source.language_version {
whisky_common::LanguageVersion::V1 => Some(
ScriptRef::new(ScriptRefKind::PlutusV1Script {
plutus_v1_script_hex: provided_script_source.script_cbor,
})
.unwrap()
.encode()?,
),
whisky_common::LanguageVersion::V2 => Some(
ScriptRef::new(ScriptRefKind::PlutusV2Script {
plutus_v2_script_hex: provided_script_source.script_cbor,
})
.unwrap()
.encode()?,
),
whisky_common::LanguageVersion::V3 => Some(
ScriptRef::new(ScriptRefKind::PlutusV3Script {
plutus_v3_script_hex: provided_script_source.script_cbor,
})
.unwrap()
.encode()?,
),
},
},
None => None,
};
Ok(UTxO {
input: UtxoInput {
tx_hash: pallas_tx.inner.transaction_body.compute_hash().to_string(),
output_index: index as u32,
},
output: UtxoOutput {
address: output.address,
amount: output.amount,
plutus_data: datum_cbor,
data_hash: datum_hash,
script_ref: script_cbor,
script_hash: None,
},
})
})
.collect()
})
}
163 changes: 163 additions & 0 deletions packages/whisky-pallas/src/utils/address.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use std::str::FromStr;

use pallas::ledger::addresses::{Address, Network, ShelleyAddress, ShelleyDelegationPart};
use pallas_crypto::hash::Hash;
use whisky_common::{DeserializedAddress, WError};

pub fn script_to_address(
network_id: u8,
script_hash: &str,
stake_cred: Option<(&str, bool)>,
) -> String {
let stake_cred = match stake_cred {
Some((stake, is_script)) => {
let stake_cred = if is_script {
ShelleyDelegationPart::Script(Hash::from_str(stake).unwrap())
} else {
ShelleyDelegationPart::Key(Hash::from_str(stake).unwrap())
};
stake_cred
}
None => ShelleyDelegationPart::Null,
};
let payment_cred =
pallas::ledger::addresses::ShelleyPaymentPart::Script(Hash::from_str(script_hash).unwrap());

let address = ShelleyAddress::new(
Network::try_from(network_id).unwrap(),
payment_cred,
stake_cred,
);
address.to_bech32().unwrap()
}

pub fn serialize_address_obj(
address_obj: DeserializedAddress,
network_id: u8,
) -> Result<String, WError> {
let payment_cred = match (
address_obj.pub_key_hash.as_str(),
address_obj.script_hash.as_str(),
) {
(pub_key_hash, "") => {
pallas::ledger::addresses::ShelleyPaymentPart::Key(Hash::from_str(pub_key_hash).unwrap())
}
("", script_hash) => {
pallas::ledger::addresses::ShelleyPaymentPart::Script(Hash::from_str(script_hash).unwrap())
}
_ => Err(WError::new(
"serialze_address_obj",
&format!(
"Must provide exactly one of pub_key_hash or script_hash, pub_key_hash: {}, script_hash: {}",
address_obj.pub_key_hash, address_obj.script_hash
),
))?,
};

let stake_cred_opt = match (
address_obj.stake_key_hash.as_str(),
address_obj.stake_key_script_hash.as_str(),
) {
("","") => None,
(stake_key_hash, "") => Some(ShelleyDelegationPart::Key(Hash::from_str(stake_key_hash).unwrap())),
("", stake_script_hash) => Some(ShelleyDelegationPart::Script(Hash::from_str(stake_script_hash).unwrap())),
_ => Err(WError::new(
"serialze_address_obj",
&format!(
"Must provide at most one of stake_key_hash or stake_script_hash, stake_key_hash: {}, stake_script_hash: {}",
address_obj.stake_key_hash, address_obj.stake_key_script_hash
),
))?,
};

match stake_cred_opt {
Some(stake_cred) => Ok(ShelleyAddress::new(
Network::try_from(network_id).unwrap(),
payment_cred,
stake_cred,
)
.to_bech32()
.unwrap()),
None => Ok(ShelleyAddress::new(
Network::try_from(network_id).unwrap(),
payment_cred,
ShelleyDelegationPart::Null,
)
.to_bech32()
.unwrap()),
}
}

pub fn deserialize_address(bech32_address: &str) -> Result<DeserializedAddress, WError> {
let address = Address::from_bech32(bech32_address).map_err(|e| {
WError::new(
"deserialize_address",
&format!(
"Failed to parse address from bech32: {}, error: {}",
bech32_address, e
),
)
})?;

match address {
Address::Byron(byron_address) => {
return Err(WError::new(
"deserialize_address",
&format!(
"Byron addresses are not supported: {}",
byron_address.to_base58()
),
))?
}
Address::Shelley(shelley_address) => {
let (payment_pkh, payment_script_hash) = match shelley_address.payment() {
pallas::ledger::addresses::ShelleyPaymentPart::Key(key_hash) => {
(key_hash.to_string(), String::new())
}
pallas::ledger::addresses::ShelleyPaymentPart::Script(script_hash) => {
(String::new(), script_hash.to_string())
}
};
let (stake_pkh, stake_script_hash) = match shelley_address.delegation() {
ShelleyDelegationPart::Null => (String::new(), String::new()),
ShelleyDelegationPart::Key(stake_key_hash) => {
(stake_key_hash.to_string(), String::new())
}
ShelleyDelegationPart::Script(stake_script_hash) => {
(String::new(), stake_script_hash.to_string())
}
ShelleyDelegationPart::Pointer(_pointer) => {
return Err(WError::new(
"deserialize_address",
"Pointer stake keys are not supported",
))
}
};

Ok(DeserializedAddress::new(
&payment_pkh,
&payment_script_hash,
&stake_pkh,
&stake_script_hash,
))
}
Address::Stake(stake_address) => {
let stake_payload = stake_address.payload();
let (stake_pkh, stake_script_hash) = match stake_payload {
pallas::ledger::addresses::StakePayload::Stake(hash) => {
(hash.to_string(), String::new())
}
pallas::ledger::addresses::StakePayload::Script(hash) => {
(String::new(), hash.to_string())
}
};

Ok(DeserializedAddress::new(
&String::new(),
&String::new(),
&stake_pkh,
&stake_script_hash,
))
}
}
}
Loading