Skip to content

Commit 9a3358e

Browse files
feat: batcher client message signing (#471)
Co-authored-by: MauroFab <[email protected]>
1 parent 16e0b5b commit 9a3358e

File tree

9 files changed

+174
-45
lines changed

9 files changed

+174
-45
lines changed

README_SEND_PROOFS.md

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,37 @@ Make sure you have Aligned installed as specified [here](./README.md#how-to-use-
44

55
If you run the examples below, make sure you are in Aligned's repository root.
66

7+
## 1. Import/Create Keystore file
8+
9+
If you already have a keystore file, you can ignore this section and start sending proofs. We give two examples of how to generate one. The first one using Foundry, and the second one using EigenLayerCLI
10+
11+
### Alternative 1: With foundry
12+
13+
Install foundry following this guide:
14+
15+
Install [Foundry](https://book.getfoundry.sh/getting-started/installation):
16+
17+
- If you are creating a new account. Create a private key with:
18+
19+
```bash
20+
cast wallet new-mnemonic --words 12
21+
```
22+
23+
If you are using this wallet outside testnet, write down the mnemonic phrase given by anvil
24+
25+
- Import the wallet using the private key previously generated, or whichever you want to use, and write a password to use it.
26+
27+
```bash
28+
mkdir -p ~/.aligned_keystore/
29+
cast wallet import --private-key <YOUR_ECDSA_PRIVATE_KEY> ~/.aligned_keystore/keystore0
30+
```
31+
32+
This will create the ECDSA keystore file in `~/.aligned_keystore/keystore0`
33+
34+
### Alternative 2: With EigenlayerCLI
35+
36+
- If you have the EigenLayer CLI installed, the keystore can be generated following [this](https://docs.eigenlayer.xyz/eigenlayer/operator-guides/operator-installation#import-keys) instructions. The key will be stored into `~/.eigenlayer/operator_keys`.
37+
738
## SP1 proof
839

940
The SP1 proof needs the proof file and the vm program file.
@@ -16,7 +47,8 @@ aligned submit \
1647
--vm_program <vm_program_file> \
1748
--conn wss://batcher.alignedlayer.com \
1849
--proof_generator_addr [proof_generator_addr] \
19-
--batch_inclusion_data_directory_path [batch_inclusion_data_directory_path]
50+
--batch_inclusion_data_directory_path [batch_inclusion_data_directory_path] \
51+
--keystore_path <path_yo_ecdsa_keystore>
2052
```
2153

2254
**Example**
@@ -27,7 +59,8 @@ aligned submit \
2759
--proving_system SP1 \
2860
--proof ./batcher/aligned/test_files/sp1/sp1_fibonacci.proof \
2961
--vm_program ./batcher/aligned/test_files/sp1/sp1_fibonacci-elf \
30-
--conn wss://batcher.alignedlayer.com
62+
--conn wss://batcher.alignedlayer.com \
63+
--keystore_path ~/.aligned_keystore/keystore0
3164
```
3265

3366
## Risc0 proof
@@ -42,7 +75,8 @@ aligned submit \
4275
--vm_program <vm_program_file> \
4376
--conn wss://batcher.alignedlayer.com \
4477
--proof_generator_addr [proof_generator_addr] \
45-
--batch_inclusion_data_directory_path [batch_inclusion_data_directory_path]
78+
--batch_inclusion_data_directory_path [batch_inclusion_data_directory_path] \
79+
--keystore_path <path_yo_ecdsa_keystore>
4680
```
4781

4882
**Example**
@@ -53,7 +87,8 @@ aligned submit \
5387
--proving_system Risc0 \
5488
--proof ./batcher/aligned/test_files/risc_zero/risc_zero_fibonacci.proof \
5589
--vm_program ./batcher/aligned/test_files/risc_zero/fibonacci_id.bin \
56-
--aligned_verification_data_path ~/.aligned/aligned_verification_data
90+
--aligned_verification_data_path ~/.aligned/aligned_verification_data \
91+
--keystore_path ~/.aligned_keystore/keystore0
5792
```
5893

5994
## GnarkPlonkBn254, GnarkPlonkBls12_381 and Groth16Bn254
@@ -69,7 +104,8 @@ aligned submit \
69104
--vk <verification_key_file> \
70105
--conn wss://batcher.alignedlayer.com \
71106
--proof_generator_addr [proof_generator_addr] \
72-
--batch_inclusion_data_directory_path [batch_inclusion_data_directory_path]
107+
--batch_inclusion_data_directory_path [batch_inclusion_data_directory_path] \
108+
--keystore_path <path_yo_ecdsa_keystore>
73109
```
74110

75111
**Examples**:
@@ -81,7 +117,8 @@ aligned submit \
81117
--proof ./batcher/aligned/test_files/plonk_bn254/plonk.proof \
82118
--public_input ./batcher/aligned/test_files/plonk_bn254/plonk_pub_input.pub \
83119
--vk ./batcher/aligned/test_files/plonk_bn254/plonk.vk \
84-
--conn wss://batcher.alignedlayer.com
120+
--conn wss://batcher.alignedlayer.com \
121+
--keystore_path ~/.aligned_keystore/keystore0
85122
```
86123

87124
```bash
@@ -91,7 +128,8 @@ aligned submit \
91128
--proof ./batcher/aligned/test_files/plonk_bls12_381/plonk.proof \
92129
--public_input ./batcher/aligned/test_files/plonk_bls12_381/plonk_pub_input.pub \
93130
--vk ./batcher/aligned/test_files/plonk_bls12_381/plonk.vk \
94-
--conn wss://batcher.alignedlayer.com
131+
--conn wss://batcher.alignedlayer.com \
132+
--keystore_path ~/.aligned_keystore/keystore0
95133
```
96134

97135
```bash
@@ -101,5 +139,6 @@ aligned submit \
101139
--proof ./batcher/aligned/test_files/groth16/ineq_1_groth16.proof \
102140
--public_input ./batcher/aligned/test_files/groth16/ineq_1_groth16.pub \
103141
--vk ./batcher/aligned/test_files/groth16/ineq_1_groth16.vk \
104-
--conn wss://batcher.alignedlayer.com
142+
--conn wss://batcher.alignedlayer.com \
143+
--keystore_path ~/.aligned_keystore/keystore0
105144
```

batcher/Cargo.lock

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

batcher/aligned-batcher-lib/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ serde = { version = "1.0.201", features = ["derive"] }
1111
sha3 = { version = "0.10.8"}
1212
lambdaworks-crypto = { version = "0.7.0", features = ["serde"] }
1313
hex = "0.4.3"
14+
serde_json = "1.0.117"

batcher/aligned-batcher-lib/src/types/mod.rs

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1+
use ethers::core::k256::ecdsa::SigningKey;
2+
use ethers::signers::Signer;
3+
use ethers::signers::Wallet;
14
use ethers::types::Address;
5+
use ethers::types::Signature;
6+
use ethers::types::SignatureError;
27
use lambdaworks_crypto::merkle_tree::{
38
merkle::MerkleTree, proof::Proof, traits::IsMerkleTreeBackend,
49
};
@@ -17,7 +22,7 @@ pub enum ProvingSystemId {
1722
Risc0,
1823
}
1924

20-
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
25+
#[derive(Debug, Serialize, Deserialize, Clone)]
2126
pub struct VerificationData {
2227
pub proving_system: ProvingSystemId,
2328
pub proof: Vec<u8>,
@@ -122,31 +127,39 @@ impl BatchInclusionData {
122127
.unwrap();
123128

124129
BatchInclusionData {
125-
batch_merkle_root: batch_merkle_tree.root.clone(),
130+
batch_merkle_root: batch_merkle_tree.root,
126131
batch_inclusion_proof,
127132
index_in_batch: verification_data_batch_index,
128133
}
129134
}
130135
}
131136

132-
#[cfg(test)]
133-
mod test {
134-
use super::*;
135-
136-
#[test]
137-
fn hash_new_parent_is_correct() {
138-
let mut hasher = Keccak256::new();
139-
hasher.update(vec![1u8]);
140-
let child_1 = hasher.finalize_reset().into();
141-
hasher.update(vec![2u8]);
142-
let child_2 = hasher.finalize().into();
137+
#[derive(Debug, Clone, Serialize, Deserialize)]
138+
pub struct ClientMessage {
139+
pub verification_data: VerificationData,
140+
pub signature: Signature,
141+
}
143142

144-
let parent = VerificationCommitmentBatch::hash_new_parent(&child_1, &child_2);
143+
impl ClientMessage {
144+
/// Client message is a wrap around verification data and its signature.
145+
/// The signature is obtained by calculating the commitments and then hashing them.
146+
pub async fn new(verification_data: VerificationData, wallet: Wallet<SigningKey>) -> Self {
147+
let hashed_leaf = VerificationCommitmentBatch::hash_data(&verification_data.clone().into());
148+
let signature = wallet.sign_message(hashed_leaf).await.unwrap();
145149

146-
// This value is built using Openzeppelin's module for Merkle Trees, in particular using
147-
// the SimpleMerkleTree. For more details see the openzeppelin_merkle_tree/merkle_tree.js script.
148-
let expected_parent = "71d8979cbfae9b197a4fbcc7d387b1fae9560e2f284d30b4e90c80f6bc074f57";
150+
ClientMessage {
151+
verification_data,
152+
signature,
153+
}
154+
}
149155

150-
assert_eq!(hex::encode(parent), expected_parent)
156+
/// The signature of the message is verified, and when it correct, the
157+
/// recovered address from the signature is returned.
158+
pub fn verify_signature(&self) -> Result<Address, SignatureError> {
159+
let hashed_leaf =
160+
VerificationCommitmentBatch::hash_data(&self.verification_data.clone().into());
161+
let recovered = self.signature.recover(hashed_leaf)?;
162+
self.signature.verify(hashed_leaf, recovered)?;
163+
Ok(recovered)
151164
}
152165
}

batcher/aligned-batcher/src/lib.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use std::time::Duration;
77

88
use crate::eth::BatchVerifiedEventStream;
99
use aligned_batcher_lib::types::{
10-
BatchInclusionData, VerificationCommitmentBatch, VerificationData, VerificationDataCommitment,
10+
BatchInclusionData, ClientMessage, VerificationCommitmentBatch, VerificationData,
11+
VerificationDataCommitment,
1112
};
1213
use aws_sdk_s3::client::Client as S3Client;
1314
use eth::BatchVerifiedFilter;
@@ -167,10 +168,21 @@ impl Batcher {
167168
ws_conn_sink: Arc<RwLock<SplitSink<WebSocketStream<TcpStream>, Message>>>,
168169
) -> Result<(), tokio_tungstenite::tungstenite::Error> {
169170
// Deserialize verification data from message
170-
let verification_data: VerificationData =
171+
let client_msg: ClientMessage =
171172
serde_json::from_str(message.to_text().expect("Message is not text"))
172173
.expect("Failed to deserialize task");
173174

175+
// FIXME: We are not doing anything for the moment with the address from the
176+
// sender, this logic should be added for the payment system.
177+
info!("Verifying message signature...");
178+
if let Ok(_addr) = client_msg.verify_signature() {
179+
info!("Message signature verified");
180+
// do something with addr
181+
} else {
182+
error!("Signature verification error")
183+
}
184+
185+
let verification_data = client_msg.verification_data;
174186
if verification_data.proof.len() <= self.max_proof_size {
175187
// When pre-verification is enabled, batcher will verify proofs for faster feedback with clients
176188
if self.pre_verification_is_enabled && !zk_utils::verify(&verification_data) {

batcher/aligned-batcher/src/types/errors.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
use std::fmt;
22

3+
use ethers::types::SignatureError;
34
use tokio_tungstenite::tungstenite;
45

56
pub enum BatcherError {
67
ConnectionError(tungstenite::Error),
78
BatchVerifiedEventStreamError(String),
89
EthereumSubscriptionError(String),
10+
SignatureError(SignatureError),
911
}
1012

1113
impl From<tungstenite::Error> for BatcherError {
@@ -14,6 +16,12 @@ impl From<tungstenite::Error> for BatcherError {
1416
}
1517
}
1618

19+
impl From<SignatureError> for BatcherError {
20+
fn from(e: SignatureError) -> Self {
21+
BatcherError::SignatureError(e)
22+
}
23+
}
24+
1725
impl fmt::Debug for BatcherError {
1826
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1927
match self {
@@ -26,6 +34,9 @@ impl fmt::Debug for BatcherError {
2634
BatcherError::EthereumSubscriptionError(e) => {
2735
write!(f, "Ethereum subscription was not successful: {}", e)
2836
}
37+
BatcherError::SignatureError(e) => {
38+
write!(f, "Message signature verification error: {}", e)
39+
}
2940
}
3041
}
3142
}

batcher/aligned/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@ clap = { version = "4.5.4", features = ["derive"] }
1616
lambdaworks-crypto = { version = "0.7.0", features = ["serde"] }
1717
ethers = { version = "2.0", features = ["ws", "rustls"] }
1818
aligned-batcher-lib = { path = "../aligned-batcher-lib"}
19+
rpassword = "7.3.1"
1920
sha3 = { version = "0.10.8"}

batcher/aligned/src/errors.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ pub enum BatcherClientError {
1313
IoError(PathBuf, io::Error),
1414
SerdeError(serde_json::Error),
1515
EthError(String),
16+
SignerError(String),
17+
PasswordError(io::Error),
1618
}
1719

1820
impl From<tokio_tungstenite::tungstenite::Error> for BatcherClientError {
@@ -35,7 +37,7 @@ impl From<ProviderError> for BatcherClientError {
3537

3638
impl From<WalletError> for BatcherClientError {
3739
fn from(e: WalletError) -> Self {
38-
BatcherClientError::EthError(e.to_string())
40+
BatcherClientError::SignerError(e.to_string())
3941
}
4042
}
4143

@@ -45,6 +47,12 @@ impl From<FromHexError> for BatcherClientError {
4547
}
4648
}
4749

50+
impl From<io::Error> for BatcherClientError {
51+
fn from(e: io::Error) -> Self {
52+
BatcherClientError::PasswordError(e)
53+
}
54+
}
55+
4856
impl fmt::Debug for BatcherClientError {
4957
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5058
match self {
@@ -64,6 +72,8 @@ impl fmt::Debug for BatcherClientError {
6472
}
6573
BatcherClientError::SerdeError(e) => write!(f, "Serialization error: {}", e),
6674
BatcherClientError::EthError(e) => write!(f, "Ethereum error: {}", e),
75+
BatcherClientError::SignerError(e) => write!(f, "Signer error: {}", e),
76+
BatcherClientError::PasswordError(e) => write!(f, "Password input error: {}", e),
6777
}
6878
}
6979
}

0 commit comments

Comments
 (0)