Skip to content

Commit 676fd16

Browse files
committed
Merge #170: Policy fixes
d64e3b2 policy: Iterator over fragments and keys (Christian Lewe) 1bdedd1 policy: Add sighash cache (Christian Lewe) 67dfb83 elements: Environment with generic transaction ref (Christian Lewe) 9182897 policy: Export satisfy structs (Christian Lewe) 36b4ad9 Add accessor to Simplicity's leaf version (Christian Lewe) dfa267e policy: Implement Hash for Policy (Christian Lewe) 3fcf2f7 policy: Implement Simplicity key traits for Miniscript equivalents (Christian Lewe) Pull request description: Adds fixes to make Policy work with Miniscript, descriptors and the Simplicity wallet. The only controversial change should be the implementation of Simplicity key traits for every Miniscript key. This introduces a diamond pattern: rust-miniscript → rust-simplicity → elements-miniscript and rust-miniscript → elements-miniscript. The situation is not ideal, but it avoids violating the orphan rule that caused us so much trouble. In particular we need to convert `elements_miniscript::Satisfier` (with Miniscript keys) into `simplicity::Satisfier` (with Simplicity keys). We could make `elements_miniscript::Satisfier` a subtrait of simplicity::Satisfier, but we would have to do that for every generic implementation of `elements_miniscript::Satisfier`. We would need to copy code to rust-simplicity and implement `simplicity::Satisfier` for nonsensical types such as `HashMap<Pk, ElementsSig>`. The diamond pattern seems like a good solution that will work until we have a better architecture. It allows us to use Miniscript and Simplicity keys interchangeably, while all the messy Simplicity details stay inside rust-simplicity. ACKs for top commit: apoelstra: ACK d64e3b2 Tree-SHA512: 16a84a2dc6eadef0bd714ab8bacbac7b29a4d5813121028ff04bd290380f24e82093cd71db291a9431501cccfce4895e36727cf0af635a29413e387f98e2b752
2 parents dc0d175 + d64e3b2 commit 676fd16

File tree

12 files changed

+194
-25
lines changed

12 files changed

+194
-25
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ path = "src/lib.rs"
1717

1818
[dependencies]
1919
bitcoin = { version = "0.30.0", optional = true }
20+
bitcoin-miniscript = { package = "miniscript", version = "10.0" }
2021
byteorder = "1.3"
2122
elements = { version = "0.22.0", optional = true }
2223
hashes = { package = "bitcoin_hashes", version = "0.12" }

