Skip to content
Open
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 Cargo.lock

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

112 changes: 98 additions & 14 deletions lib/batch_types/src/batch_signature.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
use alloy::primitives::{Address, Signature as AlloySignature, SignatureError};
use alloy::primitives::{
Address, B256, Signature as AlloySignature, SignatureError, U256, keccak256,
};
use alloy::signers::Signer;
use alloy::signers::local::PrivateKeySigner;
use alloy::sol_types::SolValue;
use alloy::sol_types::{SolValue, eip712_domain};
use serde::{Deserialize, Serialize};
use zksync_os_contract_interface::IExecutor::CommitBatchInfoZKsyncOS;
use zksync_os_contract_interface::models::CommitBatchInfo;
use zksync_os_contract_interface::calldata::encode_commit_batch_data;
use zksync_os_contract_interface::models::StoredBatchInfo;
use zksync_os_types::ProtocolSemanticVersion;

use crate::BatchInfo;

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BatchSignatureSet(Vec<ValidatedBatchSignature>);
Expand Down Expand Up @@ -39,29 +44,63 @@ impl BatchSignatureSet {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}

pub fn to_vec(&self) -> &Vec<ValidatedBatchSignature> {
&self.0
}

/// Remove signatures not found on allowed list
pub fn filter(mut self, allowed_signers: &[Address]) -> Self {
self.0.retain(|s| allowed_signers.contains(&s.signer));
self
}
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct BatchSignature(AlloySignature);

