move utils to uplc and break up
This commit is contained in:
parent
ddf3cdb6ec
commit
3f27bd9f13
|
@ -676,14 +676,19 @@ checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
|
|||
name = "uplc"
|
||||
version = "0.0.12"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cryptoxide",
|
||||
"flat-rs",
|
||||
"hex",
|
||||
"pallas-addresses",
|
||||
"pallas-codec",
|
||||
"pallas-crypto",
|
||||
"pallas-primitives",
|
||||
"pallas-traverse",
|
||||
"peg",
|
||||
"pretty",
|
||||
"proptest",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use std::{
|
||||
fmt::Write as _,
|
||||
fs::{self, File},
|
||||
io::BufReader,
|
||||
fs::{self},
|
||||
};
|
||||
|
||||
use pallas_traverse::{Era, MultiEraTx};
|
||||
|
@ -12,12 +11,9 @@ use uplc::{
|
|||
};
|
||||
|
||||
mod args;
|
||||
mod utils;
|
||||
|
||||
use args::{Args, TxCommand, UplcCommand};
|
||||
|
||||
use crate::args::ResolvedInputOld;
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let args = Args::default();
|
||||
|
||||
|
@ -26,7 +22,7 @@ fn main() -> anyhow::Result<()> {
|
|||
TxCommand::Simulate {
|
||||
input,
|
||||
cbor,
|
||||
resolved_inputs,
|
||||
resolved_inputs: _,
|
||||
} => {
|
||||
let tx_bytes = if cbor {
|
||||
fs::read(input)?
|
||||
|
@ -40,6 +36,8 @@ fn main() -> anyhow::Result<()> {
|
|||
.or_else(|_| MultiEraTx::decode(Era::Alonzo, &tx_bytes))?;
|
||||
|
||||
println!("Simulating: {}", tx.hash());
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
Args::Uplc(uplc_cmd) => match uplc_cmd {
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -16,11 +16,16 @@ exclude = ["test_data/*"]
|
|||
cryptoxide = "0.4.2"
|
||||
flat-rs = { path = "../flat", version = "0.0.10" }
|
||||
hex = "0.4.3"
|
||||
pallas-addresses = "0.14.0-alpha.3"
|
||||
pallas-codec = "0.14.0-alpha.3"
|
||||
pallas-crypto = "0.14.0-alpha.3"
|
||||
pallas-primitives = "0.14.0-alpha.3"
|
||||
pallas-traverse = "0.14.0-alpha.3"
|
||||
peg = "0.8.0"
|
||||
pretty = "0.11.3"
|
||||
thiserror = "1.0.31"
|
||||
anyhow = "1.0.57"
|
||||
serde_json = "1.0.85"
|
||||
|
||||
[dev-dependencies]
|
||||
hex = "0.4.3"
|
||||
|
|
|
@ -6,6 +6,7 @@ pub mod machine;
|
|||
pub mod parser;
|
||||
mod pretty;
|
||||
pub mod program_builder;
|
||||
pub mod transaction_eval;
|
||||
|
||||
pub use pallas_primitives::alonzo::PlutusData;
|
||||
pub type Error = Box<dyn std::error::Error>;
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,85 @@
|
|||
use pallas_codec::utils::KeyValuePairs;
|
||||
use pallas_crypto::hash::Hash;
|
||||
use pallas_primitives::babbage::{
|
||||
AddrKeyhash, Certificate, Coin, DatumHash, Mint, PlutusData, PolicyId, Redeemer, RewardAccount,
|
||||
StakeCredential, TransactionInput, TransactionOutput, Value, Withdrawals,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct ResolvedInput {
|
||||
pub input: TransactionInput,
|
||||
pub output: TransactionOutput,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct TxInInfo {
|
||||
pub out_ref: TransactionInput,
|
||||
pub resolved: TxOut,
|
||||
}
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum TxOut {
|
||||
V1(TransactionOutput),
|
||||
V2(TransactionOutput),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum ScriptPurpose {
|
||||
Minting(PolicyId),
|
||||
Spending(TransactionInput),
|
||||
Rewarding(StakeCredential),
|
||||
Certifying(Certificate),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct TxInfoV1 {
|
||||
pub inputs: Vec<TxInInfo>,
|
||||
pub outputs: Vec<TxOut>,
|
||||
pub fee: Value,
|
||||
pub mint: Mint,
|
||||
pub dcert: Vec<Certificate>,
|
||||
pub wdrl: Vec<(RewardAccount, Coin)>,
|
||||
pub valid_range: TimeRange,
|
||||
pub signatories: Vec<AddrKeyhash>,
|
||||
pub data: Vec<(DatumHash, PlutusData)>,
|
||||
pub id: Hash<32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct TxInfoV2 {
|
||||
pub inputs: Vec<TxInInfo>,
|
||||
pub reference_inputs: Vec<TxInInfo>,
|
||||
pub outputs: Vec<TxOut>,
|
||||
pub fee: Value,
|
||||
pub mint: Mint,
|
||||
pub dcert: Vec<Certificate>,
|
||||
pub wdrl: Withdrawals,
|
||||
pub valid_range: TimeRange,
|
||||
pub signatories: Vec<AddrKeyhash>,
|
||||
pub redeemers: KeyValuePairs<ScriptPurpose, Redeemer>,
|
||||
pub data: KeyValuePairs<DatumHash, PlutusData>,
|
||||
pub id: Hash<32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum TxInfo {
|
||||
V1(TxInfoV1),
|
||||
V2(TxInfoV2),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct ScriptContext {
|
||||
pub tx_info: TxInfo,
|
||||
pub purpose: ScriptPurpose,
|
||||
}
|
||||
|
||||
//---- Time conversion: slot range => posix time range
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct TimeRange {
|
||||
pub lower_bound: Option<u64>,
|
||||
pub upper_bound: Option<u64>,
|
||||
}
|
||||
|
||||
pub struct SlotConfig {
|
||||
pub slot_length: u64,
|
||||
pub zero_time: u64,
|
||||
}
|
|
@ -0,0 +1,583 @@
|
|||
use pallas_addresses::{Address, ShelleyDelegationPart, ShelleyPaymentPart};
|
||||
use pallas_primitives::babbage::{AssetName, BigInt, Constr, PlutusData, ScriptRef};
|
||||
|
||||
use pallas_codec::utils::{AnyUInt, Bytes, Int, KeyValuePairs};
|
||||
use pallas_crypto::hash::Hash;
|
||||
use pallas_primitives::babbage::{
|
||||
Certificate, DatumOption, PolicyId, Redeemer, Script, StakeCredential, TransactionInput,
|
||||
TransactionOutput, Value,
|
||||
};
|
||||
use pallas_traverse::ComputeHash;
|
||||
use std::vec;
|
||||
|
||||
use super::script_context::{TxOut, TimeRange, TxInInfo, ScriptPurpose, TxInfo, ScriptContext};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
_ => 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 i64).into())),
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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.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 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::from(*n as i64))),
|
||||
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::from(*self as i64)))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToPlutusData for Value {
|
||||
fn to_plutus_data(&self) -> PlutusData {
|
||||
match self {
|
||||
Value::Coin(coin) => PlutusData::Map(KeyValuePairs::Def(vec![(
|
||||
PolicyId::from([0; 28]).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([0; 28]).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.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::<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),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,715 @@
|
|||
use crate::{
|
||||
ast::{FakeNamedDeBruijn, NamedDeBruijn, Program},
|
||||
machine::cost_model::ExBudget,
|
||||
PlutusData,
|
||||
};
|
||||
use pallas_addresses::{Address, ScriptHash, StakePayload};
|
||||
use pallas_codec::utils::{KeyValuePairs, MaybeIndefArray};
|
||||
use pallas_crypto::hash::Hash;
|
||||
use pallas_primitives::babbage::{
|
||||
Certificate, DatumHash, DatumOption, ExUnits, Mint, MintedTx,
|
||||
PlutusV1Script, PlutusV2Script, PolicyId, Redeemer, RedeemerTag, RewardAccount, Script,
|
||||
StakeCredential, TransactionInput, TransactionOutput, Value, Withdrawals,
|
||||
};
|
||||
use pallas_traverse::{ComputeHash, OriginalHash};
|
||||
use std::{collections::HashMap, convert::TryInto, ops::Deref, vec};
|
||||
|
||||
use super::{
|
||||
script_context::{
|
||||
ResolvedInput, ScriptContext, ScriptPurpose, SlotConfig, TimeRange, TxInInfo, TxInfo,
|
||||
TxInfoV1, TxInfoV2, TxOut,
|
||||
},
|
||||
to_plutus_data::ToPlutusData,
|
||||
};
|
||||
|
||||
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: slot_range
|
||||
.lower_bound
|
||||
.map(|lower_bound| slot_to_begin_posix_time(lower_bound, sc)),
|
||||
upper_bound: slot_range
|
||||
.upper_bound
|
||||
.map(|upper_bound| slot_to_begin_posix_time(upper_bound, sc)),
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
|
||||
pub struct DataLookupTable {
|
||||
datum: HashMap<DatumHash, PlutusData>,
|
||||
scripts: HashMap<ScriptHash, ScriptVersion>,
|
||||
}
|
||||
|
||||
pub fn get_tx_in_info_v1(
|
||||
inputs: &[TransactionInput],
|
||||
utxos: &[ResolvedInput],
|
||||
) -> anyhow::Result<Vec<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) => {
|
||||
if let Some(DatumOption::Data(_)) = output.datum_option {
|
||||
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(result)
|
||||
}
|
||||
|
||||
fn get_tx_in_info_v2(
|
||||
inputs: &[TransactionInput],
|
||||
utxos: &[ResolvedInput],
|
||||
) -> anyhow::Result<Vec<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(result)
|
||||
}
|
||||
|
||||
fn get_script_purpose(
|
||||
redeemer: &Redeemer,
|
||||
inputs: &[TransactionInput],
|
||||
mint: &Option<Mint>,
|
||||
dcert: &Option<Vec<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)
|
||||
.collect::<Vec<PolicyId>>();
|
||||
policy_ids.sort();
|
||||
match policy_ids.get(index as usize) {
|
||||
Some(policy_id) => Ok(ScriptPurpose::Minting(*policy_id)),
|
||||
None => unreachable!("Script purpose not found for redeemer."),
|
||||
}
|
||||
}
|
||||
RedeemerTag::Spend => {
|
||||
// sort lexical by tx_hash and index
|
||||
let mut inputs = inputs.to_vec();
|
||||
// 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)
|
||||
}
|
||||
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: &[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 = body
|
||||
.outputs
|
||||
.iter()
|
||||
.map(|output| TxOut::V1(output.clone()))
|
||||
.collect();
|
||||
|
||||
let fee = Value::Coin(body.fee);
|
||||
let mint = body.mint.clone().unwrap_or(KeyValuePairs::Indef(vec![]));
|
||||
let dcert = body.certificates.clone().unwrap_or_default();
|
||||
let wdrl = 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_default();
|
||||
|
||||
let data = tx
|
||||
.transaction_witness_set
|
||||
.plutus_data
|
||||
.as_ref()
|
||||
.unwrap_or(&vec![])
|
||||
.iter()
|
||||
.map(|d| (d.original_hash(), d.clone().unwrap()))
|
||||
.collect();
|
||||
|
||||
let id = tx.transaction_body.compute_hash();
|
||||
|
||||
Ok(TxInfo::V1(TxInfoV1 {
|
||||
inputs,
|
||||
outputs,
|
||||
fee,
|
||||
mint,
|
||||
dcert,
|
||||
wdrl,
|
||||
valid_range,
|
||||
signatories,
|
||||
data,
|
||||
id,
|
||||
}))
|
||||
}
|
||||
|
||||
fn get_tx_info_v2(
|
||||
tx: &MintedTx,
|
||||
utxos: &[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_default(), utxos)?;
|
||||
let outputs = body
|
||||
.outputs
|
||||
.iter()
|
||||
.map(|output| TxOut::V2(output.clone()))
|
||||
.collect();
|
||||
let fee = Value::Coin(body.fee);
|
||||
let mint = body.mint.clone().unwrap_or(KeyValuePairs::Indef(vec![]));
|
||||
let dcert = body.certificates.clone().unwrap_or_default();
|
||||
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_default();
|
||||
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(&vec![])
|
||||
.iter()
|
||||
.map(|d| (d.original_hash(), d.clone().unwrap()))
|
||||
.collect(),
|
||||
);
|
||||
let id = tx.transaction_body.compute_hash();
|
||||
|
||||
Ok(TxInfo::V2(TxInfoV2 {
|
||||
inputs,
|
||||
reference_inputs,
|
||||
outputs,
|
||||
fee,
|
||||
mint,
|
||||
dcert,
|
||||
wdrl,
|
||||
valid_range,
|
||||
signatories,
|
||||
redeemers,
|
||||
data,
|
||||
id,
|
||||
}))
|
||||
}
|
||||
|
||||
fn get_execution_purpose(
|
||||
utxos: &[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)
|
||||
}
|
||||
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, datum)
|
||||
}
|
||||
_ => 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, 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,
|
||||
_ => 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,
|
||||
_ => 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,
|
||||
_ => 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."),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_script_and_datum_lookup_table(
|
||||
tx: &MintedTx,
|
||||
utxos: &[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_default();
|
||||
|
||||
let scripts_v1_witnesses = tx
|
||||
.transaction_witness_set
|
||||
.plutus_v1_script
|
||||
.clone()
|
||||
.unwrap_or_default();
|
||||
|
||||
let scripts_v2_witnesses = tx
|
||||
.transaction_witness_set
|
||||
.plutus_v2_script
|
||||
.clone()
|
||||
.unwrap_or_default();
|
||||
|
||||
for plutus_data in plutus_data_witnesses.iter() {
|
||||
datum.insert(plutus_data.original_hash(), plutus_data.clone().unwrap());
|
||||
}
|
||||
|
||||
for script in scripts_v1_witnesses.iter() {
|
||||
scripts.insert(script.compute_hash(), ScriptVersion::V1(script.clone()));
|
||||
// TODO: fix hashing bug in pallas
|
||||
}
|
||||
|
||||
for script in scripts_v2_witnesses.iter() {
|
||||
scripts.insert(script.compute_hash(), ScriptVersion::V2(script.clone()));
|
||||
// TODO: fix hashing bug in pallas
|
||||
}
|
||||
|
||||
// discovery in utxos (script ref)
|
||||
|
||||
for utxo in utxos.iter() {
|
||||
match &utxo.output {
|
||||
TransactionOutput::Legacy(_) => {}
|
||||
TransactionOutput::PostAlonzo(output) => {
|
||||
if let Some(script) = &output.script_ref {
|
||||
match &script.0 {
|
||||
Script::PlutusV1Script(v1) => {
|
||||
scripts.insert(v1.compute_hash(), ScriptVersion::V1(v1.clone()));
|
||||
}
|
||||
Script::PlutusV2Script(v2) => {
|
||||
scripts.insert(v2.compute_hash(), ScriptVersion::V2(v2.clone()));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DataLookupTable { datum, scripts }
|
||||
}
|
||||
|
||||
pub fn eval_redeemer(
|
||||
tx: &MintedTx,
|
||||
utxos: &[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)
|
||||
.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)
|
||||
.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)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue