Add support for verifying merkle proofs
This commit is contained in:
parent
15f99df4ba
commit
1490ff36ee
|
@ -113,6 +113,20 @@ impl TapTweakHash {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TapLeafHash {
|
||||||
|
/// function to compute leaf hash from components
|
||||||
|
pub fn from_script(script: &Script, ver: LeafVersion) -> TapLeafHash {
|
||||||
|
let mut eng = TapLeafHash::engine();
|
||||||
|
ver.as_u8()
|
||||||
|
.consensus_encode(&mut eng)
|
||||||
|
.expect("engines don't err");
|
||||||
|
script
|
||||||
|
.consensus_encode(&mut eng)
|
||||||
|
.expect("engines don't err");
|
||||||
|
TapLeafHash::from_engine(eng)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Maximum depth of a Taproot Tree Script spend path
|
/// Maximum depth of a Taproot Tree Script spend path
|
||||||
// https://github.com/bitcoin/bitcoin/blob/e826b22da252e0599c61d21c98ff89f366b3120f/src/script/interpreter.h#L229
|
// https://github.com/bitcoin/bitcoin/blob/e826b22da252e0599c61d21c98ff89f366b3120f/src/script/interpreter.h#L229
|
||||||
pub const TAPROOT_CONTROL_MAX_NODE_COUNT: usize = 128;
|
pub const TAPROOT_CONTROL_MAX_NODE_COUNT: usize = 128;
|
||||||
|
@ -133,6 +147,8 @@ pub const TAPROOT_CONTROL_BASE_SIZE: usize = 33;
|
||||||
pub const TAPROOT_CONTROL_MAX_SIZE: usize =
|
pub const TAPROOT_CONTROL_MAX_SIZE: usize =
|
||||||
TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT;
|
TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT;
|
||||||
|
|
||||||
|
// type alias for versioned tap script corresponding merkle proof
|
||||||
|
type ScriptMerkleProofMap = BTreeMap<(Script, LeafVersion), BTreeSet<TaprootMerkleBranch>>;
|
||||||
/// Data structure for representing Taproot spending information.
|
/// Data structure for representing Taproot spending information.
|
||||||
/// Taproot output corresponds to a combination of a
|
/// Taproot output corresponds to a combination of a
|
||||||
/// single public key condition (known the internal key), and zero or more
|
/// single public key condition (known the internal key), and zero or more
|
||||||
|
@ -164,7 +180,7 @@ pub struct TaprootSpendInfo {
|
||||||
/// appears in multiple branches of the tree. In all cases, keeping one should
|
/// appears in multiple branches of the tree. In all cases, keeping one should
|
||||||
/// be enough for spending funds, but we keep all of the paths so that
|
/// be enough for spending funds, but we keep all of the paths so that
|
||||||
/// a full tree can be constructed again from spending data if required.
|
/// a full tree can be constructed again from spending data if required.
|
||||||
script_map: BTreeMap<(Script, LeafVersion), BTreeSet<TaprootMerkleBranch>>,
|
script_map: ScriptMerkleProofMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TaprootSpendInfo {
|
impl TaprootSpendInfo {
|
||||||
|
@ -268,7 +284,7 @@ impl TaprootSpendInfo {
|
||||||
|
|
||||||
/// Output key(the key used in script pubkey) from Spend data. See also
|
/// Output key(the key used in script pubkey) from Spend data. See also
|
||||||
/// [`TaprootSpendInfo::output_key_parity`]
|
/// [`TaprootSpendInfo::output_key_parity`]
|
||||||
pub fn output_key<C: secp256k1::Verification>(&self) -> schnorr::PublicKey {
|
pub fn output_key(&self) -> schnorr::PublicKey {
|
||||||
self.output_key
|
self.output_key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,6 +318,29 @@ impl TaprootSpendInfo {
|
||||||
}
|
}
|
||||||
info
|
info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Access the internal script map
|
||||||
|
pub fn as_script_map(&self) -> &ScriptMerkleProofMap {
|
||||||
|
&self.script_map
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtain a [`ControlBlock`] for particular script with the given version.
|
||||||
|
/// Returns [`None`] if the script is not contained in the [`TaprootSpendInfo`]
|
||||||
|
/// If there are multiple ControlBlocks possible, this returns the shortest one.
|
||||||
|
pub fn control_block(&self, script_ver: &(Script, LeafVersion)) -> Option<ControlBlock> {
|
||||||
|
let merkle_branch_set = self.script_map.get(script_ver)?;
|
||||||
|
// Choose the smallest one amongst the multiple script maps
|
||||||
|
let smallest = merkle_branch_set
|
||||||
|
.iter()
|
||||||
|
.min_by(|x, y| x.0.len().cmp(&y.0.len()))
|
||||||
|
.expect("Invariant: Script map key must contain non-empty set value");
|
||||||
|
Some(ControlBlock {
|
||||||
|
internal_key: self.internal_key,
|
||||||
|
output_key_parity: self.output_key_parity,
|
||||||
|
leaf_version: LeafVersion::default(),
|
||||||
|
merkle_branch: smallest.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builder for building taproot iteratively. Users can specify tap leaf or omitted/hidden
|
/// Builder for building taproot iteratively. Users can specify tap leaf or omitted/hidden
|
||||||
|
@ -521,17 +560,11 @@ impl LeafInfo {
|
||||||
|
|
||||||
// Compute a leaf hash for the given leaf
|
// Compute a leaf hash for the given leaf
|
||||||
fn hash(&self) -> sha256::Hash {
|
fn hash(&self) -> sha256::Hash {
|
||||||
let mut eng = TapLeafHash::engine();
|
let leaf_hash = TapLeafHash::from_script(&self.script, self.ver);
|
||||||
self.ver
|
sha256::Hash::from_inner(leaf_hash.into_inner())
|
||||||
.as_u8()
|
|
||||||
.consensus_encode(&mut eng)
|
|
||||||
.expect("engines don't err");
|
|
||||||
self.script
|
|
||||||
.consensus_encode(&mut eng)
|
|
||||||
.expect("engines don't err");
|
|
||||||
sha256::Hash::from_engine(eng)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The Merkle proof for inclusion of a tree in a taptree hash
|
/// The Merkle proof for inclusion of a tree in a taptree hash
|
||||||
// The type of hash is sha256::Hash because the vector might contain
|
// The type of hash is sha256::Hash because the vector might contain
|
||||||
// both TapBranchHash and TapLeafHash
|
// both TapBranchHash and TapLeafHash
|
||||||
|
@ -673,6 +706,42 @@ impl ControlBlock {
|
||||||
.expect("writers don't error");
|
.expect("writers don't error");
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verify that a control block is correct proof for a given output key and script
|
||||||
|
/// This only checks that script is contained inside the taptree described by
|
||||||
|
/// output key, full verification must also execute the script with witness data
|
||||||
|
pub fn verify_taproot_commitment<C: secp256k1::Verification>(
|
||||||
|
&self,
|
||||||
|
secp: &Secp256k1<C>,
|
||||||
|
output_key: &schnorr::PublicKey,
|
||||||
|
script: &Script,
|
||||||
|
) -> bool {
|
||||||
|
// compute the script hash
|
||||||
|
// Initially the curr_hash is the leaf hash
|
||||||
|
let leaf_hash = TapLeafHash::from_script(&script, self.leaf_version);
|
||||||
|
let mut curr_hash = TapBranchHash::from_inner(leaf_hash.into_inner());
|
||||||
|
// Verify the proof
|
||||||
|
for elem in self.merkle_branch.as_inner() {
|
||||||
|
let mut eng = TapBranchHash::engine();
|
||||||
|
if curr_hash.as_inner() < elem.as_inner() {
|
||||||
|
eng.input(&curr_hash);
|
||||||
|
eng.input(elem);
|
||||||
|
} else {
|
||||||
|
eng.input(elem);
|
||||||
|
eng.input(&curr_hash);
|
||||||
|
}
|
||||||
|
// Recalculate the curr hash as parent hash
|
||||||
|
curr_hash = TapBranchHash::from_engine(eng);
|
||||||
|
}
|
||||||
|
// compute the taptweak
|
||||||
|
let tweak = TapTweakHash::from_key_and_tweak(self.internal_key, Some(curr_hash));
|
||||||
|
self.internal_key.tweak_add_check(
|
||||||
|
secp,
|
||||||
|
output_key,
|
||||||
|
self.output_key_parity,
|
||||||
|
tweak.into_inner(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The leaf version for tapleafs
|
/// The leaf version for tapleafs
|
||||||
|
|
Loading…
Reference in New Issue