implement a strict subset of PlutusV3 transaction info

More specifically, that is simply mimicking the script context from
  v2, minus the new governance features.
This commit is contained in:
KtorZ 2024-08-08 23:01:17 +02:00
parent c454dc72eb
commit fdf7a81288
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
5 changed files with 135 additions and 24 deletions

View File

@ -96,7 +96,8 @@ pub fn eval_phase_two_raw(
run_phase_one: bool,
with_redeemer: fn(&Redeemer) -> (),
) -> Result<Vec<Vec<u8>>, Error> {
let multi_era_tx = MultiEraTx::decode_for_era(Era::Babbage, tx_bytes)
let multi_era_tx = MultiEraTx::decode_for_era(Era::Conway, tx_bytes)
.or_else(|_| MultiEraTx::decode_for_era(Era::Babbage, tx_bytes))
.or_else(|_| MultiEraTx::decode_for_era(Era::Alonzo, tx_bytes))?;
let cost_mdls = CostMdls::decode_fragment(cost_mdls_bytes)?;

View File

@ -6,7 +6,7 @@ use super::{
use crate::{
ast::{FakeNamedDeBruijn, NamedDeBruijn, Program},
machine::cost_model::ExBudget,
tx::script_context::{DataLookupTable, ScriptVersion, TxInfoV1, TxInfoV2},
tx::script_context::{DataLookupTable, ScriptVersion, TxInfoV1, TxInfoV2, TxInfoV3},
PlutusData,
};
use pallas_codec::utils::Bytes;
@ -116,7 +116,22 @@ pub fn eval_redeemer(
program(script.0)?,
),
(ScriptVersion::V3(_script), _datum) => todo!(),
(ScriptVersion::V3(script), datum) => do_eval_redeemer(
cost_mdls_opt
.map(|cost_mdls| {
cost_mdls
.plutus_v3
.as_ref()
.ok_or(Error::CostModelNotFound(Language::PlutusV3))
})
.transpose()?,
initial_budget,
&Language::PlutusV3,
datum,
redeemer,
TxInfoV3::from_transaction(tx, utxos, slot_config)?,
program(script.0)?,
),
}
.map_err(|err| Error::RedeemerError {
tag: redeemer_tag_to_string(&redeemer.tag),

View File

@ -10,7 +10,7 @@ use pallas_primitives::conway::{
};
use std::collections::HashMap;
type AlonzoScriptsNeeded = Vec<(ScriptPurpose, ScriptHash)>;
type ScriptsNeeded = Vec<(ScriptPurpose, ScriptHash)>;
// subset of phase-1 ledger checks related to scripts
pub fn eval_phase_one(
@ -28,7 +28,7 @@ pub fn eval_phase_one(
}
pub fn validate_missing_scripts(
needed: &AlonzoScriptsNeeded,
needed: &ScriptsNeeded,
txscripts: HashMap<ScriptHash, ScriptVersion>,
) -> Result<(), Error> {
let received_hashes = txscripts.keys().copied().collect::<Vec<ScriptHash>>();
@ -61,10 +61,7 @@ pub fn validate_missing_scripts(
Ok(())
}
pub fn scripts_needed(
tx: &MintedTx,
utxos: &[ResolvedInput],
) -> Result<AlonzoScriptsNeeded, Error> {
pub fn scripts_needed(tx: &MintedTx, utxos: &[ResolvedInput]) -> Result<ScriptsNeeded, Error> {
let mut needed = Vec::new();
let txb = tx.transaction_body.clone();
@ -106,7 +103,7 @@ pub fn scripts_needed(
None
})
.collect::<AlonzoScriptsNeeded>()
.collect::<ScriptsNeeded>()
})
.unwrap_or_default();
@ -127,7 +124,7 @@ pub fn scripts_needed(
_ => None,
}
})
.collect::<AlonzoScriptsNeeded>()
.collect::<ScriptsNeeded>()
})
.unwrap_or_default();
@ -137,7 +134,7 @@ pub fn scripts_needed(
.map(|m| {
m.iter()
.map(|(policy_id, _)| (ScriptPurpose::Minting(*policy_id), *policy_id))
.collect::<AlonzoScriptsNeeded>()
.collect::<ScriptsNeeded>()
})
.unwrap_or_default();
@ -156,7 +153,7 @@ pub fn scripts_needed(
/// hasExactSetOfRedeemers in Ledger Spec, but we pass `txscripts` directly
pub fn has_exact_set_of_redeemers(
tx: &MintedTx,
needed: &AlonzoScriptsNeeded,
needed: &ScriptsNeeded,
tx_scripts: HashMap<ScriptHash, ScriptVersion>,
) -> Result<(), Error> {
let mut redeemers_needed = Vec::new();

View File

@ -27,6 +27,7 @@ pub struct TxInInfo {
pub out_ref: TransactionInput,
pub resolved: TxOut,
}
#[derive(Debug, PartialEq, Clone)]
pub enum TxOut {
V1(TransactionOutput),
@ -290,15 +291,51 @@ impl TxInfoV2 {
}
}
pub struct TxInfoV3 {}
#[derive(Debug, PartialEq, Clone)]
pub struct TxInfoV3 {
pub inputs: Vec<TxInInfo>,
pub reference_inputs: Vec<TxInInfo>,
pub outputs: Vec<TxOut>,
pub fee: Value,
pub mint: MintValue,
pub certificates: Vec<Certificate>,
pub withdrawals: KeyValuePairs<Address, Coin>,
pub valid_range: TimeRange,
pub signatories: Vec<AddrKeyhash>,
pub redeemers: KeyValuePairs<ScriptPurpose, Redeemer>,
pub data: KeyValuePairs<DatumHash, PlutusData>,
// TODO:
// votes : KeyValuePairs<Voter, KeyValuePairs<GovernanceActionId, Vote>>
// proposalProcedures : Vec<ProposalProcedure>
// currentTreasuryAmount : Option<Coin>
// treasuryDonation : Option<Coin>
pub id: Hash<32>,
}
impl TxInfoV3 {
pub fn from_transaction(
_tx: &MintedTx,
_utxos: &[ResolvedInput],
_slot_config: &SlotConfig,
tx: &MintedTx,
utxos: &[ResolvedInput],
slot_config: &SlotConfig,
) -> Result<TxInfo, Error> {
todo!("TxInfoV3")
if let TxInfo::V2(tx_info_v2) = TxInfoV2::from_transaction(tx, utxos, slot_config)? {
Ok(TxInfo::V3(TxInfoV3 {
inputs: tx_info_v2.inputs,
reference_inputs: tx_info_v2.reference_inputs,
outputs: tx_info_v2.outputs,
fee: tx_info_v2.fee,
mint: tx_info_v2.mint,
certificates: tx_info_v2.certificates,
withdrawals: tx_info_v2.withdrawals,
valid_range: tx_info_v2.valid_range,
signatories: tx_info_v2.signatories,
redeemers: tx_info_v2.redeemers,
data: tx_info_v2.data,
id: tx_info_v2.id,
}))
} else {
unreachable!()
}
}
}
@ -306,12 +343,15 @@ impl TxInfoV3 {
pub enum TxInfo {
V1(TxInfoV1),
V2(TxInfoV2),
V3(TxInfoV3),
}
impl TxInfo {
pub fn purpose(&self, needle: &Redeemer) -> Option<ScriptPurpose> {
match self {
TxInfo::V1(TxInfoV1 { redeemers, .. }) | TxInfo::V2(TxInfoV2 { redeemers, .. }) => {
TxInfo::V1(TxInfoV1 { redeemers, .. })
| TxInfo::V2(TxInfoV2 { redeemers, .. })
| TxInfo::V3(TxInfoV3 { redeemers, .. }) => {
redeemers.iter().find_map(|(purpose, redeemer)| {
if redeemer == needle {
Some(purpose.clone())
@ -327,6 +367,7 @@ impl TxInfo {
match self {
TxInfo::V1(info) => &info.inputs,
TxInfo::V2(info) => &info.inputs,
TxInfo::V3(info) => &info.inputs,
}
}
@ -334,6 +375,7 @@ impl TxInfo {
match self {
TxInfo::V1(info) => &info.mint,
TxInfo::V2(info) => &info.mint,
TxInfo::V3(info) => &info.mint,
}
}
@ -341,6 +383,7 @@ impl TxInfo {
match self {
TxInfo::V1(info) => &info.withdrawals[..],
TxInfo::V2(info) => &info.withdrawals[..],
TxInfo::V3(info) => &info.withdrawals[..],
}
}
@ -348,6 +391,7 @@ impl TxInfo {
match self {
TxInfo::V1(info) => &info.certificates[..],
TxInfo::V2(info) => &info.certificates[..],
TxInfo::V3(info) => &info.certificates[..],
}
}
}

View File

@ -36,6 +36,8 @@ fn empty_constr(index: u64) -> PlutusData {
})
}
struct WithWrappedTransactionId<'a, T>(&'a T);
pub trait ToPlutusData {
fn to_plutus_data(&self) -> PlutusData;
}
@ -98,12 +100,24 @@ impl ToPlutusData for Address {
}
}
impl<'a> ToPlutusData for WithWrappedTransactionId<'a, TransactionInput> {
fn to_plutus_data(&self) -> PlutusData {
wrap_multiple_with_constr(
0,
vec![
wrap_with_constr(0, self.0.transaction_id.to_plutus_data()),
PlutusData::BigInt(BigInt::Int((self.0.index as i128).try_into().unwrap())),
],
)
}
}
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()),
self.transaction_id.to_plutus_data(),
PlutusData::BigInt(BigInt::Int((self.index as i128).try_into().unwrap())),
],
)
@ -299,7 +313,7 @@ impl ToPlutusData for TxOut {
// legacy_output.datum_hash.to_plutus_data(),
// ],
// ),
TransactionOutput::Legacy(..) => todo!("TransactionOutput::Legacy"),
TransactionOutput::Legacy(..) => unimplemented!("TransactionOutput::Legacy"),
TransactionOutput::PostAlonzo(post_alonzo_output) => wrap_multiple_with_constr(
0,
vec![
@ -329,7 +343,7 @@ impl ToPlutusData for TxOut {
// None::<ScriptRef>.to_plutus_data(),
// ],
// ),
TransactionOutput::Legacy(..) => todo!("TransactionOutput::Legacy"),
TransactionOutput::Legacy(..) => unimplemented!("TransactionOutput::Legacy"),
TransactionOutput::PostAlonzo(post_alonzo_output) => wrap_multiple_with_constr(
0,
vec![
@ -556,6 +570,29 @@ impl ToPlutusData for TimeRange {
}
}
impl<'a> ToPlutusData for WithWrappedTransactionId<'a, Vec<TxInInfo>> {
fn to_plutus_data(&self) -> PlutusData {
PlutusData::Array(
self.0
.iter()
.map(|p| WithWrappedTransactionId(p).to_plutus_data())
.collect(),
)
}
}
impl<'a> ToPlutusData for WithWrappedTransactionId<'a, TxInInfo> {
fn to_plutus_data(&self) -> PlutusData {
wrap_multiple_with_constr(
0,
vec![
WithWrappedTransactionId(&self.0.out_ref).to_plutus_data(),
self.0.resolved.to_plutus_data(),
],
)
}
}
impl ToPlutusData for TxInInfo {
fn to_plutus_data(&self) -> PlutusData {
wrap_multiple_with_constr(
@ -587,7 +624,7 @@ impl ToPlutusData for TxInfo {
TxInfo::V1(tx_info) => wrap_multiple_with_constr(
0,
vec![
tx_info.inputs.to_plutus_data(),
WithWrappedTransactionId(&tx_info.inputs).to_plutus_data(),
tx_info.outputs.to_plutus_data(),
tx_info.fee.to_plutus_data(),
tx_info.mint.to_plutus_data(),
@ -600,6 +637,23 @@ impl ToPlutusData for TxInfo {
],
),
TxInfo::V2(tx_info) => wrap_multiple_with_constr(
0,
vec![
WithWrappedTransactionId(&tx_info.inputs).to_plutus_data(),
WithWrappedTransactionId(&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.certificates.to_plutus_data(),
tx_info.withdrawals.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()),
],
),
TxInfo::V3(tx_info) => wrap_multiple_with_constr(
0,
vec![
tx_info.inputs.to_plutus_data(),
@ -613,7 +667,7 @@ impl ToPlutusData for TxInfo {
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()),
tx_info.id.to_plutus_data(),
],
),
}