Skip to content

Commit 53b92d8

Browse files
authored
Merge pull request #19 from stevenroose/elementshash
Add fast_merkle_root and AssetId type
2 parents 8f016dc + 91a3f74 commit 53b92d8

File tree

4 files changed

+378
-0
lines changed

4 files changed

+378
-0
lines changed

src/fast_merkle_root.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// Rust Elements Library
2+
// Written in 2019 by
3+
// The Elements developers
4+
//
5+
// To the extent possible under law, the author(s) have dedicated all
6+
// copyright and related and neighboring rights to this software to
7+
// the public domain worldwide. This software is distributed without
8+
// any warranty.
9+
//
10+
// You should have received a copy of the CC0 Public Domain Dedication
11+
// along with this software.
12+
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
13+
//
14+
15+
use bitcoin::hashes::{sha256, Hash, HashEngine};
16+
17+
/// Calculate a single sha256 midstate hash of the given left and right leaves.
18+
#[inline]
19+
fn sha256midstate(left: &[u8], right: &[u8]) -> sha256::Midstate {
20+
let mut engine = sha256::Hash::engine();
21+
engine.input(left);
22+
engine.input(right);
23+
engine.midstate()
24+
}
25+
26+
/// Compute the Merkle root of the give hashes using mid-state only.
27+
/// The inputs must be byte slices of length 32.
28+
/// Note that the merkle root calculated with this method is not the same as the
29+
/// one computed by a normal SHA256(d) merkle root.
30+
pub fn fast_merkle_root(leaves: &[[u8; 32]]) -> sha256::Midstate {
31+
let mut result_hash = Default::default();
32+
// Implementation based on ComputeFastMerkleRoot method in Elements Core.
33+
if leaves.is_empty() {
34+
return result_hash;
35+
}
36+
37+
// inner is an array of eagerly computed subtree hashes, indexed by tree
38+
// level (0 being the leaves).
39+
// For example, when count is 25 (11001 in binary), inner[4] is the hash of
40+
// the first 16 leaves, inner[3] of the next 8 leaves, and inner[0] equal to
41+
// the last leaf. The other inner entries are undefined.
42+
//
43+
// First process all leaves into 'inner' values.
44+
let mut inner: [sha256::Midstate; 32] = Default::default();
45+
let mut count: u32 = 0;
46+
while (count as usize) < leaves.len() {
47+
let mut temp_hash = sha256::Midstate::from_inner(leaves[count as usize]);
48+
count += 1;
49+
// For each of the lower bits in count that are 0, do 1 step. Each
50+
// corresponds to an inner value that existed before processing the
51+
// current leaf, and each needs a hash to combine it.
52+
let mut level = 0;
53+
while count & (1u32 << level) == 0 {
54+
temp_hash = sha256midstate(&inner[level][..], &temp_hash[..]);
55+
level += 1;
56+
}
57+
// Store the resulting hash at inner position level.
58+
inner[level] = temp_hash;
59+
}
60+
61+
// Do a final 'sweep' over the rightmost branch of the tree to process
62+
// odd levels, and reduce everything to a single top value.
63+
// Level is the level (counted from the bottom) up to which we've sweeped.
64+
//
65+
// As long as bit number level in count is zero, skip it. It means there
66+
// is nothing left at this level.
67+
let mut level = 0;
68+
while count & (1u32 << level) == 0 {
69+
level += 1;
70+
}
71+
result_hash = inner[level];
72+
73+
while count != (1u32 << level) {
74+
// If we reach this point, hash is an inner value that is not the top.
75+
// We combine it with itself (Bitcoin's special rule for odd levels in
76+
// the tree) to produce a higher level one.
77+
78+
// Increment count to the value it would have if two entries at this
79+
// level had existed and propagate the result upwards accordingly.
80+
count += 1 << level;
81+
level += 1;
82+
while count & (1u32 << level) == 0 {
83+
result_hash = sha256midstate(&inner[level][..], &result_hash[..]);
84+
level += 1;
85+
}
86+
}
87+
// Return result.
88+
result_hash
89+
}
90+
91+
#[cfg(test)]
92+
mod tests {
93+
use super::fast_merkle_root;
94+
use bitcoin::hashes::hex::FromHex;
95+
use bitcoin::hashes::sha256;
96+
97+
#[test]
98+
fn test_fast_merkle_root() {
99+
// unit test vectors from Elements Core
100+
let test_leaves = [
101+
"b66b041650db0f297b53f8d93c0e8706925bf3323f8c59c14a6fac37bfdcd06f",
102+
"99cb2fa68b2294ae133550a9f765fc755d71baa7b24389fed67d1ef3e5cb0255",
103+
"257e1b2fa49dd15724c67bac4df7911d44f6689860aa9f65a881ae0a2f40a303",
104+
"b67b0b9f093fa83d5e44b707ab962502b7ac58630e556951136196e65483bb80",
105+
];
106+
107+
let test_roots = [
108+
"0000000000000000000000000000000000000000000000000000000000000000",
109+
"b66b041650db0f297b53f8d93c0e8706925bf3323f8c59c14a6fac37bfdcd06f",
110+
"f752938da0cb71c051aabdd5a86658e8d0b7ac00e1c2074202d8d2a79d8a6cf6",
111+
"245d364a28e9ad20d522c4a25ffc6a7369ab182f884e1c7dcd01aa3d32896bd3",
112+
"317d6498574b6ca75ee0368ec3faec75e096e245bdd5f36e8726fa693f775dfc",
113+
];
114+
115+
let mut leaves = vec![];
116+
for i in 0..4 {
117+
let root = fast_merkle_root(&leaves);
118+
assert_eq!(root, FromHex::from_hex(&test_roots[i]).unwrap(), "root #{}", i);
119+
leaves.push(sha256::Midstate::from_hex(&test_leaves[i]).unwrap().into_inner());
120+
}
121+
assert_eq!(fast_merkle_root(&leaves), FromHex::from_hex(test_roots[4]).unwrap());
122+
}
123+
}

