Block building core (#401)

* Move core block to folder base module

* Added blob select trait

* Added tx select trait

* Implement blob and tx selection filling size

* Added blobs to block constructor
Added block builder module

* Implement block builder

* Remove unnnecesary phantoms

* Add default impl for selections

* Added example

* Fix typos

Co-authored-by: Youngjoon Lee <taxihighway@gmail.com>

* tx to item on generic function

Co-authored-by: Youngjoon Lee <taxihighway@gmail.com>

---------

Co-authored-by: Youngjoon Lee <taxihighway@gmail.com>
This commit is contained in:
Daniel Sanchez 2023-09-15 09:39:16 +02:00 committed by GitHub
parent 70dc4f3ae6
commit e1e2b84921
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 214 additions and 2 deletions

View File

@ -0,0 +1,111 @@
// std
use std::hash::Hash;
// crates
use serde::de::DeserializeOwned;
use serde::Serialize;
// internal
use crate::block::Block;
use crate::da::blob::{Blob, BlobSelect};
use crate::tx::{Transaction, TxSelect};
use consensus_engine::overlay::RandomBeaconState;
use consensus_engine::{NodeId, Qc, View};
/// Wrapper over a block building `new` method than holds intermediary state and can be
/// passed around. It also compounds the transaction selection and blob selection heuristics to be
/// used for transaction and blob selection.
///
/// Example:
/// ``` ignore
/// use nomos_core::block::builder::BlockBuilder;
/// let builder: BlockBuilder<(), (), FirstTx, FirstBlob> = {
/// BlockBuilder::new( FirstTx::default(), FirstBlob::default())
/// .with_view(View::from(0))
/// .with_parent_qc(qc)
/// .with_proposer(proposer)
/// .with_beacon_state(beacon)
/// .with_transactions([tx1].into_iter())
/// .with_blobs([blob1].into_iter())
/// };
/// builder.build().expect("All block attributes should have been set")
/// ```
pub struct BlockBuilder<Tx, Blob, TxSelector, BlobSelector> {
tx_selector: TxSelector,
blob_selector: BlobSelector,
view: Option<View>,
parent_qc: Option<Qc>,
proposer: Option<NodeId>,
beacon: Option<RandomBeaconState>,
txs: Option<Box<dyn Iterator<Item = Tx>>>,
blobs: Option<Box<dyn Iterator<Item = Blob>>>,
}
impl<Tx, B, TxSelector, BlobSelector> BlockBuilder<Tx, B, TxSelector, BlobSelector>
where
Tx: Transaction + Clone + Eq + Hash + Serialize + DeserializeOwned,
B: Blob + Clone + Eq + Hash + Serialize + DeserializeOwned,
TxSelector: TxSelect<Tx = Tx>,
BlobSelector: BlobSelect<Blob = B>,
{
pub fn new(tx_selector: TxSelector, blob_selector: BlobSelector) -> Self {
Self {
tx_selector,
blob_selector,
view: None,
parent_qc: None,
proposer: None,
beacon: None,
txs: None,
blobs: None,
}
}
#[must_use]
pub fn with_view(mut self, view: View) -> Self {
self.view = Some(view);
self
}
#[must_use]
pub fn with_parent_qc(mut self, qc: Qc) -> Self {
self.parent_qc = Some(qc);
self
}
#[must_use]
pub fn with_proposer(mut self, proposer: NodeId) -> Self {
self.proposer = Some(proposer);
self
}
#[must_use]
pub fn with_beacon_state(mut self, beacon: RandomBeaconState) -> Self {
self.beacon = Some(beacon);
self
}
#[allow(clippy::result_large_err)]
pub fn build(self) -> Result<Block<Tx, B>, Self> {
if let Self {
tx_selector,
blob_selector,
view: Some(view),
parent_qc: Some(parent_qc),
proposer: Some(proposer),
beacon: Some(beacon),
txs: Some(txs),
blobs: Some(blobs),
} = self
{
Ok(Block::new(
view,
parent_qc,
tx_selector.select_tx_from(txs),
blob_selector.select_blob_from(blobs),
proposer,
beacon,
))
} else {
Err(self)
}
}
}

View File

@ -1,3 +1,5 @@
pub mod builder;
use consensus_engine::overlay::RandomBeaconState;
use indexmap::IndexSet;
// std
@ -31,11 +33,12 @@ impl<
view: View,
parent_qc: Qc,
txs: impl Iterator<Item = Tx>,
blobs: impl Iterator<Item = Blob>,
proposer: NodeId,
beacon: RandomBeaconState,
) -> Self {
let transactions = txs.collect();
let blobs = IndexSet::new();
let blobs = blobs.collect();
let header = consensus_engine::Block {
id: BlockId::zeros(),
view,

View File

@ -1,3 +1,5 @@
pub mod select;
use bytes::Bytes;
use std::hash::Hash;
@ -11,3 +13,11 @@ pub trait Blob {
}
fn as_bytes(&self) -> Bytes;
}
pub trait BlobSelect {
type Blob: Blob;
fn select_blob_from<'i, I: Iterator<Item = Self::Blob> + 'i>(
&self,
blobs: I,
) -> Box<dyn Iterator<Item = Self::Blob> + 'i>;
}

View File

@ -0,0 +1,34 @@
// std
use std::marker::PhantomData;
// crates
// internal
use crate::da::blob::{Blob, BlobSelect};
use crate::utils;
#[derive(Default)]
pub struct FillSize<const SIZE: usize, B> {
_blob: PhantomData<B>,
}
impl<const SIZE: usize, B> FillSize<SIZE, B> {
pub fn new() -> Self {
Self {
_blob: Default::default(),
}
}
}
impl<const SIZE: usize, B: Blob> BlobSelect for FillSize<SIZE, B> {
type Blob = B;
fn select_blob_from<'i, I: Iterator<Item = Self::Blob> + 'i>(
&self,
blobs: I,
) -> Box<dyn Iterator<Item = Self::Blob> + 'i> {
utils::select::select_from_till_fill_size::<SIZE, Self::Blob>(
|blob| blob.as_bytes().len(),
blobs,
)
}
}

