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
18 changes: 17 additions & 1 deletion src/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use std::io;

use bitcoin::blockdata::script::Script;
use bitcoin::BitcoinHash;
use bitcoin::hashes::{Hash, sha256d};
use bitcoin::hashes::{Hash, sha256d, sha256};
#[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "serde")] use std::fmt;

Expand Down Expand Up @@ -222,6 +222,22 @@ pub struct BlockHeader {
}
serde_struct_impl!(BlockHeader, version, prev_blockhash, merkle_root, time, height, ext);

impl BlockHeader {
/// Calculate the root of the dynafed params. Returns [None] when not dynafed.
pub fn calculate_dynafed_params_root(&self) -> Option<sha256::Midstate> {
match self.ext {
ExtData::Proof { .. } => None,
ExtData::Dynafed { ref current, ref proposed, .. } => {
let leaves = [
current.calculate_root().into_inner(),
proposed.calculate_root().into_inner(),
];
Some(::fast_merkle_root::fast_merkle_root(&leaves[..]))
}
}
}
}

impl Encodable for BlockHeader {
fn consensus_encode<S: io::Write>(&self, mut s: S) -> Result<usize, encode::Error> {
let version = if let ExtData::Dynafed { .. } = self.ext {
Expand Down
175 changes: 175 additions & 0 deletions src/dynafed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use std::io;

use bitcoin;
use bitcoin::hashes::{Hash, sha256, sha256d};
#[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "serde")] use std::fmt;

Expand Down Expand Up @@ -53,6 +54,102 @@ pub enum Params {
},
}

impl Params {
/// Check whether this is [Params::Null].
pub fn is_null(&self) -> bool {
match *self {
Params::Null => true,
Params::Compact { .. } => false,
Params::Full { .. } => false,
}
}

/// Check whether this is [Params::Compact].
pub fn is_compact(&self) -> bool {
match *self {
Params::Null => false,
Params::Compact { .. } => true,
Params::Full { .. } => false,
}
}

/// Check whether this is [Params::Full].
pub fn is_full(&self) -> bool {
match *self {
Params::Null => false,
Params::Compact { .. } => false,
Params::Full { .. } => true,
}
}

/// Get the signblockscript. Is [None] for [Null] params.
pub fn signblockscript(&self) -> Option<&bitcoin::Script> {
match *self {
Params::Null => None,
Params::Compact { ref signblockscript, ..} => Some(signblockscript),
Params::Full { ref signblockscript, ..} => Some(signblockscript),
}
}

/// Get the signblock_witness_limit. Is [None] for [Null] params.
pub fn signblock_witness_limit(&self) -> Option<u32> {
match *self {
Params::Null => None,
Params::Compact { signblock_witness_limit, ..} => Some(signblock_witness_limit),
Params::Full { signblock_witness_limit, ..} => Some(signblock_witness_limit),
}
}

/// Get the fedpeg_program. Is [None] for non-[Full] params.
pub fn fedpeg_program(&self) -> Option<&bitcoin::Script> {
match *self {
Params::Null => None,
Params::Compact { .. } => None,
Params::Full { ref fedpeg_program, ..} => Some(fedpeg_program),
}
}

/// Get the fedpegscript. Is [None] for non-[Full] params.
pub fn fedpegscript(&self) -> Option<&Vec<u8>> {
match *self {
Params::Null => None,
Params::Compact { .. } => None,
Params::Full { ref fedpegscript, ..} => Some(fedpegscript),
}
}

/// Get the extension_space. Is [None] for non-[Full] params.
pub fn extension_space(&self) -> Option<&Vec<Vec<u8>>> {
match *self {
Params::Null => None,
Params::Compact { .. } => None,
Params::Full { ref extension_space, ..} => Some(extension_space),
}
}

/// Calculate the root of this [Params].
pub fn calculate_root(&self) -> sha256::Midstate {
fn serialize_hash<E: Encodable>(obj: &E) -> sha256d::Hash {
let mut engine = sha256d::Hash::engine();
obj.consensus_encode(&mut engine).expect("engines don't error");
sha256d::Hash::from_engine(engine)
}

if self.is_null() {
return sha256::Midstate::from_inner([0u8; 32]);
}

let leaves = [
serialize_hash(self.signblockscript().unwrap()).into_inner(),
serialize_hash(&self.signblock_witness_limit().unwrap()).into_inner(),
serialize_hash(self.fedpeg_program().unwrap_or(&bitcoin::Script::new())).into_inner(),
serialize_hash(self.fedpegscript().unwrap_or(&Vec::new())).into_inner(),
serialize_hash(self.extension_space().unwrap_or(&Vec::new())).into_inner(),
];
::fast_merkle_root::fast_merkle_root(&leaves[..])
}
}

#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Params {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
Expand Down Expand Up @@ -272,3 +369,81 @@ impl Decodable for Params {
}
}
}

