Skip to content

Commit e9245f1

Browse files
committed
Merge #276: Implement calculation of AssetId and genesis blocks
d7f8720 Implement genesis block calculations (Philip Robinson) bfc831e Add calculation of elements AssetId (Philip Robinson) 0699935 Make FullParams members public (Philip Robinson) Pull request description: Recently had cause to work with custom elements chains in regtest mode and needed some helpers to calculate the pegged in AssetId and genesis block hash for those custom network parameters. This PR implements these helpers based on how it is done in `elementsd` ACKs for top commit: apoelstra: ACK d7f8720; successfully ran local tests Tree-SHA512: 5a18ef3667cbb914e646cf4bef81ff73857d88377ad77909cf37f86d2ab8e8fe2193f779c3611fd3c15630e3ac7880afd666adceb59ce80284aaa9a7150a5177
2 parents 9304937 + d7f8720 commit e9245f1

4 files changed

Lines changed: 345 additions & 5 deletions

File tree

src/dynafed.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,17 +85,17 @@ impl Serialize for HexBytesArray<'_> {
8585
#[derive(Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
8686
pub struct FullParams {
8787
/// "scriptPubKey" used for block signing
88-
signblockscript: Script,
88+
pub signblockscript: Script,
8989
/// Maximum, in bytes, of the size of a blocksigning witness
90-
signblock_witness_limit: u32,
90+
pub signblock_witness_limit: u32,
9191
/// Untweaked `scriptPubKey` used for pegins
92-
fedpeg_program: bitcoin::ScriptBuf,
92+
pub fedpeg_program: bitcoin::ScriptBuf,
9393
/// For v0 fedpeg programs, the witness script of the untweaked
9494
/// pegin address. For future versions, this data has no defined
9595
/// meaning and will be considered "anyone can spend".
96-
fedpegscript: Vec<u8>,
96+
pub fedpegscript: Vec<u8>,
9797
/// "Extension space" used by Liquid for PAK key entries
98-
extension_space: Vec<Vec<u8>>,
98+
pub extension_space: Vec<Vec<u8>>,
9999
}
100100