impl BatchSignature {
pub async fn sign_batch(batch_info: &CommitBatchInfo, private_key: &PrivateKeySigner) -> Self {
let encoded = encode_batch_for_signing(batch_info);
let signature = private_key.sign_message(&encoded).await.unwrap();
/// Sign a batch for `commitBatchesMultisig`
pub async fn sign_batch(
prev_batch_info: &StoredBatchInfo,
batch_info: &BatchInfo,
l1_chain_id: u64,
multisig_committer: Address,
protocol_version: &ProtocolSemanticVersion,
private_key: &PrivateKeySigner,
) -> Self {
let digest = eip712_multisig_digest(
prev_batch_info,
batch_info,
l1_chain_id,
multisig_committer,
protocol_version,
);
let signature = private_key.sign_hash(&digest).await.unwrap();
BatchSignature(signature)
}

pub fn verify_signature(
self,
batch_info: &CommitBatchInfo,
prev_batch_info: &StoredBatchInfo,
batch_info: &BatchInfo,
l1_chain_id: u64,
multisig_committer: Address,
protocol_version: &ProtocolSemanticVersion,
) -> Result<ValidatedBatchSignature, SignatureError> {
let encoded = encode_batch_for_signing(batch_info);
Ok(ValidatedBatchSignature {
signer: self.0.recover_address_from_msg(encoded)?,
signer: self
.0
.recover_address_from_prehash(&eip712_multisig_digest(
prev_batch_info,
batch_info,
l1_chain_id,
multisig_committer,
protocol_version,
))?,
signature: self,
})
}

pub fn into_raw(self) -> [u8; 65] {
self.0.as_bytes()
}
Expand All @@ -72,9 +111,54 @@ impl BatchSignature {
}
}

fn encode_batch_for_signing(batch_info: &CommitBatchInfo) -> Vec<u8> {
let alloy_batch_info = CommitBatchInfoZKsyncOS::from(batch_info.clone());
alloy_batch_info.abi_encode_params()
/// Compute the full EIP-712 digest used by the `MultisigCommitter` contract
/// for the `commitBatchesMultisig` typed data, based on the given batch info
/// and L1 domain parameters.
fn eip712_multisig_digest(
prev_batch_info: &StoredBatchInfo,
batch_info: &BatchInfo,
l1_chain_id: u64,
multisig_committer: Address,
protocol_version: &ProtocolSemanticVersion,
) -> B256 {
const TYPEHASH_BYTES: &[u8] = b"CommitBatchesMultisig(address chainAddress,uint256 processBatchFrom,uint256 processBatchTo,bytes batchData)";
let typehash = keccak256(TYPEHASH_BYTES);

let batch_data = encode_commit_batch_data(
prev_batch_info,
batch_info.commit_info.clone(),
protocol_version.minor,
);

let batch_data_hash = keccak256(batch_data);

let encoded = (
typehash,
batch_info.chain_address,
U256::from(batch_info.batch_number), // processBatchFrom
U256::from(batch_info.batch_number), // processBatchTo
batch_data_hash,
)
.abi_encode_params();

let struct_hash = keccak256(encoded);

let domain = eip712_domain! {
name: "MultisigCommitter",
version: "1",
chain_id: l1_chain_id,
verifying_contract: multisig_committer,
};
let domain_separator = domain.separator();

keccak256(
[
&[0x19, 0x01],
domain_separator.as_slice(),
struct_hash.as_slice(),
]
.concat(),
)
}

#[derive(Clone, Debug, Serialize, Deserialize)]
Expand Down
43 changes: 32 additions & 11 deletions lib/batch_verification/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use tokio::sync::mpsc;
use tokio_util::codec::{FramedRead, FramedWrite};
use zksync_os_batch_types::BlockMerkleTreeData;
use zksync_os_batch_types::{BatchInfo, BatchSignature};
use zksync_os_contract_interface::l1_discovery::{BatchVerificationL1, L1State};
use zksync_os_interface::types::BlockOutput;
use zksync_os_merkle_tree::TreeBatchOutput;
use zksync_os_observability::ComponentStateHandle;
Expand All @@ -37,6 +38,7 @@ pub struct BatchVerificationClient<Finality> {
chain_id: u64,
diamond_proxy: Address,
server_address: String,
l1_state: L1State,
signer: PrivateKeySigner,
block_cache: BlockCache<Finality>,
}
Expand All @@ -59,19 +61,31 @@ type VerificationInput = (

impl<Finality: ReadFinality> BatchVerificationClient<Finality> {
pub fn new(
finality: Finality,
private_key: SecretString,
chain_id: u64,
diamond_proxy: Address,
server_address: String,
private_key: SecretString,
finality: Finality,
l1_state: L1State,
) -> Self {
let signer = PrivateKeySigner::from_str(private_key.expose_secret())
.expect("Invalid batch verification private key");
if let BatchVerificationL1::Enabled(l1_config) = l1_state.batch_verification.clone() {
if !l1_config.validators.contains(&signer.address()) {
tracing::warn!(
"Your address {} is not authorized to verify batches on L1",
signer.address()
);
}
}

Self {
signer: PrivateKeySigner::from_str(private_key.expose_secret())
.expect("Invalid batch verification private key"),
chain_id,
diamond_proxy,
block_cache: BlockCache::new(finality),
server_address,
l1_state,
signer,
block_cache: BlockCache::new(finality),
}
}

Expand Down Expand Up @@ -186,7 +200,7 @@ impl<Finality: ReadFinality> BatchVerificationClient<Finality> {
})
.collect::<Result<Vec<_>, BatchVerificationError>>()?;

let commit_batch_info = BatchInfo::new(
let batch_info = BatchInfo::new(
blocks
.iter()
.map(|(block_output, replay_record, tree)| {
Expand All @@ -202,18 +216,25 @@ impl<Finality: ReadFinality> BatchVerificationClient<Finality> {
self.diamond_proxy,
request.batch_number,
request.pubdata_mode,
)
.commit_info;
);

if commit_batch_info != request.commit_data {
let diff = request.commit_data.diff(&commit_batch_info);
if batch_info.commit_info != request.commit_data {
let diff = request.commit_data.diff(&batch_info.commit_info);

return Err(BatchVerificationError::BatchDataMismatch(format!(
"Batch data mismatch: {diff:?}",
)));
}

let signature = BatchSignature::sign_batch(&request.commit_data, &self.signer).await;
let signature = BatchSignature::sign_batch(
&request.prev_commit_data,
&batch_info,
self.l1_state.l1_chain_id,
self.l1_state.validator_timelock,
&blocks.first().unwrap().1.protocol_version,
&self.signer,
)
.await;

Ok(signature)
}
Expand Down
2 changes: 1 addition & 1 deletion lib/batch_verification/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub struct BatchVerificationConfig {
pub listen_address: String,
pub client_enabled: bool,
pub connect_address: String,
pub threshold: usize,
pub threshold: u64,
pub accepted_signers: Vec<String>,
pub request_timeout: Duration,
pub retry_delay: Duration,
Expand Down
3 changes: 2 additions & 1 deletion lib/batch_verification/src/request.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use tokio_util::bytes::BytesMut;
use tokio_util::codec::{self, LengthDelimitedCodec};
use zksync_os_contract_interface::models::CommitBatchInfo;
use zksync_os_contract_interface::models::{CommitBatchInfo, StoredBatchInfo};
use zksync_os_types::PubdataMode;

/// Request sent from main sequencer to external nodes for batch verification
Expand All @@ -12,6 +12,7 @@ pub struct BatchVerificationRequest {
pub pubdata_mode: PubdataMode,
pub request_id: u64,
pub commit_data: CommitBatchInfo,
pub prev_commit_data: StoredBatchInfo,
}

impl std::fmt::Debug for BatchVerificationRequest {
Expand Down
Loading