Skip to content

Commit f1b8aba

Browse files
committed
Implement calculation of genesis blocks
1 parent e5223d7 commit f1b8aba

2 files changed

Lines changed: 257 additions & 0 deletions

File tree

src/genesis.rs

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
// Rust Elements Library
2+
// Written 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+
//! Helpers to calculate the genesis block for a given network.
16+
17+
use bitcoin::secp256k1::impl_array_newtype;
18+
use secp256k1_zkp::Tweak;
19+
use crate::hashes::{sha256, sha256d, Hash, HashEngine};
20+
use crate::opcodes::all::OP_RETURN;
21+
use crate::opcodes::OP_TRUE;
22+
use crate::pset::serialize::Serialize;
23+
use crate::{confidential, script, AssetId, Block, BlockExtData, BlockHash, BlockHeader, LockTime, Script, Sequence, Transaction, TxIn, TxInWitness, TxOut, TxOutWitness};
24+
use crate::{AssetIssuance, ContractHash, OutPoint, Txid};
25+
use crate::confidential::Nonce;
26+
27+
/// Parameters that influence chain consensus. The contents of the genesis block for a given network
28+
/// are defined by these values.
29+
#[derive(Clone, Debug)]
30+
pub struct NetworkParams {
31+
/// The network identifier string that elementsd accepts as the `chain` config argument
32+
pub network_id: String,
33+
/// This network's Fedpeg script
34+
pub fedpeg_script: Script,
35+
/// This network's `sign_block_script`
36+
pub sign_block_script: Script,
37+
/// How many free coins are present in this network
38+
pub initial_free_coins: u64,
39+
}
40+
41+
impl NetworkParams {
42+
/// New custom network params
43+
pub fn new(
44+
network_id: String,
45+
fedpeg_script: Script,
46+
sign_block_script: Script,
47+
initial_free_coins: u64,
48+
) -> NetworkParams {
49+
NetworkParams {
50+
network_id,
51+
fedpeg_script,
52+
sign_block_script,
53+
initial_free_coins,
54+
}
55+
}
56+
57+
/// Network params for Liquid mainnet
58+
pub fn liquidv1() -> Self {
59+
NetworkParams {
60+
network_id: "liquidv1".to_string(),
61+
// Can be verified at https://github.com/ElementsProject/elements/blob/27c2fb6b7de404908f9ef2eb5c98c9989d1ab8e4/src/chainparams.cpp#L1243
62+
fedpeg_script: Script::from_hex_no_prefix("745c87635b21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b678172612102675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af992102896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d4821029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c2102a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc40102102f8a00b269f8c5e59c67d36db3cdc11b11b21f64b4bffb2815e9100d9aa8daf072103079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b2103111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2210318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa08401742103230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de121035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a62103bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c2103cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d175462103d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d4248282103ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a5f6702c00fb275522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb5368ae").expect("constant fedpeg script parse"),
63+
// Can be verified at https://github.com/ElementsProject/elements/blob/27c2fb6b7de404908f9ef2eb5c98c9989d1ab8e4/src/chainparams.cpp#L1193
64+
sign_block_script: Script::from_hex_no_prefix("5b21026a2a106ec32c8a1e8052e5d02a7b0a150423dbd9b116fc48d46630ff6e6a05b92102791646a8b49c2740352b4495c118d876347bf47d0551c01c4332fdc2df526f1a2102888bda53a424466b0451627df22090143bbf7c060e9eacb1e38426f6b07f2ae12102aee8967150dee220f613de3b239320355a498808084a93eaf39a34dcd62024852102d46e9259d0a0bb2bcbc461a3e68f34adca27b8d08fbe985853992b4b104e27412102e9944e35e5750ab621e098145b8e6cf373c273b7c04747d1aa020be0af40ccd62102f9a9d4b10a6d6c56d8c955c547330c589bb45e774551d46d415e51cd9ad5116321033b421566c124dfde4db9defe4084b7aa4e7f36744758d92806b8f72c2e943309210353dcc6b4cf6ad28aceb7f7b2db92a4bf07ac42d357adf756f3eca790664314b621037f55980af0455e4fb55aad9b85a55068bb6dc4740ea87276dc693f4598db45fa210384001daa88dabd23db878dbb1ce5b4c2a5fa72c3113e3514bf602325d0c37b8e21039056d089f2fe72dbc0a14780b4635b0dc8a1b40b7a59106325dd1bc45cc70493210397ab8ea7b0bf85bc7fc56bb27bf85e75502e94e76a6781c409f3f2ec3d1122192103b00e3b5b77884bf3cae204c4b4eac003601da75f96982ffcb3dcb29c5ee419b92103c1f3c0874cfe34b8131af34699589aacec4093399739ae352e8a46f80a6f68375fae").expect("constant sign_block_script parse"),
65+
initial_free_coins: 0,
66+
}
67+
}
68+
69+
/// Network params for Liquid testnet
70+
pub fn liquidtestnet() -> Self {
71+
NetworkParams {
72+
network_id: "liquidtestnet".to_string(),
73+
fedpeg_script: script::Builder::new().push_opcode(OP_TRUE).into_script(),
74+
// Can be verified at https://github.com/ElementsProject/elements/blob/27c2fb6b7de404908f9ef2eb5c98c9989d1ab8e4/src/chainparams.cpp#L1108
75+
sign_block_script: Script::from_hex_no_prefix("51210217e403ddb181872c32a0cd468c710040b2f53d8cac69f18dad07985ee37e9a7151ae").expect("constant sign_block_script parse"),
76+
initial_free_coins: 2_100_000_000_000_000,
77+
}
78+
}
79+
80+
/// Network params for a custom Elements network with defaults
81+
pub fn custom_network(network_id: String, fedpeg_script: Option<Script>, sign_block_script: Option<Script>, initial_free_coins: Option<u64>) -> Self {
82+
NetworkParams {
83+
network_id,
84+
fedpeg_script: fedpeg_script.unwrap_or_else(|| script::Builder::new().push_opcode(OP_TRUE).into_script()),
85+
sign_block_script: sign_block_script.unwrap_or_else(|| script::Builder::new().push_opcode(OP_TRUE).into_script()),
86+
initial_free_coins: initial_free_coins.unwrap_or(0),
87+
}
88+
}
89+
}
90+
91+
/// Hash commitment of network parameters for a given Network
92+
pub fn commit_to_custom_network_parameters(params: &NetworkParams) -> Vec<u8> {
93+
let mut eng = sha256::Hash::engine();
94+
eng.input(params.network_id.clone().as_bytes());
95+
eng.input(format!("{:x}", params.fedpeg_script).as_bytes());
96+
eng.input(format!("{:x}", params.sign_block_script).as_bytes());
97+
sha256::Hash::from_engine(eng).serialize()
98+
}
99+
100+
/// Produce the genesis transaction for a given elements Network
101+
fn liquid_genesis_tx(network_params: &NetworkParams) -> Transaction {
102+
let commit = commit_to_custom_network_parameters(network_params);
103+
104+
let input = TxIn {
105+
previous_output: OutPoint::default(),
106+
is_pegin: false,
107+
script_sig: script::Builder::new()
108+
.push_slice(commit.as_slice())
109+
.into_script(),
110+
sequence: Sequence::default(),
111+
asset_issuance: AssetIssuance::default(),
112+
witness: TxInWitness::default(),
113+
};
114+
115+
let output = TxOut {
116+
asset: confidential::Asset::Explicit(AssetId::default()),
117+
value: confidential::Value::Explicit(0),
118+
nonce: Nonce::default(),
119+
script_pubkey: script::Builder::new().push_opcode(OP_RETURN).into_script(),
120+
witness: TxOutWitness::default(),
121+
};
122+
123+
Transaction {
124+
version: 1,
125+
lock_time: LockTime::ZERO,
126+
input: vec![input],
127+
output: vec![output],
128+
}
129+
}
130+
131+
/// Create the confidential asset transaction for the genesis block if required by the specified Network
132+
fn liquid_genesis_asset_tx(network_params: &NetworkParams) -> Option<Transaction> {
133+
let commit = commit_to_custom_network_parameters(network_params);
134+
let asset_amount = network_params.initial_free_coins;
135+
if asset_amount == 0 {
136+
return None;
137+
}
138+
let asset_outpoint = OutPoint::new(Txid::from_slice(commit.as_slice()).expect("txid"), 0);
139+
let contract_hash = ContractHash::from_byte_array([0u8; 32]);
140+
let asset_entropy = AssetId::generate_asset_entropy(asset_outpoint, contract_hash);
141+
let asset_id = AssetId::from_entropy(asset_entropy);
142+
143+
let asset_issuance = AssetIssuance {
144+
asset_blinding_nonce: Tweak::default(),
145+
asset_entropy: [0u8; 32],
146+
amount: confidential::Value::Explicit(asset_amount),
147+
inflation_keys: confidential::Value::Explicit(0),
148+
};
149+
150+
let input = TxIn {
151+
previous_output: asset_outpoint,
152+
is_pegin: false,
153+
script_sig: Script::new(),
154+
sequence: Sequence::default(),
155+
asset_issuance,
156+
witness: TxInWitness::default(),
157+
};
158+
159+
let output = TxOut {
160+
asset: confidential::Asset::Explicit(asset_id),
161+
value: confidential::Value::Explicit(asset_amount),
162+
nonce: Nonce::default(),
163+
script_pubkey: script::Builder::new().push_opcode(OP_TRUE).into_script(),
164+
witness: TxOutWitness::default(),
165+
};
166+
167+
let ret = Transaction {
168+
version: 1,
169+
lock_time: LockTime::ZERO,
170+
input: vec![input],
171+
output: vec![output],
172+
};
173+
Some(ret)
174+
}
175+
176+
/// Constructs and returns the Liquid genesis blocks assuming default consensus parameters
177+
/// Does not return upstream bitcoin network blocks as they are a different format and can be
178+
/// acquired from the `rust-bitcoin` library
179+
pub fn genesis_block(params: &NetworkParams) -> Block {
180+
let tx = liquid_genesis_tx(params);
181+
let mut txdata = vec![tx.clone()];
182+
183+
let merkle_root: sha256d::Hash =
184+
if let Some(asset_tx) = liquid_genesis_asset_tx(params) {
185+
txdata.push(asset_tx.clone());
186+
let tx_hashes = vec![tx.txid().to_raw_hash(), asset_tx.txid().to_raw_hash()];
187+
bitcoin::merkle_tree::calculate_root(tx_hashes.into_iter())
188+
.expect("merkle root")
189+
} else {
190+
tx.txid().to_raw_hash()
191+
};
192+
193+
Block {
194+
header: BlockHeader {
195+
version: 1,
196+
prev_blockhash: BlockHash::all_zeros(),
197+
merkle_root: merkle_root.into(),
198+
time: 1_296_688_602,
199+
height: 0,
200+
ext: BlockExtData::Proof {
201+
challenge: params.sign_block_script.clone(),
202+
solution: Script::default(),
203+
},
204+
},
205+
txdata,
206+
}
207+
}
208+
/// The uniquely identifying hash of the target blockchain.
209+
/// Liquid networks assume default consensus constants, Elements allows for modification of fedpeg and signblock scripts
210+
/// via configuration argument which will cause these values to differ so these are only for default parameters
211+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
212+
pub struct ChainHash([u8; 32]);
213+
impl_array_newtype!(ChainHash, u8, 32);
214+
215+
impl ChainHash {
216+
/// `ChainHash` for Liquid v1 mainnet.
217+
pub const LIQUIDV1: Self = Self([
218+
3, 96, 32, 138, 136, 150, 146, 55, 44, 141, 104, 176, 132, 166, 46, 253, 246, 14, 161, 163,
219+
89, 160, 76, 148, 178, 13, 34, 54, 88, 39, 102, 20,
220+
]);
221+
/// `ChainHash` for Liquid testnet.
222+
pub const LIQUIDTESTNET: Self = Self([
223+
193, 177, 106, 226, 79, 36, 35, 174, 162, 234, 52, 85, 34, 146, 121, 59, 91, 94, 130, 153,
224+
154, 30, 237, 129, 213, 106, 238, 82, 142, 218, 113, 167,
225+
]);
226+
227+
/// Calculates the chainhash for a set of network parameters
228+
pub fn for_params(params: &NetworkParams) -> Self {
229+
let genesis_block = genesis_block(params);
230+
ChainHash(genesis_block.block_hash().to_byte_array())
231+
}
232+
}
233+
234+
#[cfg(test)]
235+
mod test {
236+
use crate::genesis::{genesis_block, ChainHash, NetworkParams};
237+
use crate::hashes::Hash;
238+
239+
#[test]
240+
fn genesis_block_hash() {
241+
let genesis_block = genesis_block(&NetworkParams::liquidv1());
242+
assert_eq!(
243+
genesis_block.block_hash().to_byte_array(),
244+
ChainHash::LIQUIDV1.0
245+
);
246+
247+
let genesis_block =
248+
crate::genesis::genesis_block(&NetworkParams::liquidtestnet());
249+
250+
assert_eq!(
251+
genesis_block.block_hash().to_byte_array(),
252+
ChainHash::LIQUIDTESTNET.0
253+
);
254+
}
255+
}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ pub mod taproot;
7373
mod transaction;
7474
// consider making upstream public
7575
mod endian;
76+
pub mod genesis;
77+
7678
// re-export bitcoin deps which we re-use
7779
pub use bitcoin::hashes;
7880
// export everything at the top level so it can be used as `elements::Transaction` etc.

0 commit comments

Comments
 (0)