Skip to content
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
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ required-features = ["std", "base64"]
name = "big"
required-features = ["std", "base64", "compiler"]

[[example]]
name = "taptree_of_horror"
path = "examples/taptree_of_horror/taptree_of_horror.rs"
required-features = ["compiler"]

[workspace]
members = ["fuzz"]
exclude = ["embedded", "bitcoind-tests"]
20 changes: 20 additions & 0 deletions examples/taptree_of_horror/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Taptree of Horror Example

### Running this example:
- `cargo run --example taptree_of_horror --features "compiler"`

### Originally based on the TABConf 6, CTB.
The challenge can be found here:
- https://tabctb.com/six
- https://tabctb.com/six/thebeginning/thetree/grim/iacceptyourterms.html

### This example demonstrates:
- Providing multiple extended private key (xpriv) descriptors for sample personas.
- Creating a policy using logical 'and/or' conditions with preimages and signatures and timelocks.
- Structuring a Taproot tree (taptree) with an internal key into logical branches and leaves based on the policy.
- Implementing nine complex tapleaves within the taptree.
- Building a spending transaction that signs and satisfies one of the tapleaves using signatures, preimages and a timelock.

### Helpful Graphic to visualize using Excalidraw
![taptree_of_horror](./taptree_of_horror.png)

59 changes: 59 additions & 0 deletions examples/taptree_of_horror/helper_fns.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use std::str::FromStr;

use bitcoin::bip32::{DerivationPath, Xpriv};
use bitcoin::hashes::{ripemd160, sha256, Hash};
use miniscript::descriptor::DescriptorSecretKey;
use miniscript::ToPublicKey;
use secp256k1::Secp256k1;

use crate::KEYS_PER_PERSONA;

pub fn produce_grim_hash(secret: &str) -> (sha256::Hash, ripemd160::Hash) {
let mut hash_holder = sha256::Hash::hash(secret.as_bytes());
for _i in 0..5 {
hash_holder = sha256::Hash::hash(hash_holder.as_byte_array());
//println!("{} hash: {}", i, hash_holder);
}

let ripemd_160_final = ripemd160::Hash::hash(hash_holder.as_byte_array());
(hash_holder, ripemd_160_final)
}

pub fn produce_kelly_hash(secret: &str) -> (sha256::Hash, sha256::Hash) {
let prepreimage = secret.as_bytes();
let preimage_256_hash = sha256::Hash::hash(prepreimage);
let result256_final = sha256::Hash::hash(&preimage_256_hash.to_byte_array());
(preimage_256_hash, result256_final)
}

pub fn produce_key_pairs(
desc: DescriptorSecretKey,
secp: &Secp256k1<secp256k1::All>,
derivation_without_index: &str,
_alias: &str,
) -> (Vec<bitcoin::PublicKey>, Vec<Xpriv>) {
let mut pks = Vec::new();
let mut prvs = Vec::new();

let xprv = match &desc {
DescriptorSecretKey::XPrv(xpriv) => xpriv,
_ => panic!("not an xpriv"),
};

for i in 0..KEYS_PER_PERSONA {
let pk = desc
.to_public(secp)
.unwrap()
.at_derivation_index(i.try_into().unwrap())
.unwrap()
.to_public_key();

let derivation_with_index = format!("{}/{}", derivation_without_index, i);
let derivation_path = DerivationPath::from_str(&derivation_with_index).unwrap();
let derived_xpriv: Xpriv = xprv.xkey.derive_priv(secp, &derivation_path).unwrap();

pks.push(pk);
prvs.push(derived_xpriv);
}
(pks, prvs)
}
Loading
Loading