Skip to content

Commit

Permalink
feat: verification of L1Batch witness (BFT-471) (#2019)
Browse files Browse the repository at this point in the history
Verification of an L1 batch, based on StoredBatchInfo. We extract the
hash of the last block of the transaction via merkle path from the
root_state (for batch n and n-1) and we recompute the rolling block hash
based on transactions in the payload. The verification is not perfect as
there are still some fields in payload for which we don't have a
commitment - we should address that later.

Verification will be used for syncing L1Batches over p2p network (not
implemented yet).

Also removed serde serialization from TransactionRequest which is unused
- we don't need to maintain encoding compatibility for it.
  • Loading branch information
pompon0 committed Jun 13, 2024
1 parent 64cb269 commit 6cc5455
Show file tree
Hide file tree
Showing 84 changed files with 1,191 additions and 440 deletions.
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions core/bin/system-constants-generator/src/intrinsic_costs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ pub(crate) fn l2_gas_constants() -> IntrinsicSystemGasConstants {
0,
Some(U256::zero()),
None,
None,
vec![],
)
.into(),
],
Expand All @@ -99,7 +99,7 @@ pub(crate) fn l2_gas_constants() -> IntrinsicSystemGasConstants {
0,
Some(U256::zero()),
Some(vec![0u8; DELTA_IN_TX_SIZE]),
None,
vec![],
)
.into()],
true,
Expand All @@ -117,7 +117,7 @@ pub(crate) fn l2_gas_constants() -> IntrinsicSystemGasConstants {
0,
Some(U256::zero()),
None,
Some(vec![vec![0u8; 32]]),
vec![vec![0u8; 32]],
)
.into()],
true,
Expand Down
8 changes: 4 additions & 4 deletions core/bin/system-constants-generator/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ pub(super) fn get_l2_tx(
U256::from(0),
L2ChainId::from(270),
signer,
None,
vec![],
Default::default(),
)
.unwrap()
Expand Down Expand Up @@ -128,7 +128,7 @@ pub(super) fn get_l1_tx(
pubdata_price: u32,
custom_gas_limit: Option<U256>,
custom_calldata: Option<Vec<u8>>,
factory_deps: Option<Vec<Vec<u8>>>,
factory_deps: Vec<Vec<u8>>,
) -> L1Tx {
L1Tx {
execute: Execute {
Expand Down Expand Up @@ -157,10 +157,10 @@ pub(super) fn get_l1_txs(number_of_txs: usize) -> (Vec<Transaction>, Vec<Transac
let contract_address = Address::random();

txs_without_pubdata_price
.push(get_l1_tx(id as u64, sender, contract_address, 0, None, None, None).into());
.push(get_l1_tx(id as u64, sender, contract_address, 0, None, None, vec![]).into());

txs_with_pubdata_price
.push(get_l1_tx(id as u64, sender, contract_address, 1, None, None, None).into());
.push(get_l1_tx(id as u64, sender, contract_address, 1, None, None, vec![]).into());
}

(txs_with_pubdata_price, txs_without_pubdata_price)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ impl<TypedStructure: EIP712TypedStructure> StructMember for TypedStructure {
}

/// Interface for defining the structure for the EIP712 signature.
pub trait EIP712TypedStructure: Serialize {
pub trait EIP712TypedStructure {
const TYPE_NAME: &'static str;

fn build_structure<BUILDER: StructBuilder>(&self, builder: &mut BUILDER);
Expand Down
2 changes: 1 addition & 1 deletion core/lib/crypto_primitives/src/eip712_signature/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::eip712_signature::typed_structure::{EIP712TypedStructure, Eip712Domai

/// Formats the data that needs to be signed in json according to the standard eip-712.
/// Compatible with `eth_signTypedData` RPC call.
pub fn get_eip712_json<T: EIP712TypedStructure>(
pub fn get_eip712_json<T: EIP712TypedStructure + serde::Serialize>(
eip712_domain: &Eip712Domain,
typed_struct: &T,
) -> Value {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 2 additions & 8 deletions core/lib/dal/src/consensus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,10 +277,7 @@ impl ProtoRepr for proto::Transaction {
.and_then(|x| parse_h256(x))
.map(h256_to_u256)
.context("execute.value")?,
factory_deps: match execute.factory_deps.is_empty() {
true => None,
false => Some(execute.factory_deps.clone()),
},
factory_deps: execute.factory_deps.clone(),
},
received_timestamp_ms: 0, // This timestamp is local to the node
raw_bytes: self.raw_bytes.as_ref().map(|x| x.clone().into()),
Expand Down Expand Up @@ -361,10 +358,7 @@ impl ProtoRepr for proto::Transaction {
contract_address: Some(this.execute.contract_address.as_bytes().into()),
calldata: Some(this.execute.calldata.clone()),
value: Some(u256_to_h256(this.execute.value).as_bytes().into()),
factory_deps: match &this.execute.factory_deps {
Some(inner) => inner.clone(),
None => vec![],
},
factory_deps: this.execute.factory_deps.clone(),
};
Self {
common_data: Some(common_data),
Expand Down
4 changes: 3 additions & 1 deletion core/lib/dal/src/consensus/proto/mod.proto
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ message Payload {
}

message Transaction {
reserved 5;
reserved "received_timestamp_ms";
oneof common_data {
L1TxCommonData l1 = 1;
L2TxCommonData l2 = 2;
Expand Down Expand Up @@ -80,7 +82,7 @@ message Execute {
optional bytes contract_address = 1; // required; H160
optional bytes calldata = 2; // required
optional bytes value = 3; // required; U256
repeated bytes factory_deps = 4; // optional
repeated bytes factory_deps = 4;
}

message InputData {
Expand Down
61 changes: 41 additions & 20 deletions core/lib/dal/src/consensus_dal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,33 +279,54 @@ impl ConsensusDal<'_, '_> {
.await
}

/// Converts the L2 block `block_number` into consensus payload. `Payload` is an
/// opaque format for the L2 block that consensus understands and generates a
/// certificate for it.
pub async fn block_payload(
/// Fetches a range of L2 blocks from storage and converts them to `Payload`s.
pub async fn block_payloads(
&mut self,
block_number: validator::BlockNumber,
) -> DalResult<Option<Payload>> {
let instrumentation =
Instrumented::new("block_payload").with_arg("block_number", &block_number);
let block_number = u32::try_from(block_number.0)
.map_err(|err| instrumentation.arg_error("block_number", err))?;
let block_number = L2BlockNumber(block_number);
numbers: std::ops::Range<validator::BlockNumber>,
) -> DalResult<Vec<Payload>> {
let numbers = (|| {
anyhow::Ok(std::ops::Range {
start: L2BlockNumber(numbers.start.0.try_into().context("start")?),
end: L2BlockNumber(numbers.end.0.try_into().context("end")?),
})
})()
.map_err(|err| {
Instrumented::new("block_payloads")
.with_arg("numbers", &numbers)
.arg_error("numbers", err)
})?;

let Some(block) = self
let blocks = self
.storage
.sync_dal()
.sync_block_inner(block_number)
.await?
else {
return Ok(None);
};
let transactions = self
.sync_blocks_inner(numbers.clone())
.await?;
let mut transactions = self
.storage
.transactions_web3_dal()
.get_raw_l2_block_transactions(block_number)
.get_raw_l2_blocks_transactions(numbers)
.await?;
Ok(Some(block.into_payload(transactions)))
Ok(blocks
.into_iter()
.map(|b| {
let txs = transactions.remove(&b.number).unwrap_or_default();
b.into_payload(txs)
})
.collect())
}

/// Fetches an L2 block from storage and converts it to `Payload`. `Payload` is an
/// opaque format for the L2 block that consensus understands and generates a
/// certificate for it.
pub async fn block_payload(
&mut self,
number: validator::BlockNumber,
) -> DalResult<Option<Payload>> {
Ok(self
.block_payloads(number..number + 1)
.await?
.into_iter()
.next())
}

/// Inserts a certificate for the L2 block `cert.header().number`. It verifies that
Expand Down
2 changes: 1 addition & 1 deletion core/lib/dal/src/models/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fn default_execute() -> Execute {
8cdfd0000000000000000000000000000000000000000000000000000000157d600d0",
)
.unwrap(),
factory_deps: None,
factory_deps: vec![],
}
}

Expand Down
43 changes: 28 additions & 15 deletions core/lib/dal/src/sync_dal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@ pub struct SyncDal<'a, 'c> {
}

impl SyncDal<'_, '_> {
pub(super) async fn sync_block_inner(
pub(super) async fn sync_blocks_inner(
&mut self,
block_number: L2BlockNumber,
) -> DalResult<Option<SyncBlock>> {
let block = sqlx::query_as!(
numbers: std::ops::Range<L2BlockNumber>,
) -> DalResult<Vec<SyncBlock>> {
// Check if range is non-empty, because BETWEEN in SQL in `unordered`.
if numbers.is_empty() {
return Ok(vec![]);
}
let blocks = sqlx::query_as!(
StorageSyncBlock,
r#"
SELECT
Expand Down Expand Up @@ -53,35 +57,44 @@ impl SyncDal<'_, '_> {
FROM
miniblocks
WHERE
miniblocks.number = $1
miniblocks.number BETWEEN $1 AND $2
"#,
i64::from(block_number.0)
i64::from(numbers.start.0),
i64::from(numbers.end.0 - 1),
)
.try_map(SyncBlock::try_from)
.instrument("sync_dal_sync_block.block")
.with_arg("block_number", &block_number)
.fetch_optional(self.storage)
.instrument("sync_dal_sync_blocks.block")
.with_arg("numbers", &numbers)
.fetch_all(self.storage)
.await?;

Ok(block)
Ok(blocks)
}

pub async fn sync_block(
&mut self,
block_number: L2BlockNumber,
number: L2BlockNumber,
include_transactions: bool,
) -> DalResult<Option<en::SyncBlock>> {
let _latency = MethodLatency::new("sync_dal_sync_block");
let Some(block) = self.sync_block_inner(block_number).await? else {
let numbers = number..number + 1;
let Some(block) = self
.sync_blocks_inner(numbers.clone())
.await?
.into_iter()
.next()
else {
return Ok(None);
};
let transactions = if include_transactions {
let transactions = self
let mut transactions = self
.storage
.transactions_web3_dal()
.get_raw_l2_block_transactions(block_number)
.get_raw_l2_blocks_transactions(numbers)
.await?;
Some(transactions)
// If there are no transactions in the block,
// return `Some(vec![])`.
Some(transactions.remove(&number).unwrap_or_default())
} else {
None
};
Expand Down
6 changes: 3 additions & 3 deletions core/lib/dal/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ pub(crate) fn mock_l2_transaction() -> L2Tx {
Default::default(),
L2ChainId::from(270),
&K256PrivateKey::random(),
None,
vec![],
Default::default(),
)
.unwrap();
Expand Down Expand Up @@ -98,7 +98,7 @@ pub(crate) fn mock_l1_execute() -> L1Tx {
contract_address: H160::random(),
value: Default::default(),
calldata: vec![],
factory_deps: None,
factory_deps: vec![],
};

L1Tx {
Expand Down Expand Up @@ -126,7 +126,7 @@ pub(crate) fn mock_protocol_upgrade_transaction() -> ProtocolUpgradeTx {
contract_address: H160::random(),
value: Default::default(),
calldata: vec![],
factory_deps: None,
factory_deps: vec![],
};

ProtocolUpgradeTx {
Expand Down
Loading

0 comments on commit 6cc5455

Please sign in to comment.