Skip to content

Commit

Permalink
feat(primitives): Improve ergonomics for Byron primitives (#47)
Browse files Browse the repository at this point in the history
* feat(primitives): Improve ergonomics for Byron primitives
* fix(ci): Fix tests
  • Loading branch information
scarmuega authored Feb 12, 2022
1 parent 82c581f commit c3662e1
Show file tree
Hide file tree
Showing 9 changed files with 289 additions and 41 deletions.
1 change: 1 addition & 0 deletions pallas-primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ minicbor-derive = "0.8.0"
hex = "0.4.3"
log = "0.4.14"
pallas-crypto = { version = "0.5.0-alpha.0", path = "../pallas-crypto" }
base58 = "0.2.0"
62 changes: 62 additions & 0 deletions pallas-primitives/src/byron/address.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use crate::Error;

use super::Address;
use base58::ToBase58;
use minicbor::to_vec;

impl Address {
pub fn to_addr_string(&self) -> Result<String, Error> {
let cbor = to_vec(self)?;
Ok(cbor.to_base58())
}
}

#[cfg(test)]
mod tests {
use std::ops::Deref;

use crate::byron::Block;
use crate::Fragment;

const KNOWN_ADDRESSES: &[&str] = &[
"DdzFFzCqrht8QHTQXbWy2qoyPaqTN8BjyfKygGmpy9dtot1tvkBfCaVTnR22XCaaDVn3M1U6aiMShoCLzw6VWSwzQKhhJrM3YjYp3wyy",
"DdzFFzCqrhsjUjMRukzoFx8sToHzCt4iidB17STXk9adAoVMNus5SvAjS1cXfPKbuNbPUZ5xQG25sMK85n9GdMkqo2ytqBnKWC68s8P3",
"Ae2tdPwUPEZFBnsqpm2RkDQfwJseUrBKrTECCDom4bAqNsxTNwbMPCZtbyJ",
"DdzFFzCqrhsvNcX48aHrYdcQhKhAwEdLebH7xPskKtQnZucNQmXXmEMJEU2NDqipuDTZKFec5UMCtm4vYoUgjixxULJexAzHGa3Bmctk",
"Ae2tdPwUPEZGEC75fV3vktzbwxhkD71JHxSYVgiNCgKB7Yo1rWamWVJDFsV",
"DdzFFzCqrht1K1sR9fQWEdcxhTxgqKQqwPHucez1McJy14uqbxu1UKnnq12EdHr9NxYs8RqKnSvswegh8wLvfC4fw6arB3nyqC5Wy4Ky",
"DdzFFzCqrhtC8HauYJMa59DzoAeTLnpDcdst1hFWmjkTdg5Xu55ougiBpAmwuo2Coe2DfAj26m52aF4e2yk8v5GQc4umZxsXUT2CuTB2",
"DdzFFzCqrht4Zsigv43q9LRHNsgu6TWdASuGe6tCYuv2B9H2wTggkvyuwHMb5WALqWDDiNQEHYq7BvnFJ65UDzKi6ThdZusVYmYLpJg9",
];

#[test]
fn known_address_matches() {
// TODO: expand this test to include more test blocks
let block_idx = 1;
let block_str = include_str!("test_data/test2.block");

let block_bytes = hex::decode(block_str).expect(&format!("bad block file {}", block_idx));
let block = Block::decode_fragment(&block_bytes[..])
.expect(&format!("error decoding cbor for file {}", block_idx));

let block = match block {
Block::MainBlock(x) => x,
Block::EbBlock(_) => panic!(),
};

// don't want to pass if we don't have tx in the block
assert!(block.body.tx_payload.len() > 0);

for tx in block.body.tx_payload.iter() {
for output in tx.deref().transaction.outputs.iter() {
let addr_str = output.address.to_addr_string().unwrap();

assert!(
KNOWN_ADDRESSES.contains(&addr_str.as_str()),
"address {} not in known list",
addr_str
);
}
}
}
}
38 changes: 23 additions & 15 deletions pallas-primitives/src/byron/crypto.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,34 @@
use super::{Block, BlockHead, EbbHead};
use super::{Block, BlockHead, EbbHead, Tx};
use pallas_crypto::hash::{Hash, Hasher};

pub fn hash_boundary_block_header(header: &EbbHead) -> Hash<32> {
// hash expects to have a prefix for the type of block
Hasher::<256>::hash_cbor(&(0, header))
impl EbbHead {
pub fn to_hash(&self) -> Hash<32> {
// hash expects to have a prefix for the type of block
Hasher::<256>::hash_cbor(&(0, self))
}
}

pub fn hash_main_block_header(header: &BlockHead) -> Hash<32> {
// hash expects to have a prefix for the type of block
Hasher::<256>::hash_cbor(&(1, header))
impl BlockHead {
pub fn to_hash(&self) -> Hash<32> {
// hash expects to have a prefix for the type of block
Hasher::<256>::hash_cbor(&(1, self))
}
}

pub fn hash_block_header(block: &Block) -> Hash<32> {
match block {
Block::EbBlock(x) => hash_boundary_block_header(&x.header),
Block::MainBlock(x) => hash_main_block_header(&x.header),
impl Block {
pub fn to_hash(&self) -> Hash<32> {
match self {
Block::EbBlock(x) => x.header.to_hash(),
Block::MainBlock(x) => x.header.to_hash(),
}
}
}

//pub fn hash_transaction(data: &TransactionBody) -> Hash<32> {
// Hasher::<256>::hash_cbor(data)
//}
impl Tx {
pub fn to_hash(&self) -> Hash<32> {
Hasher::<256>::hash_cbor(self)
}
}

#[cfg(test)]
mod tests {
Expand All @@ -40,7 +48,7 @@ mod tests {
let block_model = Block::decode_fragment(&block_bytes[..])
.expect(&format!("error decoding cbor for file {}", block_idx));

let computed_hash = super::hash_block_header(&block_model);
let computed_hash = block_model.to_hash();

assert_eq!(hex::encode(computed_hash), KNOWN_HASH)
}
Expand Down
76 changes: 76 additions & 0 deletions pallas-primitives/src/byron/fees.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use crate::Error;

use super::TxPayload;
use minicbor::to_vec;

pub struct PolicyParams {
constant: u64,
size_coeficient: u64,
}

impl Default for PolicyParams {
fn default() -> Self {
Self {
constant: 155_381_000_000_000u64,
size_coeficient: 43_946_000_000u64,
}
}
}

fn compute_linear_fee_policy(tx_size: u64, params: &PolicyParams) -> u64 {
println!("tx size: {}", tx_size);
let nanos = params.constant + (tx_size * params.size_coeficient);

let loves = nanos / 1_000_000_000;

let rem = match nanos % 1_000_000_000 {
0 => 0u64,
_ => 1u64,
};

loves + rem
}

impl TxPayload {
pub fn compute_fee(&self, params: &PolicyParams) -> Result<u64, Error> {
let tx_size = to_vec(&self)?.len();
let fee = compute_linear_fee_policy(tx_size as u64, params);

Ok(fee)
}

pub fn compute_fee_with_defaults(&self) -> Result<u64, Error> {
self.compute_fee(&PolicyParams::default())
}
}

#[cfg(test)]
mod tests {
use crate::byron::Block;
use crate::Fragment;

#[test]
fn known_fee_matches() {
// TODO: expand this test to include more test blocks
let block_idx = 1;
let block_str = include_str!("test_data/test4.block");

let block_bytes = hex::decode(block_str).expect(&format!("bad block file {}", block_idx));
let block = Block::decode_fragment(&block_bytes[..])
.expect(&format!("error decoding cbor for file {}", block_idx));

let block = match block {
Block::MainBlock(x) => x,
Block::EbBlock(_) => panic!(),
};

// don't want to pass if we don't have tx in the block
assert!(block.body.tx_payload.len() > 0);

for tx in block.body.tx_payload.iter().take(1) {
println!("{}", tx.transaction.to_hash());
let fee = tx.compute_fee_with_defaults().unwrap();
assert_eq!(fee, 171070);
}
}
}
6 changes: 4 additions & 2 deletions pallas-primitives/src/byron/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//! Ledger primitives and cbor codec for the Byron era

mod address;
mod crypto;
mod fees;
mod model;
mod time;

pub use model::*;

pub mod crypto;
82 changes: 62 additions & 20 deletions pallas-primitives/src/byron/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,39 @@ impl minicbor::Encode for AddrAttrProperty {

pub type AddrAttr = OrderPreservingProperties<AddrAttrProperty>;

#[derive(Debug, Encode, Decode)]
pub struct AddressPayload {
#[n(0)]
pub root: AddressId,

#[n(1)]
pub attributes: AddrAttr,

#[n(2)]
pub addrtype: AddrType,
}

// address = [ #6.24(bytes .cbor ([addressid, addrattr, addrtype])), u64 ]
pub type Address = (CborWrap<(AddressId, AddrAttr, AddrType)>, u64);
#[derive(Debug, Encode, Decode)]
pub struct Address {
#[n(0)]
pub payload: CborWrap<AddressPayload>,

#[n(1)]
pub crc: u64,
}

// Transactions

// txout = [address, u64]
pub type TxOut = (Address, u64);
#[derive(Debug, Encode, Decode)]
pub struct TxOut {
#[n(0)]
pub address: Address,

#[n(1)]
pub amount: u64,
}

#[derive(Debug)]
pub enum TxIn {
Expand Down Expand Up @@ -228,21 +254,34 @@ impl minicbor::Encode for TxIn {
}

// tx = [[+ txin], [+ txout], attributes]
pub type Tx = (Vec<TxIn>, Vec<TxOut>, Attributes);
#[derive(Debug, Encode, Decode)]
pub struct Tx {
#[n(0)]
pub inputs: MaybeIndefArray<TxIn>,

#[n(1)]
pub outputs: MaybeIndefArray<TxOut>,

#[n(2)]
pub attributes: Attributes,
}

// txproof = [u32, hash, hash]
pub type TxProof = (u32, ByronHash, ByronHash);

pub type ValidatorScript = (u16, ByteVec);
pub type RedeemerScript = (u16, ByteVec);

#[derive(Debug)]
pub enum Twit {
// [0, #6.24(bytes .cbor ([pubkey, signature]))]
Variant0(CborWrap<(PubKey, Signature)>),
PkWitness(CborWrap<(PubKey, Signature)>),

// [1, #6.24(bytes .cbor ([[u16, bytes], [u16, bytes]]))]
Variant1(CborWrap<((u16, ByteVec), (u16, ByteVec))>),
ScriptWitness(CborWrap<(ValidatorScript, RedeemerScript)>),

// [2, #6.24(bytes .cbor ([pubkey, signature]))]
Variant2(CborWrap<(PubKey, Signature)>),
RedeemWitness(CborWrap<(PubKey, Signature)>),

// [u8 .gt 2, encoded-cbor]
Other(u8, ByteVec),
Expand All @@ -255,9 +294,9 @@ impl<'b> minicbor::Decode<'b> for Twit {
let variant = d.u8()?;

match variant {
0 => Ok(Twit::Variant0(d.decode()?)),
1 => Ok(Twit::Variant1(d.decode()?)),
2 => Ok(Twit::Variant2(d.decode()?)),
0 => Ok(Twit::PkWitness(d.decode()?)),
1 => Ok(Twit::ScriptWitness(d.decode()?)),
2 => Ok(Twit::RedeemWitness(d.decode()?)),
x => Ok(Twit::Other(x, d.decode()?)),
}
}
Expand All @@ -269,21 +308,21 @@ impl minicbor::Encode for Twit {
e: &mut minicbor::Encoder<W>,
) -> Result<(), minicbor::encode::Error<W::Error>> {
match self {
Twit::Variant0(x) => {
Twit::PkWitness(x) => {
e.array(2)?;
e.u8(0)?;
e.encode(x)?;

Ok(())
}
Twit::Variant1(x) => {
Twit::ScriptWitness(x) => {
e.array(2)?;
e.u8(1)?;
e.encode(x)?;

Ok(())
}
Twit::Variant2(x) => {
Twit::RedeemWitness(x) => {
e.array(2)?;
e.u8(2)?;
e.encode(x)?;
Expand Down Expand Up @@ -327,7 +366,7 @@ pub type VssDec = ByteVec;
// cddl note:
// This is encoded using the
// 'Binary' instance for Scrape.Proof
pub type VssProof = (ByteVec, ByteVec, ByteVec, Vec<ByteVec>);
pub type VssProof = (ByteVec, ByteVec, ByteVec, MaybeIndefArray<ByteVec>);

//ssccomm = [pubkey, [{vsspubkey => vssenc},vssproof], signature]
pub type SscComm = (
Expand Down Expand Up @@ -781,7 +820,14 @@ pub struct BlockHead {
}

// [tx, [* twit]]
pub type TxPayload = (Tx, Vec<Twit>);
#[derive(Debug, Encode, Decode)]
pub struct TxPayload {
#[n(0)]
pub transaction: Tx,

#[n(1)]
pub witness: MaybeIndefArray<Twit>,
}

#[derive(Encode, Decode, Debug)]
pub struct BlockBody {
Expand Down Expand Up @@ -920,12 +966,10 @@ mod tests {
let block = Block::decode_fragment(&bytes[..])
.expect(&format!("error decoding cbor for file {}", idx));

let _bytes2 =
let bytes2 =
to_vec(block).expect(&format!("error encoding block cbor for file {}", idx));

// HACK: we ommit the ismorphic requirement until we find the
// offending difference
// assert_eq!(bytes, bytes2);
assert_eq!(hex::encode(bytes), hex::encode(bytes2));
}
}

Expand All @@ -940,8 +984,6 @@ mod tests {
let block = BlockHead::decode_fragment(&bytes[..])
.expect(&format!("error decoding cbor for file {}", idx));

println!("{:?}", block);

let bytes2 =
to_vec(block).expect(&format!("error encoding header cbor for file {}", idx));

Expand Down
1 change: 1 addition & 0 deletions pallas-primitives/src/byron/test_data/test4.block
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
820183851a2d964a095820b5bdd15fbfbe7f618d2b7db0b20632658466b1b17654e9a8b865ec0e9fdccd4e8483015820c2c44f1f28599c08c6c1da0e375dc995d2b686c445b19110fa96ca68dfb2880e5820314b3c77650d6eba459864966dbcda18c438ee21bdd7e6b3272bf1e6b241d75d83025820d36a2619a672494604e11bb447cbcf5231e9f2ba25c2169177edc941bd50ad6c5820d36a2619a672494604e11bb447cbcf5231e9f2ba25c2169177edc941bd50ad6c5820afc0da64183bf2664f3d4eec7238d524ba607faeeab24fc100eb861dba69971b58204e66280cd94d591072349bec0a3090a53aa945562efb6d08d56e53654b0e4098848201195457584026566e86fc6b9b177c8480e275b2b112b573f6d073f9deea53b8d99c4ed976b335b2b3842f0e380001f090bc923caa9691ed9115e286da9421e2745c7acc87f18119a89f8202828400584026566e86fc6b9b177c8480e275b2b112b573f6d073f9deea53b8d99c4ed976b335b2b3842f0e380001f090bc923caa9691ed9115e286da9421e2745c7acc87f15840f14f712dc600d793052d4842d50cefa4e65884ea6cf83707079eb8ce302efc85dae922d5eb3838d2b91784f04824d26767bfb65bd36a36e74fec46d09d98858d58408ab43e904b06e799c1817c5ced4f3a7bbe15cdbf422dea9d2d5dc2c6105ce2f4d4c71e5d4779f6c44b770a133636109949e1f7786acb5a732bcdea0470fea4065840cfd641e91f908471af31762e7124147e8d3b27036d436f24d785de7330ebe33c03dae5ec27eb2944a82545fbcc30016737ba7696fb5e3fcaf8963ea12dbb87098483000000826a63617264616e6f2d736c01a058204ba92aa320c60acc9ad7b9a64f2eda55c4d2ec28e604faf186708b4f0c4e8edf849f82839f8200d81858248258200ca95f3bb516e3fa36b3c5ce18316a3d197b4faf2e36635baecae47e8a714b8d00ff9f8282d818584283581caca526063940ef762c899b92f20134264a43c5ab36d46cc6d36540d1a101581e581c2729cbfd641133bd0633ff422b246fa0d95cc2bef293d39adf3fd22b001aa25f5bd41b0000021a9f3ab0c28282d818584283581ce0751a974c40abdac7e71ee5b09d12b0591b1f4ecab73062ac8f96caa101581e581cca3e553c9c63c531002ff143535ea35088673bf86d25026baf12db3e001afdf29bac1a02625a00ffa0818200d81858858258408b9397dce473d3f296ad24e24e48795a495ad2f1602896e913fdc7e55e55e21f6fa53491a197e86428c86fadc0253ddec8d88bee623d474632603633643b26eb5840555e2f86fd803d1ed5bb3a3bc7f08dd82896744c4a0d99ce7b61f696ef632b12f6d8bd4787fcfe27de2bb7b1127fb1646d8d2f26755b4186f605210709016709ff8302a0d90102809fff82809fff81a0
Loading

0 comments on commit c3662e1

Please sign in to comment.