feat: generate genesis utxos from genesis file (#59)

Co-authored-by: rvcas <x@rvcas.dev>
Co-authored-by: jmhrpr <harper.jme@gmail.com>
This commit is contained in:
Santiago Carmuega 2023-10-04 06:45:59 -03:00 committed by GitHub
parent 90b228492c
commit a8df6dc666
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 15080 additions and 42 deletions

24
pallas-configs/Cargo.toml Normal file
View file

@ -0,0 +1,24 @@
[package]
name = "pallas-configs"
description = "Config structs and utilities matching the Haskell implementation"
version = "0.19.0-alpha.2"
edition = "2021"
repository = "https://github.com/txpipe/pallas"
homepage = "https://github.com/txpipe/pallas"
documentation = "https://docs.rs/pallas-configs"
license = "Apache-2.0"
readme = "README.md"
authors = ["Santiago Carmuega <santiago@carmuega.me>"]
[dependencies]
hex = "0.4.3"
pallas-addresses = { version = "0.19.0-alpha.0", path = "../pallas-addresses" }
pallas-crypto = { version = "0.19.0-alpha.0", path = "../pallas-crypto" }
pallas-codec = { version = "0.19.0-alpha.0", path = "../pallas-codec" }
serde = { version = "1.0.136", optional = true, features = ["derive"] }
serde_json = { version = "1.0.79", optional = true }
base64 = "0.21.2"
[features]
json = ["serde", "serde_json"]
default = ["json"]

2
pallas-configs/README.md Normal file
View file

@ -0,0 +1,2 @@
# Pallas Configs

218
pallas-configs/src/byron.rs Normal file
View file

@ -0,0 +1,218 @@
//! Parsing of Byron configuration data
use pallas_addresses::ByronAddress;
use pallas_crypto::hash::Hash;
use serde::Deserialize;
use std::collections::HashMap;
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GenesisFile {
pub avvm_distr: HashMap<String, String>,
pub block_version_data: BlockVersionData,
pub fts_seed: Option<String>,
pub protocol_consts: ProtocolConsts,
pub start_time: u64,
pub boot_stakeholders: HashMap<String, BootStakeWeight>,
pub heavy_delegation: HashMap<String, HeavyDelegation>,
pub non_avvm_balances: HashMap<String, String>,
pub vss_certs: Option<HashMap<String, VssCert>>,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BlockVersionData {
pub heavy_del_thd: String,
pub max_block_size: String,
pub max_header_size: String,
pub max_proposal_size: String,
pub max_tx_size: String,
pub mpc_thd: String,
pub script_version: u32,
pub slot_duration: String,
pub softfork_rule: SoftForkRule,
pub tx_fee_policy: TxFeePolicy,
pub unlock_stake_epoch: String,
pub update_implicit: String,
pub update_proposal_thd: String,
pub update_vote_thd: String,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProtocolConsts {
pub k: usize,
pub protocol_magic: u32,
#[serde(rename = "vssMaxTTL")]
pub vss_max_ttl: Option<u32>,
#[serde(rename = "vssMinTTL")]
pub vss_min_ttl: Option<u32>,
}
pub type BootStakeWeight = u16;
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct HeavyDelegation {
pub issuer_pk: String,
pub delegate_pk: String,
pub cert: String,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VssCert {
pub vss_key: String,
// TODO: is this size fine?
pub expiry_epoch: u32,
pub signature: String,
pub signing_key: String,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SoftForkRule {
pub init_thd: String,
pub min_thd: String,
pub thd_decrement: String,
}
#[derive(Debug, Deserialize)]
pub struct TxFeePolicy {
pub multiplier: String,
pub summand: String,
}
pub fn from_file(path: &std::path::Path) -> Result<GenesisFile, std::io::Error> {
let file = std::fs::File::open(path)?;
let reader = std::io::BufReader::new(file);
let parsed: GenesisFile = serde_json::from_reader(reader)?;
Ok(parsed)
}
use base64::Engine;
pub type GenesisUtxo = (Hash<32>, ByronAddress, u64);
pub fn genesis_avvm_utxos(config: &GenesisFile) -> Vec<GenesisUtxo> {
config
.avvm_distr
.iter()
.map(|(pubkey, amount)| {
let amount = amount.parse().unwrap();
let pubkey = base64::engine::general_purpose::URL_SAFE
.decode(pubkey)
.unwrap();
let pubkey = pallas_crypto::key::ed25519::PublicKey::try_from(&pubkey[..]).unwrap();
// TODO: network tag
//let network_tag = Some(config.protocol_consts.protocol_magic);
let network_tag = None;
let addr = pallas_addresses::byron::AddressPayload::new_redeem(pubkey, network_tag);
let addr: pallas_addresses::ByronAddress = addr.into();
let txid = pallas_crypto::hash::Hasher::<256>::hash_cbor(&addr);
(txid, addr, amount)
})
.collect()
}
pub fn genesis_non_avvm_utxos(config: &GenesisFile) -> Vec<GenesisUtxo> {
config
.non_avvm_balances
.iter()
.map(|(addr, amount)| {
let amount = amount.parse().unwrap();
let addr = ByronAddress::from_base58(addr).unwrap();
let txid = pallas_crypto::hash::Hasher::<256>::hash_cbor(&addr);
(txid, addr, amount)
})
.collect()
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use super::*;
fn load_test_data_config(network: &str) -> GenesisFile {
let path = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap())
.join("..")
.join("test_data")
.join(format!("{network}-byron-genesis.json"));
from_file(&path).unwrap()
}
#[test]
fn test_preview_json_loads() {
load_test_data_config("preview");
}
#[test]
fn test_mainnet_json_loads() {
load_test_data_config("mainnet");
}
fn utxo_exists(set: &[GenesisUtxo], expected: GenesisUtxo) -> bool {
set.iter().any(|(hash, addr, amount)| {
hash.eq(&expected.0) && addr.eq(&expected.1) && amount.eq(&expected.2)
})
}
fn genesis_utxo_from_raw(hash_hex: &str, addr_base58: &str, amount: u64) -> GenesisUtxo {
(
Hash::from_str(hash_hex).unwrap(),
ByronAddress::from_base58(addr_base58).unwrap(),
amount,
)
}
#[test]
fn test_preview_non_avvm_utxos() {
let f = load_test_data_config("preview");
let utxos = super::genesis_non_avvm_utxos(&f);
assert_eq!(utxos.len(), 8);
// check known tx as seen: https://preview.cexplorer.io/tx/4843cf2e582b2f9ce37600e5ab4cc678991f988f8780fed05407f9537f7712bd
let expected = genesis_utxo_from_raw(
"4843cf2e582b2f9ce37600e5ab4cc678991f988f8780fed05407f9537f7712bd",
"FHnt4NL7yPXvDWHa8bVs73UEUdJd64VxWXSFNqetECtYfTd9TtJguJ14Lu3feth",
30_000_000_000_000_000,
);
assert!(utxo_exists(&utxos, expected));
}
#[test]
pub fn test_mainnet_avvm_utxos() {
let f = load_test_data_config("mainnet");
let utxos = super::genesis_non_avvm_utxos(&f);
// there aren't non-avvm utxos in mainnet
assert!(utxos.is_empty());
let utxos = super::genesis_avvm_utxos(&f);
assert_eq!(utxos.len(), 14505);
// check known tx as seen: https://cexplorer.io/tx/0ae3da29711600e94a33fb7441d2e76876a9a1e98b5ebdefbf2e3bc535617616
let expected = genesis_utxo_from_raw(
"0ae3da29711600e94a33fb7441d2e76876a9a1e98b5ebdefbf2e3bc535617616",
"Ae2tdPwUPEZKQuZh2UndEoTKEakMYHGNjJVYmNZgJk2qqgHouxDsA5oT83n",
2_463_071_701_000_000,
);
assert!(utxo_exists(&utxos, expected));
}
}

View file

@ -0,0 +1,3 @@
//! Genesis data structs and utilities
pub mod byron;