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

fix(sidecar): change back to bls signatures for messages #69

Merged
merged 2 commits into from
Jun 6, 2024
Merged
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
792 changes: 739 additions & 53 deletions bolt-sidecar/Cargo.lock

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions bolt-sidecar/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ warp = "0.3.7"
futures = "0.3"

# crypto
blst = "0.3.12"
secp256k1 = { version = "0.29.0", features = ["hashes", "rand"] }

# alloy
Expand All @@ -31,12 +32,16 @@ alloy-transport-http = { git = "https://github.com/alloy-rs/alloy" }
alloy-transport-ws = { git = "https://github.com/alloy-rs/alloy" }
alloy-pubsub = { git = "https://github.com/alloy-rs/alloy" }
alloy-rpc-types = { git = "https://github.com/alloy-rs/alloy" }
alloy-consensus = { git = "https://github.com/alloy-rs/alloy", features = ["k256"] }
alloy-rpc-types-beacon = { git = "https://github.com/alloy-rs/alloy" }
alloy-consensus = { git = "https://github.com/alloy-rs/alloy", features = [
"k256",
] }
alloy-json-rpc = { git = "https://github.com/alloy-rs/alloy" }
alloy-primitives = "0.7.1"
alloy-rlp = "0.3"

reqwest = "0.12"
ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "cf3c404" }

# types
serde = { version = "1.0.197", features = ["derive"] }
Expand All @@ -50,6 +55,7 @@ hex = "0.4.3"
# utils
eyre = "0.6.12"
thiserror = "1.0"
rand = "0.8.5"

# tracing
tracing = "0.1.40"
Expand All @@ -61,4 +67,4 @@ alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy" }

[[bin]]
name = "bolt-sidecar"
path = "bin/sidecar.rs"
path = "bin/sidecar.rs"
11 changes: 6 additions & 5 deletions bolt-sidecar/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::str::FromStr;

use blst::min_pk::SecretKey;
use clap::Parser;
use secp256k1::{rand, SecretKey};

use crate::crypto::bls::random_bls_secret;

/// Command-line options for the sidecar
#[derive(Parser, Debug)]
Expand Down Expand Up @@ -38,7 +38,7 @@ impl Default for Config {
Self {
rpc_port: 8000,
mevboost_url: "http://localhost:3030".to_string(),
private_key: SecretKey::new(&mut rand::thread_rng()),
private_key: random_bls_secret(),
limits: Limits::default(),
}
}
Expand All @@ -59,7 +59,8 @@ impl TryFrom<Opts> for Config {
}

config.mevboost_url = opts.mevboost_url;
config.private_key = SecretKey::from_str(&opts.private_key)?;
config.private_key = SecretKey::from_bytes(&hex::decode(opts.private_key)?)
.map_err(|e| eyre::eyre!("Failed decoding BLS secret key: {:?}", e))?;

Ok(config)
}
Expand Down
105 changes: 105 additions & 0 deletions bolt-sidecar/src/crypto/bls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
use blst::{
min_pk::{PublicKey, SecretKey, Signature},
BLST_ERROR,
};
use ethereum_consensus::deneb::BlsSignature;
use rand::RngCore;

/// The BLS Domain Separator used in Ethereum 2.0.
pub const BLS_DST_PREFIX: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_NUL_";

/// Trait for any types that can be signed and verified with BLS.
/// This trait is used to abstract over the signing and verification of different types.
pub trait SignableBLS {
/// Create a digest of the object that can be signed.
/// This API doesn't enforce a specific hash or encoding method.
fn digest(&self) -> Vec<u8>;

/// Sign the object with the given key. Returns the signature.
///
/// Note: The default implementation should be used where possible.
fn sign(&self, key: &SecretKey) -> Signature {
key.sign(&self.digest(), BLS_DST_PREFIX, &[])
}

/// Verify the signature of the object with the given public key.
///
/// Note: The default implementation should be used where possible.
fn verify(&self, signature: &Signature, pubkey: &PublicKey) -> bool {
signature.verify(false, &self.digest(), BLS_DST_PREFIX, &[], pubkey, true)
== BLST_ERROR::BLST_SUCCESS
}
}

/// A BLS signer that can sign any type that implements the `Signable` trait.
pub struct BLSSigner {
key: SecretKey,
}

impl BLSSigner {
pub fn new(key: SecretKey) -> Self {
Self { key }
}

pub fn sign<T: SignableBLS>(&self, obj: &T) -> Signature {
obj.sign(&self.key)
}

#[allow(dead_code)]
pub fn verify<T: SignableBLS>(
&self,
obj: &T,
signature: &Signature,
pubkey: &PublicKey,
) -> bool {
obj.verify(signature, pubkey)
}
}

pub fn from_bls_signature_to_consensus_signature(sig: Signature) -> BlsSignature {
let bytes = sig.to_bytes();
BlsSignature::try_from(bytes.as_slice()).unwrap()
}

pub fn random_bls_secret() -> SecretKey {
let mut rng = rand::thread_rng();
let mut ikm = [0u8; 32];
rng.fill_bytes(&mut ikm);
SecretKey::key_gen(&ikm, &[]).unwrap()
}

