pub use super::BlockHeight;
use crate::{
common::{
fuel_crypto::Hasher,
fuel_tx::{
Bytes32,
Input,
Transaction,
UniqueIdentifier,
},
fuel_types::{
bytes::SerializableVec,
Address,
},
},
model::DaBlockHeight,
};
use derive_more::{
AsRef,
Display,
From,
FromStr,
Into,
LowerHex,
UpperHex,
};
use fuel_vm::{
fuel_crypto,
fuel_merkle,
fuel_types::MessageId,
prelude::Signature,
};
use tai64::Tai64;
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Default,
FromStr,
From,
Into,
LowerHex,
UpperHex,
Display,
AsRef,
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
#[repr(transparent)]
pub struct BlockId(Bytes32);
impl BlockId {
pub fn into_message(self) -> fuel_crypto::Message {
unsafe { fuel_crypto::Message::from_bytes_unchecked(*self.0) }
}
pub fn as_message(&self) -> &fuel_crypto::Message {
unsafe { fuel_crypto::Message::as_ref_unchecked(self.0.as_slice()) }
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))]
pub struct FuelBlockHeader {
pub application: FuelApplicationHeader<GeneratedApplicationFields>,
pub consensus: FuelConsensusHeader<GeneratedConsensusFields>,
#[cfg_attr(feature = "serde", serde(skip))]
pub metadata: Option<HeaderMetadata>,
}
#[derive(Clone, Debug)]
#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))]
pub struct PartialFuelBlockHeader {
pub application: FuelApplicationHeader<Empty>,
pub consensus: FuelConsensusHeader<Empty>,
pub metadata: Option<HeaderMetadata>,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Empty;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))]
pub struct FuelApplicationHeader<Generated> {
pub da_height: DaBlockHeight,
pub generated: Generated,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))]
pub struct GeneratedApplicationFields {
pub transactions_count: u64,
pub output_messages_count: u64,
pub transactions_root: Bytes32,
pub output_messages_root: Bytes32,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FuelConsensusHeader<Generated> {
pub prev_root: Bytes32,
pub height: BlockHeight,
pub time: Tai64,
pub generated: Generated,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))]
pub struct GeneratedConsensusFields {
pub application_hash: Bytes32,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct HeaderMetadata {
id: BlockId,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConsensusType {
PoA,
}
impl FuelBlockHeader {
pub fn prev_root(&self) -> &Bytes32 {
&self.as_ref().prev_root
}
pub fn height(&self) -> &BlockHeight {
&self.as_ref().height
}
pub fn time(&self) -> Tai64 {
self.as_ref().time
}
pub fn application_hash(&self) -> &Bytes32 {
&self.as_ref().application_hash
}
pub fn consensus_type(&self) -> ConsensusType {
ConsensusType::PoA
}
}
impl PartialFuelBlockHeader {
pub fn prev_root(&self) -> &Bytes32 {
&self.as_ref().prev_root
}
pub fn height(&self) -> &BlockHeight {
&self.as_ref().height
}
pub fn time(&self) -> &Tai64 {
&self.as_ref().time
}
pub fn consensus_type(&self) -> ConsensusType {
ConsensusType::PoA
}
}
impl FuelBlockHeader {
pub fn recalculate_metadata(&mut self) {
self.metadata = Some(HeaderMetadata { id: self.hash() });
}
pub fn hash(&self) -> BlockId {
self.consensus.hash()
}
pub fn id(&self) -> BlockId {
if let Some(ref metadata) = self.metadata {
metadata.id
} else {
self.hash()
}
}
}
impl PartialFuelBlockHeader {
pub fn generate(
self,
transactions: &[Vec<u8>],
message_ids: &[MessageId],
) -> FuelBlockHeader {
let mut transaction_tree = fuel_merkle::binary::in_memory::MerkleTree::new();
for id in transactions {
transaction_tree.push(id.as_ref());
}
let transactions_root = transaction_tree.root().into();
let mut message_tree = fuel_merkle::binary::in_memory::MerkleTree::new();
for id in message_ids {
message_tree.push(id.as_ref());
}
let output_messages_root = message_tree.root().into();
let application = FuelApplicationHeader {
da_height: self.application.da_height,
generated: GeneratedApplicationFields {
transactions_count: transactions.len() as u64,
output_messages_count: message_ids.len() as u64,
transactions_root,
output_messages_root,
},
};
let application_hash = application.hash();
let mut header = FuelBlockHeader {
application,
consensus: FuelConsensusHeader {
prev_root: self.consensus.prev_root,
height: self.consensus.height,
time: self.consensus.time,
generated: GeneratedConsensusFields { application_hash },
},
metadata: None,
};
header.recalculate_metadata();
header
}
fn without_generated(self) -> FuelBlockHeader {
FuelBlockHeader {
application: FuelApplicationHeader {
da_height: self.application.da_height,
generated: GeneratedApplicationFields {
transactions_count: Default::default(),
output_messages_count: Default::default(),
transactions_root: Default::default(),
output_messages_root: Default::default(),
},
},
consensus: FuelConsensusHeader {
prev_root: self.consensus.prev_root,
height: self.consensus.height,
time: self.consensus.time,
generated: GeneratedConsensusFields {
application_hash: Default::default(),
},
},
metadata: None,
}
}
}
impl FuelApplicationHeader<GeneratedApplicationFields> {
fn hash(&self) -> Bytes32 {
let mut hasher = Hasher::default();
hasher.input(&self.da_height.to_bytes()[..]);
hasher.input(self.transactions_count.to_be_bytes());
hasher.input(self.output_messages_count.to_be_bytes());
hasher.input(self.transactions_root.as_ref());
hasher.input(self.output_messages_root.as_ref());
hasher.digest()
}
}
impl FuelConsensusHeader<GeneratedConsensusFields> {
fn hash(&self) -> BlockId {
let mut hasher = Hasher::default();
hasher.input(self.prev_root.as_ref());
hasher.input(&self.height.to_bytes()[..]);
hasher.input(self.time.0.to_be_bytes());
hasher.input(self.application_hash.as_ref());
BlockId(hasher.digest())
}
}
impl core::ops::Deref for FuelBlockHeader {
type Target = FuelApplicationHeader<GeneratedApplicationFields>;
fn deref(&self) -> &Self::Target {
&self.application
}
}
impl core::ops::Deref for PartialFuelBlockHeader {
type Target = FuelApplicationHeader<Empty>;
fn deref(&self) -> &Self::Target {
&self.application
}
}
impl core::ops::Deref for FuelApplicationHeader<GeneratedApplicationFields> {
type Target = GeneratedApplicationFields;
fn deref(&self) -> &Self::Target {
&self.generated
}
}
impl core::ops::Deref for FuelConsensusHeader<GeneratedConsensusFields> {
type Target = GeneratedConsensusFields;
fn deref(&self) -> &Self::Target {
&self.generated
}
}
impl core::convert::AsRef<FuelConsensusHeader<GeneratedConsensusFields>>
for FuelBlockHeader
{
fn as_ref(&self) -> &FuelConsensusHeader<GeneratedConsensusFields> {
&self.consensus
}
}
impl core::convert::AsRef<FuelConsensusHeader<Empty>> for PartialFuelBlockHeader {
fn as_ref(&self) -> &FuelConsensusHeader<Empty> {
&self.consensus
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))]
pub struct FuelBlockDb {
pub header: FuelBlockHeader,
pub transactions: Vec<Bytes32>,
}
impl FuelBlockDb {
pub fn id(&self) -> BlockId {
self.header.id()
}
pub fn consensus_type(&self) -> ConsensusType {
self.header.consensus_type()
}
pub fn fix_me_default_block() -> Self {
Self {
header: FuelBlockHeader {
application: FuelApplicationHeader {
da_height: Default::default(),
generated: GeneratedApplicationFields {
transactions_count: Default::default(),
output_messages_count: Default::default(),
transactions_root: Default::default(),
output_messages_root: Default::default(),
},
},
consensus: FuelConsensusHeader {
prev_root: Default::default(),
height: Default::default(),
time: Tai64::UNIX_EPOCH,
generated: GeneratedConsensusFields {
application_hash: Default::default(),
},
},
metadata: None,
},
transactions: Default::default(),
}
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))]
pub struct FuelBlock {
header: FuelBlockHeader,
transactions: Vec<Transaction>,
}
#[derive(Clone, Debug)]
pub struct PartialFuelBlock {
pub header: PartialFuelBlockHeader,
pub transactions: Vec<Transaction>,
}
impl FuelBlock {
pub fn new(
header: PartialFuelBlockHeader,
mut transactions: Vec<Transaction>,
message_ids: &[MessageId],
) -> Self {
let transaction_ids: Vec<_> =
transactions.iter_mut().map(|tx| tx.to_bytes()).collect();
Self {
header: header.generate(&transaction_ids[..], message_ids),
transactions,
}
}
pub fn id(&self) -> BlockId {
self.header.id()
}
pub fn to_db_block(&self) -> FuelBlockDb {
FuelBlockDb {
header: self.header.clone(),
transactions: self.transactions.iter().map(|tx| tx.id()).collect(),
}
}
pub fn from_db_block(db_block: FuelBlockDb, transactions: Vec<Transaction>) -> Self {
Self {
header: db_block.header,
transactions,
}
}
pub fn transactions(&self) -> &[Transaction] {
&self.transactions[..]
}
pub fn header(&self) -> &FuelBlockHeader {
&self.header
}
#[cfg(any(test, feature = "test-helpers"))]
pub fn transactions_mut(&mut self) -> &mut Vec<Transaction> {
&mut self.transactions
}
#[cfg(any(test, feature = "test-helpers"))]
pub fn header_mut(&mut self) -> &mut FuelBlockHeader {
&mut self.header
}
}
impl PartialFuelBlock {
pub fn new(header: PartialFuelBlockHeader, transactions: Vec<Transaction>) -> Self {
Self {
header,
transactions,
}
}
pub fn to_partial_db_block(&self) -> FuelBlockDb {
FuelBlockDb {
header: self.header.clone().without_generated(),
transactions: self.transactions.iter().map(|tx| tx.id()).collect(),
}
}
pub fn generate(self, message_ids: &[MessageId]) -> FuelBlock {
FuelBlock::new(self.header, self.transactions, message_ids)
}
}
impl From<FuelBlock> for PartialFuelBlock {
fn from(block: FuelBlock) -> Self {
let FuelBlock {
header:
FuelBlockHeader {
application: FuelApplicationHeader { da_height, .. },
consensus:
FuelConsensusHeader {
prev_root,
height,
time,
..
},
..
},
transactions,
} = block;
Self {
header: PartialFuelBlockHeader {
application: FuelApplicationHeader {
da_height,
generated: Empty {},
},
consensus: FuelConsensusHeader {
prev_root,
height,
time,
generated: Empty {},
},
metadata: None,
},
transactions,
}
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FuelBlockConsensus {
PoA(FuelBlockPoAConsensus),
}
impl FuelBlockConsensus {
pub fn block_producer(&self, block_id: &BlockId) -> anyhow::Result<Address> {
match &self {
FuelBlockConsensus::PoA(poa_data) => {
let public_key = poa_data.signature.recover(block_id.as_message())?;
let address = Input::owner(&public_key);
Ok(address)
}
}
}
}
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FuelBlockPoAConsensus {
pub signature: Signature,
}
#[cfg(any(test, feature = "test-helpers"))]
impl<T> Default for FuelConsensusHeader<T>
where
T: Default,
{
fn default() -> Self {
Self {
time: Tai64::UNIX_EPOCH,
height: BlockHeight::default(),
prev_root: Bytes32::default(),
generated: Default::default(),
}
}
}
#[cfg(any(test, feature = "test-helpers"))]
impl Default for FuelBlockConsensus {
fn default() -> Self {
FuelBlockConsensus::PoA(Default::default())
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))]
pub struct SealedFuelBlock {
pub block: FuelBlock,
pub consensus: FuelBlockConsensus,
}
impl FuelBlockPoAConsensus {
pub fn new(signature: Signature) -> Self {
Self { signature }
}
}
impl SealedFuelBlock {
pub fn fix_me_default_block() -> Self {
Self {
block: FuelBlock {
header: FuelBlockHeader {
application: FuelApplicationHeader {
da_height: Default::default(),
generated: GeneratedApplicationFields {
transactions_count: Default::default(),
output_messages_count: Default::default(),
transactions_root: Default::default(),
output_messages_root: Default::default(),
},
},
consensus: FuelConsensusHeader {
prev_root: Default::default(),
height: Default::default(),
time: Tai64::UNIX_EPOCH,
generated: GeneratedConsensusFields {
application_hash: Default::default(),
},
},
metadata: None,
},
transactions: Default::default(),
},
consensus: FuelBlockConsensus::PoA(FuelBlockPoAConsensus {
signature: Default::default(),
}),
}
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(any(test, feature = "test-helpers"), derive(Default))]
pub struct SealedFuelBlockHeader {
pub header: FuelBlockHeader,
pub consensus: FuelBlockConsensus,
}