View File

@ -5,5 +5,6 @@ pub mod da;
pub mod fountain;
pub mod staking;
pub mod tx;
pub mod utils;
pub mod vote;
pub mod wire;

View File

@ -7,6 +7,7 @@ use bytes::Bytes;
pub mod carnot;
#[cfg(feature = "mock")]
pub mod mock;
pub mod select;
pub type TransactionHasher<T> = fn(&T) -> <T as Transaction>::Hash;
@ -18,3 +19,11 @@ pub trait Transaction {
}
fn as_bytes(&self) -> Bytes;
}
pub trait TxSelect {
type Tx: Transaction;
fn select_tx_from<'i, I: Iterator<Item = Self::Tx> + 'i>(
&self,
txs: I,
) -> Box<dyn Iterator<Item = Self::Tx> + 'i>;
}

View File

@ -0,0 +1,31 @@
// std
use std::marker::PhantomData;
// crates
// internal
use crate::tx::{Transaction, TxSelect};
use crate::utils;
#[derive(Default)]
pub struct FillSize<const SIZE: usize, Tx> {
_tx: PhantomData<Tx>,
}
impl<const SIZE: usize, Tx> FillSize<SIZE, Tx> {
pub fn new() -> Self {
Self {
_tx: Default::default(),
}
}
}
impl<const SIZE: usize, Tx: Transaction> TxSelect for FillSize<SIZE, Tx> {
type Tx = Tx;
fn select_tx_from<'i, I: Iterator<Item = Self::Tx> + 'i>(
&self,
txs: I,
) -> Box<dyn Iterator<Item = Self::Tx> + 'i> {
utils::select::select_from_till_fill_size::<SIZE, Self::Tx>(|tx| tx.as_bytes().len(), txs)
}
}

View File

@ -0,0 +1 @@
pub mod select;

View File

@ -0,0 +1,10 @@
pub fn select_from_till_fill_size<'i, const SIZE: usize, T>(
mut measure: impl FnMut(&T) -> usize + 'i,
items: impl Iterator<Item = T> + 'i,
) -> Box<dyn Iterator<Item = T> + 'i> {
let mut current_size = 0usize;
Box::new(items.take_while(move |item: &T| {
current_size += measure(item);
current_size <= SIZE
}))
}

View File

@ -594,7 +594,7 @@ where
match rx.await {
Ok(txs) => {
let beacon = RandomBeaconState::generate_happy(qc.view(), &private_key);
let proposal = Block::new(qc.view().next(), qc, txs, id, beacon);
let proposal = Block::new(qc.view().next(), qc, txs, [].into_iter(), id, beacon);
output = Some(Output::BroadcastProposal { proposal });
}
Err(e) => tracing::error!("Could not fetch txs {e}"),

View File

@ -110,6 +110,7 @@ impl SimulationApp {
View::new(0),
Block::genesis().parent_qc,
[].into_iter(),
[].into_iter(),
leader,
RandomBeaconState::Sad {
entropy: Box::new([0; 32]),

View File

@ -269,6 +269,7 @@ impl<
qc.view().next(),
qc.clone(),
[].into_iter(),
[].into_iter(),
self.id,
RandomBeaconState::generate_happy(qc.view().next(), &self.random_beacon_pk),
),