1674 lines
76 KiB
Rust
1674 lines
76 KiB
Rust
use pallas_addresses::{
|
|
Address, ScriptHash, ShelleyDelegationPart, ShelleyPaymentPart, StakePayload,
|
|
};
|
|
use pallas_codec::{
|
|
minicbor::{bytes::ByteVec, data::Int},
|
|
utils::{AnyUInt, KeyValuePairs, MaybeIndefArray},
|
|
};
|
|
use pallas_crypto::hash::{Hash, Hasher};
|
|
use pallas_primitives::{
|
|
babbage::{
|
|
AddrKeyhash, AssetName, BigInt, Certificate, Coin, Constr, CostModel, DatumHash,
|
|
DatumOption, ExUnits, Language, Mint, MintedTx, PlutusV1Script, PlutusV2Script, PolicyId,
|
|
Redeemer, RedeemerTag, RewardAccount, Script, ScriptRef, StakeCredential, TransactionInput,
|
|
TransactionOutput, Tx, Value, Withdrawals,
|
|
},
|
|
Fragment, ToHash,
|
|
};
|
|
use pallas_traverse::{Era, MultiEraTx};
|
|
use std::{
|
|
collections::HashMap,
|
|
convert::{TryFrom, TryInto},
|
|
ops::Deref,
|
|
str::FromStr,
|
|
vec,
|
|
};
|
|
use uplc::{
|
|
ast::{FakeNamedDeBruijn, NamedDeBruijn, Program},
|
|
machine::cost_model::ExBudget,
|
|
PlutusData,
|
|
};
|
|
|
|
use crate::args::ResolvedInputOld;
|
|
|
|
pub fn get_tx_in_info_old(resolved_inputs: &[ResolvedInputOld]) -> anyhow::Result<Vec<PlutusData>> {
|
|
let mut tx_in_info = Vec::new();
|
|
|
|
for resolved_input in resolved_inputs {
|
|
let tx_out_ref = TransactionInput {
|
|
transaction_id: Hash::from_str(resolved_input.input.tx_hash.as_str())?,
|
|
index: resolved_input.input.index,
|
|
}
|
|
.to_plutus_data();
|
|
|
|
let address = Address::from_bech32(&resolved_input.output.address)?;
|
|
|
|
let lovelace = resolved_input.output.value.0;
|
|
|
|
let mut assets = resolved_input.output.value.1.clone();
|
|
|
|
assets.insert(
|
|
"".to_string(),
|
|
vec![("".to_string(), lovelace)].into_iter().collect(),
|
|
);
|
|
|
|
let tx_out = PlutusData::Constr(Constr {
|
|
tag: 0,
|
|
any_constructor: None,
|
|
fields: MaybeIndefArray::Indef(vec![
|
|
// txOutAddress
|
|
address.to_plutus_data(),
|
|
// txOutValue
|
|
PlutusData::Map(KeyValuePairs::Def(
|
|
assets
|
|
.iter()
|
|
.map(|val| {
|
|
let currency_symbol =
|
|
PlutusData::BoundedBytes(hex::decode(val.0).unwrap().into());
|
|
let token_map = PlutusData::Map(KeyValuePairs::Def(
|
|
val.1
|
|
.iter()
|
|
.map(|token| {
|
|
(
|
|
PlutusData::BoundedBytes(
|
|
token.0.as_bytes().to_vec().into(),
|
|
),
|
|
PlutusData::BigInt(BigInt::Int((*token.1).into())),
|
|
)
|
|
})
|
|
.collect(),
|
|
));
|
|
(currency_symbol, token_map)
|
|
})
|
|
.collect(),
|
|
)),
|
|
]),
|
|
});
|
|
|
|
tx_in_info.push(PlutusData::Constr(Constr {
|
|
tag: 0,
|
|
any_constructor: None,
|
|
fields: MaybeIndefArray::Indef(vec![tx_out_ref, tx_out]),
|
|
}));
|
|
}
|
|
|
|
Ok(tx_in_info)
|
|
}
|
|
|
|
// ---------------
|
|
|
|
fn wrap_with_constr(index: u64, data: PlutusData) -> PlutusData {
|
|
PlutusData::Constr(Constr {
|
|
tag: constr_index(index),
|
|
any_constructor: None,
|
|
fields: MaybeIndefArray::Indef(vec![data]),
|
|
})
|
|
}
|
|
|
|
fn wrap_multiple_with_constr(index: u64, data: Vec<PlutusData>) -> PlutusData {
|
|
PlutusData::Constr(Constr {
|
|
tag: constr_index(index),
|
|
any_constructor: None,
|
|
fields: MaybeIndefArray::Indef(data),
|
|
})
|
|
}
|
|
|
|
fn empty_constr(index: u64) -> PlutusData {
|
|
PlutusData::Constr(Constr {
|
|
tag: constr_index(index),
|
|
any_constructor: None,
|
|
fields: MaybeIndefArray::Indef(vec![]),
|
|
})
|
|
}
|
|
|
|
/// Translate constructor index to cbor tag.
|
|
fn constr_index(index: u64) -> u64 {
|
|
121 + index
|
|
}
|
|
|
|
pub trait ToPlutusData {
|
|
fn to_plutus_data(&self) -> PlutusData;
|
|
}
|
|
|
|
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.clone())).to_plutus_data()
|
|
}
|
|
ShelleyDelegationPart::Script(script_hash) => {
|
|
Some(StakeCredential::Scripthash(script_hash.clone())).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])
|
|
}
|
|
_ => 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.into())),
|
|
],
|
|
)
|
|
}
|
|
}
|
|
|
|
impl<const BYTES: usize> ToPlutusData for Hash<BYTES> {
|
|
fn to_plutus_data(&self) -> PlutusData {
|
|
PlutusData::BoundedBytes(self.to_vec().into())
|
|
}
|
|
}
|
|
|
|
impl ToPlutusData for ByteVec {
|
|
fn to_plutus_data(&self) -> PlutusData {
|
|
PlutusData::BoundedBytes(self.clone())
|
|
}
|
|
}
|
|
|
|
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> ToPlutusData for MaybeIndefArray<A> {
|
|
fn to_plutus_data(&self) -> PlutusData {
|
|
PlutusData::Array(MaybeIndefArray::Indef(
|
|
self.iter().map(|p| p.to_plutus_data()).collect(),
|
|
))
|
|
}
|
|
}
|
|
|
|
impl<K: ToPlutusData, V: ToPlutusData> ToPlutusData for KeyValuePairs<K, V> {
|
|
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(u8) => PlutusData::BigInt(BigInt::Int(Int::from(*u8))),
|
|
AnyUInt::U16(u16) => PlutusData::BigInt(BigInt::Int(Int::from(*u16))),
|
|
AnyUInt::U32(u32) => PlutusData::BigInt(BigInt::Int(Int::from(*u32))),
|
|
AnyUInt::U64(u64) => PlutusData::BigInt(BigInt::Int(Int::from(*u64))),
|
|
AnyUInt::MajorByte(u8) => PlutusData::BigInt(BigInt::Int(Int::from(*u8))), // 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.clone()))
|
|
}
|
|
}
|
|
|
|
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::from(*self)))
|
|
}
|
|
}
|
|
|
|
impl ToPlutusData for Value {
|
|
fn to_plutus_data(&self) -> PlutusData {
|
|
match self {
|
|
Value::Coin(coin) => PlutusData::Map(KeyValuePairs::Def(vec![(
|
|
PolicyId::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![(
|
|
PolicyId::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 ScriptRef {
|
|
fn to_plutus_data(&self) -> PlutusData {
|
|
match &self.0 {
|
|
Script::NativeScript(native_script) => native_script.to_hash().to_plutus_data(),
|
|
Script::PlutusV1Script(plutus_v1) => plutus_v1.to_hash().to_plutus_data(),
|
|
Script::PlutusV2Script(plutus_v2) => plutus_v2.to_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::<DatumOption>.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),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
pub struct ResolvedInput {
|
|
input: TransactionInput,
|
|
output: TransactionOutput,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
pub struct TxInInfo {
|
|
out_ref: TransactionInput,
|
|
resolved: TxOut,
|
|
}
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
pub enum TxOut {
|
|
V1(TransactionOutput),
|
|
V2(TransactionOutput),
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
pub enum ScriptPurpose {
|
|
Minting(PolicyId),
|
|
Spending(TransactionInput),
|
|
Rewarding(StakeCredential),
|
|
Certifying(Certificate),
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
pub struct TxInfoV1 {
|
|
inputs: MaybeIndefArray<TxInInfo>,
|
|
outputs: MaybeIndefArray<TxOut>,
|
|
fee: Value,
|
|
mint: Mint,
|
|
dcert: MaybeIndefArray<Certificate>,
|
|
wdrl: MaybeIndefArray<(RewardAccount, Coin)>,
|
|
valid_range: TimeRange,
|
|
signatories: MaybeIndefArray<AddrKeyhash>,
|
|
data: MaybeIndefArray<(DatumHash, PlutusData)>,
|
|
id: Hash<32>,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
pub struct TxInfoV2 {
|
|
inputs: MaybeIndefArray<TxInInfo>,
|
|
reference_inputs: MaybeIndefArray<TxInInfo>,
|
|
outputs: MaybeIndefArray<TxOut>,
|
|
fee: Value,
|
|
mint: Mint,
|
|
dcert: MaybeIndefArray<Certificate>,
|
|
wdrl: Withdrawals,
|
|
valid_range: TimeRange,
|
|
signatories: MaybeIndefArray<AddrKeyhash>,
|
|
redeemers: KeyValuePairs<ScriptPurpose, Redeemer>,
|
|
data: KeyValuePairs<DatumHash, PlutusData>,
|
|
id: Hash<32>,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
pub enum TxInfo {
|
|
V1(TxInfoV1),
|
|
V2(TxInfoV2),
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
pub struct ScriptContext {
|
|
tx_info: TxInfo,
|
|
purpose: ScriptPurpose,
|
|
}
|
|
|
|
//---- Time conversion: slot range => posix time range
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
struct TimeRange {
|
|
lower_bound: Option<u64>,
|
|
upper_bound: Option<u64>,
|
|
}
|
|
|
|
pub struct SlotConfig {
|
|
slot_length: u64,
|
|
zero_time: u64,
|
|
}
|
|
|
|
fn slot_to_begin_posix_time(slot: u64, sc: &SlotConfig) -> u64 {
|
|
let ms_after_begin = slot * sc.slot_length;
|
|
sc.zero_time + ms_after_begin
|
|
}
|
|
|
|
fn slot_range_to_posix_time_range(slot_range: TimeRange, sc: &SlotConfig) -> TimeRange {
|
|
TimeRange {
|
|
lower_bound: match slot_range.lower_bound {
|
|
Some(lower_bound) => Some(slot_to_begin_posix_time(lower_bound, sc)),
|
|
None => None,
|
|
},
|
|
upper_bound: match slot_range.upper_bound {
|
|
Some(upper_bound) => Some(slot_to_begin_posix_time(upper_bound, sc)),
|
|
None => None,
|
|
},
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
enum ScriptVersion {
|
|
V1(PlutusV1Script),
|
|
V2(PlutusV2Script),
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
enum ExecutionPurpose {
|
|
WithDatum(ScriptVersion, PlutusData), // Spending
|
|
NoDatum(ScriptVersion), // Minting, Wdrl, DCert
|
|
}
|
|
|
|
struct DataLookupTable {
|
|
datum: HashMap<DatumHash, PlutusData>,
|
|
scripts: HashMap<ScriptHash, ScriptVersion>,
|
|
}
|
|
|
|
fn get_tx_in_info_v1(
|
|
inputs: &MaybeIndefArray<TransactionInput>,
|
|
utxos: &MaybeIndefArray<ResolvedInput>,
|
|
) -> anyhow::Result<MaybeIndefArray<TxInInfo>> {
|
|
let result = inputs
|
|
.iter()
|
|
.map(|input| {
|
|
let utxo = match utxos.iter().find(|utxo| utxo.input == *input) {
|
|
Some(u) => u,
|
|
None => unreachable!("Resolved input not found."),
|
|
};
|
|
let address = Address::from_bytes(match &utxo.output {
|
|
TransactionOutput::Legacy(output) => output.address.as_ref(),
|
|
TransactionOutput::PostAlonzo(output) => output.address.as_ref(),
|
|
})
|
|
.unwrap();
|
|
|
|
match address {
|
|
Address::Byron(_) => unreachable!("Byron addresses not supported in Plutus."),
|
|
Address::Stake(_) => {
|
|
unreachable!("This is impossible. A stake address cannot lock a UTxO.")
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
match &utxo.output {
|
|
TransactionOutput::Legacy(_) => {}
|
|
TransactionOutput::PostAlonzo(output) => {
|
|
match output.datum_option {
|
|
Some(DatumOption::Data(_)) => {
|
|
unreachable!("Inline datum not allowed in PlutusV1.")
|
|
}
|
|
_ => {}
|
|
};
|
|
if output.script_ref.is_some() {
|
|
unreachable!("Reference scripts not allowed in PlutusV1.")
|
|
}
|
|
}
|
|
}
|
|
|
|
TxInInfo {
|
|
out_ref: utxo.input.clone(),
|
|
resolved: TxOut::V1(utxo.output.clone()),
|
|
}
|
|
})
|
|
.collect::<Vec<TxInInfo>>();
|
|
Ok(MaybeIndefArray::Indef(result))
|
|
}
|
|
|
|
fn get_tx_in_info_v2(
|
|
inputs: &MaybeIndefArray<TransactionInput>,
|
|
utxos: &MaybeIndefArray<ResolvedInput>,
|
|
) -> anyhow::Result<MaybeIndefArray<TxInInfo>> {
|
|
let result = inputs
|
|
.iter()
|
|
.map(|input| {
|
|
let utxo = match utxos.iter().find(|utxo| utxo.input == *input) {
|
|
Some(u) => u,
|
|
None => unreachable!("Resolved input not found."),
|
|
};
|
|
let address = Address::from_bytes(match &utxo.output {
|
|
TransactionOutput::Legacy(output) => output.address.as_ref(),
|
|
TransactionOutput::PostAlonzo(output) => output.address.as_ref(),
|
|
})
|
|
.unwrap();
|
|
|
|
match address {
|
|
Address::Byron(_) => unreachable!("Byron addresses not supported in Plutus."),
|
|
Address::Stake(_) => {
|
|
unreachable!("This is impossible. A stake address cannot lock a UTxO.")
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
TxInInfo {
|
|
out_ref: utxo.input.clone(),
|
|
resolved: TxOut::V2(utxo.output.clone()),
|
|
}
|
|
})
|
|
.collect::<Vec<TxInInfo>>();
|
|
Ok(MaybeIndefArray::Indef(result))
|
|
}
|
|
|
|
fn get_script_purpose(
|
|
redeemer: &Redeemer,
|
|
inputs: &MaybeIndefArray<TransactionInput>,
|
|
mint: &Option<Mint>,
|
|
dcert: &Option<MaybeIndefArray<Certificate>>,
|
|
wdrl: &Option<Withdrawals>,
|
|
) -> anyhow::Result<ScriptPurpose> {
|
|
// sorting according to specs section 4.1: https://hydra.iohk.io/build/18583827/download/1/alonzo-changes.pdf
|
|
let tag = redeemer.tag.clone();
|
|
let index = redeemer.index;
|
|
match tag {
|
|
RedeemerTag::Mint => {
|
|
// sort lexical by policy id
|
|
let mut policy_ids = mint
|
|
.as_ref()
|
|
.unwrap_or(&KeyValuePairs::Indef(vec![]))
|
|
.iter()
|
|
.map(|(policy_id, _)| policy_id.clone())
|
|
.collect::<Vec<ByteVec>>();
|
|
policy_ids.sort();
|
|
match policy_ids.get(index as usize) {
|
|
Some(policy_id) => Ok(ScriptPurpose::Minting(policy_id.clone())),
|
|
None => unreachable!("Script purpose not found for redeemer."),
|
|
}
|
|
}
|
|
RedeemerTag::Spend => {
|
|
// sort lexical by tx_hash and index
|
|
let mut inputs = inputs
|
|
.iter()
|
|
.map(|input| input.clone())
|
|
.collect::<Vec<TransactionInput>>();
|
|
// is this correct? Does this sort lexical from low to high? maybe get Ordering into pallas for TransactionInput?
|
|
inputs.sort_by(
|
|
|i_a, i_b| match i_a.transaction_id.cmp(&i_b.transaction_id) {
|
|
std::cmp::Ordering::Less => std::cmp::Ordering::Less,
|
|
std::cmp::Ordering::Equal => i_a.index.cmp(&i_b.index),
|
|
std::cmp::Ordering::Greater => std::cmp::Ordering::Greater,
|
|
},
|
|
);
|
|
match inputs.get(index as usize) {
|
|
Some(input) => Ok(ScriptPurpose::Spending(input.clone())),
|
|
None => unreachable!("Script purpose not found for redeemer."),
|
|
}
|
|
}
|
|
RedeemerTag::Reward => {
|
|
// sort lexical by reward account
|
|
let mut reward_accounts = wdrl
|
|
.as_ref()
|
|
.unwrap_or(&KeyValuePairs::Indef(vec![]))
|
|
.iter()
|
|
.map(|(policy_id, _)| policy_id.clone())
|
|
.collect::<Vec<RewardAccount>>();
|
|
reward_accounts.sort();
|
|
let reward_account = match reward_accounts.get(index as usize) {
|
|
Some(ra) => ra.clone(),
|
|
None => unreachable!("Script purpose not found for redeemer."),
|
|
};
|
|
let addresss = Address::from_bytes(&reward_account)?;
|
|
let credential = match addresss {
|
|
Address::Stake(stake_address) => match stake_address.payload() {
|
|
StakePayload::Script(script_hash) => {
|
|
StakeCredential::Scripthash(script_hash.clone())
|
|
}
|
|
StakePayload::Stake(_) => {
|
|
unreachable!(
|
|
"This is impossible. A key hash cannot be the hash of a script."
|
|
);
|
|
}
|
|
},
|
|
_ => unreachable!(
|
|
"This is impossible. Only shelley reward addresses can be a part of withdrawals."
|
|
),
|
|
};
|
|
Ok(ScriptPurpose::Rewarding(credential))
|
|
}
|
|
RedeemerTag::Cert => {
|
|
// sort by order given in the tx (just take it as it is basically)
|
|
match dcert
|
|
.as_ref()
|
|
.unwrap_or(&MaybeIndefArray::Indef(vec![]))
|
|
.get(index as usize)
|
|
{
|
|
Some(cert) => Ok(ScriptPurpose::Certifying(cert.clone())),
|
|
None => unreachable!("Script purpose not found for redeemer."),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_tx_info_v1(
|
|
tx: &MintedTx,
|
|
utxos: &MaybeIndefArray<ResolvedInput>,
|
|
slot_config: &SlotConfig,
|
|
) -> anyhow::Result<TxInfo> {
|
|
let body = tx.transaction_body.clone();
|
|
|
|
if body.reference_inputs.is_some() {
|
|
unreachable!("Reference inputs not allowed in PlutusV1.")
|
|
}
|
|
|
|
let inputs = get_tx_in_info_v1(&body.inputs, &utxos)?;
|
|
let outputs = MaybeIndefArray::Indef(
|
|
body.outputs
|
|
.iter()
|
|
.map(|output| TxOut::V1(output.clone()))
|
|
.collect(),
|
|
);
|
|
let fee = Value::Coin(AnyUInt::U64(body.fee));
|
|
let mint = body.mint.clone().unwrap_or(KeyValuePairs::Indef(vec![]));
|
|
let dcert = body
|
|
.certificates
|
|
.clone()
|
|
.unwrap_or(MaybeIndefArray::Indef(vec![]));
|
|
let wdrl = MaybeIndefArray::Indef(
|
|
body.withdrawals
|
|
.clone()
|
|
.unwrap_or(KeyValuePairs::Indef(vec![]))
|
|
.deref()
|
|
.clone(),
|
|
);
|
|
let valid_range = slot_range_to_posix_time_range(
|
|
TimeRange {
|
|
lower_bound: body.validity_interval_start,
|
|
upper_bound: body.ttl,
|
|
},
|
|
&slot_config,
|
|
);
|
|
let signatories = body
|
|
.required_signers
|
|
.clone()
|
|
.unwrap_or(MaybeIndefArray::Indef(vec![]));
|
|
let data = MaybeIndefArray::Indef(
|
|
tx.transaction_witness_set
|
|
.plutus_data
|
|
.as_ref()
|
|
.unwrap_or(&MaybeIndefArray::Indef(vec![]))
|
|
.iter()
|
|
.map(|d| (d.to_hash(), d.clone()))
|
|
.collect(),
|
|
);
|
|
let id = tx.transaction_body.to_hash();
|
|
|
|
Ok(TxInfo::V1(TxInfoV1 {
|
|
inputs,
|
|
outputs,
|
|
fee,
|
|
mint,
|
|
dcert,
|
|
wdrl,
|
|
valid_range,
|
|
signatories,
|
|
data,
|
|
id,
|
|
}))
|
|
}
|
|
|
|
fn get_tx_info_v2(
|
|
tx: &MintedTx,
|
|
utxos: &MaybeIndefArray<ResolvedInput>,
|
|
slot_config: &SlotConfig,
|
|
) -> anyhow::Result<TxInfo> {
|
|
let body = tx.transaction_body.clone();
|
|
|
|
let inputs = get_tx_in_info_v2(&body.inputs, &utxos)?;
|
|
let reference_inputs = get_tx_in_info_v2(
|
|
&body
|
|
.reference_inputs
|
|
.clone()
|
|
.unwrap_or(MaybeIndefArray::Indef(vec![])),
|
|
&utxos,
|
|
)?;
|
|
let outputs = MaybeIndefArray::Indef(
|
|
body.outputs
|
|
.iter()
|
|
.map(|output| TxOut::V2(output.clone()))
|
|
.collect(),
|
|
);
|
|
let fee = Value::Coin(AnyUInt::U64(body.fee));
|
|
let mint = body.mint.clone().unwrap_or(KeyValuePairs::Indef(vec![]));
|
|
let dcert = body
|
|
.certificates
|
|
.clone()
|
|
.unwrap_or(MaybeIndefArray::Indef(vec![]));
|
|
let wdrl = body
|
|
.withdrawals
|
|
.clone()
|
|
.unwrap_or(KeyValuePairs::Indef(vec![]));
|
|
let valid_range = slot_range_to_posix_time_range(
|
|
TimeRange {
|
|
lower_bound: body.validity_interval_start,
|
|
upper_bound: body.ttl,
|
|
},
|
|
&slot_config,
|
|
);
|
|
let signatories = body
|
|
.required_signers
|
|
.clone()
|
|
.unwrap_or(MaybeIndefArray::Indef(vec![]));
|
|
let redeemers = KeyValuePairs::Indef(
|
|
tx.transaction_witness_set
|
|
.redeemer
|
|
.as_ref()
|
|
.unwrap_or(&MaybeIndefArray::Indef(vec![]))
|
|
.iter()
|
|
.map(|r| {
|
|
(
|
|
get_script_purpose(
|
|
&r,
|
|
&tx.transaction_body.inputs,
|
|
&tx.transaction_body.mint,
|
|
&tx.transaction_body.certificates,
|
|
&tx.transaction_body.withdrawals,
|
|
)
|
|
.unwrap(),
|
|
r.clone(),
|
|
)
|
|
})
|
|
.collect(),
|
|
);
|
|
let data = KeyValuePairs::Indef(
|
|
tx.transaction_witness_set
|
|
.plutus_data
|
|
.as_ref()
|
|
.unwrap_or(&MaybeIndefArray::Indef(vec![]))
|
|
.iter()
|
|
.map(|d| (d.to_hash(), d.clone()))
|
|
.collect(),
|
|
);
|
|
let id = tx.transaction_body.to_hash();
|
|
|
|
Ok(TxInfo::V2(TxInfoV2 {
|
|
inputs,
|
|
reference_inputs,
|
|
outputs,
|
|
fee,
|
|
mint,
|
|
dcert,
|
|
wdrl,
|
|
valid_range,
|
|
signatories,
|
|
redeemers,
|
|
data,
|
|
id,
|
|
}))
|
|
}
|
|
|
|
fn get_execution_purpose(
|
|
utxos: &MaybeIndefArray<ResolvedInput>,
|
|
script_purpose: &ScriptPurpose,
|
|
lookup_table: &DataLookupTable,
|
|
) -> ExecutionPurpose {
|
|
match script_purpose {
|
|
ScriptPurpose::Minting(policy_id) => {
|
|
let policy_id_array: [u8; 28] = policy_id.to_vec().try_into().unwrap();
|
|
let hash = Hash::from(policy_id_array);
|
|
|
|
let script = match lookup_table.scripts.get(&hash) {
|
|
Some(s) => s.clone(),
|
|
None => unreachable!("Missing required scripts.")
|
|
};
|
|
ExecutionPurpose::NoDatum(script.clone())
|
|
}
|
|
ScriptPurpose::Spending(out_ref) => {
|
|
let utxo = utxos.iter().find(|utxo| utxo.input == *out_ref).unwrap();
|
|
match &utxo.output {
|
|
TransactionOutput::Legacy(output) => {
|
|
let address = Address::from_bytes(&output.address).unwrap();
|
|
match address {
|
|
Address::Shelley(shelley_address) => {
|
|
|
|
let script = match lookup_table
|
|
.scripts
|
|
.get(&shelley_address.payment().as_hash()) {
|
|
Some(s) => s.clone(),
|
|
None => unreachable!("Missing required scripts.")
|
|
};
|
|
|
|
|
|
|
|
let datum = match lookup_table.datum.get(&output.datum_hash.unwrap_or_else(|| unreachable!("Missing datum hash in input."))) {
|
|
Some(d) => d.clone(),
|
|
None => unreachable!("Missing datum in witness set.")
|
|
};
|
|
|
|
ExecutionPurpose::WithDatum(script.clone(), datum.clone())
|
|
}
|
|
_ => unreachable!(
|
|
"This is impossible. Only shelley addresses can contain a script hash."
|
|
),
|
|
}
|
|
}
|
|
TransactionOutput::PostAlonzo(output) => {
|
|
let address = Address::from_bytes(&output.address).unwrap();
|
|
match address {
|
|
Address::Shelley(shelley_address) => {
|
|
|
|
let script = match lookup_table
|
|
.scripts
|
|
.get(&shelley_address.payment().as_hash()) {
|
|
Some(s) => s.clone(),
|
|
None => unreachable!("Missing required scripts.")
|
|
};
|
|
|
|
|
|
let datum = match &output.datum_option {
|
|
Some(DatumOption::Hash(hash)) => {
|
|
lookup_table.datum.get(&hash).unwrap().clone()
|
|
}
|
|
Some(DatumOption::Data(data)) => data.0.clone(),
|
|
_ => unreachable!( "Missing datum hash or inline datum in input."),
|
|
};
|
|
|
|
ExecutionPurpose::WithDatum(script.clone(), datum)
|
|
}
|
|
_ => unreachable!(
|
|
"This is impossible. Only shelley addresses can contain a script hash."
|
|
),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ScriptPurpose::Rewarding(stake_credential) => {
|
|
let script_hash = match stake_credential {
|
|
StakeCredential::Scripthash(hash) => hash.clone(),
|
|
_ => unreachable!("This is impossible. A key hash cannot be the hash of a script."),
|
|
};
|
|
|
|
let script = match lookup_table.scripts.get(&script_hash) {
|
|
Some(s) => s.clone(),
|
|
None => unreachable!("Missing required scripts.")
|
|
};
|
|
|
|
ExecutionPurpose::NoDatum(script)
|
|
}
|
|
ScriptPurpose::Certifying(cert) => match cert {
|
|
// StakeRegistration doesn't require a witness from a stake key/script. So I assume it doesn't need to be handled in Plutus either?
|
|
Certificate::StakeDeregistration(stake_credential) => {
|
|
let script_hash = match stake_credential {
|
|
StakeCredential::Scripthash(hash) => hash.clone(),
|
|
_ => unreachable!(
|
|
"This is impossible. A key hash cannot be the hash of a script."
|
|
),
|
|
};
|
|
|
|
let script = match lookup_table.scripts.get(&script_hash) {
|
|
Some(s) => s.clone(),
|
|
None => unreachable!("Missing required scripts.")
|
|
};
|
|
|
|
ExecutionPurpose::NoDatum(script)
|
|
}
|
|
Certificate::StakeDelegation(stake_credential, _) => {
|
|
let script_hash = match stake_credential {
|
|
StakeCredential::Scripthash(hash) => hash.clone(),
|
|
_ => unreachable!(
|
|
"This is impossible. A key hash cannot be the hash of a script."
|
|
),
|
|
};
|
|
|
|
let script = match lookup_table.scripts.get(&script_hash) {
|
|
Some(s) => s.clone(),
|
|
None => unreachable!("Missing required scripts.")
|
|
};
|
|
|
|
ExecutionPurpose::NoDatum(script)
|
|
}
|
|
_ => unreachable!("This is impossible. Only stake deregistration and stake delegation are valid script purposes."),
|
|
},
|
|
}
|
|
}
|
|
|
|
fn get_script_and_datum_lookup_table(
|
|
tx: &MintedTx,
|
|
utxos: &MaybeIndefArray<ResolvedInput>,
|
|
) -> DataLookupTable {
|
|
let mut datum = HashMap::new();
|
|
let mut scripts = HashMap::new();
|
|
|
|
// discovery in witness set
|
|
|
|
let plutus_data_witnesses = tx
|
|
.transaction_witness_set
|
|
.plutus_data
|
|
.clone()
|
|
.unwrap_or(MaybeIndefArray::Indef(vec![]));
|
|
|
|
let scripts_v1_witnesses = tx
|
|
.transaction_witness_set
|
|
.plutus_v1_script
|
|
.clone()
|
|
.unwrap_or(MaybeIndefArray::Indef(vec![]));
|
|
|
|
let scripts_v2_witnesses = tx
|
|
.transaction_witness_set
|
|
.plutus_v2_script
|
|
.clone()
|
|
.unwrap_or(MaybeIndefArray::Indef(vec![]));
|
|
|
|
for plutus_data in plutus_data_witnesses.iter() {
|
|
datum.insert(plutus_data.to_hash(), plutus_data.clone());
|
|
}
|
|
|
|
for script in scripts_v1_witnesses.iter() {
|
|
// scripts.insert(script.to_hash(), ScriptVersion::PlutusV1(script.clone())); // TODO: fix hashing bug in pallas
|
|
|
|
let mut prefixed_script: Vec<u8> = vec![0x01];
|
|
prefixed_script.extend(script.0.iter());
|
|
|
|
let hash = Hasher::<224>::hash(&prefixed_script);
|
|
scripts.insert(hash, ScriptVersion::V1(script.clone()));
|
|
}
|
|
|
|
for script in scripts_v2_witnesses.iter() {
|
|
// scripts.insert(script.to_hash(), ScriptVersion::PlutusV2(script.clone())); // TODO: fix hashing bug in pallas
|
|
|
|
let mut prefixed_script: Vec<u8> = vec![0x02];
|
|
prefixed_script.extend(script.0.iter());
|
|
|
|
let hash = Hasher::<224>::hash(&prefixed_script);
|
|
scripts.insert(hash, ScriptVersion::V2(script.clone()));
|
|
}
|
|
|
|
// discovery in utxos (script ref)
|
|
|
|
for utxo in utxos.iter() {
|
|
match &utxo.output {
|
|
TransactionOutput::Legacy(_) => {}
|
|
TransactionOutput::PostAlonzo(output) => match &output.script_ref {
|
|
Some(script) => match &script.0 {
|
|
Script::PlutusV1Script(v1) => {
|
|
// scripts.insert(v1.to_hash(), ScriptVersion::PlutusV1(v1.clone())); // TODO: fix hashing bug in pallas
|
|
let mut prefixed_script: Vec<u8> = vec![0x01];
|
|
prefixed_script.extend(v1.0.iter());
|
|
|
|
let hash = Hasher::<224>::hash(&prefixed_script);
|
|
scripts.insert(hash, ScriptVersion::V1(v1.clone()));
|
|
}
|
|
Script::PlutusV2Script(v2) => {
|
|
// scripts.insert(v2.to_hash(), ScriptVersion::PlutusV2(v2.clone())); // TODO: fix hashing bug in pallas
|
|
|
|
let mut prefixed_script: Vec<u8> = vec![0x02];
|
|
prefixed_script.extend(v2.0.iter());
|
|
|
|
let hash = Hasher::<224>::hash(&prefixed_script);
|
|
scripts.insert(hash, ScriptVersion::V2(v2.clone()));
|
|
}
|
|
_ => {}
|
|
},
|
|
_ => {}
|
|
},
|
|
}
|
|
}
|
|
|
|
DataLookupTable { datum, scripts }
|
|
}
|
|
|
|
fn eval_redeemer(
|
|
tx: &MintedTx,
|
|
utxos: &MaybeIndefArray<ResolvedInput>,
|
|
slot_config: &SlotConfig,
|
|
redeemer: &Redeemer,
|
|
lookup_table: &DataLookupTable,
|
|
) -> anyhow::Result<Redeemer> {
|
|
let purpose = get_script_purpose(
|
|
redeemer,
|
|
&tx.transaction_body.inputs,
|
|
&tx.transaction_body.mint,
|
|
&tx.transaction_body.certificates,
|
|
&tx.transaction_body.withdrawals,
|
|
)?;
|
|
|
|
let execution_purpose: ExecutionPurpose = get_execution_purpose(utxos, &purpose, lookup_table);
|
|
|
|
match execution_purpose {
|
|
ExecutionPurpose::WithDatum(script_version, datum) => match script_version {
|
|
ScriptVersion::V1(script) => {
|
|
let tx_info = get_tx_info_v1(tx, utxos, slot_config)?;
|
|
let script_context = ScriptContext { tx_info, purpose };
|
|
|
|
let program: Program<NamedDeBruijn> = {
|
|
let mut buffer = Vec::new();
|
|
|
|
let prog = Program::<FakeNamedDeBruijn>::from_cbor(&script.0, &mut buffer)?;
|
|
|
|
prog.into()
|
|
};
|
|
|
|
let result = program
|
|
.apply_data(datum.clone())
|
|
.apply_data(redeemer.data.clone())
|
|
.apply_data(script_context.to_plutus_data())
|
|
.eval();
|
|
|
|
match result.0 {
|
|
Ok(_) => {}
|
|
Err(err) => unreachable!("Error in Plutus core."), // TODO: Add the actual error message
|
|
}
|
|
|
|
let new_redeemer = Redeemer {
|
|
tag: redeemer.tag.clone(),
|
|
index: redeemer.index,
|
|
data: redeemer.data.clone(),
|
|
ex_units: ExUnits {
|
|
mem: (ExBudget::default().mem - result.1.mem) as u32,
|
|
steps: (ExBudget::default().cpu - result.1.cpu) as u64,
|
|
},
|
|
};
|
|
|
|
Ok(new_redeemer)
|
|
}
|
|
ScriptVersion::V2(script) => {
|
|
let tx_info = get_tx_info_v2(tx, utxos, slot_config)?;
|
|
let script_context = ScriptContext { tx_info, purpose };
|
|
|
|
let program: Program<NamedDeBruijn> = {
|
|
let mut buffer = Vec::new();
|
|
|
|
let prog = Program::<FakeNamedDeBruijn>::from_cbor(&script.0, &mut buffer)?;
|
|
|
|
prog.into()
|
|
};
|
|
|
|
let result = program
|
|
.apply_data(datum.clone())
|
|
.apply_data(redeemer.data.clone())
|
|
.apply_data(script_context.to_plutus_data())
|
|
.eval();
|
|
|
|
match result.0 {
|
|
Ok(_) => {}
|
|
Err(err) => unreachable!("Error in Plutus core."), // TODO: Add the actual error message
|
|
}
|
|
|
|
let new_redeemer = Redeemer {
|
|
tag: redeemer.tag.clone(),
|
|
index: redeemer.index,
|
|
data: redeemer.data.clone(),
|
|
ex_units: ExUnits {
|
|
mem: (ExBudget::default().mem - result.1.mem) as u32,
|
|
steps: (ExBudget::default().cpu - result.1.cpu) as u64,
|
|
},
|
|
};
|
|
|
|
Ok(new_redeemer)
|
|
}
|
|
},
|
|
ExecutionPurpose::NoDatum(script_version) => match script_version {
|
|
ScriptVersion::V1(script) => {
|
|
let tx_info = get_tx_info_v1(tx, utxos, slot_config)?;
|
|
let script_context = ScriptContext { tx_info, purpose };
|
|
|
|
let program: Program<NamedDeBruijn> = {
|
|
let mut buffer = Vec::new();
|
|
|
|
let prog = Program::<FakeNamedDeBruijn>::from_cbor(&script.0, &mut buffer)?;
|
|
|
|
prog.into()
|
|
};
|
|
|
|
let result = program
|
|
.apply_data(redeemer.data.clone())
|
|
.apply_data(script_context.to_plutus_data())
|
|
.eval();
|
|
|
|
match result.0 {
|
|
Ok(_) => {}
|
|
Err(err) => unreachable!("Error in Plutus core."), // TODO: Add the actual error message
|
|
}
|
|
|
|
let new_redeemer = Redeemer {
|
|
tag: redeemer.tag.clone(),
|
|
index: redeemer.index,
|
|
data: redeemer.data.clone(),
|
|
ex_units: ExUnits {
|
|
mem: (ExBudget::default().mem - result.1.mem) as u32,
|
|
steps: (ExBudget::default().cpu - result.1.cpu) as u64,
|
|
},
|
|
};
|
|
|
|
Ok(new_redeemer)
|
|
}
|
|
ScriptVersion::V2(script) => {
|
|
let tx_info = get_tx_info_v2(tx, utxos, slot_config)?;
|
|
let script_context = ScriptContext { tx_info, purpose };
|
|
|
|
let program: Program<NamedDeBruijn> = {
|
|
let mut buffer = Vec::new();
|
|
|
|
let prog = Program::<FakeNamedDeBruijn>::from_cbor(&script.0, &mut buffer)?;
|
|
|
|
prog.into()
|
|
};
|
|
|
|
let result = program
|
|
.apply_data(redeemer.data.clone())
|
|
.apply_data(script_context.to_plutus_data())
|
|
.eval();
|
|
|
|
match result.0 {
|
|
Ok(_) => {}
|
|
Err(err) => unreachable!("Error in Plutus core."), // TODO: Add the actual error message
|
|
}
|
|
|
|
let new_redeemer = Redeemer {
|
|
tag: redeemer.tag.clone(),
|
|
index: redeemer.index,
|
|
data: redeemer.data.clone(),
|
|
ex_units: ExUnits {
|
|
mem: (ExBudget::default().mem - result.1.mem) as u32,
|
|
steps: (ExBudget::default().cpu - result.1.cpu) as u64,
|
|
},
|
|
};
|
|
|
|
Ok(new_redeemer)
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
fn eval_tx(
|
|
tx: &MintedTx,
|
|
utxos: &MaybeIndefArray<ResolvedInput>,
|
|
//TODO: costMdls
|
|
slot_config: &SlotConfig,
|
|
) -> anyhow::Result<MaybeIndefArray<Redeemer>> {
|
|
let redeemers = tx.transaction_witness_set.redeemer.as_ref();
|
|
|
|
let lookup_table = get_script_and_datum_lookup_table(tx, utxos);
|
|
|
|
match redeemers {
|
|
Some(rs) => {
|
|
let mut collected_redeemers = vec![];
|
|
for redeemer in rs.iter() {
|
|
collected_redeemers.push(eval_redeemer(
|
|
tx,
|
|
utxos,
|
|
slot_config,
|
|
&redeemer,
|
|
&lookup_table,
|
|
)?)
|
|
}
|
|
Ok(MaybeIndefArray::Indef(collected_redeemers))
|
|
}
|
|
None => Ok(MaybeIndefArray::Indef(vec![])),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use pallas_codec::utils::MaybeIndefArray;
|
|
use pallas_primitives::{
|
|
babbage::{TransactionInput, TransactionOutput},
|
|
Fragment, ToHash,
|
|
};
|
|
use pallas_traverse::{Era, MultiEraTx};
|
|
use uplc::PlutusData;
|
|
|
|
use super::{eval_tx, ResolvedInput, SlotConfig, TxInInfo};
|
|
|
|
#[test]
|
|
fn test_eval() {
|
|
/*
|
|
|
|
PlutusV2
|
|
|
|
{-# INLINEABLE mintTestValidator #-}
|
|
mintTestValidator :: () -> Api.ScriptContext -> Bool
|
|
mintTestValidator _ ctx = Api.txInfoFee txInfo == Api.txInfoFee txInfo && (case Api.txInfoSignatories txInfo of [] -> True)
|
|
|
|
where
|
|
txInfo :: Api.TxInfo
|
|
txInfo = Api.scriptContextTxInfo ctx */
|
|
|
|
let tx_bytes = hex::decode("84a80081825820975c17a4fed0051be622328efa548e206657d2b65a19224bf6ff8132571e6a5002018282581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f67235821a000f41f0a1581cc4f241450001af08f3ddbaf9335db79883cbcd81071b8e3508de3055a1400a82581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a0084192f021a00053b6109a1581cc4f241450001af08f3ddbaf9335db79883cbcd81071b8e3508de3055a1400a0b5820b4f96b0acec8beff2adededa8ba317bcac92174f0f65ccefe569b9a6aac7375a0d818258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d011082581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af0cdfa2111a0007d912a3008182582031ae74f8058527afb305d7495b10a99422d9337fc199e1f28044f2c477a0f9465840b8b97b7c3b4e19ecfc2fcd9884ee53a35887ee6e4d36901b9ecbac3fe032d7e8a4358305afa573a86396e378255651ed03501906e9def450e588d4bb36f42a050581840100d87980821a000b68081a0cf3a5bf06815909b25909af010000323322323232323232323232323232323232323232332232323232323232323233223232223232533533223233025323233355300f1200135028502623500122333553012120013502b50292350012233350012330314800000488cc0c80080048cc0c400520000013355300e1200123500122335501c0023335001233553012120012350012233550200023550140010012233355500f0150020012335530121200123500122335502000235501300100133355500a01000200130105002300f5001533532350012222222222220045001102a2216135001220023333573466e1cd55ce9baa0044800080808c98c8080cd5ce01081000f1999ab9a3370e6aae7540092000233221233001003002323232323232323232323232323333573466e1cd55cea8062400046666666666664444444444442466666666666600201a01801601401201000e00c00a00800600466a03803a6ae854030cd4070074d5d0a80599a80e00f1aba1500a3335502075ca03e6ae854024ccd54081d7280f9aba1500833501c02835742a00e666aa040052eb4d5d0a8031919191999ab9a3370e6aae75400920002332212330010030023232323333573466e1cd55cea8012400046644246600200600466a066eb4d5d0a801181a1aba135744a004464c6406c66ae700dc0d80d04d55cf280089baa00135742a0046464646666ae68cdc39aab9d5002480008cc8848cc00400c008cd40cdd69aba150023034357426ae8940088c98c80d8cd5ce01b81b01a09aab9e5001137540026ae84d5d1280111931901919ab9c033032030135573ca00226ea8004d5d0a80299a80e3ae35742a008666aa04004a40026ae85400cccd54081d710009aba150023027357426ae8940088c98c80b8cd5ce01781701609aba25001135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba150023017357426ae8940088c98c8080cd5ce01081000f080f89931900f99ab9c4901035054350001f135573ca00226ea8004444888ccd54c010480054040cd54c01c480048d400488cd54054008d54024004ccd54c0104800488d4008894cd4ccd54c03048004c8cd409c88ccd400c88008008004d40048800448cc004894cd400840b040040a48d400488cc028008014018400c4cd405001000d4044004cd54c01c480048d400488c8cd5405800cc004014c8004d540a4894cd40044d5402800c884d4008894cd4cc03000802044888cc0080280104c01800c008c8004d5408888448894cd40044008884cc014008ccd54c01c480040140100044484888c00c0104484888c004010c8004d5407c8844894cd400454038884cd403cc010008cd54c01848004010004c8004d5407888448894cd40044d400c88004884ccd401488008c010008ccd54c01c4800401401000488ccd5cd19b8f00200101e01d2350012222222222220091232230023758002640026aa038446666aae7c004940288cd4024c010d5d080118019aba2002015232323333573466e1cd55cea80124000466442466002006004601a6ae854008c014d5d09aba2500223263201533573802c02a02626aae7940044dd50009191919191999ab9a3370e6aae75401120002333322221233330010050040030023232323333573466e1cd55cea80124000466442466002006004602c6ae854008cd4040054d5d09aba2500223263201a33573803603403026aae7940044dd50009aba150043335500875ca00e6ae85400cc8c8c8cccd5cd19b875001480108c84888c008010d5d09aab9e500323333573466e1d4009200223212223001004375c6ae84d55cf280211999ab9a3370ea00690001091100191931900e19ab9c01d01c01a019018135573aa00226ea8004d5d0a80119a8063ae357426ae8940088c98c8058cd5ce00b80b00a09aba25001135744a00226aae7940044dd5000899aa800bae75a224464460046eac004c8004d5406488c8cccd55cf80112804119a80399aa80498031aab9d5002300535573ca00460086ae8800c04c4d5d08008891001091091198008020018891091980080180109119191999ab9a3370ea0029000119091180100198029aba135573ca00646666ae68cdc3a801240044244002464c6402066ae700440400380344d55cea80089baa001232323333573466e1d400520062321222230040053007357426aae79400c8cccd5cd19b875002480108c848888c008014c024d5d09aab9e500423333573466e1d400d20022321222230010053007357426aae7940148cccd5cd19b875004480008c848888c00c014dd71aba135573ca00c464c6402066ae7004404003803403002c4d55cea80089baa001232323333573466e1cd55cea80124000466442466002006004600a6ae854008dd69aba135744a004464c6401866ae700340300284d55cf280089baa0012323333573466e1cd55cea800a400046eb8d5d09aab9e500223263200a33573801601401026ea80048c8c8c8c8c8cccd5cd19b8750014803084888888800c8cccd5cd19b875002480288488888880108cccd5cd19b875003480208cc8848888888cc004024020dd71aba15005375a6ae84d5d1280291999ab9a3370ea00890031199109111111198010048041bae35742a00e6eb8d5d09aba2500723333573466e1d40152004233221222222233006009008300c35742a0126eb8d5d09aba2500923333573466e1d40192002232122222223007008300d357426aae79402c8cccd5cd19b875007480008c848888888c014020c038d5d09aab9e500c23263201333573802802602202001e01c01a01801626aae7540104d55cf280189aab9e5002135573ca00226ea80048c8c8c8c8cccd5cd19b875001480088ccc888488ccc00401401000cdd69aba15004375a6ae85400cdd69aba135744a00646666ae68cdc3a80124000464244600400660106ae84d55cf280311931900619ab9c00d00c00a009135573aa00626ae8940044d55cf280089baa001232323333573466e1d400520022321223001003375c6ae84d55cf280191999ab9a3370ea004900011909118010019bae357426aae7940108c98c8024cd5ce00500480380309aab9d50011375400224464646666ae68cdc3a800a40084244400246666ae68cdc3a8012400446424446006008600c6ae84d55cf280211999ab9a3370ea00690001091100111931900519ab9c00b00a008007006135573aa00226ea80048c8cccd5cd19b8750014800880348cccd5cd19b8750024800080348c98c8018cd5ce00380300200189aab9d37540029309000a4810350543100112330010020072253350021001100612335002223335003220020020013500122001122123300100300222333573466e1c00800401000c488008488004448c8c00400488cc00cc008008005f5f6").unwrap();
|
|
|
|
let raw_inputs = hex::decode("84825820b16778c9cf065d9efeefe37ec269b4fc5107ecdbd0dd6bf3274b224165c2edd9008258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d01825820975c17a4fed0051be622328efa548e206657d2b65a19224bf6ff8132571e6a500282582018f86700660fc88d0370a8f95ea58f75507e6b27a18a17925ad3b1777eb0d77600").unwrap();
|
|
let raw_outputs = hex::decode("8482581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f67235821a000f8548a1581c15be994a64bdb79dde7fe080d8e7ff81b33a9e4860e9ee0d857a8e85a144576177610182581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af14b8b482581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a0098968082581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a00acd8c6").unwrap();
|
|
|
|
let inputs = MaybeIndefArray::<TransactionInput>::decode_fragment(&raw_inputs).unwrap();
|
|
let outputs = MaybeIndefArray::<TransactionOutput>::decode_fragment(&raw_outputs).unwrap();
|
|
|
|
let utxos: MaybeIndefArray<ResolvedInput> = MaybeIndefArray::Indef(
|
|
inputs
|
|
.iter()
|
|
.zip(outputs.iter())
|
|
.map(|(input, output)| ResolvedInput {
|
|
input: input.clone(),
|
|
output: output.clone(),
|
|
})
|
|
.collect(),
|
|
);
|
|
|
|
let slot_config = SlotConfig {
|
|
zero_time: 1660003200000, // Preview network
|
|
slot_length: 1000,
|
|
};
|
|
|
|
let multi_era_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes)
|
|
.or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes))
|
|
.unwrap();
|
|
match multi_era_tx {
|
|
MultiEraTx::Babbage(tx) => {
|
|
let redeemers = eval_tx(&tx, &utxos, &slot_config).unwrap();
|
|
|
|
println!("{:?}", redeemers.len());
|
|
}
|
|
_ => unreachable!(),
|
|
};
|
|
}
|
|
|
|
#[test]
|
|
fn test_eval_1() {
|
|
/*
|
|
|
|
PlutusV2
|
|
|
|
{-# INLINEABLE mintTestValidator #-}
|
|
mintTestValidator :: () -> Api.ScriptContext -> Bool
|
|
mintTestValidator _ ctx = Api.txInfoFee txInfo == Api.txInfoFee txInfo
|
|
|
|
where
|
|
txInfo :: Api.TxInfo
|
|
txInfo = Api.scriptContextTxInfo ctx */
|
|
|
|
let tx_bytes = hex::decode("84a800818258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d01018282581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f67235821a000f41f0a1581c88c9dfd60601e22509d58b904c2730fe2bdef6a52a41a6f376b0ba94a1400a82581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af004152021a0005357209a1581c88c9dfd60601e22509d58b904c2730fe2bdef6a52a41a6f376b0ba94a1400a0b5820ff1a62ad8cb2d73ffb8687471e2c99b48bf3b067966a7ea9285f95adcee708a20d818258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d011082581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af0ce889111a0007d02ba3008182582031ae74f8058527afb305d7495b10a99422d9337fc199e1f28044f2c477a0f9465840b3dde25e3a2b825d3f120955211e722cc4a7c65fa67697076f2725a2ed0adec0d4bfc742934fe7c29d475bb0630aed1b1cdcf5fac9e06d84976455a661b0dc080581840100d87980821a000b46701a0cd5772f068159099a59099701000032332232323232323232323232323232323232323322323232323232323232332232322232325335332232323233355300f1200135027502623500122333553012120013502a50292350012233350012330304800000488cc0c40080048cc0c000520000013355300e1200123500122335501c0023335001233553012120012350012233550200023550140010012233355500f0150020012335530121200123500122335502000235501300100133355500a01000200130105002300f5001135001220023333573466e1cd55ce9baa0044800080808c98c8080cd5ce01081000f1999ab9a3370e6aae7540092000233221233001003002323232323232323232323232323333573466e1cd55cea8062400046666666666664444444444442466666666666600201a01801601401201000e00c00a00800600466a03803a6ae854030cd4070074d5d0a80599a80e00f1aba1500a3335502075ca03e6ae854024ccd54081d7280f9aba1500833501c02835742a00e666aa040052eb4d5d0a8031919191999ab9a3370e6aae75400920002332212330010030023232323333573466e1cd55cea8012400046644246600200600466a066eb4d5d0a801181a1aba135744a004464c6406c66ae700dc0d80d04d55cf280089baa00135742a0046464646666ae68cdc39aab9d5002480008cc8848cc00400c008cd40cdd69aba150023034357426ae8940088c98c80d8cd5ce01b81b01a09aab9e5001137540026ae84d5d1280111931901919ab9c033032030135573ca00226ea8004d5d0a80299a80e3ae35742a008666aa04004a40026ae85400cccd54081d710009aba150023027357426ae8940088c98c80b8cd5ce01781701609aba25001135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba150023017357426ae8940088c98c8080cd5ce01081000f080f89931900f99ab9c491035054350001f135573ca00226ea8004444888ccd54c010480054040cd54c01c480048d400488cd54054008d54024004ccd54c0104800488d4008894cd4ccd54c03048004c8cd409888ccd400c88008008004d40048800448cc004894cd400840ac40040a08d400488cc028008014018400c4cd405001000d4044004cd54c01c480048d400488c8cd5405800cc004014c8004d540a0894cd40044d5402800c884d4008894cd4cc03000802044888cc0080280104c01800c008c8004d5408488448894cd40044008884cc014008ccd54c01c480040140100044484888c00c0104484888c004010c8004d540788844894cd400454038884cd403cc010008cd54c01848004010004c8004d5407488448894cd40044d400c88004884ccd401488008c010008ccd54c01c4800401401000488ccd5cd19b8f00200101d01c2350012222222222220091232230023758002640026aa036446666aae7c004940288cd4024c010d5d080118019aba2002015232323333573466e1cd55cea80124000466442466002006004601a6ae854008c014d5d09aba2500223263201533573802c02a02626aae7940044dd50009191919191999ab9a3370e6aae75401120002333322221233330010050040030023232323333573466e1cd55cea80124000466442466002006004602c6ae854008cd4040054d5d09aba2500223263201a33573803603403026aae7940044dd50009aba150043335500875ca00e6ae85400cc8c8c8cccd5cd19b875001480108c84888c008010d5d09aab9e500323333573466e1d4009200223212223001004375c6ae84d55cf280211999ab9a3370ea00690001091100191931900e19ab9c01d01c01a019018135573aa00226ea8004d5d0a80119a8063ae357426ae8940088c98c8058cd5ce00b80b00a09aba25001135744a00226aae7940044dd5000899aa800bae75a224464460046eac004c8004d5406088c8cccd55cf80112804119a80399aa80498031aab9d5002300535573ca00460086ae8800c04c4d5d08008891001091091198008020018891091980080180109119191999ab9a3370ea0029000119091180100198029aba135573ca00646666ae68cdc3a801240044244002464c6402066ae700440400380344d55cea80089baa001232323333573466e1d400520062321222230040053007357426aae79400c8cccd5cd19b875002480108c848888c008014c024d5d09aab9e500423333573466e1d400d20022321222230010053007357426aae7940148cccd5cd19b875004480008c848888c00c014dd71aba135573ca00c464c6402066ae7004404003803403002c4d55cea80089baa001232323333573466e1cd55cea80124000466442466002006004600a6ae854008dd69aba135744a004464c6401866ae700340300284d55cf280089baa0012323333573466e1cd55cea800a400046eb8d5d09aab9e500223263200a33573801601401026ea80048c8c8c8c8c8cccd5cd19b8750014803084888888800c8cccd5cd19b875002480288488888880108cccd5cd19b875003480208cc8848888888cc004024020dd71aba15005375a6ae84d5d1280291999ab9a3370ea00890031199109111111198010048041bae35742a00e6eb8d5d09aba2500723333573466e1d40152004233221222222233006009008300c35742a0126eb8d5d09aba2500923333573466e1d40192002232122222223007008300d357426aae79402c8cccd5cd19b875007480008c848888888c014020c038d5d09aab9e500c23263201333573802802602202001e01c01a01801626aae7540104d55cf280189aab9e5002135573ca00226ea80048c8c8c8c8cccd5cd19b875001480088ccc888488ccc00401401000cdd69aba15004375a6ae85400cdd69aba135744a00646666ae68cdc3a80124000464244600400660106ae84d55cf280311931900619ab9c00d00c00a009135573aa00626ae8940044d55cf280089baa001232323333573466e1d400520022321223001003375c6ae84d55cf280191999ab9a3370ea004900011909118010019bae357426aae7940108c98c8024cd5ce00500480380309aab9d50011375400224464646666ae68cdc3a800a40084244400246666ae68cdc3a8012400446424446006008600c6ae84d55cf280211999ab9a3370ea00690001091100111931900519ab9c00b00a008007006135573aa00226ea80048c8cccd5cd19b8750014800880308cccd5cd19b8750024800080308c98c8018cd5ce00380300200189aab9d37540029309000a4810350543100112330012253350021001100700612335002223335003220020020013500122001122123300100300222333573466e1c00800401000c488008488004448c8c00400488cc00cc0080080041f5f6").unwrap();
|
|
|
|
let raw_inputs = hex::decode("84825820b16778c9cf065d9efeefe37ec269b4fc5107ecdbd0dd6bf3274b224165c2edd9008258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d01825820975c17a4fed0051be622328efa548e206657d2b65a19224bf6ff8132571e6a500282582018f86700660fc88d0370a8f95ea58f75507e6b27a18a17925ad3b1777eb0d77600").unwrap();
|
|
let raw_outputs = hex::decode("8482581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f67235821a000f8548a1581c15be994a64bdb79dde7fe080d8e7ff81b33a9e4860e9ee0d857a8e85a144576177610182581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af14b8b482581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a0098968082581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a00acd8c6").unwrap();
|
|
|
|
let inputs = MaybeIndefArray::<TransactionInput>::decode_fragment(&raw_inputs).unwrap();
|
|
let outputs = MaybeIndefArray::<TransactionOutput>::decode_fragment(&raw_outputs).unwrap();
|
|
|
|
let utxos: MaybeIndefArray<ResolvedInput> = MaybeIndefArray::Indef(
|
|
inputs
|
|
.iter()
|
|
.zip(outputs.iter())
|
|
.map(|(input, output)| ResolvedInput {
|
|
input: input.clone(),
|
|
output: output.clone(),
|
|
})
|
|
.collect(),
|
|
);
|
|
|
|
let slot_config = SlotConfig {
|
|
zero_time: 1660003200000, // Preview network
|
|
slot_length: 1000,
|
|
};
|
|
|
|
let multi_era_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes)
|
|
.or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes))
|
|
.unwrap();
|
|
match multi_era_tx {
|
|
MultiEraTx::Babbage(tx) => {
|
|
let redeemers = eval_tx(&tx, &utxos, &slot_config).unwrap();
|
|
|
|
println!("{:?}", redeemers.len());
|
|
}
|
|
_ => unreachable!(),
|
|
};
|
|
}
|
|
|
|
#[test]
|
|
fn test_eval_2() {
|
|
/*
|
|
|
|
Plutus V1
|
|
|
|
{-# INLINEABLE mintTestValidator #-}
|
|
mintTestValidator :: () -> Api.ScriptContext -> Bool
|
|
mintTestValidator _ ctx = Api.txInfoFee txInfo == Api.txInfoFee txInfo
|
|
|
|
where
|
|
txInfo :: Api.TxInfo
|
|
txInfo = Api.scriptContextTxInfo ctx */
|
|
|
|
let tx_bytes = hex::decode("84a800818258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d01018282581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f67235821a000f41f0a1581ccba7bc9e83499376b6ad49304157778dba7c14bd748e4fd31792a930a1400a82581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af006ac1021a00050c0309a1581ccba7bc9e83499376b6ad49304157778dba7c14bd748e4fd31792a930a1400a0b5820eb3b868ec2b33dffaf5d5481703ed00870333812b96e0f75ae89fd150b4744300d818258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d011082581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af0d26af111a00079205a3008182582031ae74f8058527afb305d7495b10a99422d9337fc199e1f28044f2c477a0f94658405a2dd70be89483bd6291a018c6ca91328dad37e092fdeab2eea14685004878da5f1e5962f35d771498bf54e79be3dcf922ea93b46a1356960dcf0bfd80a91b0b038159094259093f010000323322323232323232323232323232323232323233223232323232323232332232322232325335332232323233355300f12001350265025235001223335530121200135029502823500122333500123302f4800000488cc0c00080048cc0bc00520000013355300e120012350012233550250023335001233553012120012350012233550290023550140010012233355500f0150020012335530121200123500122335502900235501300100133355500a01000200130105002300f5001135001220023333573466e1cd55ce9baa00448000807c8c98c8078cd5ce01000f80e1999ab9a3370e6aae754009200023322123300100300232323232323232323232323333573466e1cd55cea8052400046666666666444444444424666666666600201601401201000e00c00a00800600466a034464646666ae68cdc39aab9d5002480008cc8848cc00400c008c094d5d0a801180f9aba135744a004464c6405c66ae700c00bc0b04d55cf280089baa00135742a01466a0340366ae854024ccd54075d7280e1aba150083335501d75ca0386ae85401ccd4068094d5d0a80319a80d19aa8140133ad35742a00a6464646666ae68cdc39aab9d5002480008cc8848cc00400c008c8c8c8cccd5cd19b8735573aa004900011991091980080180119a815bad35742a00460586ae84d5d1280111931901919ab9c034033030135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae754009200023322123300100300233502b75a6ae854008c0b0d5d09aba2500223263203233573806806606026aae7940044dd50009aba135744a004464c6405c66ae700c00bc0b04d55cf280089baa00135742a00866a034eb8d5d0a80199a80d19aa8143ae200135742a00460446ae84d5d1280111931901519ab9c02c02b028135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea0029003119091111802002980e9aba135573ca00646666ae68cdc3a8012400846424444600400a603e6ae84d55cf280211999ab9a3370ea0069001119091111800802980d9aba135573ca00a46666ae68cdc3a8022400046424444600600a6eb8d5d09aab9e500623263202533573804e04c04604404204026aae7540044dd50009aba135744a004464c6403c66ae7008007c07040784c98c8074cd5ce249035054350001e135573ca00226ea8004444888ccd54c01048005403ccd54c01c480048d400488cd54078008d54024004ccd54c0104800488d4008894cd4ccd54c03048004c8cd409488ccd400c88008008004d40048800448cc004894cd400840a8400409c8d400488cc028008014018400c4cd404c01000d4040004cd54c01c480048d400488c8cd5407c00cc004014c8004d5409c894cd40044d5402800c884d4008894cd4cc03000802044888cc0080280104c01800c008c8004d5408088448894cd40044008884cc014008ccd54c01c480040140100044484888c00c0104484888c004010c8004d540748844894cd400454034884cd4038c010008cd54c01848004010004c8004d5407088448894cd40044d400c88004884ccd401488008c010008ccd54c01c4800401401000488ccd5cd19b8f00200101c01b23500122222222220081232230023758002640026aa034446666aae7c004940248cd4020c010d5d080118019aba200201423232323333573466e1cd55cea801a40004666444246660020080060046464646666ae68cdc39aab9d5002480008cc8848cc00400c008c054d5d0a80119a80700a1aba135744a004464c6403066ae700680640584d55cf280089baa00135742a006666aa00eeb94018d5d0a80119a8053ae357426ae8940088c98c8050cd5ce00b00a80909aba25001135573ca00226ea80044cd54005d73ad112232230023756002640026aa03044646666aae7c008940208cd401ccd5404cc018d55cea80118029aab9e500230043574400602626ae840044488008488488cc00401000c488c8c8cccd5cd19b875001480008c8488c00800cc014d5d09aab9e500323333573466e1d40092002212200123263201033573802402201c01a26aae7540044dd5000919191999ab9a3370e6aae7540092000233221233001003002300535742a0046eb4d5d09aba2500223263200d33573801e01c01626aae7940044dd50009191999ab9a3370e6aae75400520002375c6ae84d55cf280111931900599ab9c00d00c0091375400224464646666ae68cdc3a800a40084244400246666ae68cdc3a8012400446424446006008600c6ae84d55cf280211999ab9a3370ea00690001091100111931900719ab9c01000f00c00b00a135573aa00226ea80048c8cccd5cd19b8750014800880448cccd5cd19b8750024800080448c98c8028cd5ce00600580400389aab9d3754002464646464646666ae68cdc3a800a401842444444400646666ae68cdc3a8012401442444444400846666ae68cdc3a801a40104664424444444660020120106eb8d5d0a8029bad357426ae8940148cccd5cd19b875004480188cc8848888888cc008024020dd71aba15007375c6ae84d5d1280391999ab9a3370ea00a900211991091111111980300480418061aba15009375c6ae84d5d1280491999ab9a3370ea00c900111909111111180380418069aba135573ca01646666ae68cdc3a803a400046424444444600a010601c6ae84d55cf280611931900919ab9c01401301000f00e00d00c00b00a135573aa00826aae79400c4d55cf280109aab9e5001137540024646464646666ae68cdc3a800a4004466644424466600200a0080066eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d4009200023212230020033008357426aae7940188c98c802ccd5ce00680600480409aab9d5003135744a00226aae7940044dd5000919191999ab9a3370ea002900111909118008019bae357426aae79400c8cccd5cd19b875002480008c8488c00800cdd71aba135573ca008464c6401066ae700280240180144d55cea80089baa0011122232323333573466e1cd55cea80124000466aa010600c6ae854008c014d5d09aba2500223263200833573801401200c26aae7940044dd5000a4c22442466002006004240029210350543100112330012253350021001100700612335002223335003220020020013500122001122123300100300222333573466e1c00800401000c488008488004448c8c00400488cc00cc00800800410581840100d87980821a000a01a61a0b3b82b2f5f6").unwrap();
|
|
|
|
let raw_inputs = hex::decode("84825820b16778c9cf065d9efeefe37ec269b4fc5107ecdbd0dd6bf3274b224165c2edd9008258206c732139de33e916342707de2aebef2252c781640326ff37b86ec99d97f1ba8d01825820975c17a4fed0051be622328efa548e206657d2b65a19224bf6ff8132571e6a500282582018f86700660fc88d0370a8f95ea58f75507e6b27a18a17925ad3b1777eb0d77600").unwrap();
|
|
let raw_outputs = hex::decode("8482581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f67235821a000f8548a1581c15be994a64bdb79dde7fe080d8e7ff81b33a9e4860e9ee0d857a8e85a144576177610182581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351b00000001af14b8b482581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a0098968082581d60b6c8794e9a7a26599440a4d0fd79cd07644d15917ff13694f1f672351a00acd8c6").unwrap();
|
|
|
|
let inputs = MaybeIndefArray::<TransactionInput>::decode_fragment(&raw_inputs).unwrap();
|
|
let outputs = MaybeIndefArray::<TransactionOutput>::decode_fragment(&raw_outputs).unwrap();
|
|
|
|
let utxos: MaybeIndefArray<ResolvedInput> = MaybeIndefArray::Indef(
|
|
inputs
|
|
.iter()
|
|
.zip(outputs.iter())
|
|
.map(|(input, output)| ResolvedInput {
|
|
input: input.clone(),
|
|
output: output.clone(),
|
|
})
|
|
.collect(),
|
|
);
|
|
|
|
let slot_config = SlotConfig {
|
|
zero_time: 1660003200000, // Preview network
|
|
slot_length: 1000,
|
|
};
|
|
|
|
let multi_era_tx = MultiEraTx::decode(Era::Babbage, &tx_bytes)
|
|
.or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes))
|
|
.unwrap();
|
|
match multi_era_tx {
|
|
MultiEraTx::Babbage(tx) => {
|
|
let redeemers = eval_tx(&tx, &utxos, &slot_config).unwrap();
|
|
|
|
println!("{:?}", redeemers.len());
|
|
}
|
|
_ => unreachable!(),
|
|
};
|
|
}
|
|
}
|