Skip to content

Policy fixes #170

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Aug 20, 2023
Merged
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ path = "src/lib.rs"

[dependencies]
bitcoin = { version = "0.30.0", optional = true }
bitcoin-miniscript = { package = "miniscript", version = "10.0" }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In eb055fa:

I am tempted to change this from 10.0 to a range from 8.0.1 to 10. Later we will just bump the 10. Using a range will minimize the amount of grief that we'll have with coordinating new crate versions. (Though arguably we have no need to support miniscript 8 and 9 since those are in the past.)

byteorder = "1.3"
elements = { version = "0.22.0", optional = true }
hashes = { package = "bitcoin_hashes", version = "0.12" }
Expand Down
8 changes: 4 additions & 4 deletions jets-bench/benches/elements/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ pub enum EnvSampling {

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

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

fn null_env() -> ElementsEnv {
fn null_env() -> ElementsEnv<Arc<Transaction>> {
let tx = Transaction {
version: u32::default(),
lock_time: LockTime::ZERO,
Expand Down
2 changes: 1 addition & 1 deletion jets-bench/benches/elements/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ enum ElementsBenchEnvType {
}

impl ElementsBenchEnvType {
fn env(&self) -> ElementsEnv {
fn env(&self) -> ElementsEnv<Arc<elements::Transaction>> {
let n_in = NUM_TX_INPUTS;
let n_out = NUM_TX_OUTPUTS;
let env_sampler = match self {
Expand Down
17 changes: 10 additions & 7 deletions src/jet/elements/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::merkle::cmr::Cmr;
use elements::confidential;
use elements::taproot::ControlBlock;
use simplicity_sys::c_jets::c_env::CElementsTxEnv;
use std::sync::Arc;
use std::ops::Deref;

use super::c_env;

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

impl ElementsEnv {
impl<T> ElementsEnv<T>
where
T: Deref<Target = elements::Transaction>,
{
pub fn new(
tx: Arc<elements::Transaction>,
tx: T,
utxos: Vec<ElementsUtxo>,
ix: u32,
script_cmr: Cmr,
Expand Down Expand Up @@ -128,7 +131,7 @@ impl ElementsEnv {
}

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

ElementsEnv::new(
Arc::new(elements::Transaction {
std::sync::Arc::new(elements::Transaction {
version: 2,
lock_time,
// Enable locktime in dummy txin
Expand Down
3 changes: 2 additions & 1 deletion src/jet/init/elements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use std::io::Write;
use std::{fmt, str};
use crate::jet::elements::ElementsEnv;
use simplicity_sys::CElementsTxEnv;
use std::sync::Arc;

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

impl Jet for Elements {

type Environment = ElementsEnv;
type Environment = ElementsEnv<Arc<elements::Transaction>>;
type CJetEnvironment = CElementsTxEnv;

fn c_jet_env<'env>(&self, env: &'env Self::Environment) -> &'env Self::CJetEnvironment {
Expand Down
10 changes: 9 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ pub use bit_encoding::BitWriter;
pub use bit_encoding::{BitIter, EarlyEndOfStreamError};

#[cfg(feature = "elements")]
pub use crate::policy::{Policy, SimplicityKey, ToXOnlyPubkey, Translator};
pub use crate::policy::{
sighash, Policy, Preimage32, Satisfier, SimplicityKey, ToXOnlyPubkey, Translator,
};

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

/// Return the version of Simplicity leaves inside a tap tree.
#[cfg(feature = "elements")]
pub fn leaf_version() -> elements::taproot::LeafVersion {
elements::taproot::LeafVersion::from_u8(0xbe).expect("constant leaf version")
}

/// Error type for simplicity
#[non_exhaustive]
#[derive(Debug)]
Expand Down
51 changes: 50 additions & 1 deletion src/policy/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use super::serialize;
/// given a witness.
///
/// Furthermore, the policy can be normalized and is amenable to semantic analysis.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Policy<Pk: SimplicityKey> {
/// Unsatisfiable
Unsatisfiable(FailEntropy),
Expand Down Expand Up @@ -215,6 +215,19 @@ impl<Pk: SimplicityKey> Policy<Pk> {
_ => {}
}
}

/// Return an iterator over the fragments of the policy.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a036b0b:

You mention maybe using the dag module in the commit message -- actually we should use the miniscript::iter module which will be available in rust-miniscript 11.0. rust-bitcoin/rust-miniscript#567

But for now it's fine to have a manual implementation.

Copy link
Collaborator Author

@uncomputable uncomputable Aug 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Miniscript's iter module is the cousin of Simplicity's dag module. I look forward to using it :)

pub fn iter(&self) -> PolicyIter<'_, Pk> {
PolicyIter::new(self)
}

/// Return an iterator over the public keys of the policy.
pub fn iter_pk(&self) -> impl Iterator<Item = Pk> + '_ {
self.iter().filter_map(|fragment| match fragment {
Policy::Key(key) => Some(key.clone()),
_ => None,
})
}
}

impl<Pk: SimplicityKey> fmt::Debug for Policy<Pk> {
Expand Down Expand Up @@ -244,3 +257,39 @@ impl<Pk: SimplicityKey> fmt::Display for Policy<Pk> {
fmt::Debug::fmt(self, f)
}
}

/// Iterator over the fragments of a Simplicity policy.
///
/// The fragments are visited in preorder:
/// We first visit the parent, then the left subtree, then the right subtree.
pub struct PolicyIter<'a, Pk: SimplicityKey> {
stack: Vec<&'a Policy<Pk>>,
}

impl<'a, Pk: SimplicityKey> PolicyIter<'a, Pk> {
/// Create an iterator for the given policy.
pub fn new(policy: &'a Policy<Pk>) -> Self {
Self {
stack: vec![policy],
}
}
}

impl<'a, Pk: SimplicityKey> Iterator for PolicyIter<'a, Pk> {
type Item = &'a Policy<Pk>;

fn next(&mut self) -> Option<Self::Item> {
let top = self.stack.pop()?;
match top {
Policy::And { left, right } | Policy::Or { left, right } => {
self.stack.push(right);
self.stack.push(left);
}
Policy::Threshold(_, children) => {
self.stack.extend(children.iter().rev());
}
_ => {}
}
Some(top)
}
}
11 changes: 6 additions & 5 deletions src/policy/key.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use bitcoin_miniscript::{MiniscriptKey, ToPublicKey};
use elements::bitcoin::key::XOnlyPublicKey;
use hashes::sha256;
use std::fmt::{Debug, Display};
Expand All @@ -8,8 +9,8 @@ pub trait SimplicityKey: Clone + Eq + Ord + Debug + Display + std::hash::Hash {
type Sha256: Clone + Eq + Ord + Display + Debug + std::hash::Hash;
}

impl SimplicityKey for XOnlyPublicKey {
type Sha256 = sha256::Hash;
impl<Pk: MiniscriptKey> SimplicityKey for Pk {
type Sha256 = <Pk as MiniscriptKey>::Sha256;
}

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

impl ToXOnlyPubkey for XOnlyPublicKey {
impl<Pk: ToPublicKey> ToXOnlyPubkey for Pk {
fn to_x_only_pubkey(&self) -> XOnlyPublicKey {
*self
<Pk as ToPublicKey>::to_x_only_pubkey(self)
}

fn to_sha256(hash: &Self::Sha256) -> sha256::Hash {
*hash
<Pk as ToPublicKey>::to_sha256(hash)
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/policy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@
mod ast;
mod error;
mod key;
pub mod satisfy;
mod satisfy;
mod serialize;
pub mod sighash;

pub use ast::Policy;
pub use error::Error;
pub use key::{SimplicityKey, ToXOnlyPubkey, Translator};
pub use satisfy::{Preimage32, Satisfier};
9 changes: 7 additions & 2 deletions src/policy/satisfy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,9 @@ mod tests {
}
}

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

for i in 0..3 {
Expand Down Expand Up @@ -283,7 +285,10 @@ mod tests {
}
}

fn execute_successful(program: Arc<RedeemNode<Elements>>, env: &ElementsEnv) {
fn execute_successful(
program: Arc<RedeemNode<Elements>>,
env: &ElementsEnv<Arc<elements::Transaction>>,
) {
let mut mac = BitMachine::for_program(&program);
assert!(mac.exec(&program, env).is_ok());
}
Expand Down
9 changes: 7 additions & 2 deletions src/policy/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,12 @@ mod tests {
use hashes::{sha256, Hash};
use std::sync::Arc;

fn compile(policy: Policy<XOnlyPublicKey>) -> (Arc<ConstructNode<Elements>>, ElementsEnv) {
fn compile(
policy: Policy<XOnlyPublicKey>,
) -> (
Arc<ConstructNode<Elements>>,
ElementsEnv<Arc<elements::Transaction>>,
) {
let commit = policy.serialize_no_witness();
let env = ElementsEnv::dummy();

Expand All @@ -265,7 +270,7 @@ mod tests {
fn execute_successful(
commit: &ConstructNode<Elements>,
witness: Vec<Value>,
env: &ElementsEnv,
env: &ElementsEnv<Arc<elements::Transaction>>,
) -> bool {
let finalized = commit
.finalize_types()
Expand Down
Loading