Skip to content
Open
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
10 changes: 5 additions & 5 deletions src/dynafed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,17 +85,17 @@ impl Serialize for HexBytesArray<'_> {
#[derive(Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct FullParams {
/// "scriptPubKey" used for block signing
signblockscript: Script,
pub signblockscript: Script,
/// Maximum, in bytes, of the size of a blocksigning witness
signblock_witness_limit: u32,
pub signblock_witness_limit: u32,
/// Untweaked `scriptPubKey` used for pegins
fedpeg_program: bitcoin::ScriptBuf,
pub fedpeg_program: bitcoin::ScriptBuf,
/// For v0 fedpeg programs, the witness script of the untweaked
/// pegin address. For future versions, this data has no defined
/// meaning and will be considered "anyone can spend".
fedpegscript: Vec<u8>,
pub fedpegscript: Vec<u8>,
/// "Extension space" used by Liquid for PAK key entries
extension_space: Vec<Vec<u8>>,
pub extension_space: Vec<Vec<u8>>,
}

impl FullParams {
Expand Down
255 changes: 255 additions & 0 deletions src/genesis.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
// Rust Elements Library
// Written by
// The Elements developers
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//

//! Helpers to calculate the genesis block for a given network.

use bitcoin::secp256k1::impl_array_newtype;
use secp256k1_zkp::Tweak;
use crate::hashes::{sha256, sha256d, Hash, HashEngine};
use crate::opcodes::all::OP_RETURN;
use crate::opcodes::OP_TRUE;
use crate::pset::serialize::Serialize;
use crate::{confidential, script, AssetId, Block, BlockExtData, BlockHash, BlockHeader, LockTime, Script, Sequence, Transaction, TxIn, TxInWitness, TxOut, TxOutWitness};
use crate::{AssetIssuance, ContractHash, OutPoint, Txid};
use crate::confidential::Nonce;

/// Parameters that influence chain consensus. The contents of the genesis block for a given network
/// are defined by these values.
#[derive(Clone, Debug)]
pub struct NetworkParams {
/// The network identifier string that elementsd accepts as the `chain` config argument
pub network_id: String,
/// This network's Fedpeg script
pub fedpeg_script: Script,
/// This network's `sign_block_script`
pub sign_block_script: Script,
/// How many free coins are present in this network
pub initial_free_coins: u64,
}

impl NetworkParams {
/// New custom network params
pub fn new(
network_id: String,
fedpeg_script: Script,
sign_block_script: Script,
initial_free_coins: u64,
) -> NetworkParams {
NetworkParams {
network_id,
fedpeg_script,
sign_block_script,
initial_free_coins,
}
}

/// Network params for Liquid mainnet
pub fn liquidv1() -> Self {
NetworkParams {
network_id: "liquidv1".to_string(),
// Can be verified at https://github.com/ElementsProject/elements/blob/27c2fb6b7de404908f9ef2eb5c98c9989d1ab8e4/src/chainparams.cpp#L1243
fedpeg_script: Script::from_hex_no_prefix("745c87635b21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b678172612102675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af992102896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d4821029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c2102a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc40102102f8a00b269f8c5e59c67d36db3cdc11b11b21f64b4bffb2815e9100d9aa8daf072103079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b2103111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2210318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa08401742103230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de121035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a62103bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c2103cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d175462103d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d4248282103ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a5f6702c00fb275522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb5368ae").expect("constant fedpeg script parse"),
// Can be verified at https://github.com/ElementsProject/elements/blob/27c2fb6b7de404908f9ef2eb5c98c9989d1ab8e4/src/chainparams.cpp#L1193
sign_block_script: Script::from_hex_no_prefix("5b21026a2a106ec32c8a1e8052e5d02a7b0a150423dbd9b116fc48d46630ff6e6a05b92102791646a8b49c2740352b4495c118d876347bf47d0551c01c4332fdc2df526f1a2102888bda53a424466b0451627df22090143bbf7c060e9eacb1e38426f6b07f2ae12102aee8967150dee220f613de3b239320355a498808084a93eaf39a34dcd62024852102d46e9259d0a0bb2bcbc461a3e68f34adca27b8d08fbe985853992b4b104e27412102e9944e35e5750ab621e098145b8e6cf373c273b7c04747d1aa020be0af40ccd62102f9a9d4b10a6d6c56d8c955c547330c589bb45e774551d46d415e51cd9ad5116321033b421566c124dfde4db9defe4084b7aa4e7f36744758d92806b8f72c2e943309210353dcc6b4cf6ad28aceb7f7b2db92a4bf07ac42d357adf756f3eca790664314b621037f55980af0455e4fb55aad9b85a55068bb6dc4740ea87276dc693f4598db45fa210384001daa88dabd23db878dbb1ce5b4c2a5fa72c3113e3514bf602325d0c37b8e21039056d089f2fe72dbc0a14780b4635b0dc8a1b40b7a59106325dd1bc45cc70493210397ab8ea7b0bf85bc7fc56bb27bf85e75502e94e76a6781c409f3f2ec3d1122192103b00e3b5b77884bf3cae204c4b4eac003601da75f96982ffcb3dcb29c5ee419b92103c1f3c0874cfe34b8131af34699589aacec4093399739ae352e8a46f80a6f68375fae").expect("constant sign_block_script parse"),
initial_free_coins: 0,
}
}

/// Network params for Liquid testnet
pub fn liquidtestnet() -> Self {
NetworkParams {
network_id: "liquidtestnet".to_string(),
fedpeg_script: script::Builder::new().push_opcode(OP_TRUE).into_script(),
// Can be verified at https://github.com/ElementsProject/elements/blob/27c2fb6b7de404908f9ef2eb5c98c9989d1ab8e4/src/chainparams.cpp#L1108
sign_block_script: Script::from_hex_no_prefix("51210217e403ddb181872c32a0cd468c710040b2f53d8cac69f18dad07985ee37e9a7151ae").expect("constant sign_block_script parse"),
initial_free_coins: 2_100_000_000_000_000,
}
}

/// Network params for a custom Elements network with defaults
pub fn custom_network(network_id: String, fedpeg_script: Option<Script>, sign_block_script: Option<Script>, initial_free_coins: Option<u64>) -> Self {
NetworkParams {
network_id,
fedpeg_script: fedpeg_script.unwrap_or_else(|| script::Builder::new().push_opcode(OP_TRUE).into_script()),
sign_block_script: sign_block_script.unwrap_or_else(|| script::Builder::new().push_opcode(OP_TRUE).into_script()),
initial_free_coins: initial_free_coins.unwrap_or(0),
}
}
}

/// Hash commitment of network parameters for a given Network
pub fn commit_to_custom_network_parameters(params: &NetworkParams) -> Vec<u8> {
let mut eng = sha256::Hash::engine();
eng.input(params.network_id.clone().as_bytes());
eng.input(format!("{:x}", params.fedpeg_script).as_bytes());
eng.input(format!("{:x}", params.sign_block_script).as_bytes());
sha256::Hash::from_engine(eng).serialize()
}

/// Produce the genesis transaction for a given elements Network
fn liquid_genesis_tx(network_params: &NetworkParams) -> Transaction {
let commit = commit_to_custom_network_parameters(network_params);

let input = TxIn {
previous_output: OutPoint::default(),
is_pegin: false,
script_sig: script::Builder::new()
.push_slice(commit.as_slice())
.into_script(),
sequence: Sequence::default(),
asset_issuance: AssetIssuance::default(),
witness: TxInWitness::default(),
};

let output = TxOut {
asset: confidential::Asset::Explicit(AssetId::default()),
value: confidential::Value::Explicit(0),
nonce: Nonce::default(),
script_pubkey: script::Builder::new().push_opcode(OP_RETURN).into_script(),
witness: TxOutWitness::default(),
};

Transaction {
version: 1,
lock_time: LockTime::ZERO,
input: vec![input],
output: vec![output],
}
}

/// Create the confidential asset transaction for the genesis block if required by the specified Network
fn liquid_genesis_asset_tx(network_params: &NetworkParams) -> Option<Transaction> {
let commit = commit_to_custom_network_parameters(network_params);
let asset_amount = network_params.initial_free_coins;
if asset_amount == 0 {
return None;
}
let asset_outpoint = OutPoint::new(Txid::from_slice(commit.as_slice()).expect("txid"), 0);
let contract_hash = ContractHash::from_byte_array([0u8; 32]);
let asset_entropy = AssetId::generate_asset_entropy(asset_outpoint, contract_hash);
let asset_id = AssetId::from_entropy(asset_entropy);

let asset_issuance = AssetIssuance {
asset_blinding_nonce: Tweak::default(),
asset_entropy: [0u8; 32],
amount: confidential::Value::Explicit(asset_amount),
inflation_keys: confidential::Value::Explicit(0),
};

let input = TxIn {
previous_output: asset_outpoint,
is_pegin: false,
script_sig: Script::new(),
sequence: Sequence::default(),
asset_issuance,
witness: TxInWitness::default(),
};

let output = TxOut {
asset: confidential::Asset::Explicit(asset_id),
value: confidential::Value::Explicit(asset_amount),
nonce: Nonce::default(),
script_pubkey: script::Builder::new().push_opcode(OP_TRUE).into_script(),
witness: TxOutWitness::default(),
};

let ret = Transaction {
version: 1,
lock_time: LockTime::ZERO,
input: vec![input],
output: vec![output],
};
Some(ret)
}

/// Constructs and returns the Liquid genesis blocks assuming default consensus parameters
/// Does not return upstream bitcoin network blocks as they are a different format and can be
/// acquired from the `rust-bitcoin` library
pub fn genesis_block(params: &NetworkParams) -> Block {
let tx = liquid_genesis_tx(params);
let mut txdata = vec![tx.clone()];

let merkle_root: sha256d::Hash =
if let Some(asset_tx) = liquid_genesis_asset_tx(params) {
txdata.push(asset_tx.clone());
let tx_hashes = vec![tx.txid().to_raw_hash(), asset_tx.txid().to_raw_hash()];
bitcoin::merkle_tree::calculate_root(tx_hashes.into_iter())
.expect("merkle root")
} else {
tx.txid().to_raw_hash()
};

Block {
header: BlockHeader {
version: 1,
prev_blockhash: BlockHash::all_zeros(),
merkle_root: merkle_root.into(),
time: 1_296_688_602,
height: 0,
ext: BlockExtData::Proof {
challenge: params.sign_block_script.clone(),
solution: Script::default(),
},
},
txdata,
}
}
/// The uniquely identifying hash of the target blockchain.
/// Liquid networks assume default consensus constants, Elements allows for modification of fedpeg and signblock scripts
/// via configuration argument which will cause these values to differ so these are only for default parameters
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ChainHash([u8; 32]);
impl_array_newtype!(ChainHash, u8, 32);

impl ChainHash {
/// `ChainHash` for Liquid v1 mainnet.
pub const LIQUIDV1: Self = Self([
3, 96, 32, 138, 136, 150, 146, 55, 44, 141, 104, 176, 132, 166, 46, 253, 246, 14, 161, 163,
89, 160, 76, 148, 178, 13, 34, 54, 88, 39, 102, 20,
]);
/// `ChainHash` for Liquid testnet.
pub const LIQUIDTESTNET: Self = Self([
193, 177, 106, 226, 79, 36, 35, 174, 162, 234, 52, 85, 34, 146, 121, 59, 91, 94, 130, 153,
154, 30, 237, 129, 213, 106, 238, 82, 142, 218, 113, 167,
]);

/// Calculates the chainhash for a set of network parameters
pub fn for_params(params: &NetworkParams) -> Self {
let genesis_block = genesis_block(params);
ChainHash(genesis_block.block_hash().to_byte_array())
}
}

#[cfg(test)]
mod test {
use crate::genesis::{genesis_block, ChainHash, NetworkParams};
use crate::hashes::Hash;

#[test]
fn genesis_block_hash() {
let genesis_block = genesis_block(&NetworkParams::liquidv1());
assert_eq!(
genesis_block.block_hash().to_byte_array(),
ChainHash::LIQUIDV1.0
);

let genesis_block =
crate::genesis::genesis_block(&NetworkParams::liquidtestnet());

assert_eq!(
genesis_block.block_hash().to_byte_array(),
ChainHash::LIQUIDTESTNET.0
);
}
}
83 changes: 83 additions & 0 deletions src/issuance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ use crate::encode::{self, Encodable, Decodable};
use crate::hashes::{hash_newtype, sha256, sha256d, Hash};
use crate::fast_merkle_root::fast_merkle_root;
use secp256k1_zkp::Tag;
use crate::genesis::{commit_to_custom_network_parameters, NetworkParams};
use crate::transaction::OutPoint;
use crate::Txid;

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

/// The asset ID for L-BTC, Bitcoin on the Liquidtestnet network.
pub const LIQUIDTESTNET_BTC: AssetId = AssetId([
0x49, 0x9a, 0x81, 0x85, 0x45, 0xf6, 0xba, 0xe3,
0x9f, 0xc0, 0x3b, 0x63, 0x7f, 0x2a, 0x4e, 0x1e,
0x64, 0xe5, 0x90, 0xca, 0xc1, 0xbc, 0x3a, 0x6f,
0x6d, 0x71, 0xaa, 0x44, 0x43, 0x65, 0x4c, 0x14,
]);

/// Generate the asset entropy from the issuance prevout and the contract hash.
pub fn generate_asset_entropy(
prevout: OutPoint,
Expand Down Expand Up @@ -137,6 +147,30 @@ impl AssetId {
pub fn into_tag(self) -> Tag {
self.0.into()
}

/// Pegged asset id for given network parameters
pub fn pegged_asset_id_for_network_params(params: &NetworkParams) -> AssetId {
match params.network_id.as_str() {
"liquidv1" => Self::LIQUID_BTC,
"liquidtestnet" => Self::LIQUIDTESTNET_BTC,
_ => {
// Else calculate the asset_id
Self::pegged_asset_id_for_params_and_parent_chain_hash(
params,
bitcoin::Network::Regtest.chain_hash(),
)
}
}
}

/// Calculate the `AssetId` for the pegged asset for a given set of network parameters assuming
/// a Regtest parent network
fn pegged_asset_id_for_params_and_parent_chain_hash(params: &NetworkParams, parent_chainhash: bitcoin::blockdata::constants::ChainHash) -> AssetId {
let commit = commit_to_custom_network_parameters(params);
let asset_outpoint = OutPoint::new(Txid::from_slice(commit.as_slice()).expect("txid"), 0);
let asset_entropy = AssetId::generate_asset_entropy(asset_outpoint, ContractHash::from_slice(parent_chainhash.to_bytes().as_slice()).unwrap());
AssetId::from_entropy(asset_entropy)
}
}

impl Encodable for AssetId {
Expand All @@ -155,6 +189,7 @@ impl Decodable for AssetId {
mod test {
use super::*;
use std::str::FromStr;
use bitcoin::constants::ChainHash;

#[test]
fn example_elements_core() {
Expand Down Expand Up @@ -259,4 +294,52 @@ mod test {
"6f0279e9ed041c3d710a9f57d0c02928416460c4b722ae3457a11eec381c526d",
);
}

#[test]
fn liquid_asset_ids() {
// Testing the two most common Regtest networks using in Liquid and CLN codebases
let network_params = NetworkParams::custom_network("elementsregtest".to_string(), None, None, None);
let asset_id = AssetId::pegged_asset_id_for_params_and_parent_chain_hash(
&network_params,
bitcoin::Network::Regtest.chain_hash(),
);

let elementsregtest_asset_id = AssetId([
0x23, 0x0f, 0x4f, 0x5d, 0x4b, 0x7c, 0x6f, 0xa8, 0x45, 0x80, 0x6e, 0xe4,
0xf6, 0x77, 0x13, 0x45, 0x9e, 0x1b, 0x69, 0xe8, 0xe6, 0x0f, 0xce, 0xe2,
0xe4, 0x94, 0x0c, 0x7a, 0x0d, 0x5d, 0xe1, 0xb2,
]);

assert_eq!(asset_id, elementsregtest_asset_id);

let network_params = NetworkParams::custom_network("liquid-regtest".to_string(), None, None, None);
let asset_id = AssetId::pegged_asset_id_for_params_and_parent_chain_hash(
&network_params,
bitcoin::Network::Regtest.chain_hash(),
);

let liquid_regtest_assetid = AssetId([
0x5c, 0xe7, 0xb9, 0x63, 0xd3, 0x7f, 0x8f, 0x2d, 0x51, 0xca, 0xfb, 0xba,
0x92, 0x8a, 0xaa, 0x9e, 0x22, 0x0b, 0x8b, 0xbc, 0x66, 0x05, 0x71, 0x49,
0x9c, 0x03, 0x62, 0x8a, 0x38, 0x51, 0xb8, 0xce,
]);

assert_eq!(asset_id, liquid_regtest_assetid);

let liquidv1_params = NetworkParams::liquidv1();
let asset_id = AssetId::pegged_asset_id_for_params_and_parent_chain_hash(
&liquidv1_params,
bitcoin::Network::Bitcoin.chain_hash(),
);

assert_eq!(asset_id, AssetId::LIQUID_BTC);

let liquidtestnet_params = NetworkParams::liquidtestnet();
let asset_id = AssetId::pegged_asset_id_for_params_and_parent_chain_hash(
&liquidtestnet_params,
ChainHash::from([0u8; 32]),
);

assert_eq!(asset_id, AssetId::LIQUIDTESTNET_BTC);
}
}
Loading
Loading