Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ target/
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
.idea/

.scratchpad/
41 changes: 15 additions & 26 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions crates/tx3-cardano/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ homepage.workspace = true
readme.workspace = true

[dependencies]
pallas = { version = ">=1.0.0-alpha, <2.0.0" }
# pallas = { version = ">=1.0.0-alpha, <2.0.0" }
# pallas = { version = ">=1.0.0-alpha, <2.0.0", path = "../../../../txpipe/pallas/pallas" }
# pallas = { git = "https://github.com/txpipe/pallas.git" }
pallas = { git = "https://github.com/txpipe/pallas.git", rev = "065df6542e376761d7b4bc90449493c8880f24c6" }

hex = "0.4.3"
thiserror = "2.0.11"
Expand Down
51 changes: 34 additions & 17 deletions crates/tx3-cardano/src/compile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,19 @@ fn compile_output_block(

let datum_option = ir.datum.as_option().map(compile_data_expr).transpose()?;

let datum_option = datum_option.map(|datum| {
if ir.datum_hash_mode {
primitives::DatumOption::Hash(datum.compute_hash()).into()
} else {
primitives::DatumOption::Data(pallas::codec::utils::CborWrap(datum.into())).into()
}
});

let output = primitives::TransactionOutput::PostAlonzo(
primitives::PostAlonzoTransactionOutput {
address: address.to_vec().into(),
value,
datum_option: datum_option.map(|x| {
primitives::DatumOption::Data(pallas::codec::utils::CborWrap(x.into())).into()
}),
datum_option,
script_ref: None,
}
.into(),
Expand Down Expand Up @@ -868,6 +874,19 @@ fn compile_adhoc_native_witness(tx: &ir::Tx) -> Result<Vec<NativeWitness>, Error
.collect::<Result<Vec<_>, _>>()
}

pub type DatumWitness = KeepRaw<'static, primitives::PlutusData>;

fn compile_datum_witnesses(tx: &ir::Tx) -> Result<Vec<DatumWitness>, Error> {
// TODO: also lookup and include datum witnesses from inputs that have datum hashes

tx.outputs
.iter()
.filter(|x| x.datum_hash_mode)
.filter_map(|output| output.datum.as_option().map(compile_data_expr))
.map(|result| result.map(KeepRaw::from))
.collect::<Result<Vec<_>, _>>()
}

fn compile_witness_set(
tx: &ir::Tx,
compiled_body: &primitives::TransactionBody,
Expand All @@ -888,7 +907,7 @@ fn compile_witness_set(
vkeywitness: None,
native_script: NonEmptySet::from_vec(compile_adhoc_native_witness(tx)?),
bootstrap_witness: None,
plutus_data: None,
plutus_data: NonEmptySet::from_vec(compile_datum_witnesses(tx)?).map(KeepRaw::from),
plutus_v1_script: NonEmptySet::from_vec(compile_adhoc_plutus_witness::<1>(tx)),
plutus_v2_script: NonEmptySet::from_vec(compile_adhoc_plutus_witness::<2>(tx)),
plutus_v3_script: NonEmptySet::from_vec(compile_adhoc_plutus_witness::<3>(tx)),
Expand All @@ -897,19 +916,17 @@ fn compile_witness_set(
Ok(witness_set)
}

fn infer_plutus_version(witness_set: &primitives::WitnessSet) -> PlutusVersion {
fn infer_plutus_version(witness_set: &primitives::WitnessSet) -> Option<PlutusVersion> {
// TODO: how do we handle this for reference scripts?

if witness_set.plutus_v1_script.is_some() {
0
Some(0)
} else if witness_set.plutus_v2_script.is_some() {
1
Some(1)
} else if witness_set.plutus_v3_script.is_some() {
2
Some(2)
} else {
// TODO: should we error here?
// Defaulting to Plutus V3 for now
2
None
}
}

Expand All @@ -919,11 +936,12 @@ fn compute_script_data_hash(
) -> Option<primitives::Hash<32>> {
let version = infer_plutus_version(witness_set);

let cost_model = pparams.cost_models.get(&version).unwrap();

let language_view = primitives::LanguageView(version, cost_model.clone());
let language_view = version.map(|v|{
let cost_model = pparams.cost_models.get(&v).unwrap();
primitives::LanguageView(v, cost_model.clone())
});

let data = primitives::ScriptData::build_for(witness_set, language_view);
let data = primitives::ScriptData::build_for(witness_set, &language_view);

data.map(|x| x.hash())
}
Expand Down Expand Up @@ -957,8 +975,7 @@ pub fn entry_point(tx: &ir::Tx, pparams: &PParams) -> Result<primitives::Tx<'sta
transaction_body.script_data_hash = compute_script_data_hash(&transaction_witness_set, pparams);

transaction_body.auxiliary_data_hash = auxiliary_data
.as_ref()
.map(|x| primitives::Bytes::from(x.compute_hash().to_vec()));
.as_ref().map(|a| a.compute_hash());

Ok(primitives::Tx {
transaction_body: transaction_body.into(),
Expand Down
36 changes: 36 additions & 0 deletions crates/tx3-cardano/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,42 @@ async fn input_datum_test() {
);
}

#[pollster::test]
async fn input_hashed_datum_test() {
let mut compiler = test_compiler(None);

let utxos = wildcard_utxos(Some(tx3_lang::ir::Expression::Struct(
tx3_lang::ir::StructExpr {
constructor: 0,
fields: vec![
tx3_lang::ir::Expression::Number(1),
tx3_lang::ir::Expression::Bytes(b"abc".to_vec()),
],
},
)));

let protocol = load_protocol("input_hashed_datum");

let mut tx = protocol
.new_tx("increase_counter")
.unwrap()
.apply()
.unwrap();

tx.set_arg("myparty", address_to_bytes("addr1qx0rs5qrvx9qkndwu0w88t0xghgy3f53ha76kpx8uf496m9rn2ursdm3r0fgf5pmm4lpufshl8lquk5yykg4pd00hp6quf2hh2"));

let tx = tx.apply().unwrap();

let tx = test_compile(tx.into(), &mut compiler, utxos);

println!("{}", hex::encode(tx.payload));

assert_eq!(
hex::encode(tx.hash),
"2a9ae249a6be7be3c5e5fc2da72bdb760ca1d507c706fc453a384b0792efecb4"
);
}

#[pollster::test]
async fn env_vars_test() {
let mut compiler = test_compiler(None);
Expand Down
2 changes: 2 additions & 0 deletions crates/tx3-lang/src/analyzing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,7 @@ impl Analyzable for OutputBlockField {
OutputBlockField::To(x) => x.analyze(parent),
OutputBlockField::Amount(x) => x.analyze(parent),
OutputBlockField::Datum(x) => x.analyze(parent),
OutputBlockField::HashedDatum(x) => x.analyze(parent),
}
}

Expand All @@ -1017,6 +1018,7 @@ impl Analyzable for OutputBlockField {
OutputBlockField::To(x) => x.is_resolved(),
OutputBlockField::Amount(x) => x.is_resolved(),
OutputBlockField::Datum(x) => x.is_resolved(),
OutputBlockField::HashedDatum(x) => x.is_resolved(),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/tx3-lang/src/applying.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,7 @@ impl Composite for ir::Output {
Ok(Self {
address: f(self.address)?,
datum: f(self.datum)?,
datum_hash_mode: self.datum_hash_mode,
amount: f(self.amount)?,
optional: self.optional,
})
Expand Down
2 changes: 2 additions & 0 deletions crates/tx3-lang/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ pub enum OutputBlockField {
To(Box<DataExpr>),
Amount(Box<DataExpr>),
Datum(Box<DataExpr>),
HashedDatum(Box<DataExpr>),
}

impl OutputBlockField {
Expand All @@ -395,6 +396,7 @@ impl OutputBlockField {
OutputBlockField::To(_) => "to",
OutputBlockField::Amount(_) => "amount",
OutputBlockField::Datum(_) => "datum",
OutputBlockField::HashedDatum(_) => "hashed_datum",
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/tx3-lang/src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ pub struct Input {
pub struct Output {
pub address: Expression,
pub datum: Expression,
pub datum_hash_mode: bool,
pub amount: Expression,
pub optional: bool,
}
Expand Down Expand Up @@ -536,6 +537,7 @@ impl Node for Output {
let visited = Self {
address: self.address.apply(visitor)?,
datum: self.datum.apply(visitor)?,
datum_hash_mode: self.datum_hash_mode,
amount: self.amount.apply(visitor)?,
optional: self.optional,
};
Expand Down
Loading