src/issuance.rs

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
// Rust Elements Library
2+
// Written in 2019 by
3+
// The Elements developers
4+
//
5+
// To the extent possible under law, the author(s) have dedicated all
6+
// copyright and related and neighboring rights to this software to
7+
// the public domain worldwide. This software is distributed without
8+
// any warranty.
9+
//
10+
// You should have received a copy of the CC0 Public Domain Dedication
11+
// along with this software.
12+
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
13+
//
14+
15+
//! Asset Issuance
16+
17+
use bitcoin::util::hash::BitcoinHash;
18+
use bitcoin::hashes::{hex, sha256, Hash};
19+
use fast_merkle_root::fast_merkle_root;
20+
use transaction::OutPoint;
21+
22+
/// The zero hash.
23+
const ZERO32: [u8; 32] = [
24+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
25+
];
26+
/// The one hash.
27+
const ONE32: [u8; 32] = [
28+
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
29+
];
30+
/// The two hash.
31+
const TWO32: [u8; 32] = [
32+
2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
33+
];
34+
35+
/// An issued asset ID.
36+
#[derive(Copy, Clone, PartialEq, Eq, Default, PartialOrd, Ord, Hash)]
37+
pub struct AssetId(sha256::Midstate);
38+
39+
impl AssetId {
40+
/// Create an [AssetId] from its inner type.
41+
pub fn from_inner(midstate: sha256::Midstate) -> AssetId {
42+
AssetId(midstate)
43+
}
44+
45+
/// Convert the [AssetId] into its inner type.
46+
pub fn into_inner(self) -> sha256::Midstate {
47+
self.0
48+
}
49+
50+
/// Generate the asset entropy from the issuance prevout and the contract hash.
51+
pub fn generate_asset_entropy(
52+
prevout: OutPoint,
53+
contract_hash: sha256::Hash,
54+
) -> sha256::Midstate {
55+
// E : entropy
56+
// I : prevout
57+
// C : contract
58+
// E = H( H(I) || H(C) )
59+
fast_merkle_root(&[prevout.bitcoin_hash().into_inner(), contract_hash.into_inner()])
60+
}
61+
62+
/// Calculate the asset ID from the asset entropy.
63+
pub fn from_entropy(entropy: sha256::Midstate) -> AssetId {
64+
// H_a : asset tag
65+
// E : entropy
66+
// H_a = H( E || 0 )
67+
AssetId(fast_merkle_root(&[entropy.into_inner(), ZERO32]))
68+
}
69+
70+
/// Calculate the reissuance token asset ID from the asset entropy.
71+
pub fn reissuance_token_from_entropy(entropy: sha256::Midstate, confidential: bool) -> AssetId {
72+
// H_a : asset reissuance tag
73+
// E : entropy
74+
// if not fConfidential:
75+
// H_a = H( E || 1 )
76+
// else
77+
// H_a = H( E || 2 )
78+
let second = match confidential {
79+
false => ONE32,
80+
true => TWO32,
81+
};
82+
AssetId(fast_merkle_root(&[entropy.into_inner(), second]))
83+
}
84+
}
85+
86+
impl hex::FromHex for AssetId {
87+
fn from_byte_iter<I>(iter: I) -> Result<Self, hex::Error>
88+
where
89+
I: Iterator<Item = Result<u8, hex::Error>> + ExactSizeIterator + DoubleEndedIterator,
90+
{
91+
sha256::Midstate::from_byte_iter(iter).map(AssetId)
92+
}
93+
}
94+
95+
impl ::std::fmt::Display for AssetId {
96+
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
97+
::std::fmt::Display::fmt(&self.0, f)
98+
}
99+
}
100+
101+
impl ::std::fmt::Debug for AssetId {
102+
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
103+
::std::fmt::Display::fmt(&self, f)
104+
}
105+
}
106+
107+
impl ::std::fmt::LowerHex for AssetId {
108+
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
109+
::std::fmt::LowerHex::fmt(&self.0, f)
110+
}
111+
}
112+
113+
#[cfg(feature = "serde")]
114+
impl ::serde::Serialize for AssetId {
115+
fn serialize<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
116+
use bitcoin::hashes::hex::ToHex;
117+
if s.is_human_readable() {
118+
s.serialize_str(&self.to_hex())
119+
} else {
120+
s.serialize_bytes(&self.0[..])
121+
}
122+
}
123+
}
124+
125+
#[cfg(feature = "serde")]
126+
impl<'de> ::serde::Deserialize<'de> for AssetId {
127+
fn deserialize<D: ::serde::Deserializer<'de>>(d: D) -> Result<AssetId, D::Error> {
128+
use bitcoin::hashes::hex::FromHex;
129+
130+
if d.is_human_readable() {
131+
struct HexVisitor;
132+
133+
impl<'de> ::serde::de::Visitor<'de> for HexVisitor {
134+
type Value = AssetId;
135+
136+
fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
137+
formatter.write_str("an ASCII hex string")
138+
}
139+
140+
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
141+
where
142+
E: ::serde::de::Error,
143+
{
144+
if let Ok(hex) = ::std::str::from_utf8(v) {
145+
AssetId::from_hex(hex).map_err(E::custom)
146+
} else {
147+
return Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self));
148+
}
149+
}
150+
151+
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
152+
where
153+
E: ::serde::de::Error,
154+
{
155+
AssetId::from_hex(v).map_err(E::custom)
156+
}
157+
}
158+
159+
d.deserialize_str(HexVisitor)
160+
} else {
161+
struct BytesVisitor;
162+
163+
impl<'de> ::serde::de::Visitor<'de> for BytesVisitor {
164+
type Value = AssetId;
165+
166+
fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
167+
formatter.write_str("a bytestring")
168+
}
169+
170+
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
171+
where
172+
E: ::serde::de::Error,
173+
{
174+
if v.len() != 32 {
175+
Err(E::invalid_length(v.len(), &stringify!($len)))
176+
} else {
177+
let mut ret = [0; 32];
178+
ret.copy_from_slice(v);
179+
Ok(AssetId(sha256::Midstate::from_inner(ret)))
180+
}
181+
}
182+
}
183+
184+
d.deserialize_bytes(BytesVisitor)
185+
}
186+
}
187+
}
188+
189+
#[cfg(test)]
190+
mod test {
191+
use super::*;
192+
use std::str::FromStr;
193+
194+
use bitcoin::hashes::hex::FromHex;
195+
use bitcoin::hashes::sha256;
196+
197+
#[test]
198+
fn example_elements_core() {
199+
// example test data from Elements Core 0.17
200+
let prevout_str = "05a047c98e82a848dee94efcf32462b065198bebf2404d201ba2e06db30b28f4:0";
201+
let entropy_hex = "746f447f691323502cad2ef646f932613d37a83aeaa2133185b316648df4b70a";
202+
let asset_id_hex = "dcd60818d863b5c026c40b2bc3ba6fdaf5018bcc8606c18adf7db4da0bcd8533";
203+
let token_id_hex = "c1adb114f4f87d33bf9ce90dd4f9ca523dd414d6cd010a7917903e2009689530";
204+
205+
let contract_hash = sha256::Hash::from_inner(ZERO32);
206+
let prevout = OutPoint::from_str(prevout_str).unwrap();
207+
let entropy = sha256::Midstate::from_hex(entropy_hex).unwrap();
208+
assert_eq!(AssetId::generate_asset_entropy(prevout, contract_hash), entropy);
209+
let asset_id = AssetId::from_hex(asset_id_hex).unwrap();
210+
assert_eq!(AssetId::from_entropy(entropy), asset_id);
211+
let token_id = AssetId::from_hex(token_id_hex).unwrap();
212+
assert_eq!(AssetId::reissuance_token_from_entropy(entropy, false), token_id);
213+
}
214+
}

src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ mod block;
3838
pub mod confidential;
3939
pub mod dynafed;
4040
pub mod encode;
41+
mod fast_merkle_root;
42+
pub mod issuance;
4143
mod transaction;
4244

4345
// export everything at the top level so it can be used as `elements::Transaction` etc.
@@ -46,4 +48,6 @@ pub use transaction::{OutPoint, PeginData, PegoutData, TxIn, TxOut, TxInWitness,
4648
pub use block::{BlockHeader, Block};
4749
pub use block::ExtData as BlockExtData;
4850
pub use ::bitcoin::consensus::encode::VarInt;
51+
pub use fast_merkle_root::fast_merkle_root;
52+
pub use issuance::AssetId;
4953

0 commit comments

Comments
 (0)