#[cfg(test)]
mod tests {
use super::*;

use bitcoin;
use bitcoin::hashes::hex::ToHex;

#[test]
fn test_param_roots() {
// Taken from the following Elements Core test:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would have been better I think to just include a github link to commit and line number. But not all that important.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah the test in Elements was still up for review and might not be merged or be changed in the future. I figured it's not that big so better just make sure to have it.


// CScript signblockscript(opcodetype(1));
// uint32_t signblock_wl(2);
// CScript fp_program(opcodetype(3));
// CScript fp_script(opcodetype(4));
// std::vector<std::vector<unsigned char>> ext{ {5, 6}, {7} };
//
// DynaFedParamEntry compact_entry = DynaFedParamEntry(signblockscript, signblock_wl);
// BOOST_CHECK_EQUAL(
// compact_entry.CalculateRoot().GetHex(),
// "dff5f3793abc06a6d75e80fe3cfd47406f732fa4ec9305960ae2a229222a1ad5"
// );
//
// DynaFedParamEntry full_entry =
// DynaFedParamEntry(signblockscript, signblock_wl, fp_program, fp_script, ext);
// BOOST_CHECK_EQUAL(
// full_entry.CalculateRoot().GetHex(),
// "175be2087ba7cc0e33348bef493bd3e34f31f64bf9226e5881ab310dafa432ff"
// );
//
// DynaFedParams params = DynaFedParams(compact_entry, full_entry);
// BOOST_CHECK_EQUAL(
// params.CalculateRoot().GetHex(),
// "e56cf79487952dfa85fe6a85829600adc19714ba6ab1157fdff02b25ae60cee2"
// );

let signblockscript: bitcoin::Script = vec![1].into();
let signblock_wl = 2;
let fp_program: bitcoin::Script = vec![3].into();
let fp_script = vec![4];
let ext = vec![vec![5, 6], vec![7]];

let compact_entry = Params::Compact {
signblockscript: signblockscript.clone(),
signblock_witness_limit: signblock_wl,
};
assert_eq!(
compact_entry.calculate_root().to_hex(),
"dff5f3793abc06a6d75e80fe3cfd47406f732fa4ec9305960ae2a229222a1ad5"
Copy link
Collaborator

Choose a reason for hiding this comment

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

This doesn't appear to match elements test hash 4587cf1c2448740c3fe4bf0d3ab22f3f193b18f43c4ee47da3690c53becb7fa4?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah yeah I forgot to update those after I changed the test. In this PR you can see the correct ones: https://github.com/ElementsProject/elements/pull/719/files
I'll fix this in a force-push.

);

let full_entry = Params::Full {
signblockscript: signblockscript,
signblock_witness_limit: signblock_wl,
fedpeg_program: fp_program,
fedpegscript: fp_script,
extension_space: ext,
};
assert_eq!(
full_entry.calculate_root().to_hex(),
"175be2087ba7cc0e33348bef493bd3e34f31f64bf9226e5881ab310dafa432ff"
);

let header = ::block::BlockHeader{
ext: ::block::ExtData::Dynafed {
current: compact_entry,
proposed: full_entry,
signblock_witness: vec![],
},
..Default::default()
};
assert_eq!(
header.calculate_dynafed_params_root().unwrap().to_hex(),
"e56cf79487952dfa85fe6a85829600adc19714ba6ab1157fdff02b25ae60cee2"
);
}
}