#[cfg(test)]
mod tests {
use blst::min_pk::SecretKey;

use super::BLSSigner;
use super::SignableBLS;

fn test_bls_secret_key() -> SecretKey {
SecretKey::key_gen(&[0u8; 32], &[]).unwrap()
}

struct TestSignableData {
data: Vec<u8>,
}

impl SignableBLS for TestSignableData {
fn digest(&self) -> Vec<u8> {
self.data.clone()
}
}

#[test]
fn test_signer() {
let key = test_bls_secret_key();
let pubkey = key.sk_to_pk();
let signer = BLSSigner::new(key);

let msg = TestSignableData {
data: vec![1, 2, 3, 4],
};

let signature = signer.sign(&msg);
assert!(signer.verify(&msg, &signature, &pubkey));
}
}
28 changes: 26 additions & 2 deletions bolt-sidecar/src/crypto.rs → bolt-sidecar/src/crypto/ecdsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ pub trait SignableECDSA {
}

/// A signer that can sign any type that implements `Signable{curve}` trait.
pub struct Signer {
pub struct ECDSASigner {
secp256k1_key: SecretKey,
}

impl Signer {
impl ECDSASigner {
/// Create a new signer with the given SECP256K1 secret key.
pub fn new(secp256k1_key: SecretKey) -> Self {
Self { secp256k1_key }
Expand All @@ -51,3 +51,27 @@ impl Signer {
obj.verify(sig, pubkey)
}
}

#[cfg(test)]
mod tests {
use super::{ECDSASigner, SignableECDSA};
use secp256k1::{PublicKey, SecretKey};

impl SignableECDSA for String {
fn digest(&self) -> secp256k1::Message {
secp256k1::Message::from_digest_slice(self.as_bytes()).unwrap()
}
}

#[test]
fn test_signer() {
let secp256k1_key = SecretKey::from_slice(&[1; 32]).unwrap();
let signer = ECDSASigner::new(secp256k1_key);

let message = "hello world".to_string();
let signature = signer.sign_ecdsa(&message);
let pubkey = PublicKey::from_secret_key(&secp256k1::Secp256k1::new(), &secp256k1_key);

assert!(signer.verify_ecdsa(&message, &signature, &pubkey));
}
}
4 changes: 4 additions & 0 deletions bolt-sidecar/src/crypto/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod bls;
pub use bls::{BLSSigner, SignableBLS};

pub mod ecdsa;
22 changes: 11 additions & 11 deletions bolt-sidecar/src/json_rpc/api.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
use std::{num::NonZeroUsize, sync::Arc};

use parking_lot::RwLock;
use secp256k1::SecretKey;
use serde_json::Value;
use thiserror::Error;
use tracing::info;

use super::mevboost::MevBoostClient;
use crate::{
crypto::Signer,
crypto::{bls::from_bls_signature_to_consensus_signature, BLSSigner},
json_rpc::types::{BatchedSignedConstraints, ConstraintsMessage, SignedConstraints},
primitives::{CommitmentRequest, Slot},
};
Expand All @@ -31,6 +30,8 @@ pub enum ApiError {
Rlp(#[from] alloy_rlp::Error),
#[error("failed during HTTP call: {0}")]
Http(#[from] reqwest::Error),
#[error("downstream error: {0}")]
Eyre(#[from] eyre::Report),
#[error("failed while processing API request: {0}")]
Custom(String),
}
Expand All @@ -56,20 +57,20 @@ pub struct JsonRpcApi {
/// A cache of commitment requests.
cache: Arc<RwLock<lru::LruCache<Slot, Vec<CommitmentRequest>>>>,
/// The signer for this sidecar.
signer: Signer,
signer: BLSSigner,
/// The client for the MEV-Boost sidecar.
mevboost_client: MevBoostClient,
}

impl JsonRpcApi {
/// Create a new instance of the JSON-RPC API.
pub fn new(private_key: SecretKey, mevboost_url: String) -> Arc<Self> {
pub fn new(private_key: blst::min_pk::SecretKey, mevboost_url: String) -> Arc<Self> {
let cap = NonZeroUsize::new(DEFAULT_API_REQUEST_CACHE_SIZE).unwrap();

Arc::new(Self {
cache: Arc::new(RwLock::new(lru::LruCache::new(cap))),
mevboost_client: MevBoostClient::new(mevboost_url),
signer: Signer::new(private_key),
signer: BLSSigner::new(private_key),
})
}
}
Expand Down Expand Up @@ -121,12 +122,11 @@ impl CommitmentsRpc for JsonRpcApi {

// parse the request into constraints and sign them with the sidecar signer
// TODO: get the validator index from somewhere
let constraints = ConstraintsMessage::build(0, params.slot, params.clone());
let constraints_sig = self.signer.sign_ecdsa(&constraints).to_string();
let signed_constraints: BatchedSignedConstraints = vec![SignedConstraints {
message: constraints,
signature: constraints_sig,
}];
let message = ConstraintsMessage::build(0, params.slot, params.clone())?;

let signature = from_bls_signature_to_consensus_signature(self.signer.sign(&message));
let signed_constraints: BatchedSignedConstraints =
vec![SignedConstraints { message, signature }];

// TODO: simulate and check if the transaction can be included in the next block
// self.block_builder.try_append(params.slot, params.tx)
Expand Down
Loading