aiken/crates/uplc/src/tx/to_plutus_data.rs

623 lines
22 KiB
Rust

use pallas_addresses::{Address, ShelleyDelegationPart, ShelleyPaymentPart, StakePayload};
use pallas_codec::utils::{AnyUInt, Bytes, Int, KeyValuePairs};
use pallas_crypto::hash::Hash;
use pallas_primitives::babbage::{AssetName, BigInt, Constr, Mint, PlutusData, ScriptRef};
use pallas_primitives::babbage::{
Certificate, DatumOption, Redeemer, Script, StakeCredential, TransactionInput,
TransactionOutput, Value,
};
use pallas_traverse::ComputeHash;
use super::script_context::{ScriptContext, ScriptPurpose, TimeRange, TxInInfo, TxInfo, TxOut};
fn wrap_with_constr(index: u64, data: PlutusData) -> PlutusData {
PlutusData::Constr(Constr {
tag: constr_index(index),
any_constructor: None,
fields: vec![data],
})
}
fn wrap_multiple_with_constr(index: u64, data: Vec<PlutusData>) -> PlutusData {
PlutusData::Constr(Constr {
tag: constr_index(index),
any_constructor: None,
fields: data,
})
}
fn empty_constr(index: u64) -> PlutusData {
PlutusData::Constr(Constr {
tag: constr_index(index),
any_constructor: None,
fields: vec![],
})
}
/// Translate constructor index to cbor tag.
fn constr_index(index: u64) -> u64 {
121 + index
}
pub trait ToPlutusData {
fn to_plutus_data(&self) -> PlutusData;
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct MintValue {
pub mint_value: Mint,
}
impl ToPlutusData for Address {
fn to_plutus_data(&self) -> PlutusData {
match self {
Address::Shelley(shelley_address) => {
let payment_part = shelley_address.payment();
let stake_part = shelley_address.delegation();
let payment_part_plutus_data = match payment_part {
ShelleyPaymentPart::Key(payment_keyhash) => {
wrap_with_constr(0, payment_keyhash.to_plutus_data())
}
ShelleyPaymentPart::Script(script_hash) => {
wrap_with_constr(1, script_hash.to_plutus_data())
}
};
let stake_part_plutus_data = match stake_part {
ShelleyDelegationPart::Key(stake_keyhash) => {
Some(StakeCredential::AddrKeyhash(*stake_keyhash)).to_plutus_data()
}
ShelleyDelegationPart::Script(script_hash) => {
Some(StakeCredential::Scripthash(*script_hash)).to_plutus_data()
}
ShelleyDelegationPart::Pointer(pointer) => Some(wrap_multiple_with_constr(
1,
vec![
pointer.slot().to_plutus_data(),
pointer.tx_idx().to_plutus_data(),
pointer.cert_idx().to_plutus_data(),
],
))
.to_plutus_data(),
ShelleyDelegationPart::Null => None::<StakeCredential>.to_plutus_data(),
};
wrap_multiple_with_constr(0, vec![payment_part_plutus_data, stake_part_plutus_data])
}
Address::Stake(stake_address) => {
// This is right now only used in Withdrawals (Reward account)
match stake_address.payload() {
StakePayload::Stake(stake_keyhash) => {
StakeCredential::AddrKeyhash(*stake_keyhash).to_plutus_data()
}
StakePayload::Script(script_hash) => {
StakeCredential::Scripthash(*script_hash).to_plutus_data()
}
}
}
_ => unreachable!(),
}
}
}
impl ToPlutusData for TransactionInput {
fn to_plutus_data(&self) -> PlutusData {
wrap_multiple_with_constr(
0,
vec![
wrap_with_constr(0, self.transaction_id.to_plutus_data()),
PlutusData::BigInt(BigInt::Int((self.index as i128).try_into().unwrap())),
],
)
}
}
impl<const BYTES: usize> ToPlutusData for Hash<BYTES> {
fn to_plutus_data(&self) -> PlutusData {
PlutusData::BoundedBytes(self.to_vec().into())
}
}
impl ToPlutusData for Bytes {
fn to_plutus_data(&self) -> PlutusData {
PlutusData::BoundedBytes(self.to_vec().into())
}
}
impl<K: ToPlutusData, V: ToPlutusData> ToPlutusData for (K, V) {
fn to_plutus_data(&self) -> PlutusData {
wrap_multiple_with_constr(0, vec![self.0.to_plutus_data(), self.1.to_plutus_data()])
}
}
impl<A> ToPlutusData for Vec<A>
where
A: ToPlutusData,
{
fn to_plutus_data(&self) -> PlutusData {
PlutusData::Array(self.iter().map(|p| p.to_plutus_data()).collect())
}
}
impl<K, V> ToPlutusData for KeyValuePairs<K, V>
where
K: ToPlutusData + Clone,
V: ToPlutusData + Clone,
{
fn to_plutus_data(&self) -> PlutusData {
let mut data_vec: Vec<(PlutusData, PlutusData)> = vec![];
for (key, value) in self.iter() {
data_vec.push((key.to_plutus_data(), value.to_plutus_data()))
}
PlutusData::Map(KeyValuePairs::Def(data_vec))
}
}
impl<A: ToPlutusData> ToPlutusData for Option<A> {
fn to_plutus_data(&self) -> PlutusData {
match self {
None => empty_constr(1),
Some(data) => wrap_with_constr(0, data.to_plutus_data()),
}
}
}
// Does this here surely overwrite Option from above for DatumOption?
impl ToPlutusData for Option<DatumOption> {
// NoOutputDatum = 0 | OutputDatumHash = 1 | OutputDatum = 2
fn to_plutus_data(&self) -> PlutusData {
match self {
None => empty_constr(0),
Some(option) => match option {
DatumOption::Hash(hash) => wrap_with_constr(1, hash.to_plutus_data()),
DatumOption::Data(data) => wrap_with_constr(2, data.0.clone()),
},
}
}
}
impl ToPlutusData for AnyUInt {
fn to_plutus_data(&self) -> PlutusData {
match self {
AnyUInt::U8(n) => PlutusData::BigInt(BigInt::Int(Int::from(*n as i64))),
AnyUInt::U16(n) => PlutusData::BigInt(BigInt::Int(Int::from(*n as i64))),
AnyUInt::U32(n) => PlutusData::BigInt(BigInt::Int(Int::from(*n as i64))),
AnyUInt::U64(n) => PlutusData::BigInt(BigInt::Int(Int::try_from(*n as i128).unwrap())),
AnyUInt::MajorByte(n) => PlutusData::BigInt(BigInt::Int(Int::from(*n as i64))), // is this correct? I don't know exactly what is does
}
}
}
impl ToPlutusData for Int {
fn to_plutus_data(&self) -> PlutusData {
PlutusData::BigInt(BigInt::Int(*self))
}
}
impl ToPlutusData for BigInt {
fn to_plutus_data(&self) -> PlutusData {
PlutusData::BigInt(self.clone())
}
}
impl ToPlutusData for i64 {
fn to_plutus_data(&self) -> PlutusData {
PlutusData::BigInt(BigInt::Int(Int::from(*self)))
}
}
impl ToPlutusData for u64 {
fn to_plutus_data(&self) -> PlutusData {
PlutusData::BigInt(BigInt::Int(Int::try_from(*self as i128).unwrap()))
}
}
impl ToPlutusData for Value {
fn to_plutus_data(&self) -> PlutusData {
match self {
Value::Coin(coin) => PlutusData::Map(KeyValuePairs::Def(vec![(
Bytes::from(vec![]).to_plutus_data(),
PlutusData::Map(KeyValuePairs::Def(vec![(
AssetName::from(vec![]).to_plutus_data(),
coin.to_plutus_data(),
)])),
)])),
Value::Multiasset(coin, multiassets) => {
let mut data_vec: Vec<(PlutusData, PlutusData)> = vec![(
Bytes::from(vec![]).to_plutus_data(),
PlutusData::Map(KeyValuePairs::Def(vec![(
AssetName::from(vec![]).to_plutus_data(),
coin.to_plutus_data(),
)])),
)];
for (policy_id, assets) in multiassets.iter() {
let mut assets_vec = vec![];
for (asset, amount) in assets.iter() {
assets_vec.push((asset.to_plutus_data(), amount.to_plutus_data()));
}
data_vec.push((
policy_id.to_plutus_data(),
PlutusData::Map(KeyValuePairs::Def(assets_vec)),
));
}
PlutusData::Map(KeyValuePairs::Def(data_vec))
}
}
}
}
impl ToPlutusData for MintValue {
fn to_plutus_data(&self) -> PlutusData {
let mut data_vec: Vec<(PlutusData, PlutusData)> = vec![(
Bytes::from(vec![]).to_plutus_data(),
PlutusData::Map(KeyValuePairs::Def(vec![(
AssetName::from(vec![]).to_plutus_data(),
0_i64.to_plutus_data(),
)])),
)];
for (policy_id, assets) in self.mint_value.iter() {
let mut assets_vec = vec![];
for (asset, amount) in assets.iter() {
assets_vec.push((asset.to_plutus_data(), amount.to_plutus_data()));
}
data_vec.push((
policy_id.to_plutus_data(),
PlutusData::Map(KeyValuePairs::Def(assets_vec)),
));
}
PlutusData::Map(KeyValuePairs::Def(data_vec))
}
}
impl ToPlutusData for ScriptRef {
fn to_plutus_data(&self) -> PlutusData {
match &self.0 {
Script::NativeScript(native_script) => native_script.compute_hash().to_plutus_data(),
Script::PlutusV1Script(plutus_v1) => plutus_v1.compute_hash().to_plutus_data(),
Script::PlutusV2Script(plutus_v2) => plutus_v2.compute_hash().to_plutus_data(),
}
}
}
impl ToPlutusData for TxOut {
fn to_plutus_data(&self) -> PlutusData {
match self {
TxOut::V1(output) => match output {
TransactionOutput::Legacy(legacy_output) => wrap_multiple_with_constr(
0,
vec![
Address::from_bytes(&legacy_output.address)
.unwrap()
.to_plutus_data(),
legacy_output.amount.to_plutus_data(),
legacy_output.datum_hash.to_plutus_data(),
],
),
TransactionOutput::PostAlonzo(post_alonzo_output) => wrap_multiple_with_constr(
0,
vec![
Address::from_bytes(&post_alonzo_output.address)
.unwrap()
.to_plutus_data(),
post_alonzo_output.value.to_plutus_data(),
match post_alonzo_output.datum_option {
Some(DatumOption::Hash(hash)) => Some(hash).to_plutus_data(),
_ => None::<Hash<32>>.to_plutus_data(),
},
],
),
},
TxOut::V2(output) => match output {
TransactionOutput::Legacy(legacy_output) => wrap_multiple_with_constr(
0,
vec![
Address::from_bytes(&legacy_output.address)
.unwrap()
.to_plutus_data(),
legacy_output.amount.to_plutus_data(),
match legacy_output.datum_hash {
Some(hash) => wrap_with_constr(1, hash.to_plutus_data()),
_ => empty_constr(0),
},
None::<ScriptRef>.to_plutus_data(),
],
),
TransactionOutput::PostAlonzo(post_alonzo_output) => wrap_multiple_with_constr(
0,
vec![
Address::from_bytes(&post_alonzo_output.address)
.unwrap()
.to_plutus_data(),
post_alonzo_output.value.to_plutus_data(),
post_alonzo_output.datum_option.to_plutus_data(),
post_alonzo_output.script_ref.to_plutus_data(),
],
),
},
}
}
}
impl ToPlutusData for StakeCredential {
// Stake Credential needs to be wrapped inside another Constr, because we could have either a StakingHash or a StakingPtr
// The current implementation of StakeCredential doesn't capture the credential of a Pointer address.
// So a StakeCredential for a Pointer address needs to be converted separately
fn to_plutus_data(&self) -> PlutusData {
match self {
StakeCredential::AddrKeyhash(addr_keyhas) => {
wrap_with_constr(0, wrap_with_constr(0, addr_keyhas.to_plutus_data()))
}
StakeCredential::Scripthash(script_hash) => {
wrap_with_constr(0, wrap_with_constr(1, script_hash.to_plutus_data()))
}
}
}
}
impl ToPlutusData for Certificate {
fn to_plutus_data(&self) -> PlutusData {
match self {
Certificate::StakeRegistration(stake_credential) => {
wrap_with_constr(0, stake_credential.to_plutus_data())
}
Certificate::StakeDeregistration(stake_credential) => {
wrap_with_constr(1, stake_credential.to_plutus_data())
}
Certificate::StakeDelegation(stake_credential, pool_keyhash) => {
wrap_multiple_with_constr(
2,
vec![
stake_credential.to_plutus_data(),
pool_keyhash.to_plutus_data(),
],
)
}
Certificate::PoolRegistration {
operator,
vrf_keyhash,
pledge: _,
cost: _,
margin: _,
reward_account: _,
pool_owners: _,
relays: _,
pool_metadata: _,
} => wrap_multiple_with_constr(
3,
vec![operator.to_plutus_data(), vrf_keyhash.to_plutus_data()],
),
Certificate::PoolRetirement(pool_keyhash, epoch) => wrap_multiple_with_constr(
4,
vec![pool_keyhash.to_plutus_data(), epoch.to_plutus_data()],
),
Certificate::GenesisKeyDelegation(_, _, _) => empty_constr(5),
Certificate::MoveInstantaneousRewardsCert(_) => empty_constr(6),
}
}
}
impl ToPlutusData for Redeemer {
fn to_plutus_data(&self) -> PlutusData {
self.data.clone()
}
}
impl ToPlutusData for PlutusData {
fn to_plutus_data(&self) -> PlutusData {
self.clone()
}
}
impl ToPlutusData for TimeRange {
fn to_plutus_data(&self) -> PlutusData {
match &self {
TimeRange {
lower_bound: Some(lower_bound),
upper_bound: None,
} => {
wrap_multiple_with_constr(
0,
vec![
// LowerBound
wrap_multiple_with_constr(
0,
vec![
// Finite
wrap_with_constr(1, lower_bound.to_plutus_data()),
// Closure
true.to_plutus_data(),
],
), //UpperBound
wrap_multiple_with_constr(
0,
vec![
// PosInf
empty_constr(2),
// Closure
true.to_plutus_data(),
],
),
],
)
}
TimeRange {
lower_bound: None,
upper_bound: Some(upper_bound),
} => {
wrap_multiple_with_constr(
0,
vec![
// LowerBound
wrap_multiple_with_constr(
0,
vec![
// NegInf
empty_constr(0),
// Closure
true.to_plutus_data(),
],
),
//UpperBound
wrap_multiple_with_constr(
0,
vec![
// Finite
wrap_with_constr(1, upper_bound.to_plutus_data()),
// Closure
true.to_plutus_data(),
],
),
],
)
}
TimeRange {
lower_bound: Some(lower_bound),
upper_bound: Some(upper_bound),
} => {
wrap_multiple_with_constr(
0,
vec![
// LowerBound
wrap_multiple_with_constr(
0,
vec![
// Finite
wrap_with_constr(1, lower_bound.to_plutus_data()),
// Closure
true.to_plutus_data(),
],
),
//UpperBound
wrap_multiple_with_constr(
0,
vec![
// Finite
wrap_with_constr(1, upper_bound.to_plutus_data()),
// Closure
false.to_plutus_data(),
],
),
],
)
}
TimeRange {
lower_bound: None,
upper_bound: None,
} => {
wrap_multiple_with_constr(
0,
vec![
// LowerBound
wrap_multiple_with_constr(
0,
vec![
// NegInf
empty_constr(0),
// Closure
true.to_plutus_data(),
],
),
//UpperBound
wrap_multiple_with_constr(
0,
vec![
// PosInf
empty_constr(2),
// Closure
true.to_plutus_data(),
],
),
],
)
}
}
}
}
impl ToPlutusData for TxInInfo {
fn to_plutus_data(&self) -> PlutusData {
wrap_multiple_with_constr(
0,
vec![
self.out_ref.to_plutus_data(),
self.resolved.to_plutus_data(),
],
)
}
}
impl ToPlutusData for ScriptPurpose {
fn to_plutus_data(&self) -> PlutusData {
match self {
ScriptPurpose::Minting(policy_id) => wrap_with_constr(0, policy_id.to_plutus_data()),
ScriptPurpose::Spending(out_ref) => wrap_with_constr(1, out_ref.to_plutus_data()),
ScriptPurpose::Rewarding(stake_credential) => {
wrap_with_constr(2, stake_credential.to_plutus_data())
}
ScriptPurpose::Certifying(dcert) => wrap_with_constr(3, dcert.to_plutus_data()),
}
}
}
impl ToPlutusData for TxInfo {
fn to_plutus_data(&self) -> PlutusData {
match self {
TxInfo::V1(tx_info) => wrap_multiple_with_constr(
0,
vec![
tx_info.inputs.to_plutus_data(),
tx_info.outputs.to_plutus_data(),
tx_info.fee.to_plutus_data(),
tx_info.mint.to_plutus_data(),
tx_info.dcert.to_plutus_data(),
tx_info.wdrl.to_plutus_data(),
tx_info.valid_range.to_plutus_data(),
tx_info.signatories.to_plutus_data(),
tx_info.data.to_plutus_data(),
wrap_with_constr(0, tx_info.id.to_plutus_data()),
],
),
TxInfo::V2(tx_info) => wrap_multiple_with_constr(
0,
vec![
tx_info.inputs.to_plutus_data(),
tx_info.reference_inputs.to_plutus_data(),
tx_info.outputs.to_plutus_data(),
tx_info.fee.to_plutus_data(),
tx_info.mint.to_plutus_data(),
tx_info.dcert.to_plutus_data(),
tx_info.wdrl.to_plutus_data(),
tx_info.valid_range.to_plutus_data(),
tx_info.signatories.to_plutus_data(),
tx_info.redeemers.to_plutus_data(),
tx_info.data.to_plutus_data(),
wrap_with_constr(0, tx_info.id.to_plutus_data()),
],
),
}
}
}
impl ToPlutusData for ScriptContext {
fn to_plutus_data(&self) -> PlutusData {
wrap_multiple_with_constr(
0,
vec![self.tx_info.to_plutus_data(), self.purpose.to_plutus_data()],
)
}
}
impl ToPlutusData for bool {
fn to_plutus_data(&self) -> PlutusData {
match self {
false => empty_constr(0),
true => empty_constr(1),
}
}
}