jets-bench/benches/elements/env.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ pub enum EnvSampling {
2828

2929
impl EnvSampling {
3030
/// Obtain a random environment without any annex
31-
pub fn env(&self) -> ElementsEnv {
31+
pub fn env(&self) -> ElementsEnv<Arc<Transaction>> {
3232
self.env_with_annex(None)
3333
}
3434

3535
/// Obtains a env with annex
36-
pub fn env_with_annex(&self, annex: Option<Vec<u8>>) -> ElementsEnv {
36+
pub fn env_with_annex(&self, annex: Option<Vec<u8>>) -> ElementsEnv<Arc<Transaction>> {
3737
let ((txin, spent_utxo), n_in, n_out) = match self {
3838
EnvSampling::Null => return null_env(),
3939
EnvSampling::Issuance(n_in, n_out) => (txin_utils::issuance(), n_in, n_out),
@@ -81,7 +81,7 @@ fn env_with_spent_utxos(
8181
tx: Transaction,
8282
utxos: Vec<ElementsUtxo>,
8383
annex: Option<Vec<u8>>,
84-
) -> ElementsEnv {
84+
) -> ElementsEnv<Arc<Transaction>> {
8585
let ctrl_blk: [u8; 33] = [
8686
0xc0, 0xeb, 0x04, 0xb6, 0x8e, 0x9a, 0x26, 0xd1, 0x16, 0x04, 0x6c, 0x76, 0xe8, 0xff, 0x47,
8787
0x33, 0x2f, 0xb7, 0x1d, 0xda, 0x90, 0xff, 0x4b, 0xef, 0x53, 0x70, 0xf2, 0x52, 0x26, 0xd3,
@@ -98,7 +98,7 @@ fn env_with_spent_utxos(
9898
)
9999
}
100100

101-
fn null_env() -> ElementsEnv {
101+
fn null_env() -> ElementsEnv<Arc<Transaction>> {
102102
let tx = Transaction {
103103
version: u32::default(),
104104
lock_time: LockTime::ZERO,

jets-bench/benches/elements/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ enum ElementsBenchEnvType {
5959
}
6060

6161
impl ElementsBenchEnvType {
62-
fn env(&self) -> ElementsEnv {
62+
fn env(&self) -> ElementsEnv<Arc<elements::Transaction>> {
6363
let n_in = NUM_TX_INPUTS;
6464
let n_out = NUM_TX_OUTPUTS;
6565
let env_sampler = match self {

src/jet/elements/environment.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use crate::merkle::cmr::Cmr;
1616
use elements::confidential;
1717
use elements::taproot::ControlBlock;
1818
use simplicity_sys::c_jets::c_env::CElementsTxEnv;
19-
use std::sync::Arc;
19+
use std::ops::Deref;
2020

2121
use super::c_env;
2222

@@ -58,11 +58,11 @@ impl From<elements::TxOut> for ElementsUtxo {
5858
// Similar story if we tried to use a &'a elements::Transaction rather than
5959
// an Arc: we'd have a lifetime parameter <'a> that would cause us trouble.
6060
#[allow(dead_code)]
61-
pub struct ElementsEnv {
61+
pub struct ElementsEnv<T: Deref<Target = elements::Transaction>> {
6262
/// The CTxEnv struct
6363
c_tx_env: CElementsTxEnv,
6464
/// The elements transaction
65-
tx: Arc<elements::Transaction>,
65+
tx: T,
6666
/// the current index of the input
6767
ix: u32,
6868
/// Control block used to spend this leaf script
@@ -73,9 +73,12 @@ pub struct ElementsEnv {
7373
genesis_hash: elements::BlockHash,
7474
}
7575

76-
impl ElementsEnv {
76+
impl<T> ElementsEnv<T>
77+
where
78+
T: Deref<Target = elements::Transaction>,
79+
{
7780
pub fn new(
78-
tx: Arc<elements::Transaction>,
81+
tx: T,
7982
utxos: Vec<ElementsUtxo>,
8083
ix: u32,
8184
script_cmr: Cmr,
@@ -128,7 +131,7 @@ impl ElementsEnv {
128131
}
129132

130133
#[cfg(test)]
131-
impl ElementsEnv {
134+
impl ElementsEnv<std::sync::Arc<elements::Transaction>> {
132135
/// Return a dummy Elements environment
133136
pub fn dummy() -> Self {
134137
Self::dummy_with(elements::LockTime::ZERO, elements::Sequence::MAX)
@@ -146,7 +149,7 @@ impl ElementsEnv {
146149
];
147150

148151
ElementsEnv::new(
149-
Arc::new(elements::Transaction {
152+
std::sync::Arc::new(elements::Transaction {
150153
version: 2,
151154
lock_time,
152155
// Enable locktime in dummy txin

src/jet/init/elements.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use std::io::Write;
1212
use std::{fmt, str};
1313
use crate::jet::elements::ElementsEnv;
1414
use simplicity_sys::CElementsTxEnv;
15+
use std::sync::Arc;
1516

1617
/// Elements jet family
1718
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
@@ -313,7 +314,7 @@ pub enum Elements {
313314

314315
impl Jet for Elements {
315316

316-
type Environment = ElementsEnv;
317+
type Environment = ElementsEnv<Arc<elements::Transaction>>;
317318
type CJetEnvironment = CElementsTxEnv;
318319

319320
fn c_jet_env<'env>(&self, env: &'env Self::Environment) -> &'env Self::CJetEnvironment {

src/lib.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ pub use bit_encoding::BitWriter;
5555
pub use bit_encoding::{BitIter, EarlyEndOfStreamError};
5656

5757
#[cfg(feature = "elements")]
58-
pub use crate::policy::{Policy, SimplicityKey, ToXOnlyPubkey, Translator};
58+
pub use crate::policy::{
59+
sighash, Policy, Preimage32, Satisfier, SimplicityKey, ToXOnlyPubkey, Translator,
60+
};
5961

6062
pub use crate::bit_machine::BitMachine;
6163
pub use crate::encode::{encode_natural, encode_value, encode_witness};
@@ -71,6 +73,12 @@ pub use crate::value::Value;
7173
pub use simplicity_sys as ffi;
7274
use std::fmt;
7375

76+
/// Return the version of Simplicity leaves inside a tap tree.
77+
#[cfg(feature = "elements")]
78+
pub fn leaf_version() -> elements::taproot::LeafVersion {
79+
elements::taproot::LeafVersion::from_u8(0xbe).expect("constant leaf version")
80+
}
81+
7482
/// Error type for simplicity
7583
#[non_exhaustive]
7684
#[derive(Debug)]

src/policy/ast.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use super::serialize;
3737
/// given a witness.
3838
///
3939
/// Furthermore, the policy can be normalized and is amenable to semantic analysis.
40-
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
40+
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
4141
pub enum Policy<Pk: SimplicityKey> {
4242
/// Unsatisfiable
4343
Unsatisfiable(FailEntropy),
@@ -215,6 +215,19 @@ impl<Pk: SimplicityKey> Policy<Pk> {
215215
_ => {}
216216
}
217217
}
218+
219+
/// Return an iterator over the fragments of the policy.
220+
pub fn iter(&self) -> PolicyIter<'_, Pk> {
221+
PolicyIter::new(self)
222+
}
223+
224+
/// Return an iterator over the public keys of the policy.
225+
pub fn iter_pk(&self) -> impl Iterator<Item = Pk> + '_ {
226+
self.iter().filter_map(|fragment| match fragment {
227+
Policy::Key(key) => Some(key.clone()),
228+
_ => None,
229+
})
230+
}
218231
}
219232

220233
impl<Pk: SimplicityKey> fmt::Debug for Policy<Pk> {
@@ -244,3 +257,39 @@ impl<Pk: SimplicityKey> fmt::Display for Policy<Pk> {
244257
fmt::Debug::fmt(self, f)
245258
}
246259
}
260+
261+
/// Iterator over the fragments of a Simplicity policy.
262+
///
263+
/// The fragments are visited in preorder:
264+
/// We first visit the parent, then the left subtree, then the right subtree.
265+
pub struct PolicyIter<'a, Pk: SimplicityKey> {
266+
stack: Vec<&'a Policy<Pk>>,
267+
}
268+
269+
impl<'a, Pk: SimplicityKey> PolicyIter<'a, Pk> {
270+
/// Create an iterator for the given policy.
271+
pub fn new(policy: &'a Policy<Pk>) -> Self {
272+
Self {
273+
stack: vec![policy],
274+
}
275+
}
276+
}
277+
278+
impl<'a, Pk: SimplicityKey> Iterator for PolicyIter<'a, Pk> {
279+
type Item = &'a Policy<Pk>;
280+
281+
fn next(&mut self) -> Option<Self::Item> {
282+
let top = self.stack.pop()?;
283+
match top {
284+
Policy::And { left, right } | Policy::Or { left, right } => {
285+
self.stack.push(right);
286+
self.stack.push(left);
287+
}
288+
Policy::Threshold(_, children) => {
289+
self.stack.extend(children.iter().rev());
290+
}
291+
_ => {}
292+
}
293+
Some(top)
294+
}
295+
}

src/policy/key.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use bitcoin_miniscript::{MiniscriptKey, ToPublicKey};
12
use elements::bitcoin::key::XOnlyPublicKey;
23
use hashes::sha256;
34
use std::fmt::{Debug, Display};
@@ -8,8 +9,8 @@ pub trait SimplicityKey: Clone + Eq + Ord + Debug + Display + std::hash::Hash {
89
type Sha256: Clone + Eq + Ord + Display + Debug + std::hash::Hash;
910
}
1011

11-
impl SimplicityKey for XOnlyPublicKey {
12-
type Sha256 = sha256::Hash;
12+
impl<Pk: MiniscriptKey> SimplicityKey for Pk {
13+
type Sha256 = <Pk as MiniscriptKey>::Sha256;
1314
}
1415

1516
/// Public key which can be converted to a (x-only) public key which can be used in Simplicity.
@@ -21,13 +22,13 @@ pub trait ToXOnlyPubkey: SimplicityKey {
2122
fn to_sha256(hash: &Self::Sha256) -> sha256::Hash;
2223
}
2324

24-
impl ToXOnlyPubkey for XOnlyPublicKey {
25+
impl<Pk: ToPublicKey> ToXOnlyPubkey for Pk {
2526
fn to_x_only_pubkey(&self) -> XOnlyPublicKey {
26-
*self
27+
<Pk as ToPublicKey>::to_x_only_pubkey(self)
2728
}
2829

2930
fn to_sha256(hash: &Self::Sha256) -> sha256::Hash {
30-
*hash
31+
<Pk as ToPublicKey>::to_sha256(hash)
3132
}
3233
}
3334

src/policy/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@
2828
mod ast;
2929
mod error;
3030
mod key;
31-
pub mod satisfy;
31+
mod satisfy;
3232
mod serialize;
33+
pub mod sighash;
3334

3435
pub use ast::Policy;
3536
pub use error::Error;
3637
pub use key::{SimplicityKey, ToXOnlyPubkey, Translator};
38+
pub use satisfy::{Preimage32, Satisfier};

src/policy/satisfy.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,9 @@ mod tests {
249249
}
250250
}
251251

252-
fn get_satisfier(env: &ElementsEnv) -> PolicySatisfier<XOnlyPublicKey> {
252+
fn get_satisfier(
253+
env: &ElementsEnv<Arc<elements::Transaction>>,
254+
) -> PolicySatisfier<XOnlyPublicKey> {
253255
let mut preimages = HashMap::new();
254256

255257
for i in 0..3 {
@@ -283,7 +285,10 @@ mod tests {
283285
}
284286
}
285287

286-
fn execute_successful(program: Arc<RedeemNode<Elements>>, env: &ElementsEnv) {
288+
fn execute_successful(
289+
program: Arc<RedeemNode<Elements>>,
290+
env: &ElementsEnv<Arc<elements::Transaction>>,
291+
) {
287292
let mut mac = BitMachine::for_program(&program);
288293
assert!(mac.exec(&program, env).is_ok());
289294
}

src/policy/serialize.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,12 @@ mod tests {
255255
use hashes::{sha256, Hash};
256256
use std::sync::Arc;
257257

258-
fn compile(policy: Policy<XOnlyPublicKey>) -> (Arc<ConstructNode<Elements>>, ElementsEnv) {
258+
fn compile(
259+
policy: Policy<XOnlyPublicKey>,
260+
) -> (
261+
Arc<ConstructNode<Elements>>,
262+
ElementsEnv<Arc<elements::Transaction>>,
263+
) {
259264
let commit = policy.serialize_no_witness();
260265
let env = ElementsEnv::dummy();
261266

@@ -265,7 +270,7 @@ mod tests {
265270
fn execute_successful(
266271
commit: &ConstructNode<Elements>,
267272
witness: Vec<Value>,
268-
env: &ElementsEnv,
273+
env: &ElementsEnv<Arc<elements::Transaction>>,
269274
) -> bool {
270275
let finalized = commit
271276
.finalize_types()

0 commit comments

Comments
 (0)