|
17 | 17 | use std::io; |
18 | 18 |
|
19 | 19 | use bitcoin; |
| 20 | +use bitcoin::hashes::{Hash, sha256, sha256d}; |
20 | 21 | #[cfg(feature = "serde")] use serde::{Deserialize, Deserializer, Serialize, Serializer}; |
21 | 22 | #[cfg(feature = "serde")] use std::fmt; |
22 | 23 |
|
@@ -53,6 +54,102 @@ pub enum Params { |
53 | 54 | }, |
54 | 55 | } |
55 | 56 |
|
| 57 | +impl Params { |
| 58 | + /// Check whether this is [Params::Null]. |
| 59 | + pub fn is_null(&self) -> bool { |
| 60 | + match *self { |
| 61 | + Params::Null => true, |
| 62 | + Params::Compact { .. } => false, |
| 63 | + Params::Full { .. } => false, |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + /// Check whether this is [Params::Compact]. |
| 68 | + pub fn is_compact(&self) -> bool { |
| 69 | + match *self { |
| 70 | + Params::Null => false, |
| 71 | + Params::Compact { .. } => true, |
| 72 | + Params::Full { .. } => false, |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + /// Check whether this is [Params::Full]. |
| 77 | + pub fn is_full(&self) -> bool { |
| 78 | + match *self { |
| 79 | + Params::Null => false, |
| 80 | + Params::Compact { .. } => false, |
| 81 | + Params::Full { .. } => true, |
| 82 | + } |
| 83 | + } |
| 84 | + |
| 85 | + /// Get the signblockscript. Is [None] for [Null] params. |
| 86 | + pub fn signblockscript(&self) -> Option<&bitcoin::Script> { |
| 87 | + match *self { |
| 88 | + Params::Null => None, |
| 89 | + Params::Compact { ref signblockscript, ..} => Some(signblockscript), |
| 90 | + Params::Full { ref signblockscript, ..} => Some(signblockscript), |
| 91 | + } |
| 92 | + } |
| 93 | + |
| 94 | + /// Get the signblock_witness_limit. Is [None] for [Null] params. |
| 95 | + pub fn signblock_witness_limit(&self) -> Option<u32> { |
| 96 | + match *self { |
| 97 | + Params::Null => None, |
| 98 | + Params::Compact { signblock_witness_limit, ..} => Some(signblock_witness_limit), |
| 99 | + Params::Full { signblock_witness_limit, ..} => Some(signblock_witness_limit), |
| 100 | + } |
| 101 | + } |
| 102 | + |
| 103 | + /// Get the fedpeg_program. Is [None] for non-[Full] params. |
| 104 | + pub fn fedpeg_program(&self) -> Option<&bitcoin::Script> { |
| 105 | + match *self { |
| 106 | + Params::Null => None, |
| 107 | + Params::Compact { .. } => None, |
| 108 | + Params::Full { ref fedpeg_program, ..} => Some(fedpeg_program), |
| 109 | + } |
| 110 | + } |
| 111 | + |
| 112 | + /// Get the fedpegscript. Is [None] for non-[Full] params. |
| 113 | + pub fn fedpegscript(&self) -> Option<&Vec<u8>> { |
| 114 | + match *self { |
| 115 | + Params::Null => None, |
| 116 | + Params::Compact { .. } => None, |
| 117 | + Params::Full { ref fedpegscript, ..} => Some(fedpegscript), |
| 118 | + } |
| 119 | + } |
| 120 | + |
| 121 | + /// Get the extension_space. Is [None] for non-[Full] params. |
| 122 | + pub fn extension_space(&self) -> Option<&Vec<Vec<u8>>> { |
| 123 | + match *self { |
| 124 | + Params::Null => None, |
| 125 | + Params::Compact { .. } => None, |
| 126 | + Params::Full { ref extension_space, ..} => Some(extension_space), |
| 127 | + } |
| 128 | + } |
| 129 | + |
| 130 | + /// Calculate the root of this [Params]. |
| 131 | + pub fn calculate_root(&self) -> sha256::Midstate { |
| 132 | + fn serialize_hash<E: Encodable>(obj: &E) -> sha256d::Hash { |
| 133 | + let mut engine = sha256d::Hash::engine(); |
| 134 | + obj.consensus_encode(&mut engine).expect("engines don't error"); |
| 135 | + sha256d::Hash::from_engine(engine) |
| 136 | + } |
| 137 | + |
| 138 | + if self.is_null() { |
| 139 | + return sha256::Midstate::from_inner([0u8; 32]); |
| 140 | + } |
| 141 | + |
| 142 | + let leaves = [ |
| 143 | + serialize_hash(self.signblockscript().unwrap()).into_inner(), |
| 144 | + serialize_hash(&self.signblock_witness_limit().unwrap()).into_inner(), |
| 145 | + serialize_hash(self.fedpeg_program().unwrap_or(&bitcoin::Script::new())).into_inner(), |
| 146 | + serialize_hash(self.fedpegscript().unwrap_or(&Vec::new())).into_inner(), |
| 147 | + serialize_hash(self.extension_space().unwrap_or(&Vec::new())).into_inner(), |
| 148 | + ]; |
| 149 | + ::fast_merkle_root::fast_merkle_root(&leaves[..]) |
| 150 | + } |
| 151 | +} |
| 152 | + |
56 | 153 | #[cfg(feature = "serde")] |
57 | 154 | impl<'de> Deserialize<'de> for Params { |
58 | 155 | fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> { |
@@ -272,3 +369,81 @@ impl Decodable for Params { |
272 | 369 | } |
273 | 370 | } |
274 | 371 | } |
| 372 | + |
| 373 | +#[cfg(test)] |
| 374 | +mod tests { |
| 375 | + use super::*; |
| 376 | + |
| 377 | + use bitcoin; |
| 378 | + use bitcoin::hashes::hex::ToHex; |
| 379 | + |
| 380 | + #[test] |
| 381 | + fn test_param_roots() { |
| 382 | + // Taken from the following Elements Core test: |
| 383 | + |
| 384 | + // CScript signblockscript(opcodetype(1)); |
| 385 | + // uint32_t signblock_wl(2); |
| 386 | + // CScript fp_program(opcodetype(3)); |
| 387 | + // CScript fp_script(opcodetype(4)); |
| 388 | + // std::vector<std::vector<unsigned char>> ext{ {5, 6}, {7} }; |
| 389 | + // |
| 390 | + // DynaFedParamEntry compact_entry = DynaFedParamEntry(signblockscript, signblock_wl); |
| 391 | + // BOOST_CHECK_EQUAL( |
| 392 | + // compact_entry.CalculateRoot().GetHex(), |
| 393 | + // "dff5f3793abc06a6d75e80fe3cfd47406f732fa4ec9305960ae2a229222a1ad5" |
| 394 | + // ); |
| 395 | + // |
| 396 | + // DynaFedParamEntry full_entry = |
| 397 | + // DynaFedParamEntry(signblockscript, signblock_wl, fp_program, fp_script, ext); |
| 398 | + // BOOST_CHECK_EQUAL( |
| 399 | + // full_entry.CalculateRoot().GetHex(), |
| 400 | + // "175be2087ba7cc0e33348bef493bd3e34f31f64bf9226e5881ab310dafa432ff" |
| 401 | + // ); |
| 402 | + // |
| 403 | + // DynaFedParams params = DynaFedParams(compact_entry, full_entry); |
| 404 | + // BOOST_CHECK_EQUAL( |
| 405 | + // params.CalculateRoot().GetHex(), |
| 406 | + // "e56cf79487952dfa85fe6a85829600adc19714ba6ab1157fdff02b25ae60cee2" |
| 407 | + // ); |
| 408 | + |
| 409 | + let signblockscript: bitcoin::Script = vec![1].into(); |
| 410 | + let signblock_wl = 2; |
| 411 | + let fp_program: bitcoin::Script = vec![3].into(); |
| 412 | + let fp_script = vec![4]; |
| 413 | + let ext = vec![vec![5, 6], vec![7]]; |
| 414 | + |
| 415 | + let compact_entry = Params::Compact { |
| 416 | + signblockscript: signblockscript.clone(), |
| 417 | + signblock_witness_limit: signblock_wl, |
| 418 | + }; |
| 419 | + assert_eq!( |
| 420 | + compact_entry.calculate_root().to_hex(), |
| 421 | + "dff5f3793abc06a6d75e80fe3cfd47406f732fa4ec9305960ae2a229222a1ad5" |
| 422 | + ); |
| 423 | + |
| 424 | + let full_entry = Params::Full { |
| 425 | + signblockscript: signblockscript, |
| 426 | + signblock_witness_limit: signblock_wl, |
| 427 | + fedpeg_program: fp_program, |
| 428 | + fedpegscript: fp_script, |
| 429 | + extension_space: ext, |
| 430 | + }; |
| 431 | + assert_eq!( |
| 432 | + full_entry.calculate_root().to_hex(), |
| 433 | + "175be2087ba7cc0e33348bef493bd3e34f31f64bf9226e5881ab310dafa432ff" |
| 434 | + ); |
| 435 | + |
| 436 | + let header = ::block::BlockHeader{ |
| 437 | + ext: ::block::ExtData::Dynafed { |
| 438 | + current: compact_entry, |
| 439 | + proposed: full_entry, |
| 440 | + signblock_witness: vec![], |
| 441 | + }, |
| 442 | + ..Default::default() |
| 443 | + }; |
| 444 | + assert_eq!( |
| 445 | + header.calculate_dynafed_params_root().unwrap().to_hex(), |
| 446 | + "e56cf79487952dfa85fe6a85829600adc19714ba6ab1157fdff02b25ae60cee2" |
| 447 | + ); |
| 448 | + } |
| 449 | +} |
0 commit comments