Skip to content

Commit b57dba2

Browse files
committed
Merge #345: feat: add annex to env
c4d6ee6 feat: add serde Serialize support for Cost and NodeBounds (stringhandler) 050f9bd feat(elements): pass taproot annex to C FFI transaction construction (stringhandler) Pull request description: Add the annex to the transaction environment so that it is included in hashes created by jets. This is almost entirely coded by delta1 but is required to add functionality to `hal-simplicity` and can be seen/tested here https://github.com/stringhandler/hal-simplicity/tree/feat/add-annex. ACKs for top commit: apoelstra: ACK c4d6ee6; successfully ran local tests Tree-SHA512: e6620f2f61d8c04d0a46c9a40e373823d36942da7f32ed6015c87af7b49a086fbb5ac913bba90461d5fd03c0d56dc64b361ab99c01ac21023bd374b1088ec145
2 parents 11ec169 + c4d6ee6 commit b57dba2

2 files changed

Lines changed: 43 additions & 10 deletions

File tree

src/analysis.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use std::{cmp, fmt};
66
use crate::value::Word;
77
#[cfg(feature = "elements")]
88
use elements::encode::Encodable;
9+
#[cfg(feature = "serde")]
10+
use serde::Serialize;
911
#[cfg(feature = "elements")]
1012
use std::{convert::TryFrom, io};
1113

@@ -65,6 +67,7 @@ impl From<U32Weight> for bitcoin::Weight {
6567
/// Programs that are CPU-heavy need to be padded
6668
/// so that the witness stack provides a large-enough budget.
6769
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
70+
#[cfg_attr(feature = "serde", derive(Serialize))]
6871
pub struct Cost(u32);
6972

7073
impl Cost {
@@ -234,6 +237,7 @@ impl From<Cost> for bitcoin::Weight {
234237

235238
/// Bounds on the resources required by a node during execution on the Bit Machine
236239
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
240+
#[cfg_attr(feature = "serde", derive(Serialize))]
237241
pub struct NodeBounds {
238242
/// Upper bound on the required number of cells (bits).
239243
/// The root additionally requires the bit width of its source and target type (input, output)

src/jet/elements/c_env.rs

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//! High level APIs for creating C FFI compatible environment.
44
//!
55
6+
use bitcoin::taproot::TAPROOT_ANNEX_PREFIX;
67
use hashes::Hash;
78
use std::os::raw::c_uchar;
89

@@ -33,7 +34,6 @@ struct RawOutputData {
3334
/// passed to the C FFI.
3435
#[derive(Debug)]
3536
struct RawInputData {
36-
#[allow(dead_code)] // see FIXME below
3737
pub annex: Option<Vec<c_uchar>>,
3838
// pegin
3939
pub genesis_hash: Option<[c_uchar; 32]>,
@@ -73,10 +73,10 @@ fn new_raw_input<'raw>(
7373
inp: &'raw elements::TxIn,
7474
in_utxo: &'raw ElementsUtxo,
7575
inp_data: &'raw RawInputData,
76+
annex: *const c_elements::CRawBuffer,
7677
) -> c_elements::CRawInput<'raw> {
7778
c_elements::CRawInput {
78-
// FIXME actually pass the annex in; see https://github.com/BlockstreamResearch/simplicity/issues/311 for some difficulty here.
79-
annex: core::ptr::null(),
79+
annex,
8080
prev_txid: inp.previous_output.txid.as_ref(),
8181
pegin: inp_data.genesis_hash.as_ref(),
8282
issuance: if inp.has_issuance() {
@@ -114,7 +114,7 @@ fn new_tx_data(tx: &elements::Transaction, in_utxos: &[ElementsUtxo]) -> RawTran
114114
};
115115
for (inp, in_utxo) in tx.input.iter().zip(in_utxos.iter()) {
116116
let inp_data = RawInputData {
117-
annex: None, // Actually store annex
117+
annex: get_annex(&inp.witness).map(|s| s.to_vec()),
118118
genesis_hash: inp
119119
.pegin_data()
120120
.map(|x| x.genesis_hash.to_raw_hash().to_byte_array()),
@@ -148,15 +148,28 @@ pub(super) fn new_tx(
148148
) -> *mut c_elements::CTransaction {
149149
let mut raw_inputs = Vec::new();
150150
let mut raw_outputs = Vec::new();
151+
152+
// SAFETY: this allocation *must* live until after the `simplicity_mallocTransaction`
153+
// at the bottom of this function. We convert the vector to a boxed slice to ensure
154+
// it cannot be resized, which would potentially trigger a reallocation.
155+
let mut raw_annexes = Vec::from_iter((0..tx.input.len()).map(|_| None)).into_boxed_slice();
156+
151157
let txid = tx.txid();
152158
let tx_data = new_tx_data(tx, in_utxos);
153-
for ((inp, in_utxo), inp_data) in tx
154-
.input
155-
.iter()
159+
for (((raw_annex, inp), in_utxo), inp_data) in raw_annexes
160+
.iter_mut()
161+
.zip(tx.input.iter())
156162
.zip(in_utxos.iter())
157163
.zip(tx_data.inputs.iter())
158164
{
159-
let res = new_raw_input(inp, in_utxo, inp_data);
165+
*raw_annex = inp_data
166+
.annex
167+
.as_ref()
168+
.map(|annex| c_elements::CRawBuffer::new(annex));
169+
let annex_ptr = raw_annex
170+
.as_ref()
171+
.map_or(core::ptr::null(), |b| b as *const _);
172+
let res = new_raw_input(inp, in_utxo, inp_data, annex_ptr);
160173
raw_inputs.push(res);
161174
}
162175
for (out, out_data) in tx.output.iter().zip(tx_data.outputs.iter()) {
@@ -172,10 +185,16 @@ pub(super) fn new_tx(
172185
version: tx.version,
173186
locktime: tx.lock_time.to_consensus_u32(),
174187
};
175-
unsafe {
188+
let ret = unsafe {
176189
// SAFETY: this is a FFI call and we constructed its argument correctly.
177190
c_elements::simplicity_mallocTransaction(&c_raw_tx)
178-
}
191+
};
192+
193+
// Explicitly drop raw_annexes so Rust doesn't try any funny business dropping it early.
194+
// Drop raw_inputs first since it contains pointers into raw_annexes
195+
drop(raw_inputs);
196+
drop(raw_annexes);
197+
ret
179198
}
180199

181200
pub(super) fn new_tap_env(
@@ -256,3 +275,13 @@ fn serialize_surjection_proof(surjection_proof: &Option<Box<SurjectionProof>>) -
256275
.map(|x| x.serialize())
257276
.unwrap_or_default()
258277
}
278+
279+
/// If the last item in the witness stack is an annex, return the data following the 0x50 byte.
280+
fn get_annex(in_witness: &elements::TxInWitness) -> Option<&[u8]> {
281+
let last_item = in_witness.script_witness.last()?;
282+
if *last_item.first()? == TAPROOT_ANNEX_PREFIX {
283+
Some(&last_item[1..])
284+
} else {
285+
None
286+
}
287+
}

0 commit comments

Comments
 (0)