101101
impl FullParams {

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/issuance.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ use crate::encode::{self, Encodable, Decodable};
2020
use crate::hashes::{hash_newtype, sha256, sha256d, Hash};
2121
use crate::fast_merkle_root::fast_merkle_root;
2222
use secp256k1_zkp::Tag;
23+
use crate::genesis::{commit_to_custom_network_parameters, NetworkParams};
2324
use crate::transaction::OutPoint;
25+
use crate::Txid;
2426

2527
/// The zero hash.
2628
const ZERO32: [u8; 32] = [
@@ -81,6 +83,14 @@ impl AssetId {
8183
0x3d, 0x1c, 0x04, 0xed, 0xe9, 0x79, 0x02, 0x6f,
8284
]);
8385

86+
/// The asset ID for L-BTC, Bitcoin on the Liquidtestnet network.
87+
pub const LIQUIDTESTNET_BTC: AssetId = AssetId([
88+
0x49, 0x9a, 0x81, 0x85, 0x45, 0xf6, 0xba, 0xe3,
89+
0x9f, 0xc0, 0x3b, 0x63, 0x7f, 0x2a, 0x4e, 0x1e,
90+
0x64, 0xe5, 0x90, 0xca, 0xc1, 0xbc, 0x3a, 0x6f,
91+
0x6d, 0x71, 0xaa, 0x44, 0x43, 0x65, 0x4c, 0x14,
92+
]);
93+
8494
/// Generate the asset entropy from the issuance prevout and the contract hash.
8595
pub fn generate_asset_entropy(
8696
prevout: OutPoint,
@@ -137,6 +147,30 @@ impl AssetId {
137147
pub fn into_tag(self) -> Tag {
138148
self.0.into()
139149
}
150+
151+
/// Pegged asset id for given network parameters
152+
pub fn pegged_asset_id_for_network_params(params: &NetworkParams) -> AssetId {
153+
match params.network_id.as_str() {
154+
"liquidv1" => Self::LIQUID_BTC,
155+
"liquidtestnet" => Self::LIQUIDTESTNET_BTC,
156+
_ => {
157+
// Else calculate the asset_id
158+
Self::pegged_asset_id_for_params_and_parent_chain_hash(
159+
params,
160+
bitcoin::Network::Regtest.chain_hash(),
161+
)
162+
}
163+
}
164+
}
165+
166+
/// Calculate the `AssetId` for the pegged asset for a given set of network parameters assuming
167+
/// a Regtest parent network
168+
fn pegged_asset_id_for_params_and_parent_chain_hash(params: &NetworkParams, parent_chainhash: bitcoin::blockdata::constants::ChainHash) -> AssetId {
169+
let commit = commit_to_custom_network_parameters(params);
170+
let asset_outpoint = OutPoint::new(Txid::from_slice(commit.as_slice()).expect("txid"), 0);
171+
let asset_entropy = AssetId::generate_asset_entropy(asset_outpoint, ContractHash::from_slice(parent_chainhash.to_bytes().as_slice()).unwrap());
172+
AssetId::from_entropy(asset_entropy)
173+
}
140174
}
141175

142176
impl Encodable for AssetId {
@@ -155,6 +189,7 @@ impl Decodable for AssetId {
155189
mod test {
156190
use super::*;
157191
use std::str::FromStr;
192+
use bitcoin::constants::ChainHash;
158193

159194
#[test]
160195
fn example_elements_core() {
@@ -259,4 +294,52 @@ mod test {
259294
"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d",
260295
);
261296
}
297+
298+
#[test]
299+
fn liquid_asset_ids() {
300+
// Testing the two most common Regtest networks using in Liquid and CLN codebases
301+
let network_params = NetworkParams::custom_network("elementsregtest".to_string(), None, None, None);
302+
let asset_id = AssetId::pegged_asset_id_for_params_and_parent_chain_hash(
303+
&network_params,
304+
bitcoin::Network::Regtest.chain_hash(),
305+
);
306+
307+
let elementsregtest_asset_id = AssetId([
308+
0x23, 0x0f, 0x4f, 0x5d, 0x4b, 0x7c, 0x6f, 0xa8, 0x45, 0x80, 0x6e, 0xe4,
309+
0xf6, 0x77, 0x13, 0x45, 0x9e, 0x1b, 0x69, 0xe8, 0xe6, 0x0f, 0xce, 0xe2,
310+
0xe4, 0x94, 0x0c, 0x7a, 0x0d, 0x5d, 0xe1, 0xb2,
311+
]);
312+
313+
assert_eq!(asset_id, elementsregtest_asset_id);
314+
315+
let network_params = NetworkParams::custom_network("liquid-regtest".to_string(), None, None, None);
316+
let asset_id = AssetId::pegged_asset_id_for_params_and_parent_chain_hash(
317+
&network_params,
318+
bitcoin::Network::Regtest.chain_hash(),
319+
);
320+
321+
let liquid_regtest_assetid = AssetId([
322+
0x5c, 0xe7, 0xb9, 0x63, 0xd3, 0x7f, 0x8f, 0x2d, 0x51, 0xca, 0xfb, 0xba,
323+
0x92, 0x8a, 0xaa, 0x9e, 0x22, 0x0b, 0x8b, 0xbc, 0x66, 0x05, 0x71, 0x49,
324+
0x9c, 0x03, 0x62, 0x8a, 0x38, 0x51, 0xb8, 0xce,
325+
]);
326+
327+
assert_eq!(asset_id, liquid_regtest_assetid);
328+
329+
let liquidv1_params = NetworkParams::liquidv1();
330+
let asset_id = AssetId::pegged_asset_id_for_params_and_parent_chain_hash(
331+
&liquidv1_params,
332+
bitcoin::Network::Bitcoin.chain_hash(),
333+
);
334+
335+
assert_eq!(asset_id, AssetId::LIQUID_BTC);
336+
337+
let liquidtestnet_params = NetworkParams::liquidtestnet();
338+
let asset_id = AssetId::pegged_asset_id_for_params_and_parent_chain_hash(
339+
&liquidtestnet_params,
340+
ChainHash::from([0u8; 32]),
341+
);
342+
343+
assert_eq!(asset_id, AssetId::LIQUIDTESTNET_BTC);
344+
}
262345
}

0 commit comments

Comments
 (0)