Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fault_proving(compression): include commitment to transaction ids within compressed block header #2572

Closed
wants to merge 6 commits into from
Closed
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Added
- [2551](https://github.com/FuelLabs/fuel-core/pull/2551): Enhanced the DA compressed block header to include block id.
- [2572](https://github.com/FuelLabs/fuel-core/pull/2572): Enhanced the DA compressed block header to include tx id commitment.

### Changed
- [2603](https://github.com/FuelLabs/fuel-core/pull/2603): Sets the latest recorded height on initialization, not just when DA costs are received
Expand Down
9 changes: 9 additions & 0 deletions crates/compression/src/compress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ use crate::{
VersionedCompressedBlock,
};
use anyhow::Context;
#[cfg(feature = "fault-proving")]
use fuel_core_types::fuel_tx::UniqueIdentifier;

use fuel_core_types::{
blockchain::block::Block,
fuel_compression::{
Expand Down Expand Up @@ -51,12 +54,16 @@ pub async fn compress<D>(
config: Config,
mut db: D,
block: &Block,
#[cfg(feature = "fault-proving")] chain_id: fuel_core_types::fuel_types::ChainId,
) -> anyhow::Result<VersionedCompressedBlock>
where
D: CompressDb,
{
let target = block.transactions_vec();

#[cfg(feature = "fault-proving")]
let tx_ids = target.iter().map(|tx| tx.id(&chain_id)).collect::<Vec<_>>();

let mut prepare_ctx = PrepareCtx {
config,
timestamp: block.header().time(),
Expand All @@ -73,6 +80,8 @@ where
block.header(),
registrations,
transactions,
#[cfg(feature = "fault-proving")]
&tx_ids,
))
}

Expand Down
10 changes: 9 additions & 1 deletion crates/compression/src/compressed_block_payload/v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
};
use fuel_core_types::{
blockchain::{
block::PartialFuelBlock,
header::{
ApplicationHeader,
BlockHeader,
Expand All @@ -13,7 +14,10 @@ use fuel_core_types::{
primitives::Empty,
},
fuel_tx::CompressedTransaction,
fuel_types::BlockHeight,
fuel_types::{
BlockHeight,
ChainId,
},
};

/// Compressed block, without the preceding version byte.
Expand Down Expand Up @@ -51,6 +55,10 @@ impl VersionedBlockPayload for CompressedBlockPayloadV0 {
fn partial_block_header(&self) -> PartialBlockHeader {
self.header
}

fn validate_with(&self, _: &PartialFuelBlock, _: &ChainId) -> anyhow::Result<()> {
Ok(())
}
}

impl CompressedBlockPayloadV0 {
Expand Down
51 changes: 46 additions & 5 deletions crates/compression/src/compressed_block_payload/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
};
use fuel_core_types::{
blockchain::{
block::PartialFuelBlock,
header::{
ApplicationHeader,
BlockHeader,
Expand All @@ -15,10 +16,26 @@ use fuel_core_types::{
Empty,
},
},
fuel_tx::CompressedTransaction,
fuel_types::BlockHeight,
fuel_crypto,
fuel_tx::{
Bytes32,
CompressedTransaction,
UniqueIdentifier,
},
fuel_types::{
BlockHeight,
ChainId,
},
};

pub fn generate_tx_commitment(tx_ids: &[Bytes32]) -> Bytes32 {
let mut hasher = fuel_crypto::Hasher::default();
for tx_id in tx_ids {
hasher.input(tx_id.as_ref());
}
hasher.digest()
}

/// A partially complete fuel block header that does not
/// have any generated fields because it has not been executed yet.
#[derive(
Expand All @@ -31,10 +48,12 @@ pub struct CompressedBlockHeader {
pub consensus: ConsensusHeader<Empty>,
// The block id.
pub block_id: BlockId,
// A commitment to all transaction ids in the block
pub tx_commitment: Bytes32,
}

impl From<&BlockHeader> for CompressedBlockHeader {
fn from(header: &BlockHeader) -> Self {
impl CompressedBlockHeader {
fn new(header: &BlockHeader, tx_ids: &[Bytes32]) -> Self {
let ConsensusHeader {
prev_root,
height,
Expand All @@ -56,6 +75,7 @@ impl From<&BlockHeader> for CompressedBlockHeader {
generated: Empty {},
},
block_id: header.id(),
tx_commitment: generate_tx_commitment(tx_ids),
}
}
}
Expand Down Expand Up @@ -103,6 +123,26 @@ impl VersionedBlockPayload for CompressedBlockPayloadV1 {
fn partial_block_header(&self) -> PartialBlockHeader {
PartialBlockHeader::from(&self.header)
}

fn validate_with(
&self,
partial_block: &PartialFuelBlock,
chain_id: &ChainId,
) -> anyhow::Result<()> {
let txs = partial_block
.transactions
.iter()
.map(|tx| tx.id(chain_id))
.collect::<Vec<_>>();
let tx_commitment = generate_tx_commitment(&txs);
let expected = self.header.tx_commitment;
if tx_commitment != expected {
anyhow::bail!(
"Invalid tx commitment. got {tx_commitment}, expected {expected}"
);
}
Ok(())
}
}

impl CompressedBlockPayloadV1 {
Expand All @@ -112,9 +152,10 @@ impl CompressedBlockPayloadV1 {
header: &BlockHeader,
registrations: RegistrationsPerTable,
transactions: Vec<CompressedTransaction>,
tx_ids: &[Bytes32],
) -> Self {
Self {
header: CompressedBlockHeader::from(header),
header: CompressedBlockHeader::new(header, tx_ids),
registrations,
transactions,
}
Expand Down
10 changes: 8 additions & 2 deletions crates/compression/src/decompress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ use fuel_core_types::{
},
fuel_types::{
Address,
ChainId,
ContractId,
},
tai64::Tai64,
Expand All @@ -54,6 +55,7 @@ pub async fn decompress<D>(
config: Config,
mut db: D,
block: VersionedCompressedBlock,
chain_id: &ChainId,
) -> anyhow::Result<PartialFuelBlock>
where
D: DecompressDb,
Expand Down Expand Up @@ -93,10 +95,14 @@ where
anyhow::bail!("Last transaction is not a mint");
}

Ok(PartialFuelBlock {
let partial_block = PartialFuelBlock {
header: block.partial_block_header(),
transactions,
})
};

block.validate_with(&partial_block, chain_id)?;

Ok(partial_block)
}

pub struct DecompressCtx<D> {
Expand Down
16 changes: 15 additions & 1 deletion crates/compression/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::compressed_block_payload::v0::CompressedBlockPayloadV0;
use crate::compressed_block_payload::v1::CompressedBlockPayloadV1;
use fuel_core_types::{
blockchain::{
block::PartialFuelBlock,
header::{
ApplicationHeader,
BlockHeader,
Expand All @@ -29,7 +30,10 @@ use fuel_core_types::{
primitives::Empty,
},
fuel_tx::CompressedTransaction,
fuel_types::BlockHeight,
fuel_types::{
BlockHeight,
ChainId,
},
};
use registry::RegistrationsPerTable;

Expand All @@ -44,6 +48,11 @@ pub trait VersionedBlockPayload {
fn registrations(&self) -> &RegistrationsPerTable;
fn transactions(&self) -> Vec<CompressedTransaction>;
fn partial_block_header(&self) -> PartialBlockHeader;
fn validate_with(
&self,
partial_block: &PartialFuelBlock,
chain_id: &ChainId,
) -> anyhow::Result<()>;
}

/// Versioned compressed block.
Expand All @@ -60,6 +69,8 @@ impl VersionedCompressedBlock {
header: &BlockHeader,
registrations: RegistrationsPerTable,
transactions: Vec<CompressedTransaction>,
#[cfg(feature = "fault-proving")]
tx_ids: &[fuel_core_types::fuel_types::Bytes32],
) -> Self {
#[cfg(not(feature = "fault-proving"))]
return Self::V0(CompressedBlockPayloadV0::new(
Expand All @@ -72,6 +83,7 @@ impl VersionedCompressedBlock {
header,
registrations,
transactions,
tx_ids,
))
}
}
Expand Down Expand Up @@ -274,6 +286,7 @@ mod tests {
generated: Empty,
},
block_id: BlockId::from_str("0xecea85c17070bc2e65f911310dbd01198f4436052ebba96cded9ddf30c58dd1a").unwrap(),
tx_commitment: Default::default(),
};


Expand Down Expand Up @@ -302,6 +315,7 @@ mod tests {

if let VersionedCompressedBlock::V1(block) = decompressed {
assert_eq!(block.header.block_id, header.block_id);
assert_eq!(block.header.tx_commitment, header.tx_commitment);
} else {
panic!("Expected V1 block, got {:?}", decompressed);
}
Expand Down
3 changes: 3 additions & 0 deletions crates/fuel-core/src/graphql_api/da_compression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub fn da_compress_block<T>(
block: &Block,
block_events: &[Event],
db_tx: &mut T,
#[cfg(feature = "fault-proving")] chain_id: fuel_core_types::fuel_types::ChainId,
) -> anyhow::Result<()>
where
T: OffChainDatabaseTransaction,
Expand All @@ -61,6 +62,8 @@ where
block_events,
},
block,
#[cfg(feature = "fault-proving")]
chain_id,
)
.now_or_never()
.expect("The current implementation resolved all futures instantly")?;
Expand Down
9 changes: 8 additions & 1 deletion crates/fuel-core/src/graphql_api/worker_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,14 @@ where
match self.da_compression_config {
DaCompressionConfig::Disabled => {}
DaCompressionConfig::Enabled(config) => {
da_compress_block(config, block, &result.events, &mut transaction)?;
da_compress_block(
config,
block,
&result.events,
&mut transaction,
#[cfg(feature = "fault-proving")]
self.chain_id,
)?;
}
}

Expand Down
5 changes: 5 additions & 0 deletions tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,8 @@ tracing = { workspace = true }
default = ["fuel-core/default"]
only-p2p = ["fuel-core-p2p"]
aws-kms = ["dep:aws-config", "dep:aws-sdk-kms", "fuel-core-bin/aws-kms"]
fault-proving = [
"fuel-core-compression/fault-proving",
"fuel-core/fault-proving",
"fuel-core-bin/fault-proving",
]
4 changes: 3 additions & 1 deletion tests/tests/da_compression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,9 @@ async fn can_fetch_da_compressed_block_from_graphql() {
},
onchain_db: on_chain_before_execution,
};
let decompressed = decompress(compression_config, db_tx, block).await.unwrap();
let decompressed = decompress(compression_config, db_tx, block, &chain_id)
.await
.unwrap();

let block_from_on_chain_db = db
.on_chain()
Expand Down
Loading