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

Keep record of events from L1 in Block Header #1769

Merged
merged 11 commits into from
Mar 22, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Description of the upcoming release here.
### Changed

#### Breaking
- [#1769](https://github.com/FuelLabs/fuel-core/pull/1769): Include new field on header for the merkle root of imported events. Rename other message root field.
- [#1768](https://github.com/FuelLabs/fuel-core/pull/1768): Moved `ContractsInfo` table to the off-chain database. Removed `salt` field from the `ContractConfig`.
- [#1761](https://github.com/FuelLabs/fuel-core/pull/1761): Adjustments to the upcoming testnet configs:
- Decreased the max size of the contract/predicate/script to be 100KB.
Expand Down
6 changes: 5 additions & 1 deletion crates/client/assets/schema.sdl
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,11 @@ type Header {
"""
Merkle root of message receipts in this block.
"""
messageReceiptRoot: Bytes32!
messageOutboxRoot: Bytes32!
"""
Merkle root of inbox events in this block.
"""
eventInboxRoot: Bytes32!
"""
Fuel block height.
"""
Expand Down
3 changes: 2 additions & 1 deletion crates/client/src/client/schema/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ pub struct Header {
pub transactions_count: U64,
pub message_receipt_count: U64,
pub transactions_root: Bytes32,
pub message_receipt_root: Bytes32,
pub message_outbox_root: Bytes32,
pub event_inbox_root: Bytes32,
pub height: U32,
pub prev_root: Bytes32,
pub time: Tai64Timestamp,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ query($height: U32) {
transactionsCount
messageReceiptCount
transactionsRoot
messageReceiptRoot
messageOutboxRoot
eventInboxRoot
height
prevRoot
time
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ query($id: BlockId) {
transactionsCount
messageReceiptCount
transactionsRoot
messageReceiptRoot
messageOutboxRoot
eventInboxRoot
height
prevRoot
time
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ query($after: String, $before: String, $first: Int, $last: Int) {
transactionsCount
messageReceiptCount
transactionsRoot
messageReceiptRoot
messageOutboxRoot
eventInboxRoot
height
prevRoot
time
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ query {
transactionsCount
messageReceiptCount
transactionsRoot
messageReceiptRoot
messageOutboxRoot
eventInboxRoot
height
prevRoot
time
Expand Down
6 changes: 4 additions & 2 deletions crates/client/src/client/types/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ pub struct Header {
pub transactions_count: u64,
pub message_receipt_count: u64,
pub transactions_root: MerkleRoot,
pub message_receipt_root: MerkleRoot,
pub message_outbox_root: MerkleRoot,
pub event_inbox_root: MerkleRoot,
Voxelot marked this conversation as resolved.
Show resolved Hide resolved
pub height: u32,
Voxelot marked this conversation as resolved.
Show resolved Hide resolved
pub prev_root: MerkleRoot,
pub time: Tai64,
Expand Down Expand Up @@ -77,7 +78,8 @@ impl From<schema::block::Header> for Header {
transactions_count: value.transactions_count.into(),
message_receipt_count: value.message_receipt_count.into(),
transactions_root: value.transactions_root.into(),
message_receipt_root: value.message_receipt_root.into(),
message_outbox_root: value.message_outbox_root.into(),
event_inbox_root: value.event_inbox_root.into(),
height: value.height.into(),
Voxelot marked this conversation as resolved.
Show resolved Hide resolved
prev_root: value.prev_root.into(),
time: value.time.0,
Expand Down
2 changes: 1 addition & 1 deletion crates/fuel-core/src/database/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ mod tests {
},
};
let block = PartialFuelBlock::new(header, vec![]);
block.generate(&[])
block.generate(&[], Default::default())
})
.collect::<Vec<_>>();

Expand Down
37 changes: 37 additions & 0 deletions crates/fuel-core/src/executor.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#[allow(clippy::arithmetic_side_effects)]
#[allow(clippy::cast_possible_truncation)]
#[allow(non_snake_case)]
#[cfg(test)]
mod tests {
use crate as fuel_core;
Expand Down Expand Up @@ -2953,6 +2954,7 @@ mod tests {
},
StorageAsMut,
};
use fuel_core_types::fuel_merkle::binary::root_calculator::MerkleRootCalculator;

fn database_with_genesis_block(da_block_height: u64) -> Database<OnChain> {
let mut db = Database::default();
Expand Down Expand Up @@ -3099,6 +3101,41 @@ mod tests {
Ok(())
}

#[test]
fn execute_without_commit__block_producer_includes_correct_inbox_event_merkle_root(
) {
// given
let genesis_da_height = 3u64;
let on_chain_db = database_with_genesis_block(genesis_da_height);
let mut relayer_db = Database::<Relayer>::default();
let block_height = 1u32;
let relayer_da_height = 10u64;
let mut root_calculator = MerkleRootCalculator::new();
for da_height in (genesis_da_height + 1)..=relayer_da_height {
let mut message = Message::default();
message.set_da_height(da_height.into());
message.set_nonce(da_height.into());
root_calculator.push(message.id().as_ref());
add_message_to_relayer(&mut relayer_db, message);
}
let producer = create_relayer_executor(on_chain_db, relayer_db);
let block = test_block(block_height.into(), relayer_da_height.into(), 0);

// when
let (result, _) = producer
.execute_without_commit(
ExecutionTypes::Production(block.into()),
Default::default(),
)
.unwrap()
.into();

// then
let expected = root_calculator.root().into();
let actual = result.block.header().application().event_inbox_root;
assert_eq!(actual, expected);
}

#[test]
fn block_producer_does_not_take_messages_for_the_same_height() {
let genesis_da_height = 1u64;
Expand Down
8 changes: 4 additions & 4 deletions crates/fuel-core/src/query/message/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ async fn can_build_message_proof() {
generated: Default::default(),
},
}
.generate(&[], &[]);
.generate(&[], &[], Default::default());
let commit_block = CompressedBlock::test(commit_block_header, vec![]);
let message_block_header = PartialBlockHeader {
application: ApplicationHeader {
Expand All @@ -157,7 +157,7 @@ async fn can_build_message_proof() {
generated: Default::default(),
},
}
.generate(&[], &message_ids);
.generate(&[], &message_ids, Default::default());
let message_block = CompressedBlock::test(message_block_header, TXNS.to_vec());

let block_proof = MerkleProof {
Expand Down Expand Up @@ -213,8 +213,8 @@ async fn can_build_message_proof() {
.unwrap()
.unwrap();
assert_eq!(
proof.message_block_header.message_receipt_root,
message_block.header().message_receipt_root
proof.message_block_header.message_outbox_root,
message_block.header().message_outbox_root
);
assert_eq!(
proof.message_block_header.height(),
Expand Down
9 changes: 7 additions & 2 deletions crates/fuel-core/src/schema/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,13 @@ impl Header {
}

/// Merkle root of message receipts in this block.
async fn message_receipt_root(&self) -> Bytes32 {
self.0.message_receipt_root.into()
async fn message_outbox_root(&self) -> Bytes32 {
self.0.message_outbox_root.into()
}

Voxelot marked this conversation as resolved.
Show resolved Hide resolved
/// Merkle root of inbox events in this block.
async fn event_inbox_root(&self) -> Bytes32 {
self.0.event_inbox_root.into()
}

/// Fuel block height.
Expand Down
11 changes: 6 additions & 5 deletions crates/fuel-core/src/service/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ fn cleanup_genesis_progress(
pub fn create_genesis_block(config: &Config) -> Block {
let block_height = config.state_reader.block_height();
let da_block_height = config.state_reader.da_block_height();
let transactions = vec![];
let message_ids = &[];
let events = Default::default();
Block::new(
PartialBlockHeader {
application: ApplicationHeader::<Empty> {
Expand All @@ -171,17 +174,15 @@ pub fn create_genesis_block(config: &Config) -> Block {
generated: Empty,
},
consensus: ConsensusHeader::<Empty> {
// The genesis is a first block, so previous root is zero.
prev_root: Bytes32::zeroed(),
// The block height at genesis.
height: block_height,
time: fuel_core_types::tai64::Tai64::UNIX_EPOCH,
generated: Empty,
},
},
// Genesis block doesn't have any transaction.
vec![],
&[],
transactions,
message_ids,
events,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ fn correct() -> Input {
..Default::default()
},
};
let block_header = partial_header.generate(&txs, &[]);
let block_header = partial_header.generate(&txs, &[], Default::default());

Input {
block_header_merkle_root: [2u8; 32],
Expand Down
11 changes: 10 additions & 1 deletion crates/services/executor/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ use fuel_core_types::{
RegId,
Word,
},
fuel_merkle::binary::root_calculator::MerkleRootCalculator,
fuel_tx::{
field::{
InputContract,
Expand Down Expand Up @@ -304,6 +305,7 @@ pub struct ExecutionData {
events: Vec<ExecutorEvent>,
changes: Changes,
pub skipped_transactions: Vec<(TxId, ExecutorError)>,
event_inbox_root: Bytes32,
}

/// Per-block execution options
Expand Down Expand Up @@ -499,12 +501,13 @@ where
skipped_transactions,
events,
changes,
event_inbox_root,
..
} = execution_data;

// Now that the transactions have been executed, generate the full header.

let block = block.generate(&message_ids[..]);
let block = block.generate(&message_ids[..], event_inbox_root);

let finalized_block_id = block.id();

Expand Down Expand Up @@ -557,6 +560,7 @@ where
events: Vec::new(),
changes: Default::default(),
skipped_transactions: Vec::new(),
event_inbox_root: Default::default(),
};
let execution_data = &mut data;

Expand Down Expand Up @@ -724,13 +728,16 @@ where
return Err(ExecutorError::DaHeightExceededItsLimit)
};

let mut root_calculator = MerkleRootCalculator::new();

for da_height in next_unprocessed_da_height..=header.da_height.0 {
let da_height = da_height.into();
let events = self
.relayer
.get_events(&da_height)
.map_err(|err| ExecutorError::RelayerError(err.into()))?;
for event in events {
root_calculator.push(event.hash().as_ref());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I'm curious: does Ethereal RPC guarantee that the order of events is the same as they were emitted? @MitchTurner , Could you double-check that?=)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at ethers, there is a field for indexing fields within each block. We could sort all the logs by this in the relayer to play it safe.

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also consider lexicographically sorting all the event hashes before merklizing

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order we receive the events shouldn't be dependent on the Ethereal RPC specifically, rather our implementation of Relayer as a whole.

IF the order isn't deterministic, then I agree with @Voxelot that we could just do some deterministic sorting in our Relayer adapter.

Do we want to do that as part of this PR?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or we could just order them after we get them from the Relayer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The best ordering Imo is to use log_index, as it ensures fair ordering of event processing. I'd prefer to avoid muddling our events in the executor with ethereum specific fields, so I'd be fine with making a separate PR to ensure ordering on the relayer side.

The alternative is to add some kind of ordering field to events, and making the executor sort them every time. However, this would also incur additional consensus overhead which I think we could avoid.

match event {
Event::Message(message) => {
if message.da_height() != da_height {
Expand All @@ -747,6 +754,8 @@ where
}
}

execution_data.event_inbox_root = root_calculator.root().into();

Ok(())
}

Expand Down
8 changes: 4 additions & 4 deletions crates/services/producer/src/block_producer/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ mod produce_and_execute_block_txpool {
},
transactions: vec![],
}
.generate(&[])
.generate(&[], Default::default())
.compress(&Default::default());

let db = MockDb {
Expand Down Expand Up @@ -162,7 +162,7 @@ mod produce_and_execute_block_txpool {
},
transactions: vec![],
}
.generate(&[])
.generate(&[], Default::default())
.compress(&Default::default());

// Given
Expand Down Expand Up @@ -215,7 +215,7 @@ mod produce_and_execute_block_txpool {
},
transactions: vec![],
}
.generate(&[])
.generate(&[], Default::default())
.compress(&Default::default());

// Given
Expand Down Expand Up @@ -284,7 +284,7 @@ mod produce_and_execute_block_txpool {
},
transactions: vec![],
}
.generate(&[])
.generate(&[], Default::default())
.compress(&Default::default());

let db = MockDb {
Expand Down
7 changes: 6 additions & 1 deletion crates/services/producer/src/mocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,12 @@ fn to_block(component: &Components<Vec<ArcPoolTx>>) -> Block {
.into_iter()
.map(|tx| tx.as_ref().into())
.collect();
Block::new(component.header_to_produce.clone(), transactions, &[])
Block::new(
component.header_to_produce.clone(),
transactions,
&[],
Default::default(),
)
}

impl Executor<Vec<ArcPoolTx>> for MockExecutor {
Expand Down
2 changes: 1 addition & 1 deletion crates/storage/src/structured_storage/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ mod tests {
},
};
let block = PartialFuelBlock::new(header, vec![]);
block.generate(&[])
block.generate(&[], Default::default())
})
.collect::<Vec<_>>();

Expand Down
Loading
Loading