From b0e27a1fc1cf0eda25723faa10c55cd88ef3509a Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Fri, 13 Sep 2024 15:07:37 +0100 Subject: [PATCH 01/15] fix: deps. --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index a8d7a05aa..53a0f845c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -299,6 +299,7 @@ rustix = "0.38.34" paste = "1.0.15" uuid = { version = "1.10.0", features = ["v4"] } blake-3 = "1.4.0" +ecdsa = "0.13.0" # trying to pin diesel # diesel = "=2.1.1" From b3b132c4fc7da87d89603aacdb8b34a120f4d596 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Mon, 16 Sep 2024 13:46:22 +0100 Subject: [PATCH 02/15] fix: add signer. --- Cargo.lock | 17 ++- Cargo.toml | 9 +- .../da/m1/light_node/v1beta1.proto | 3 +- .../da/m1/light-node-client/Cargo.toml | 2 + protocol-units/da/m1/light-node/Cargo.toml | 2 + protocol-units/da/m1/light-node/src/main.rs | 4 +- .../da/m1/light-node/src/v1/manager.rs | 30 +++- .../da/m1/light-node/src/v1/passthrough.rs | 95 ++++++++++-- .../da/m1/light-node/src/v1/sequencer.rs | 62 +++++++- protocol-units/da/m1/util/Cargo.toml | 5 + .../da/m1/util/src/config/common.rs | 9 ++ .../util/src/config/local/m1_da_light_node.rs | 10 +- protocol-units/da/m1/util/src/config/mod.rs | 9 ++ protocol-units/da/m1/util/src/inner_blob.rs | 138 ++++++++++++++++++ protocol-units/da/m1/util/src/lib.rs | 1 + .../mcr/contracts/lib/safe-smart-account | 1 + 16 files changed, 358 insertions(+), 39 deletions(-) create mode 100644 protocol-units/da/m1/util/src/inner_blob.rs create mode 160000 protocol-units/settlement/mcr/contracts/lib/safe-smart-account diff --git a/Cargo.lock b/Cargo.lock index 63ebce3ab..54e7e3730 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1966,7 +1966,7 @@ dependencies = [ [[package]] name = "aptos-moving-average" version = "0.1.0" -source = "git+https://github.com/movementlabsxyz/aptos-indexer-processors?rev=a11a7ef5346e11a827cec7394a7a3cc4461af820#a11a7ef5346e11a827cec7394a7a3cc4461af820" +source = "git+https://github.com/movementlabsxyz/aptos-indexer-processors?rev=1d1d7c72a01a0b5a55477f8fdca59e17c9ce2e58#1d1d7c72a01a0b5a55477f8fdca59e17c9ce2e58" dependencies = [ "chrono", ] @@ -7887,9 +7887,11 @@ dependencies = [ "celestia-types", "chrono", "dot-movement", + "ecdsa", "futures", "godfig", "hex", + "k256", "m1-da-light-node-grpc", "m1-da-light-node-util", "m1-da-light-node-verifier", @@ -7915,11 +7917,13 @@ name = "m1-da-light-node-client" version = "0.0.2" dependencies = [ "anyhow", + "ecdsa", "m1-da-light-node-grpc", "movement-types", "serde_json", "tokio", "tokio-stream", + "tonic 0.11.0", ] [[package]] @@ -7983,14 +7987,18 @@ dependencies = [ name = "m1-da-light-node-util" version = "0.0.2" dependencies = [ + "alloy", "anyhow", "async-stream", + "bcs 0.1.4", "celestia-rpc", "celestia-types", "dot-movement", + "ecdsa", "godfig", "hex", "jsonrpsee 0.20.4", + "k256", "m1-da-light-node-grpc", "memseq-util", "prost 0.12.6", @@ -8006,6 +8014,7 @@ dependencies = [ "tonic-web", "tracing", "tracing-subscriber 0.3.18", + "zstd 0.13.2", ] [[package]] @@ -10638,13 +10647,13 @@ dependencies = [ [[package]] name = "processor" version = "1.0.0" -source = "git+https://github.com/movementlabsxyz/aptos-indexer-processors?rev=a11a7ef5346e11a827cec7394a7a3cc4461af820#a11a7ef5346e11a827cec7394a7a3cc4461af820" +source = "git+https://github.com/movementlabsxyz/aptos-indexer-processors?rev=1d1d7c72a01a0b5a55477f8fdca59e17c9ce2e58#1d1d7c72a01a0b5a55477f8fdca59e17c9ce2e58" dependencies = [ "ahash 0.8.11", "allocative", "allocative_derive", "anyhow", - "aptos-moving-average 0.1.0 (git+https://github.com/movementlabsxyz/aptos-indexer-processors?rev=a11a7ef5346e11a827cec7394a7a3cc4461af820)", + "aptos-moving-average 0.1.0 (git+https://github.com/movementlabsxyz/aptos-indexer-processors?rev=1d1d7c72a01a0b5a55477f8fdca59e17c9ce2e58)", "aptos-protos 1.3.0 (git+https://github.com/movementlabsxyz/aptos-core?rev=338f9a1bcc06f62ce4a4994f1642b9a61b631ee0)", "async-trait", "bcs 0.1.4", @@ -12146,7 +12155,7 @@ dependencies = [ [[package]] name = "server-framework" version = "1.0.0" -source = "git+https://github.com/movementlabsxyz/aptos-indexer-processors?rev=a11a7ef5346e11a827cec7394a7a3cc4461af820#a11a7ef5346e11a827cec7394a7a3cc4461af820" +source = "git+https://github.com/movementlabsxyz/aptos-indexer-processors?rev=1d1d7c72a01a0b5a55477f8fdca59e17c9ce2e58#1d1d7c72a01a0b5a55477f8fdca59e17c9ce2e58" dependencies = [ "anyhow", "aptos-system-utils", diff --git a/Cargo.toml b/Cargo.toml index 53a0f845c..cba2b8d5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -109,13 +109,6 @@ serde_yaml = "0.9.34" # External Dependencies ## Aptos dependencies -aptos-api = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "6d2ec939e10cc00283519dd0ad9d1cf12e7bf80f" } ## Aptos dependencies -aptos-api-types = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "6d2ec939e10cc00283519dd0ad9d1cf12e7bf80f" } -aptos-bitvec = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "6d2ec939e10cc00283519dd0ad9d1cf12e7bf80f" } -aptos-block-executor = { git = "https://github.com/movementlabsxyz/aptos-core.git", rev = "6d2ec939e10cc00283519dd0ad9d1cf12e7bf80f" } -aptos-cached-packages = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "6d2ec939e10cc00283519dd0ad9d1cf12e7bf80f" } -aptos-config = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "6d2ec939e10cc00283519dd0ad9d1cf12e7bf80f" } -aptos-consensus-types = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "6d2ec939e10cc00283519dd0ad9d1cf12e7bf80f" } aptos-api = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "c9d4c2d25dfdde02eb2fd3bf73f39ac9d6b3300b" } aptos-api-types = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "c9d4c2d25dfdde02eb2fd3bf73f39ac9d6b3300b" } aptos-bitvec = { git = "https://github.com/movementlabsxyz/aptos-core", rev = "c9d4c2d25dfdde02eb2fd3bf73f39ac9d6b3300b" } @@ -299,7 +292,7 @@ rustix = "0.38.34" paste = "1.0.15" uuid = { version = "1.10.0", features = ["v4"] } blake-3 = "1.4.0" -ecdsa = "0.13.0" +ecdsa = { version = "0.16.9", features = ["signing", "verifying"] } # trying to pin diesel # diesel = "=2.1.1" diff --git a/proto/movementlabs/protocol_units/da/m1/light_node/v1beta1.proto b/proto/movementlabs/protocol_units/da/m1/light_node/v1beta1.proto index 759253046..8f785cda7 100644 --- a/proto/movementlabs/protocol_units/da/m1/light_node/v1beta1.proto +++ b/proto/movementlabs/protocol_units/da/m1/light_node/v1beta1.proto @@ -7,8 +7,9 @@ message Blob { string blob_id = 1; bytes data = 2; uint64 height = 3; - // bytes signature = 4; // at some point a signature will be added here + bytes signature = 4; // at some point a signature will be added here uint64 timestamp = 5; + bytes signer = 6; } enum VerificationMode { diff --git a/protocol-units/da/m1/light-node-client/Cargo.toml b/protocol-units/da/m1/light-node-client/Cargo.toml index d4f04db91..f2170938f 100644 --- a/protocol-units/da/m1/light-node-client/Cargo.toml +++ b/protocol-units/da/m1/light-node-client/Cargo.toml @@ -18,6 +18,8 @@ anyhow = { workspace = true } tokio-stream = { workspace = true } movement-types = { workspace = true } serde_json = { workspace = true } +ecdsa = { workspace = true } +tonic = { workspace = true } [features] sequencer = [] diff --git a/protocol-units/da/m1/light-node/Cargo.toml b/protocol-units/da/m1/light-node/Cargo.toml index 52b511c34..c7abef8ad 100644 --- a/protocol-units/da/m1/light-node/Cargo.toml +++ b/protocol-units/da/m1/light-node/Cargo.toml @@ -39,6 +39,8 @@ movement-tracing = { workspace = true } futures = { workspace = true } bcs = { workspace = true } zstd = { workspace = true } +ecdsa = { workspace = true } +k256 = { workspace = true } # sequencer memseq = { workspace = true, optional = true } diff --git a/protocol-units/da/m1/light-node/src/main.rs b/protocol-units/da/m1/light-node/src/main.rs index e9a8cb7f5..4d8037ca8 100644 --- a/protocol-units/da/m1/light-node/src/main.rs +++ b/protocol-units/da/m1/light-node/src/main.rs @@ -1,3 +1,4 @@ +use k256::Secp256k1; use m1_da_light_node::v1::{LightNodeV1, Manager}; use std::env; @@ -13,7 +14,8 @@ async fn main() -> Result<(), Box> { let dot_movement = dot_movement::DotMovement::try_from_env()?; let config_path = dot_movement.get_config_json_path(); let config_file = tokio::fs::File::open(config_path).await?; - let manager = Manager::::new(config_file).await?; + // todo: consider whether LightNode implementation should encapsulate signing type + let manager = Manager::>::new(config_file).await?; manager.try_run().await?; Ok(()) diff --git a/protocol-units/da/m1/light-node/src/v1/manager.rs b/protocol-units/da/m1/light-node/src/v1/manager.rs index 25c4d2f92..38cf6bfbf 100644 --- a/protocol-units/da/m1/light-node/src/v1/manager.rs +++ b/protocol-units/da/m1/light-node/src/v1/manager.rs @@ -1,4 +1,16 @@ use super::{LightNodeV1, LightNodeV1Operations}; +use ecdsa::{ + elliptic_curve::{ + generic_array::ArrayLength, + ops::Invert, + point::PointCompression, + sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint}, + subtle::CtOption, + AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, Scalar, + }, + hazmat::{DigestPrimitive, SignPrimitive}, + SignatureSize, +}; use godfig::{backend::config_file::ConfigFile, Godfig}; use m1_da_light_node_util::config::Config; @@ -12,7 +24,14 @@ where } // Implements a very simple manager using a marker strategy pattern. -impl Manager { +impl Manager> +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldBytesSize: ModulusSize, +{ pub async fn new(file: tokio::fs::File) -> Result { let godfig = Godfig::new( ConfigFile::new(file), @@ -23,7 +42,14 @@ impl Manager { Ok(Self { godfig, _marker: std::marker::PhantomData }) } - pub async fn try_light_node(&self) -> Result { + pub async fn try_light_node(&self) -> Result, anyhow::Error> + where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldBytesSize: ModulusSize, + { let config = self.godfig.try_wait_for_ready().await?; LightNodeV1::try_from_config(config).await } diff --git a/protocol-units/da/m1/light-node/src/v1/passthrough.rs b/protocol-units/da/m1/light-node/src/v1/passthrough.rs index c8d1cc3e9..50f3f3ecb 100644 --- a/protocol-units/da/m1/light-node/src/v1/passthrough.rs +++ b/protocol-units/da/m1/light-node/src/v1/passthrough.rs @@ -12,21 +12,51 @@ use celestia_types::{blob::GasPrice, nmt::Namespace, Blob as CelestiaBlob}; // FIXME: glob imports are bad style use m1_da_light_node_grpc::light_node_service_server::LightNodeService; use m1_da_light_node_grpc::*; -use m1_da_light_node_util::config::Config; +use m1_da_light_node_util::{ + config::Config, + inner_blob::{celestia::CelestiaInnerBlob, InnerBlob, InnerSignedBlobV1Data}, +}; use m1_da_light_node_verifier::{v1::V1Verifier, Verifier}; use crate::v1::LightNodeV1Operations; +use ecdsa::{ + elliptic_curve::{ + generic_array::ArrayLength, + ops::Invert, + point::PointCompression, + sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint}, + subtle::CtOption, + AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, Scalar, + }, + hazmat::{DigestPrimitive, SignPrimitive}, + SignatureSize, SigningKey, +}; #[derive(Clone)] -pub struct LightNodeV1 { +pub struct LightNodeV1 +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldBytesSize: ModulusSize, +{ pub config: Config, pub celestia_namespace: Namespace, pub default_client: Arc, pub verification_mode: Arc>, pub verifier: Arc>, + pub signing_key: SigningKey, } -impl Debug for LightNodeV1 { +impl Debug for LightNodeV1 +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldBytesSize: ModulusSize, +{ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("LightNodeV1") .field("celestia_namespace", &self.config.celestia_namespace()) @@ -34,11 +64,22 @@ impl Debug for LightNodeV1 { } } -impl LightNodeV1Operations for LightNodeV1 { +impl LightNodeV1Operations for LightNodeV1 +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldBytesSize: ModulusSize, +{ /// Tries to create a new LightNodeV1 instance from the toml config file. async fn try_from_config(config: Config) -> Result { let client = Arc::new(config.connect_celestia().await?); + let signing_key_str = config.da_signing_key(); + let signing_key = SigningKey::from_bytes(signing_key_str.as_bytes().into()) + .map_err(|e| anyhow::anyhow!("Failed to create signing key: {}", e))?; + Ok(Self { config: config.clone(), celestia_namespace: config.celestia_namespace(), @@ -51,6 +92,7 @@ impl LightNodeV1Operations for LightNodeV1 { client, namespace: config.celestia_namespace(), })), + signing_key, }) } @@ -64,14 +106,27 @@ impl LightNodeV1Operations for LightNodeV1 { } } -impl LightNodeV1 { - /// Creates a new blob instance with the provided data. +impl LightNodeV1 +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldBytesSize: ModulusSize, +{ + /// Creates a new signed blob instance with the provided data. pub fn create_new_celestia_blob(&self, data: Vec) -> Result { - CelestiaBlob::new(self.celestia_namespace, data) - .map_err(|e| anyhow::anyhow!("Failed to create a blob: {}", e)) + // mark the timestamp as now + let timestamp = chrono::Utc::now().timestamp() as u64; + + // sign the blob data and the timestamp + let data = InnerSignedBlobV1Data::new(data, timestamp).try_to_sign(&self.signing_key)?; + + // create the celestia blob + CelestiaInnerBlob(data.into(), self.celestia_namespace.clone()).try_into() } - /// Submits a CelestiaNlob to the Celestia node. + /// Submits a CelestiaBlob to the Celestia node. pub async fn submit_celestia_blob(&self, blob: CelestiaBlob) -> Result { let height = self .default_client @@ -240,14 +295,17 @@ impl LightNodeV1 { } pub fn celestia_blob_to_blob(blob: CelestiaBlob, height: u64) -> Result { - let timestamp = chrono::Utc::now().timestamp_micros() as u64; + let blob_id: serde_json::Value = serde_json::to_value(&blob.commitment) + .map_err(|e| anyhow::anyhow!("Failed to serialize blob id: {}", e))?; + let inner_blob: InnerBlob = blob.try_into()?; Ok(Blob { - data: blob.data, - blob_id: serde_json::to_string(&blob.commitment) - .map_err(|e| anyhow::anyhow!("Failed to serialize commitment: {}", e))?, + data: inner_blob.blob().to_vec(), + signature: inner_blob.signature().to_vec(), + timestamp: inner_blob.timestamp(), + signer: inner_blob.signer().to_vec(), + blob_id: blob_id.to_string(), height, - timestamp, }) } @@ -269,7 +327,14 @@ impl LightNodeV1 { } #[tonic::async_trait] -impl LightNodeService for LightNodeV1 { +impl LightNodeService for LightNodeV1 +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldBytesSize: ModulusSize, +{ /// Server streaming response type for the StreamReadFromHeight method. type StreamReadFromHeightStream = std::pin::Pin< Box< diff --git a/protocol-units/da/m1/light-node/src/v1/sequencer.rs b/protocol-units/da/m1/light-node/src/v1/sequencer.rs index ac0e8b58c..053d7275e 100644 --- a/protocol-units/da/m1/light-node/src/v1/sequencer.rs +++ b/protocol-units/da/m1/light-node/src/v1/sequencer.rs @@ -1,3 +1,15 @@ +use ecdsa::{ + elliptic_curve::{ + generic_array::ArrayLength, + ops::Invert, + point::PointCompression, + sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint}, + subtle::CtOption, + AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, Scalar, + }, + hazmat::{DigestPrimitive, SignPrimitive}, + SignatureSize, +}; use std::{ sync::{atomic::AtomicU64, Arc}, time::Duration, @@ -28,18 +40,39 @@ use crate::v1::{passthrough::LightNodeV1 as LightNodeV1PassThrough, LightNodeV1O const LOGGING_UID: AtomicU64 = AtomicU64::new(0); #[derive(Clone)] -pub struct LightNodeV1 { - pub pass_through: LightNodeV1PassThrough, +pub struct LightNodeV1 +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldBytesSize: ModulusSize, +{ + pub pass_through: LightNodeV1PassThrough, pub memseq: Arc>, } -impl Debug for LightNodeV1 { +impl Debug for LightNodeV1 +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldBytesSize: ModulusSize, +{ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("LightNodeV1").field("pass_through", &self.pass_through).finish() } } -impl LightNodeV1Operations for LightNodeV1 { +impl LightNodeV1Operations for LightNodeV1 +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldBytesSize: ModulusSize, +{ async fn try_from_config(config: Config) -> Result { info!("Initializing LightNodeV1 in sequencer mode from environment."); @@ -71,7 +104,14 @@ impl LightNodeV1Operations for LightNodeV1 { } } -impl LightNodeV1 { +impl LightNodeV1 +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldBytesSize: ModulusSize, +{ async fn tick_build_blocks(&self, sender: Sender) -> Result<(), anyhow::Error> { let memseq = self.memseq.clone(); @@ -305,6 +345,9 @@ impl LightNodeV1 { data, blob_id: "".to_string(), height, + // todo: at some point it would be good to sign these intents, as they can then be used as pre-confirmations against which we can slash + signature: vec![], + signer: vec![], timestamp: 0, })), }) @@ -312,7 +355,14 @@ impl LightNodeV1 { } #[tonic::async_trait] -impl LightNodeService for LightNodeV1 { +impl LightNodeService for LightNodeV1 +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldBytesSize: ModulusSize, +{ /// Server streaming response type for the StreamReadFromHeight method. type StreamReadFromHeightStream = std::pin::Pin< Box< diff --git a/protocol-units/da/m1/util/Cargo.toml b/protocol-units/da/m1/util/Cargo.toml index 82edfb3ce..7afc86d06 100644 --- a/protocol-units/da/m1/util/Cargo.toml +++ b/protocol-units/da/m1/util/Cargo.toml @@ -38,6 +38,11 @@ memseq-util = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } godfig = { workspace = true } +alloy = { workspace = true } +zstd = { workspace = true } +bcs = { workspace = true } +ecdsa = { workspace = true } +k256 = { workspace = true } [dev-dependencies] tempfile = { workspace = true } diff --git a/protocol-units/da/m1/util/src/config/common.rs b/protocol-units/da/m1/util/src/config/common.rs index 1c8ddc0d1..a580bdc3a 100644 --- a/protocol-units/da/m1/util/src/config/common.rs +++ b/protocol-units/da/m1/util/src/config/common.rs @@ -1,5 +1,7 @@ +use alloy::signers::local::PrivateKeySigner; use celestia_types::nmt::Namespace; use godfig::env_default; +use std::env; // The default hostname for the Celestia RPC env_default!( @@ -125,3 +127,10 @@ pub fn default_celestia_bridge_replace_args() -> Vec { // Whether to use replace args for Celestia bridge env_default!(default_m1_da_light_node_is_initial, "M1_DA_LIGHT_NODE_IS_INITIAL", bool, true); + +// The default da signing private key +pub fn default_da_signing_private_key() -> String { + let random_wallet = PrivateKeySigner::random(); + let random_wallet_string = random_wallet.to_bytes().to_string(); + env::var("DA_SIGNING_PRIVATE_KEY").unwrap_or(random_wallet_string) +} diff --git a/protocol-units/da/m1/util/src/config/local/m1_da_light_node.rs b/protocol-units/da/m1/util/src/config/local/m1_da_light_node.rs index 5f9f467a0..616b7e347 100644 --- a/protocol-units/da/m1/util/src/config/local/m1_da_light_node.rs +++ b/protocol-units/da/m1/util/src/config/local/m1_da_light_node.rs @@ -1,8 +1,9 @@ use crate::config::common::{ default_celestia_rpc_connection_hostname, default_celestia_rpc_connection_port, default_celestia_websocket_connection_hostname, default_celestia_websocket_connection_port, - default_m1_da_light_node_connection_hostname, default_m1_da_light_node_connection_port, - default_m1_da_light_node_listen_hostname, default_m1_da_light_node_listen_port, + default_da_signing_private_key, default_m1_da_light_node_connection_hostname, + default_m1_da_light_node_connection_port, default_m1_da_light_node_listen_hostname, + default_m1_da_light_node_listen_port, }; use serde::{Deserialize, Serialize}; @@ -40,6 +41,10 @@ pub struct Config { /// The port for m1-da-light-node connection #[serde(default = "default_m1_da_light_node_connection_port")] pub m1_da_light_node_connection_port: u16, + + /// The private key for signing DA messages + #[serde(default = "default_da_signing_private_key")] + pub da_signing_private_key: String, } impl Default for Config { @@ -54,6 +59,7 @@ impl Default for Config { m1_da_light_node_listen_port: default_m1_da_light_node_listen_port(), m1_da_light_node_connection_hostname: default_m1_da_light_node_connection_hostname(), m1_da_light_node_connection_port: default_m1_da_light_node_connection_port(), + da_signing_private_key: default_da_signing_private_key(), } } } diff --git a/protocol-units/da/m1/util/src/config/mod.rs b/protocol-units/da/m1/util/src/config/mod.rs index 753acd252..b7183cb60 100644 --- a/protocol-units/da/m1/util/src/config/mod.rs +++ b/protocol-units/da/m1/util/src/config/mod.rs @@ -176,6 +176,15 @@ impl Config { } } + /// Gets the da signing key as a string + pub fn da_signing_key(&self) -> String { + match self { + Config::Local(local) => local.m1_da_light_node.da_signing_private_key.clone(), + Config::Arabica(local) => local.m1_da_light_node.da_signing_private_key.clone(), + Config::Mocha(local) => local.m1_da_light_node.da_signing_private_key.clone(), + } + } + pub fn try_block_building_parameters(&self) -> Result<(u32, u64), anyhow::Error> { match self { Config::Local(local) => { diff --git a/protocol-units/da/m1/util/src/inner_blob.rs b/protocol-units/da/m1/util/src/inner_blob.rs new file mode 100644 index 000000000..2a4c86b83 --- /dev/null +++ b/protocol-units/da/m1/util/src/inner_blob.rs @@ -0,0 +1,138 @@ +use ecdsa::signature::digest::Digest; +use ecdsa::{ + elliptic_curve::{ + generic_array::ArrayLength, + ops::Invert, + point::PointCompression, + sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint}, + subtle::CtOption, + AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, Scalar, + }, + hazmat::{DigestPrimitive, SignPrimitive}, + SignatureSize, SigningKey, +}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct InnerSignedBlobV1Data { + pub blob: Vec, + pub timestamp: u64, +} + +impl InnerSignedBlobV1Data { + pub fn new(blob: Vec, timestamp: u64) -> Self { + Self { blob, timestamp } + } + + pub fn try_to_sign( + self, + signing_key: &SigningKey, + ) -> Result + where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldBytesSize: ModulusSize, + { + let mut hasher = C::Digest::new(); + hasher.update(self.blob.as_slice()); + hasher.update(&self.timestamp.to_be_bytes()); + + let (signature, _recovery_id) = signing_key.sign_digest_recoverable(hasher)?; + + Ok(InnerSignedBlobV1 { + data: self, + signature: signature.to_vec(), + signer: signing_key.verifying_key().to_sec1_bytes().to_vec(), + }) + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct InnerSignedBlobV1 { + pub data: InnerSignedBlobV1Data, + pub signature: Vec, + pub signer: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum InnerBlob { + SignedV1(InnerSignedBlobV1), +} + +impl From for InnerBlob { + fn from(inner: InnerSignedBlobV1) -> Self { + InnerBlob::SignedV1(inner) + } +} + +impl InnerBlob { + pub fn blob(&self) -> &[u8] { + match self { + InnerBlob::SignedV1(inner) => inner.data.blob.as_slice(), + } + } + + pub fn signature(&self) -> &[u8] { + match self { + InnerBlob::SignedV1(inner) => inner.signature.as_slice(), + } + } + + pub fn timestamp(&self) -> u64 { + match self { + InnerBlob::SignedV1(inner) => inner.data.timestamp, + } + } + + pub fn signer(&self) -> &[u8] { + match self { + InnerBlob::SignedV1(inner) => inner.signer.as_slice(), + } + } +} + +pub mod celestia { + + use celestia_types::{nmt::Namespace, Blob as CelestiaBlob}; + + use super::InnerBlob; + + impl TryFrom for InnerBlob { + type Error = anyhow::Error; + + // todo: it would be nice to have this be self describing over the compression and serialization format + fn try_from(blob: CelestiaBlob) -> Result { + // decompress blob.data with zstd + let decompressed = zstd::decode_all(blob.data.as_slice())?; + + // deserialize the decompressed with bcs + // todo: because this is a simple data structure, bcs might not be the best format + let blob = bcs::from_bytes(decompressed.as_slice())?; + + Ok(blob) + } + } + + pub struct CelestiaInnerBlob(pub InnerBlob, pub Namespace); + + impl TryFrom for CelestiaBlob { + type Error = anyhow::Error; + + fn try_from(inner_blob: CelestiaInnerBlob) -> Result { + // Extract the inner blob and namespace + let CelestiaInnerBlob(inner_blob, namespace) = inner_blob; + + // Serialize the inner blob with bcs + let serialized_blob = bcs::to_bytes(&inner_blob)?; + + // Compress the serialized data with zstd + let compressed_blob = zstd::encode_all(serialized_blob.as_slice(), 0)?; + + // Construct the final CelestiaBlob by assigning the compressed data + // and associating it with the provided namespace + Ok(CelestiaBlob::new(namespace, compressed_blob).map_err(|e| anyhow::anyhow!(e))?) + } + } +} diff --git a/protocol-units/da/m1/util/src/lib.rs b/protocol-units/da/m1/util/src/lib.rs index a4337c175..1ad156f18 100644 --- a/protocol-units/da/m1/util/src/lib.rs +++ b/protocol-units/da/m1/util/src/lib.rs @@ -1,2 +1,3 @@ pub mod config; pub use config::*; +pub mod inner_blob; diff --git a/protocol-units/settlement/mcr/contracts/lib/safe-smart-account b/protocol-units/settlement/mcr/contracts/lib/safe-smart-account new file mode 160000 index 000000000..bf943f80f --- /dev/null +++ b/protocol-units/settlement/mcr/contracts/lib/safe-smart-account @@ -0,0 +1 @@ +Subproject commit bf943f80fec5ac647159d26161446ac5d716a294 From b43a5b83d91673ef22044c99f540e5977086d08c Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Mon, 16 Sep 2024 16:10:00 +0100 Subject: [PATCH 03/15] fix: add verifier primitive. --- Cargo.toml | 2 +- .../light-node-verifier/src/celestia/mod.rs | 44 +++++++++++++++++++ .../src/{ => celestia}/v1.rs | 33 +++++++------- .../da/m1/light-node-verifier/src/lib.rs | 32 +++++++++----- .../da/m1/light-node/src/v1/manager.rs | 4 +- .../da/m1/light-node/src/v1/passthrough.rs | 14 +++--- .../da/m1/light-node/src/v1/sequencer.rs | 12 ++--- protocol-units/da/m1/util/Cargo.toml | 2 +- protocol-units/da/m1/util/src/inner_blob.rs | 25 ++++++++++- 9 files changed, 121 insertions(+), 47 deletions(-) create mode 100644 protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs rename protocol-units/da/m1/light-node-verifier/src/{ => celestia}/v1.rs (87%) diff --git a/Cargo.toml b/Cargo.toml index cba2b8d5c..f73848060 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -292,7 +292,7 @@ rustix = "0.38.34" paste = "1.0.15" uuid = { version = "1.10.0", features = ["v4"] } blake-3 = "1.4.0" -ecdsa = { version = "0.16.9", features = ["signing", "verifying"] } +ecdsa = { version = "0.16.9", features = ["signing", "verifying", "der"] } # trying to pin diesel # diesel = "=2.1.1" diff --git a/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs b/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs new file mode 100644 index 000000000..212d4975d --- /dev/null +++ b/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs @@ -0,0 +1,44 @@ +pub mod v1; + +pub use m1_da_light_node_grpc::*; + +#[tonic::async_trait] +pub trait Verifier { + async fn verify( + &self, + verification_mode: VerificationMode, + blob: &[u8], + height: u64, + ) -> Result { + match verification_mode { + VerificationMode::Cowboy => self.verify_cowboy(verification_mode, blob, height).await, + VerificationMode::ValidatorIn => { + self.verifiy_validator_in(verification_mode, blob, height).await + } + VerificationMode::MOfN => self.verify_m_of_n(verification_mode, blob, height).await, + } + } + + async fn verify_cowboy( + &self, + _verification_mode: VerificationMode, + _blob: &[u8], + _height: u64, + ) -> Result { + Ok(true) + } + + async fn verifiy_validator_in( + &self, + _verification_mode: VerificationMode, + _blob: &[u8], + _height: u64, + ) -> Result; + + async fn verify_m_of_n( + &self, + _verification_mode: VerificationMode, + _blob: &[u8], + _height: u64, + ) -> Result; +} diff --git a/protocol-units/da/m1/light-node-verifier/src/v1.rs b/protocol-units/da/m1/light-node-verifier/src/celestia/v1.rs similarity index 87% rename from protocol-units/da/m1/light-node-verifier/src/v1.rs rename to protocol-units/da/m1/light-node-verifier/src/celestia/v1.rs index 0bbc28add..ae5ffd2f6 100644 --- a/protocol-units/da/m1/light-node-verifier/src/v1.rs +++ b/protocol-units/da/m1/light-node-verifier/src/celestia/v1.rs @@ -1,7 +1,8 @@ -use crate::Verifier; +use crate::{Verified, Verifier}; use celestia_rpc::{BlobClient, Client, HeaderClient}; use celestia_types::{nmt::Namespace, Blob}; use m1_da_light_node_grpc::VerificationMode; +use m1_da_light_node_util::inner_blob::InnerBlob; use std::sync::Arc; #[derive(Clone)] @@ -11,17 +12,15 @@ pub struct V1Verifier { } #[tonic::async_trait] -impl Verifier for V1Verifier { +impl Verifier for V1Verifier { /// All verification is the same for now async fn verify( &self, _verification_mode: VerificationMode, - blob: &[u8], + blob: Blob, height: u64, - ) -> Result { - let celestia_blob = Blob::new(self.namespace.clone(), blob.to_vec())?; - - celestia_blob.validate()?; + ) -> Result, anyhow::Error> { + blob.validate()?; // wait for the header to be at the correct height self.client.header_wait_for_height(height).await?; @@ -33,11 +32,11 @@ impl Verifier for V1Verifier { // get the proof let proofs = self .client - .blob_get_proof(height, self.namespace.clone(), celestia_blob.commitment) + .blob_get_proof(height, self.namespace.clone(), blob.commitment) .await?; // get the leaves - let leaves = celestia_blob.to_shares()?; + let leaves = blob.to_shares()?; // check if included for proof in proofs.iter() { @@ -46,33 +45,35 @@ impl Verifier for V1Verifier { .map_err(|e| anyhow::anyhow!("Failed to verify proof: {:?}", e))?; } - Ok(true) + let inner_blob = InnerBlob::try_from(blob)?; + + Ok(Verified::Valid(inner_blob)) } async fn verify_cowboy( &self, _verification_mode: VerificationMode, - _blob: &[u8], + _blob: Blob, _height: u64, - ) -> Result { + ) -> Result, anyhow::Error> { unimplemented!() } async fn verify_m_of_n( &self, _verification_mode: VerificationMode, - _blob: &[u8], + _blob: Blob, _height: u64, - ) -> Result { + ) -> Result, anyhow::Error> { unimplemented!() } async fn verifiy_validator_in( &self, _verification_mode: VerificationMode, - _blob: &[u8], + _blob: Blob, _height: u64, - ) -> Result { + ) -> Result, anyhow::Error> { unimplemented!() } } diff --git a/protocol-units/da/m1/light-node-verifier/src/lib.rs b/protocol-units/da/m1/light-node-verifier/src/lib.rs index 212d4975d..13645202d 100644 --- a/protocol-units/da/m1/light-node-verifier/src/lib.rs +++ b/protocol-units/da/m1/light-node-verifier/src/lib.rs @@ -1,15 +1,25 @@ -pub mod v1; +pub mod celestia; pub use m1_da_light_node_grpc::*; +/// A verified outcome. Indicates that input of A is verified as valid instance of B, or else invalid instance. +pub enum Verified { + Valid(B), + Invalid, +} + #[tonic::async_trait] -pub trait Verifier { +pub trait Verifier +where + A: Send + Sync + 'static, + B: Send + Sync + 'static, +{ async fn verify( &self, verification_mode: VerificationMode, - blob: &[u8], + blob: A, height: u64, - ) -> Result { + ) -> Result, anyhow::Error> { match verification_mode { VerificationMode::Cowboy => self.verify_cowboy(verification_mode, blob, height).await, VerificationMode::ValidatorIn => { @@ -22,23 +32,21 @@ pub trait Verifier { async fn verify_cowboy( &self, _verification_mode: VerificationMode, - _blob: &[u8], + _blob: A, _height: u64, - ) -> Result { - Ok(true) - } + ) -> Result, anyhow::Error>; async fn verifiy_validator_in( &self, _verification_mode: VerificationMode, - _blob: &[u8], + _blob: A, _height: u64, - ) -> Result; + ) -> Result, anyhow::Error>; async fn verify_m_of_n( &self, _verification_mode: VerificationMode, - _blob: &[u8], + _blob: A, _height: u64, - ) -> Result; + ) -> Result, anyhow::Error>; } diff --git a/protocol-units/da/m1/light-node/src/v1/manager.rs b/protocol-units/da/m1/light-node/src/v1/manager.rs index 38cf6bfbf..1d2027adf 100644 --- a/protocol-units/da/m1/light-node/src/v1/manager.rs +++ b/protocol-units/da/m1/light-node/src/v1/manager.rs @@ -8,7 +8,7 @@ use ecdsa::{ subtle::CtOption, AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, Scalar, }, - hazmat::{DigestPrimitive, SignPrimitive}, + hazmat::{DigestPrimitive, SignPrimitive, VerifyPrimitive}, SignatureSize, }; use godfig::{backend::config_file::ConfigFile, Godfig}; @@ -29,7 +29,7 @@ where C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, - AffinePoint: FromEncodedPoint + ToEncodedPoint, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: ModulusSize, { pub async fn new(file: tokio::fs::File) -> Result { diff --git a/protocol-units/da/m1/light-node/src/v1/passthrough.rs b/protocol-units/da/m1/light-node/src/v1/passthrough.rs index 50f3f3ecb..b4709a0b3 100644 --- a/protocol-units/da/m1/light-node/src/v1/passthrough.rs +++ b/protocol-units/da/m1/light-node/src/v1/passthrough.rs @@ -28,7 +28,7 @@ use ecdsa::{ subtle::CtOption, AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, Scalar, }, - hazmat::{DigestPrimitive, SignPrimitive}, + hazmat::{DigestPrimitive, SignPrimitive, VerifyPrimitive}, SignatureSize, SigningKey, }; @@ -38,7 +38,7 @@ where C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, - AffinePoint: FromEncodedPoint + ToEncodedPoint, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: ModulusSize, { pub config: Config, @@ -54,7 +54,7 @@ where C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, - AffinePoint: FromEncodedPoint + ToEncodedPoint, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: ModulusSize, { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { @@ -69,7 +69,7 @@ where C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, - AffinePoint: FromEncodedPoint + ToEncodedPoint, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: ModulusSize, { /// Tries to create a new LightNodeV1 instance from the toml config file. @@ -111,7 +111,7 @@ where C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, - AffinePoint: FromEncodedPoint + ToEncodedPoint, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: ModulusSize, { /// Creates a new signed blob instance with the provided data. @@ -189,7 +189,7 @@ where // FIXME: check the implications of treating errors as verification success. // @l-monninger: under the assumption we are running a light node in the same - // trusted setup and have not experience a highly intrusive(?), the vulnerability here + // trusted setup and have not experience a highly intrusive attack, the vulnerability here // is fairly low. The light node should take care of verification on its own. let verified = verified.unwrap_or(true); @@ -332,7 +332,7 @@ where C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, - AffinePoint: FromEncodedPoint + ToEncodedPoint, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: ModulusSize, { /// Server streaming response type for the StreamReadFromHeight method. diff --git a/protocol-units/da/m1/light-node/src/v1/sequencer.rs b/protocol-units/da/m1/light-node/src/v1/sequencer.rs index 053d7275e..ecce5c35e 100644 --- a/protocol-units/da/m1/light-node/src/v1/sequencer.rs +++ b/protocol-units/da/m1/light-node/src/v1/sequencer.rs @@ -7,7 +7,7 @@ use ecdsa::{ subtle::CtOption, AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, Scalar, }, - hazmat::{DigestPrimitive, SignPrimitive}, + hazmat::{DigestPrimitive, SignPrimitive, VerifyPrimitive}, SignatureSize, }; use std::{ @@ -45,7 +45,7 @@ where C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, - AffinePoint: FromEncodedPoint + ToEncodedPoint, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: ModulusSize, { pub pass_through: LightNodeV1PassThrough, @@ -57,7 +57,7 @@ where C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, - AffinePoint: FromEncodedPoint + ToEncodedPoint, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: ModulusSize, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -70,7 +70,7 @@ where C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, - AffinePoint: FromEncodedPoint + ToEncodedPoint, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: ModulusSize, { async fn try_from_config(config: Config) -> Result { @@ -109,7 +109,7 @@ where C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, - AffinePoint: FromEncodedPoint + ToEncodedPoint, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: ModulusSize, { async fn tick_build_blocks(&self, sender: Sender) -> Result<(), anyhow::Error> { @@ -360,7 +360,7 @@ where C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, - AffinePoint: FromEncodedPoint + ToEncodedPoint, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: ModulusSize, { /// Server streaming response type for the StreamReadFromHeight method. diff --git a/protocol-units/da/m1/util/Cargo.toml b/protocol-units/da/m1/util/Cargo.toml index 7afc86d06..8ea928902 100644 --- a/protocol-units/da/m1/util/Cargo.toml +++ b/protocol-units/da/m1/util/Cargo.toml @@ -41,7 +41,7 @@ godfig = { workspace = true } alloy = { workspace = true } zstd = { workspace = true } bcs = { workspace = true } -ecdsa = { workspace = true } +ecdsa = { workspace = true, features = [ "signing", "verifying", "der"] } k256 = { workspace = true } [dev-dependencies] diff --git a/protocol-units/da/m1/util/src/inner_blob.rs b/protocol-units/da/m1/util/src/inner_blob.rs index 2a4c86b83..d5e09382e 100644 --- a/protocol-units/da/m1/util/src/inner_blob.rs +++ b/protocol-units/da/m1/util/src/inner_blob.rs @@ -8,8 +8,9 @@ use ecdsa::{ subtle::CtOption, AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, Scalar, }, - hazmat::{DigestPrimitive, SignPrimitive}, - SignatureSize, SigningKey, + hazmat::{DigestPrimitive, SignPrimitive, VerifyPrimitive}, + signature::DigestVerifier, + SignatureSize, SigningKey, VerifyingKey, }; use serde::{Deserialize, Serialize}; @@ -47,6 +48,26 @@ impl InnerSignedBlobV1Data { signer: signing_key.verifying_key().to_sec1_bytes().to_vec(), }) } + + pub fn try_verify(&self, signature: &[u8], signer: &[u8]) -> Result<(), anyhow::Error> + where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + FieldBytesSize: ModulusSize, + { + let mut hasher = C::Digest::new(); + hasher.update(self.blob.as_slice()); + hasher.update(&self.timestamp.to_be_bytes()); + + let verifying_key = VerifyingKey::::from_sec1_bytes(signer)?; + let signature = ecdsa::Signature::from_bytes(signature.into())?; + + verifying_key.verify_digest(hasher, &signature)?; + + Ok(()) + } } #[derive(Debug, Clone, Serialize, Deserialize)] From 68b7fdc1867a0e7f69d965a84cfab8a39e13fb20 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Mon, 16 Sep 2024 16:48:06 +0100 Subject: [PATCH 04/15] fix: signer verified. --- Cargo.lock | 2 + .../da/m1/light_node/v1beta1.proto | 20 +----- .../da/m1/light-node-verifier/Cargo.toml | 2 + .../light-node-verifier/src/celestia/mod.rs | 41 ----------- .../m1/light-node-verifier/src/celestia/v1.rs | 68 +++++++------------ .../da/m1/light-node-verifier/src/lib.rs | 67 ++++++++---------- .../m1/light-node-verifier/src/signed/mod.rs | 1 + .../m1/light-node-verifier/src/signed/v1.rs | 42 ++++++++++++ .../da/m1/light-node/src/v1/passthrough.rs | 13 ---- .../da/m1/light-node/src/v1/sequencer.rs | 7 -- protocol-units/da/m1/util/src/inner_blob.rs | 33 ++++++++- 11 files changed, 130 insertions(+), 166 deletions(-) create mode 100644 protocol-units/da/m1/light-node-verifier/src/signed/mod.rs create mode 100644 protocol-units/da/m1/light-node-verifier/src/signed/v1.rs diff --git a/Cargo.lock b/Cargo.lock index 54e7e3730..4e6c196e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8026,12 +8026,14 @@ dependencies = [ "celestia-rpc", "celestia-types", "dot-movement", + "ecdsa", "hex", "m1-da-light-node-grpc", "m1-da-light-node-setup", "m1-da-light-node-util", "prost 0.12.6", "serde_json", + "thiserror", "tokio", "tokio-stream", "tonic 0.11.0", diff --git a/proto/movementlabs/protocol_units/da/m1/light_node/v1beta1.proto b/proto/movementlabs/protocol_units/da/m1/light_node/v1beta1.proto index 8f785cda7..173b7b579 100644 --- a/proto/movementlabs/protocol_units/da/m1/light_node/v1beta1.proto +++ b/proto/movementlabs/protocol_units/da/m1/light_node/v1beta1.proto @@ -12,12 +12,6 @@ message Blob { bytes signer = 6; } -enum VerificationMode { - COWBOY = 0; - VALIDATOR_IN = 1; - M_OF_N = 2; -} - message BlobResponse { oneof blob_type { Blob passed_through_blob = 1; @@ -83,16 +77,7 @@ message BatchWriteResponse { repeated BlobResponse blobs = 1; } -message UpdateVerificationParametersRequest { - VerificationMode mode = 1; - repeated string signers = 2; - uint32 m = 3; - uint32 n = 4; -} - -message UpdateVerificationParametersResponse { - VerificationMode mode = 1; -} + // LightNode service definition service LightNodeService { @@ -110,7 +95,4 @@ service LightNodeService { rpc BatchRead (BatchReadRequest) returns (BatchReadResponse); rpc BatchWrite (BatchWriteRequest) returns (BatchWriteResponse); - // Update and manage verification parameters. - rpc UpdateVerificationParameters (UpdateVerificationParametersRequest) returns (UpdateVerificationParametersResponse); - } diff --git a/protocol-units/da/m1/light-node-verifier/Cargo.toml b/protocol-units/da/m1/light-node-verifier/Cargo.toml index 14cd8d562..4ad124a83 100644 --- a/protocol-units/da/m1/light-node-verifier/Cargo.toml +++ b/protocol-units/da/m1/light-node-verifier/Cargo.toml @@ -26,7 +26,9 @@ celestia-types = { workspace = true } anyhow = { workspace = true } hex = { workspace = true } async-stream = { workspace = true } +thiserror = { workspace = true } serde_json = { workspace = true } +ecdsa = { workspace = true, features = [ "signing", "verifying", "der"] } [dev-dependencies] m1-da-light-node-setup = { workspace = true } diff --git a/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs b/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs index 212d4975d..f21e2bb6e 100644 --- a/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs +++ b/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs @@ -1,44 +1,3 @@ pub mod v1; pub use m1_da_light_node_grpc::*; - -#[tonic::async_trait] -pub trait Verifier { - async fn verify( - &self, - verification_mode: VerificationMode, - blob: &[u8], - height: u64, - ) -> Result { - match verification_mode { - VerificationMode::Cowboy => self.verify_cowboy(verification_mode, blob, height).await, - VerificationMode::ValidatorIn => { - self.verifiy_validator_in(verification_mode, blob, height).await - } - VerificationMode::MOfN => self.verify_m_of_n(verification_mode, blob, height).await, - } - } - - async fn verify_cowboy( - &self, - _verification_mode: VerificationMode, - _blob: &[u8], - _height: u64, - ) -> Result { - Ok(true) - } - - async fn verifiy_validator_in( - &self, - _verification_mode: VerificationMode, - _blob: &[u8], - _height: u64, - ) -> Result; - - async fn verify_m_of_n( - &self, - _verification_mode: VerificationMode, - _blob: &[u8], - _height: u64, - ) -> Result; -} diff --git a/protocol-units/da/m1/light-node-verifier/src/celestia/v1.rs b/protocol-units/da/m1/light-node-verifier/src/celestia/v1.rs index ae5ffd2f6..60ecbd004 100644 --- a/protocol-units/da/m1/light-node-verifier/src/celestia/v1.rs +++ b/protocol-units/da/m1/light-node-verifier/src/celestia/v1.rs @@ -1,7 +1,6 @@ -use crate::{Verified, Verifier}; +use crate::{Error, Verified, Verifier}; use celestia_rpc::{BlobClient, Client, HeaderClient}; use celestia_types::{nmt::Namespace, Blob}; -use m1_da_light_node_grpc::VerificationMode; use m1_da_light_node_util::inner_blob::InnerBlob; use std::sync::Arc; @@ -13,68 +12,47 @@ pub struct V1Verifier { #[tonic::async_trait] impl Verifier for V1Verifier { - /// All verification is the same for now - async fn verify( - &self, - _verification_mode: VerificationMode, - blob: Blob, - height: u64, - ) -> Result, anyhow::Error> { - blob.validate()?; + /// Verifies a Celestia Blob as a Valid InnerBlob + async fn verify(&self, blob: Blob, height: u64) -> Result, Error> { + blob.validate().map_err(|e| Error::Validation(e.to_string()))?; // wait for the header to be at the correct height - self.client.header_wait_for_height(height).await?; + self.client + .header_wait_for_height(height) + .await + .map_err(|e| Error::Internal(e.to_string()))?; // get the root - let dah = self.client.header_get_by_height(height).await?.dah; - let root_hash = dah.row_root(0).ok_or(anyhow::anyhow!("No root hash found"))?; + let dah = self + .client + .header_get_by_height(height) + .await + .map_err(|e| Error::Internal(e.to_string()))? + .dah; + let root_hash = dah.row_root(0).ok_or(Error::Validation("No root hash".to_string()))?; // get the proof let proofs = self .client .blob_get_proof(height, self.namespace.clone(), blob.commitment) - .await?; + .await + .map_err(|e| Error::Internal(e.to_string()))?; // get the leaves - let leaves = blob.to_shares()?; + let leaves = blob.to_shares().map_err(|e| Error::Internal(e.to_string()))?; // check if included for proof in proofs.iter() { proof .verify_complete_namespace(&root_hash, &leaves, self.namespace.into()) - .map_err(|e| anyhow::anyhow!("Failed to verify proof: {:?}", e))?; + .map_err(|e| { + Error::Validation("failed to verify complete namespace".to_string()) + })?; } - let inner_blob = InnerBlob::try_from(blob)?; - - Ok(Verified::Valid(inner_blob)) - } - - async fn verify_cowboy( - &self, - _verification_mode: VerificationMode, - _blob: Blob, - _height: u64, - ) -> Result, anyhow::Error> { - unimplemented!() - } - - async fn verify_m_of_n( - &self, - _verification_mode: VerificationMode, - _blob: Blob, - _height: u64, - ) -> Result, anyhow::Error> { - unimplemented!() - } + let inner_blob = InnerBlob::try_from(blob).map_err(|e| Error::Internal(e.to_string()))?; - async fn verifiy_validator_in( - &self, - _verification_mode: VerificationMode, - _blob: Blob, - _height: u64, - ) -> Result, anyhow::Error> { - unimplemented!() + Ok(Verified::new(inner_blob)) } } diff --git a/protocol-units/da/m1/light-node-verifier/src/lib.rs b/protocol-units/da/m1/light-node-verifier/src/lib.rs index 13645202d..e66ca7a25 100644 --- a/protocol-units/da/m1/light-node-verifier/src/lib.rs +++ b/protocol-units/da/m1/light-node-verifier/src/lib.rs @@ -1,11 +1,36 @@ pub mod celestia; +pub mod signed; pub use m1_da_light_node_grpc::*; +use thiserror::Error; + +/// Domain error for the transaction pipe task +#[derive(Debug, Error)] +pub enum Error { + #[error("verifier internal error: {0}")] + Internal(String), + #[error("verifier validation error: {0}")] + Validation(String), +} + +/// thiserror for validation and internal errors +#[derive(thiserror::Error, Debug)] /// A verified outcome. Indicates that input of A is verified as valid instance of B, or else invalid instance. -pub enum Verified { - Valid(B), - Invalid, +pub struct Verified(B); + +impl Verified { + pub fn new(blob: B) -> Self { + Self(blob) + } + + pub fn inner(&self) -> &B { + &self.0 + } + + pub fn into_inner(self) -> B { + self.0 + } } #[tonic::async_trait] @@ -14,39 +39,5 @@ where A: Send + Sync + 'static, B: Send + Sync + 'static, { - async fn verify( - &self, - verification_mode: VerificationMode, - blob: A, - height: u64, - ) -> Result, anyhow::Error> { - match verification_mode { - VerificationMode::Cowboy => self.verify_cowboy(verification_mode, blob, height).await, - VerificationMode::ValidatorIn => { - self.verifiy_validator_in(verification_mode, blob, height).await - } - VerificationMode::MOfN => self.verify_m_of_n(verification_mode, blob, height).await, - } - } - - async fn verify_cowboy( - &self, - _verification_mode: VerificationMode, - _blob: A, - _height: u64, - ) -> Result, anyhow::Error>; - - async fn verifiy_validator_in( - &self, - _verification_mode: VerificationMode, - _blob: A, - _height: u64, - ) -> Result, anyhow::Error>; - - async fn verify_m_of_n( - &self, - _verification_mode: VerificationMode, - _blob: A, - _height: u64, - ) -> Result, anyhow::Error>; + async fn verify(&self, blob: A, height: u64) -> Result, Error>; } diff --git a/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs b/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs new file mode 100644 index 000000000..a3a6d96c3 --- /dev/null +++ b/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs @@ -0,0 +1 @@ +pub mod v1; diff --git a/protocol-units/da/m1/light-node-verifier/src/signed/v1.rs b/protocol-units/da/m1/light-node-verifier/src/signed/v1.rs new file mode 100644 index 000000000..3b759b2c2 --- /dev/null +++ b/protocol-units/da/m1/light-node-verifier/src/signed/v1.rs @@ -0,0 +1,42 @@ +use crate::{Error, Verified, Verifier}; +use ecdsa::{ + elliptic_curve::{ + generic_array::ArrayLength, + ops::Invert, + point::PointCompression, + sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint}, + subtle::CtOption, + AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, Scalar, + }, + hazmat::{DigestPrimitive, SignPrimitive, VerifyPrimitive}, + SignatureSize, +}; +use m1_da_light_node_util::inner_blob::InnerBlob; + +#[derive(Clone)] +pub struct V1Verifier +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + FieldBytesSize: ModulusSize, +{ + pub _curve_marker: std::marker::PhantomData, +} + +#[tonic::async_trait] +impl Verifier for V1Verifier +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + FieldBytesSize: ModulusSize, +{ + async fn verify(&self, blob: InnerBlob, _height: u64) -> Result, Error> { + blob.verify_signature::().map_err(|e| Error::Validation(e.to_string()))?; + + Ok(Verified::new(blob)) + } +} diff --git a/protocol-units/da/m1/light-node/src/v1/passthrough.rs b/protocol-units/da/m1/light-node/src/v1/passthrough.rs index b4709a0b3..f7a83bdd5 100644 --- a/protocol-units/da/m1/light-node/src/v1/passthrough.rs +++ b/protocol-units/da/m1/light-node/src/v1/passthrough.rs @@ -509,17 +509,4 @@ where Ok(tonic::Response::new(BatchWriteResponse { blobs: blob_responses })) } - /// Update and manage verification parameters. - async fn update_verification_parameters( - &self, - request: tonic::Request, - ) -> std::result::Result, tonic::Status> { - let verification_mode = request.into_inner().mode(); - let mut mode = self.verification_mode.write().await; - *mode = verification_mode; - - Ok(tonic::Response::new(UpdateVerificationParametersResponse { - mode: verification_mode.into(), - })) - } } diff --git a/protocol-units/da/m1/light-node/src/v1/sequencer.rs b/protocol-units/da/m1/light-node/src/v1/sequencer.rs index ecce5c35e..1d539283c 100644 --- a/protocol-units/da/m1/light-node/src/v1/sequencer.rs +++ b/protocol-units/da/m1/light-node/src/v1/sequencer.rs @@ -457,13 +457,6 @@ where Ok(tonic::Response::new(BatchWriteResponse { blobs: intents })) } - /// Update and manage verification parameters. - async fn update_verification_parameters( - &self, - request: tonic::Request, - ) -> std::result::Result, tonic::Status> { - self.pass_through.update_verification_parameters(request).await - } } mod block { diff --git a/protocol-units/da/m1/util/src/inner_blob.rs b/protocol-units/da/m1/util/src/inner_blob.rs index d5e09382e..6d4ab07bd 100644 --- a/protocol-units/da/m1/util/src/inner_blob.rs +++ b/protocol-units/da/m1/util/src/inner_blob.rs @@ -64,9 +64,10 @@ impl InnerSignedBlobV1Data { let verifying_key = VerifyingKey::::from_sec1_bytes(signer)?; let signature = ecdsa::Signature::from_bytes(signature.into())?; - verifying_key.verify_digest(hasher, &signature)?; - - Ok(()) + match verifying_key.verify_digest(hasher, &signature) { + Ok(_) => Ok(()), + Err(_) => Err(anyhow::anyhow!("Failed to verify signature")), + } } } @@ -77,6 +78,19 @@ pub struct InnerSignedBlobV1 { pub signer: Vec, } +impl InnerSignedBlobV1 { + pub fn try_verify(&self) -> Result<(), anyhow::Error> + where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + FieldBytesSize: ModulusSize, + { + self.data.try_verify::(self.signature.as_slice(), self.signer.as_slice()) + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub enum InnerBlob { SignedV1(InnerSignedBlobV1), @@ -112,6 +126,19 @@ impl InnerBlob { InnerBlob::SignedV1(inner) => inner.signer.as_slice(), } } + + pub fn verify_signature(&self) -> Result<(), anyhow::Error> + where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + FieldBytesSize: ModulusSize, + { + match self { + InnerBlob::SignedV1(inner) => inner.try_verify::(), + } + } } pub mod celestia { From 072579b77c0bfe5c5db9f03f1a310b9bae40406c Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Mon, 16 Sep 2024 16:58:21 +0100 Subject: [PATCH 05/15] feat: well-known signers. --- .../m1/light-node-verifier/src/signed/v1.rs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/protocol-units/da/m1/light-node-verifier/src/signed/v1.rs b/protocol-units/da/m1/light-node-verifier/src/signed/v1.rs index 3b759b2c2..eb61dc79e 100644 --- a/protocol-units/da/m1/light-node-verifier/src/signed/v1.rs +++ b/protocol-units/da/m1/light-node-verifier/src/signed/v1.rs @@ -12,6 +12,7 @@ use ecdsa::{ SignatureSize, }; use m1_da_light_node_util::inner_blob::InnerBlob; +use std::collections::HashSet; #[derive(Clone)] pub struct V1Verifier @@ -40,3 +41,39 @@ where Ok(Verified::new(blob)) } } + +/// Verifies that the signer of the inner blob is in the known signers set. +/// This is built around an inner signer because we should always check the signature first. That is, this composition prevents unsafe usage. +#[derive(Clone)] +pub struct V1InKnownSignersVerifier +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + FieldBytesSize: ModulusSize, +{ + pub inner_verifier: V1Verifier, + pub known_signers_sec1_bytes: HashSet>, +} + +#[tonic::async_trait] +impl Verifier for V1InKnownSignersVerifier +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + FieldBytesSize: ModulusSize, +{ + async fn verify(&self, blob: InnerBlob, height: u64) -> Result, Error> { + let inner_blob = self.inner_verifier.verify(blob, height).await?; + + let signer = inner_blob.inner().signer(); + if !self.known_signers_sec1_bytes.contains(signer) { + return Err(Error::Validation("signer not in known signers".to_string())); + } + + Ok(inner_blob) + } +} From 9f789488e7382353db4470aa712b3bc95ff4fdc3 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Mon, 16 Sep 2024 20:24:01 +0100 Subject: [PATCH 06/15] fix: permissioned signers celestia stack. --- .../da/m1/light_node/v1beta1.proto | 2 +- .../light-node-verifier/src/celestia/mod.rs | 148 +++++++++++++++++- .../m1/light-node-verifier/src/celestia/v1.rs | 147 ----------------- .../da/m1/light-node-verifier/src/lib.rs | 3 +- .../src/permissioned_signers/mod.rs | 46 ++++++ .../m1/light-node-verifier/src/signed/mod.rs | 80 +++++++++- .../m1/light-node-verifier/src/signed/v1.rs | 79 ---------- .../da/m1/light-node/src/v1/passthrough.rs | 70 ++++----- protocol-units/da/m1/util/src/inner_blob.rs | 12 +- 9 files changed, 314 insertions(+), 273 deletions(-) delete mode 100644 protocol-units/da/m1/light-node-verifier/src/celestia/v1.rs create mode 100644 protocol-units/da/m1/light-node-verifier/src/permissioned_signers/mod.rs delete mode 100644 protocol-units/da/m1/light-node-verifier/src/signed/v1.rs diff --git a/proto/movementlabs/protocol_units/da/m1/light_node/v1beta1.proto b/proto/movementlabs/protocol_units/da/m1/light_node/v1beta1.proto index 173b7b579..dc58c8805 100644 --- a/proto/movementlabs/protocol_units/da/m1/light_node/v1beta1.proto +++ b/proto/movementlabs/protocol_units/da/m1/light_node/v1beta1.proto @@ -4,7 +4,7 @@ package movementlabs.protocol_units.da.m1.light_node.v1beta1; // Request and response messages message Blob { - string blob_id = 1; + bytes blob_id = 1; bytes data = 2; uint64 height = 3; bytes signature = 4; // at some point a signature will be added here diff --git a/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs b/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs index f21e2bb6e..699e5756b 100644 --- a/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs +++ b/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs @@ -1,3 +1,147 @@ -pub mod v1; +use crate::{Error, Verified, VerifierOperations}; +use celestia_rpc::{BlobClient, Client, HeaderClient}; +use celestia_types::{nmt::Namespace, Blob}; +use m1_da_light_node_util::inner_blob::InnerBlob; +use std::sync::Arc; -pub use m1_da_light_node_grpc::*; +#[derive(Clone)] +pub struct Verifier { + pub client: Arc, + pub namespace: Namespace, +} + +#[tonic::async_trait] +impl VerifierOperations for Verifier { + /// Verifies a Celestia Blob as a Valid InnerBlob + async fn verify(&self, blob: Blob, height: u64) -> Result, Error> { + blob.validate().map_err(|e| Error::Validation(e.to_string()))?; + + // wait for the header to be at the correct height + self.client + .header_wait_for_height(height) + .await + .map_err(|e| Error::Internal(e.to_string()))?; + + // get the root + let dah = self + .client + .header_get_by_height(height) + .await + .map_err(|e| Error::Internal(e.to_string()))? + .dah; + let root_hash = dah.row_root(0).ok_or(Error::Validation("No root hash".to_string()))?; + + // get the proof + let proofs = self + .client + .blob_get_proof(height, self.namespace.clone(), blob.commitment) + .await + .map_err(|e| Error::Internal(e.to_string()))?; + + // get the leaves + let leaves = blob.to_shares().map_err(|e| Error::Internal(e.to_string()))?; + + // check if included + for proof in proofs.iter() { + proof + .verify_complete_namespace(&root_hash, &leaves, self.namespace.into()) + .map_err(|_e| { + Error::Validation("failed to verify complete namespace".to_string()) + })?; + } + + let inner_blob = InnerBlob::try_from(blob).map_err(|e| Error::Internal(e.to_string()))?; + + Ok(Verified::new(inner_blob)) + } +} + +#[cfg(all(test, feature = "integration-tests"))] +mod tests { + use super::*; + use celestia_types::blob::GasPrice; + + /// todo: Investigate why this test sporadically fails. + #[tokio::test] + pub async fn test_valid_verifies() -> Result<(), anyhow::Error> { + let dot_movement = dot_movement::DotMovement::try_from_env()?; + let config = dot_movement + .try_get_config_from_json::()?; + + let data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let blob = Blob::new(celestia_namespace.clone(), data.clone())?; + + let height = client.blob_submit(&[blob], GasPrice::default()).await?; + + let included = verifier.verify(VerificationMode::Cowboy, &data, height).await?; + + assert!(included); + + Ok(()) + } + + #[tokio::test] + pub async fn test_absent_does_not_verify() -> Result<(), anyhow::Error> { + let dot_movement = dot_movement::DotMovement::try_from_env()?; + let config = dot_movement + .try_get_config_from_json::()?; + let client = Arc::new(config.connect_celestia().await?); + let celestia_namespace = config.celestia_namespace(); + + let verifier = Verifier { client: client.clone(), namespace: celestia_namespace.clone() }; + + let data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let blob = Blob::new(celestia_namespace.clone(), data.clone())?; + + let height = client.blob_submit(&[blob], GasPrice::default()).await?; + + let included = verifier.verify(VerificationMode::Cowboy, &data, height).await?; + + assert!(included); + + let absent_data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 7]; + + let absent_included = verifier.verify(VerificationMode::Cowboy, &absent_data, height).await; + + match absent_included { + Ok(_) => { + assert!(false, "Should not have verified") + } + Err(_) => {} + } + + Ok(()) + } + + #[tokio::test] + pub async fn test_wrong_height_does_not_verify() -> Result<(), anyhow::Error> { + let dot_movement = dot_movement::DotMovement::try_from_env()?; + let config = dot_movement + .try_get_config_from_json::()?; + let client = Arc::new(config.connect_celestia().await?); + let celestia_namespace = config.celestia_namespace(); + + let verifier = Verifier { client: client.clone(), namespace: celestia_namespace.clone() }; + + let data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + let blob = Blob::new(celestia_namespace.clone(), data.clone())?; + + let height = client.blob_submit(&[blob], GasPrice::default()).await?; + + let included = verifier.verify(VerificationMode::Cowboy, &data, height).await?; + + assert!(included); + + let wrong_height_included = + verifier.verify(VerificationMode::Cowboy, &data, height + 1).await; + + match wrong_height_included { + Ok(_) => { + assert!(false, "Should not have verified") + } + Err(_) => {} + } + + Ok(()) + } +} diff --git a/protocol-units/da/m1/light-node-verifier/src/celestia/v1.rs b/protocol-units/da/m1/light-node-verifier/src/celestia/v1.rs deleted file mode 100644 index 60ecbd004..000000000 --- a/protocol-units/da/m1/light-node-verifier/src/celestia/v1.rs +++ /dev/null @@ -1,147 +0,0 @@ -use crate::{Error, Verified, Verifier}; -use celestia_rpc::{BlobClient, Client, HeaderClient}; -use celestia_types::{nmt::Namespace, Blob}; -use m1_da_light_node_util::inner_blob::InnerBlob; -use std::sync::Arc; - -#[derive(Clone)] -pub struct V1Verifier { - pub client: Arc, - pub namespace: Namespace, -} - -#[tonic::async_trait] -impl Verifier for V1Verifier { - /// Verifies a Celestia Blob as a Valid InnerBlob - async fn verify(&self, blob: Blob, height: u64) -> Result, Error> { - blob.validate().map_err(|e| Error::Validation(e.to_string()))?; - - // wait for the header to be at the correct height - self.client - .header_wait_for_height(height) - .await - .map_err(|e| Error::Internal(e.to_string()))?; - - // get the root - let dah = self - .client - .header_get_by_height(height) - .await - .map_err(|e| Error::Internal(e.to_string()))? - .dah; - let root_hash = dah.row_root(0).ok_or(Error::Validation("No root hash".to_string()))?; - - // get the proof - let proofs = self - .client - .blob_get_proof(height, self.namespace.clone(), blob.commitment) - .await - .map_err(|e| Error::Internal(e.to_string()))?; - - // get the leaves - let leaves = blob.to_shares().map_err(|e| Error::Internal(e.to_string()))?; - - // check if included - for proof in proofs.iter() { - proof - .verify_complete_namespace(&root_hash, &leaves, self.namespace.into()) - .map_err(|e| { - Error::Validation("failed to verify complete namespace".to_string()) - })?; - } - - let inner_blob = InnerBlob::try_from(blob).map_err(|e| Error::Internal(e.to_string()))?; - - Ok(Verified::new(inner_blob)) - } -} - -#[cfg(all(test, feature = "integration-tests"))] -mod tests { - use super::*; - use celestia_types::blob::GasPrice; - - /// todo: Investigate why this test sporadically fails. - #[tokio::test] - pub async fn test_valid_verifies() -> Result<(), anyhow::Error> { - let dot_movement = dot_movement::DotMovement::try_from_env()?; - let config = dot_movement - .try_get_config_from_json::()?; - - let data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - let blob = Blob::new(celestia_namespace.clone(), data.clone())?; - - let height = client.blob_submit(&[blob], GasPrice::default()).await?; - - let included = verifier.verify(VerificationMode::Cowboy, &data, height).await?; - - assert!(included); - - Ok(()) - } - - #[tokio::test] - pub async fn test_absent_does_not_verify() -> Result<(), anyhow::Error> { - let dot_movement = dot_movement::DotMovement::try_from_env()?; - let config = dot_movement - .try_get_config_from_json::()?; - let client = Arc::new(config.connect_celestia().await?); - let celestia_namespace = config.celestia_namespace(); - - let verifier = V1Verifier { client: client.clone(), namespace: celestia_namespace.clone() }; - - let data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - let blob = Blob::new(celestia_namespace.clone(), data.clone())?; - - let height = client.blob_submit(&[blob], GasPrice::default()).await?; - - let included = verifier.verify(VerificationMode::Cowboy, &data, height).await?; - - assert!(included); - - let absent_data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 7]; - - let absent_included = verifier.verify(VerificationMode::Cowboy, &absent_data, height).await; - - match absent_included { - Ok(_) => { - assert!(false, "Should not have verified") - } - Err(_) => {} - } - - Ok(()) - } - - #[tokio::test] - pub async fn test_wrong_height_does_not_verify() -> Result<(), anyhow::Error> { - let dot_movement = dot_movement::DotMovement::try_from_env()?; - let config = dot_movement - .try_get_config_from_json::()?; - let client = Arc::new(config.connect_celestia().await?); - let celestia_namespace = config.celestia_namespace(); - - let verifier = V1Verifier { client: client.clone(), namespace: celestia_namespace.clone() }; - - let data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - let blob = Blob::new(celestia_namespace.clone(), data.clone())?; - - let height = client.blob_submit(&[blob], GasPrice::default()).await?; - - let included = verifier.verify(VerificationMode::Cowboy, &data, height).await?; - - assert!(included); - - let wrong_height_included = - verifier.verify(VerificationMode::Cowboy, &data, height + 1).await; - - match wrong_height_included { - Ok(_) => { - assert!(false, "Should not have verified") - } - Err(_) => {} - } - - Ok(()) - } -} diff --git a/protocol-units/da/m1/light-node-verifier/src/lib.rs b/protocol-units/da/m1/light-node-verifier/src/lib.rs index e66ca7a25..c2d7272da 100644 --- a/protocol-units/da/m1/light-node-verifier/src/lib.rs +++ b/protocol-units/da/m1/light-node-verifier/src/lib.rs @@ -1,4 +1,5 @@ pub mod celestia; +pub mod permissioned_signers; pub mod signed; pub use m1_da_light_node_grpc::*; @@ -34,7 +35,7 @@ impl Verified { } #[tonic::async_trait] -pub trait Verifier +pub trait VerifierOperations where A: Send + Sync + 'static, B: Send + Sync + 'static, diff --git a/protocol-units/da/m1/light-node-verifier/src/permissioned_signers/mod.rs b/protocol-units/da/m1/light-node-verifier/src/permissioned_signers/mod.rs new file mode 100644 index 000000000..9a74612bd --- /dev/null +++ b/protocol-units/da/m1/light-node-verifier/src/permissioned_signers/mod.rs @@ -0,0 +1,46 @@ +use crate::{ + celestia::Verifier as CelestiaVerifier, signed::InKnownSignersVerifier, Error, Verified, + VerifierOperations, +}; +use celestia_types::Blob as CelestiaBlob; +use ecdsa::{ + elliptic_curve::{ + generic_array::ArrayLength, + ops::Invert, + point::PointCompression, + sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint}, + subtle::CtOption, + AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, Scalar, + }, + hazmat::{DigestPrimitive, SignPrimitive, VerifyPrimitive}, + SignatureSize, +}; +use m1_da_light_node_util::inner_blob::InnerBlob; + +#[derive(Clone)] +pub struct Verifier +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + FieldBytesSize: ModulusSize, +{ + pub celestia: CelestiaVerifier, + pub known_signers: InKnownSignersVerifier, +} + +#[tonic::async_trait] +impl VerifierOperations for Verifier +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + FieldBytesSize: ModulusSize, +{ + async fn verify(&self, blob: CelestiaBlob, height: u64) -> Result, Error> { + let verified_blob = self.celestia.verify(blob, height).await?; + self.known_signers.verify(verified_blob.into_inner(), height).await + } +} diff --git a/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs b/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs index a3a6d96c3..18eb100d8 100644 --- a/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs +++ b/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs @@ -1 +1,79 @@ -pub mod v1; +use crate::{Error, Verified, VerifierOperations}; +use ecdsa::{ + elliptic_curve::{ + generic_array::ArrayLength, + ops::Invert, + point::PointCompression, + sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint}, + subtle::CtOption, + AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, Scalar, + }, + hazmat::{DigestPrimitive, SignPrimitive, VerifyPrimitive}, + SignatureSize, +}; +use m1_da_light_node_util::inner_blob::InnerBlob; +use std::collections::HashSet; + +#[derive(Clone)] +pub struct Verifier +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + FieldBytesSize: ModulusSize, +{ + pub _curve_marker: std::marker::PhantomData, +} + +#[tonic::async_trait] +impl VerifierOperations for Verifier +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + FieldBytesSize: ModulusSize, +{ + async fn verify(&self, blob: InnerBlob, _height: u64) -> Result, Error> { + blob.verify_signature::().map_err(|e| Error::Validation(e.to_string()))?; + + Ok(Verified::new(blob)) + } +} + +/// Verifies that the signer of the inner blob is in the known signers set. +/// This is built around an inner signer because we should always check the signature first. That is, this composition prevents unsafe usage. +#[derive(Clone)] +pub struct InKnownSignersVerifier +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + FieldBytesSize: ModulusSize, +{ + pub inner_verifier: Verifier, + pub known_signers_sec1_bytes: HashSet>, +} + +#[tonic::async_trait] +impl VerifierOperations for InKnownSignersVerifier +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + FieldBytesSize: ModulusSize, +{ + async fn verify(&self, blob: InnerBlob, height: u64) -> Result, Error> { + let inner_blob = self.inner_verifier.verify(blob, height).await?; + + let signer = inner_blob.inner().signer(); + if !self.known_signers_sec1_bytes.contains(signer) { + return Err(Error::Validation("signer not in known signers".to_string())); + } + + Ok(inner_blob) + } +} diff --git a/protocol-units/da/m1/light-node-verifier/src/signed/v1.rs b/protocol-units/da/m1/light-node-verifier/src/signed/v1.rs deleted file mode 100644 index eb61dc79e..000000000 --- a/protocol-units/da/m1/light-node-verifier/src/signed/v1.rs +++ /dev/null @@ -1,79 +0,0 @@ -use crate::{Error, Verified, Verifier}; -use ecdsa::{ - elliptic_curve::{ - generic_array::ArrayLength, - ops::Invert, - point::PointCompression, - sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint}, - subtle::CtOption, - AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, Scalar, - }, - hazmat::{DigestPrimitive, SignPrimitive, VerifyPrimitive}, - SignatureSize, -}; -use m1_da_light_node_util::inner_blob::InnerBlob; -use std::collections::HashSet; - -#[derive(Clone)] -pub struct V1Verifier -where - C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, - Scalar: Invert>> + SignPrimitive, - SignatureSize: ArrayLength, - AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, - FieldBytesSize: ModulusSize, -{ - pub _curve_marker: std::marker::PhantomData, -} - -#[tonic::async_trait] -impl Verifier for V1Verifier -where - C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, - Scalar: Invert>> + SignPrimitive, - SignatureSize: ArrayLength, - AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, - FieldBytesSize: ModulusSize, -{ - async fn verify(&self, blob: InnerBlob, _height: u64) -> Result, Error> { - blob.verify_signature::().map_err(|e| Error::Validation(e.to_string()))?; - - Ok(Verified::new(blob)) - } -} - -/// Verifies that the signer of the inner blob is in the known signers set. -/// This is built around an inner signer because we should always check the signature first. That is, this composition prevents unsafe usage. -#[derive(Clone)] -pub struct V1InKnownSignersVerifier -where - C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, - Scalar: Invert>> + SignPrimitive, - SignatureSize: ArrayLength, - AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, - FieldBytesSize: ModulusSize, -{ - pub inner_verifier: V1Verifier, - pub known_signers_sec1_bytes: HashSet>, -} - -#[tonic::async_trait] -impl Verifier for V1InKnownSignersVerifier -where - C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, - Scalar: Invert>> + SignPrimitive, - SignatureSize: ArrayLength, - AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, - FieldBytesSize: ModulusSize, -{ - async fn verify(&self, blob: InnerBlob, height: u64) -> Result, Error> { - let inner_blob = self.inner_verifier.verify(blob, height).await?; - - let signer = inner_blob.inner().signer(); - if !self.known_signers_sec1_bytes.contains(signer) { - return Err(Error::Validation("signer not in known signers".to_string())); - } - - Ok(inner_blob) - } -} diff --git a/protocol-units/da/m1/light-node/src/v1/passthrough.rs b/protocol-units/da/m1/light-node/src/v1/passthrough.rs index f7a83bdd5..8a383ef3e 100644 --- a/protocol-units/da/m1/light-node/src/v1/passthrough.rs +++ b/protocol-units/da/m1/light-node/src/v1/passthrough.rs @@ -1,8 +1,7 @@ use anyhow::Context; +use m1_da_light_node_util::inner_blob::InnerBlob; use std::fmt::{self, Debug, Formatter}; use std::sync::Arc; - -use tokio::sync::RwLock; use tokio_stream::{Stream, StreamExt}; use tracing::debug; @@ -14,9 +13,9 @@ use m1_da_light_node_grpc::light_node_service_server::LightNodeService; use m1_da_light_node_grpc::*; use m1_da_light_node_util::{ config::Config, - inner_blob::{celestia::CelestiaInnerBlob, InnerBlob, InnerSignedBlobV1Data}, + inner_blob::{celestia::CelestiaInnerBlob, InnerSignedBlobV1Data}, }; -use m1_da_light_node_verifier::{v1::V1Verifier, Verifier}; +use m1_da_light_node_verifier::{permissioned_signers::V1Verifier, Verifier}; use crate::v1::LightNodeV1Operations; use ecdsa::{ @@ -44,8 +43,7 @@ where pub config: Config, pub celestia_namespace: Namespace, pub default_client: Arc, - pub verification_mode: Arc>, - pub verifier: Arc>, + pub verifier: Arc + Send + Sync>>, pub signing_key: SigningKey, } @@ -84,10 +82,6 @@ where config: config.clone(), celestia_namespace: config.celestia_namespace(), default_client: client.clone(), - verification_mode: Arc::new(RwLock::new( - VerificationMode::from_str_name("M_OF_N") - .context("Failed to parse verification mode")?, - )), verifier: Arc::new(Box::new(V1Verifier { client, namespace: config.celestia_namespace(), @@ -159,10 +153,10 @@ where } /// Gets the blobs at a given height. - pub async fn get_celestia_blobs_at_height( + pub async fn get_inner_blobs_at_height( &self, height: u64, - ) -> Result, anyhow::Error> { + ) -> Result, anyhow::Error> { let blobs = self.default_client.blob_get_all(height, &[self.celestia_namespace]).await; if let Err(e) = &blobs { @@ -173,28 +167,13 @@ where let mut verified_blobs = Vec::new(); for blob in blobs { - debug!("Verifying blob"); - - let blob_data = blob.data.clone(); - - // todo: improve error boundary here to detect crashes - let verified = self - .verifier - .verify(*self.verification_mode.read().await, &blob_data, height) - .await; - - if let Err(e) = &verified { - debug!("Error verifying blob: {:?}", e); - } - - // FIXME: check the implications of treating errors as verification success. - // @l-monninger: under the assumption we are running a light node in the same - // trusted setup and have not experience a highly intrusive attack, the vulnerability here - // is fairly low. The light node should take care of verification on its own. - let verified = verified.unwrap_or(true); - - if verified { - verified_blobs.push(blob); + match self.verifier.verify(blob, height).await { + Ok(verified_blob) => { + verified_blobs.push(verified_blob.into_inner()); + } + Err(e) => { + debug!("Failed to verify blob: {:?}", e); + } } } @@ -203,11 +182,11 @@ where #[tracing::instrument(target = "movement_timing", level = "debug")] async fn get_blobs_at_height(&self, height: u64) -> Result, anyhow::Error> { - let celestia_blobs = self.get_celestia_blobs_at_height(height).await?; + let inner_blobs = self.get_inner_blobs_at_height(height).await?; let mut blobs = Vec::new(); - for celestia_blob in celestia_blobs { - let blob = Self::celestia_blob_to_blob(celestia_blob, height)?; - debug!(blob_id = %blob.blob_id, "got blob"); + for inner_blob in inner_blobs { + let blob = Self::inner_blob_to_blob(inner_blob, height)?; + // todo: update logging here blobs.push(blob); } Ok(blobs) @@ -294,9 +273,18 @@ where as std::pin::Pin> + Send>>) } + pub fn inner_blob_to_blob(inner_blob: InnerBlob, height: u64) -> Result { + Ok(Blob { + data: inner_blob.blob().to_vec(), + signature: inner_blob.signature().to_vec(), + timestamp: inner_blob.timestamp(), + signer: inner_blob.signer().to_vec(), + blob_id: inner_blob.id().to_vec(), + height, + }) + } + pub fn celestia_blob_to_blob(blob: CelestiaBlob, height: u64) -> Result { - let blob_id: serde_json::Value = serde_json::to_value(&blob.commitment) - .map_err(|e| anyhow::anyhow!("Failed to serialize blob id: {}", e))?; let inner_blob: InnerBlob = blob.try_into()?; Ok(Blob { @@ -304,7 +292,7 @@ where signature: inner_blob.signature().to_vec(), timestamp: inner_blob.timestamp(), signer: inner_blob.signer().to_vec(), - blob_id: blob_id.to_string(), + blob_id: inner_blob.id().to_vec(), height, }) } diff --git a/protocol-units/da/m1/util/src/inner_blob.rs b/protocol-units/da/m1/util/src/inner_blob.rs index 6d4ab07bd..574085d41 100644 --- a/protocol-units/da/m1/util/src/inner_blob.rs +++ b/protocol-units/da/m1/util/src/inner_blob.rs @@ -39,13 +39,16 @@ impl InnerSignedBlobV1Data { let mut hasher = C::Digest::new(); hasher.update(self.blob.as_slice()); hasher.update(&self.timestamp.to_be_bytes()); + let prehash = hasher.finalize(); + let prehash_bytes = prehash.as_slice(); - let (signature, _recovery_id) = signing_key.sign_digest_recoverable(hasher)?; + let (signature, _recovery_id) = signing_key.sign_prehash_recoverable(prehash_bytes)?; Ok(InnerSignedBlobV1 { data: self, signature: signature.to_vec(), signer: signing_key.verifying_key().to_sec1_bytes().to_vec(), + id: prehash_bytes.to_vec(), }) } @@ -76,6 +79,7 @@ pub struct InnerSignedBlobV1 { pub data: InnerSignedBlobV1Data, pub signature: Vec, pub signer: Vec, + pub id: Vec, } impl InnerSignedBlobV1 { @@ -127,6 +131,12 @@ impl InnerBlob { } } + pub fn id(&self) -> &[u8] { + match self { + InnerBlob::SignedV1(inner) => inner.id.as_slice(), + } + } + pub fn verify_signature(&self) -> Result<(), anyhow::Error> where C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, From ba5c4b7dca382612b93a2a82212faad576159314 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Mon, 16 Sep 2024 20:27:24 +0100 Subject: [PATCH 07/15] fix: usages of block_id. --- networks/suzuka/suzuka-full-node/src/da_db.rs | 4 ++-- .../suzuka/suzuka-full-node/src/tasks/execute_settle.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/networks/suzuka/suzuka-full-node/src/da_db.rs b/networks/suzuka/suzuka-full-node/src/da_db.rs index 38c057406..9dd13f05d 100644 --- a/networks/suzuka/suzuka-full-node/src/da_db.rs +++ b/networks/suzuka/suzuka-full-node/src/da_db.rs @@ -32,7 +32,7 @@ impl DaDB { Ok(Self { inner: Arc::new(db) }) } - pub async fn add_executed_block(&self, id: String) -> Result<(), anyhow::Error> { + pub async fn add_executed_block(&self, id: Vec) -> Result<(), anyhow::Error> { let da_db = self.inner.clone(); tokio::task::spawn_blocking(move || { let cf = da_db @@ -46,7 +46,7 @@ impl DaDB { Ok(()) } - pub async fn has_executed_block(&self, id: String) -> Result { + pub async fn has_executed_block(&self, id: Vec) -> Result { let da_db = self.inner.clone(); let id = tokio::task::spawn_blocking(move || { let cf = da_db diff --git a/networks/suzuka/suzuka-full-node/src/tasks/execute_settle.rs b/networks/suzuka/suzuka-full-node/src/tasks/execute_settle.rs index 6a72785f5..0a65bc37f 100644 --- a/networks/suzuka/suzuka-full-node/src/tasks/execute_settle.rs +++ b/networks/suzuka/suzuka-full-node/src/tasks/execute_settle.rs @@ -134,7 +134,7 @@ where // get the transactions let transactions_count = block.transactions().len(); - let span = info_span!(target: "movement_timing", "execute_block", id = %block_id); + let span = info_span!(target: "movement_timing", "execute_block", id = ?block_id); let commitment = self.execute_block_with_retries(block, block_timestamp).instrument(span).await?; @@ -146,7 +146,7 @@ where self.da_db.set_synced_height(da_height - 1).await?; // set the block as executed - self.da_db.add_executed_block(block_id.to_string()).await?; + self.da_db.add_executed_block(block_id.clone()).await?; // todo: this needs defaults if self.settlement_enabled() { @@ -158,7 +158,7 @@ where } } } else { - info!(block_id = %block_id, "Skipping settlement"); + info!(block_id = ?block_id, "Skipping settlement"); } Ok(()) From db0009f93ccb0b65cce35dffac426b77159865b1 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Mon, 16 Sep 2024 20:48:25 +0100 Subject: [PATCH 08/15] feat: new verifier. --- .../light-node-verifier/src/celestia/mod.rs | 6 +++++ .../src/permissioned_signers/mod.rs | 24 +++++++++++++++++ .../m1/light-node-verifier/src/signed/mod.rs | 26 +++++++++++++++++++ .../da/m1/light-node/src/v1/passthrough.rs | 12 ++++----- .../da/m1/light-node/src/v1/sequencer.rs | 2 +- .../da/m1/util/src/config/common.rs | 16 +++++++++++- .../util/src/config/local/m1_da_light_node.rs | 11 +++++--- protocol-units/da/m1/util/src/config/mod.rs | 10 +++++++ 8 files changed, 96 insertions(+), 11 deletions(-) diff --git a/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs b/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs index 699e5756b..cf09b50c9 100644 --- a/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs +++ b/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs @@ -10,6 +10,12 @@ pub struct Verifier { pub namespace: Namespace, } +impl Verifier { + pub fn new(client: Arc, namespace: Namespace) -> Self { + Self { client, namespace } + } +} + #[tonic::async_trait] impl VerifierOperations for Verifier { /// Verifies a Celestia Blob as a Valid InnerBlob diff --git a/protocol-units/da/m1/light-node-verifier/src/permissioned_signers/mod.rs b/protocol-units/da/m1/light-node-verifier/src/permissioned_signers/mod.rs index 9a74612bd..1e9a1f1e4 100644 --- a/protocol-units/da/m1/light-node-verifier/src/permissioned_signers/mod.rs +++ b/protocol-units/da/m1/light-node-verifier/src/permissioned_signers/mod.rs @@ -2,6 +2,8 @@ use crate::{ celestia::Verifier as CelestiaVerifier, signed::InKnownSignersVerifier, Error, Verified, VerifierOperations, }; +use celestia_rpc::Client; +use celestia_types::nmt::Namespace; use celestia_types::Blob as CelestiaBlob; use ecdsa::{ elliptic_curve::{ @@ -16,6 +18,8 @@ use ecdsa::{ SignatureSize, }; use m1_da_light_node_util::inner_blob::InnerBlob; +use std::collections::HashSet; +use std::sync::Arc; #[derive(Clone)] pub struct Verifier @@ -30,6 +34,26 @@ where pub known_signers: InKnownSignersVerifier, } +impl Verifier +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + FieldBytesSize: ModulusSize, +{ + pub fn new( + celestia_client: Arc, + celestia_namespace: Namespace, + known_signers_sec1_bytes: HashSet>, + ) -> Self { + Self { + celestia: CelestiaVerifier::new(celestia_client, celestia_namespace), + known_signers: InKnownSignersVerifier::new(known_signers_sec1_bytes), + } + } +} + #[tonic::async_trait] impl VerifierOperations for Verifier where diff --git a/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs b/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs index 18eb100d8..479e66a3b 100644 --- a/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs +++ b/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs @@ -26,6 +26,19 @@ where pub _curve_marker: std::marker::PhantomData, } +impl Verifier +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + FieldBytesSize: ModulusSize, +{ + pub fn new() -> Self { + Self { _curve_marker: std::marker::PhantomData } + } +} + #[tonic::async_trait] impl VerifierOperations for Verifier where @@ -57,6 +70,19 @@ where pub known_signers_sec1_bytes: HashSet>, } +impl InKnownSignersVerifier +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + FieldBytesSize: ModulusSize, +{ + pub fn new(known_signers_sec1_bytes: HashSet>) -> Self { + Self { inner_verifier: Verifier::new(), known_signers_sec1_bytes } + } +} + #[tonic::async_trait] impl VerifierOperations for InKnownSignersVerifier where diff --git a/protocol-units/da/m1/light-node/src/v1/passthrough.rs b/protocol-units/da/m1/light-node/src/v1/passthrough.rs index 8a383ef3e..ef24d8cb1 100644 --- a/protocol-units/da/m1/light-node/src/v1/passthrough.rs +++ b/protocol-units/da/m1/light-node/src/v1/passthrough.rs @@ -1,4 +1,3 @@ -use anyhow::Context; use m1_da_light_node_util::inner_blob::InnerBlob; use std::fmt::{self, Debug, Formatter}; use std::sync::Arc; @@ -15,7 +14,7 @@ use m1_da_light_node_util::{ config::Config, inner_blob::{celestia::CelestiaInnerBlob, InnerSignedBlobV1Data}, }; -use m1_da_light_node_verifier::{permissioned_signers::V1Verifier, Verifier}; +use m1_da_light_node_verifier::{permissioned_signers::Verifier, VerifierOperations}; use crate::v1::LightNodeV1Operations; use ecdsa::{ @@ -43,7 +42,7 @@ where pub config: Config, pub celestia_namespace: Namespace, pub default_client: Arc, - pub verifier: Arc + Send + Sync>>, + pub verifier: Arc + Send + Sync>>, pub signing_key: SigningKey, } @@ -82,10 +81,11 @@ where config: config.clone(), celestia_namespace: config.celestia_namespace(), default_client: client.clone(), - verifier: Arc::new(Box::new(V1Verifier { + verifier: Arc::new(Box::new(Verifier::::new( client, - namespace: config.celestia_namespace(), - })), + config.celestia_namespace(), + config.da_signers_sec1_keys(), + ))), signing_key, }) } diff --git a/protocol-units/da/m1/light-node/src/v1/sequencer.rs b/protocol-units/da/m1/light-node/src/v1/sequencer.rs index 1d539283c..141b9c08a 100644 --- a/protocol-units/da/m1/light-node/src/v1/sequencer.rs +++ b/protocol-units/da/m1/light-node/src/v1/sequencer.rs @@ -343,7 +343,7 @@ where Ok(BlobResponse { blob_type: Some(blob_response::BlobType::SequencedBlobIntent(Blob { data, - blob_id: "".to_string(), + blob_id: vec![], height, // todo: at some point it would be good to sign these intents, as they can then be used as pre-confirmations against which we can slash signature: vec![], diff --git a/protocol-units/da/m1/util/src/config/common.rs b/protocol-units/da/m1/util/src/config/common.rs index a580bdc3a..71a2e41b4 100644 --- a/protocol-units/da/m1/util/src/config/common.rs +++ b/protocol-units/da/m1/util/src/config/common.rs @@ -1,6 +1,7 @@ use alloy::signers::local::PrivateKeySigner; use celestia_types::nmt::Namespace; use godfig::env_default; +use std::collections::HashSet; use std::env; // The default hostname for the Celestia RPC @@ -128,9 +129,22 @@ pub fn default_celestia_bridge_replace_args() -> Vec { // Whether to use replace args for Celestia bridge env_default!(default_m1_da_light_node_is_initial, "M1_DA_LIGHT_NODE_IS_INITIAL", bool, true); -// The default da signing private key +/// The default da signing private key pub fn default_da_signing_private_key() -> String { let random_wallet = PrivateKeySigner::random(); let random_wallet_string = random_wallet.to_bytes().to_string(); env::var("DA_SIGNING_PRIVATE_KEY").unwrap_or(random_wallet_string) } + +pub fn default_da_signers_sec1_keys() -> HashSet> { + match std::env::var("DA_SIGNERS_SEC1_KEYS") { + Ok(val) => val.split(',').map(|s| s.as_bytes().to_vec()).collect(), + Err(_) => { + // always trust yourself + let mut set = HashSet::new(); + let signer = default_da_signing_private_key(); + set.insert(signer.as_bytes().to_vec()); + set + } + } +} diff --git a/protocol-units/da/m1/util/src/config/local/m1_da_light_node.rs b/protocol-units/da/m1/util/src/config/local/m1_da_light_node.rs index 616b7e347..1ad3e2fc4 100644 --- a/protocol-units/da/m1/util/src/config/local/m1_da_light_node.rs +++ b/protocol-units/da/m1/util/src/config/local/m1_da_light_node.rs @@ -1,11 +1,12 @@ use crate::config::common::{ default_celestia_rpc_connection_hostname, default_celestia_rpc_connection_port, default_celestia_websocket_connection_hostname, default_celestia_websocket_connection_port, - default_da_signing_private_key, default_m1_da_light_node_connection_hostname, - default_m1_da_light_node_connection_port, default_m1_da_light_node_listen_hostname, - default_m1_da_light_node_listen_port, + default_da_signers_sec1_keys, default_da_signing_private_key, + default_m1_da_light_node_connection_hostname, default_m1_da_light_node_connection_port, + default_m1_da_light_node_listen_hostname, default_m1_da_light_node_listen_port, }; use serde::{Deserialize, Serialize}; +use std::collections::HashSet; /// The inner configuration for the local Celestia Appd Runner #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -45,6 +46,9 @@ pub struct Config { /// The private key for signing DA messages #[serde(default = "default_da_signing_private_key")] pub da_signing_private_key: String, + + #[serde(default = "default_da_signers_sec1_keys")] + pub da_signers_sec1_keys: HashSet>, } impl Default for Config { @@ -60,6 +64,7 @@ impl Default for Config { m1_da_light_node_connection_hostname: default_m1_da_light_node_connection_hostname(), m1_da_light_node_connection_port: default_m1_da_light_node_connection_port(), da_signing_private_key: default_da_signing_private_key(), + da_signers_sec1_keys: default_da_signers_sec1_keys(), } } } diff --git a/protocol-units/da/m1/util/src/config/mod.rs b/protocol-units/da/m1/util/src/config/mod.rs index b7183cb60..d8a828b3f 100644 --- a/protocol-units/da/m1/util/src/config/mod.rs +++ b/protocol-units/da/m1/util/src/config/mod.rs @@ -2,6 +2,7 @@ use anyhow::Context; use celestia_rpc::Client; use celestia_types::nmt::Namespace; use serde::{Deserialize, Serialize}; +use std::collections::HashSet; pub mod common; pub mod local; @@ -185,6 +186,15 @@ impl Config { } } + /// Gets the da signers sec1 keys + pub fn da_signers_sec1_keys(&self) -> HashSet> { + match self { + Config::Local(local) => local.m1_da_light_node.da_signers_sec1_keys.clone(), + Config::Arabica(local) => local.m1_da_light_node.da_signers_sec1_keys.clone(), + Config::Mocha(local) => local.m1_da_light_node.da_signers_sec1_keys.clone(), + } + } + pub fn try_block_building_parameters(&self) -> Result<(u32, u64), anyhow::Error> { match self { Config::Local(local) => { From 3b7859f10f1e9732fc515771be7486aed8089975 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 17 Sep 2024 09:01:15 +0100 Subject: [PATCH 09/15] fix: eth signing. --- .../process-compose.setup.yml | 2 +- .../light-node-verifier/src/celestia/mod.rs | 78 ++++++++++--------- .../da/m1/util/src/config/common.rs | 12 +++ 3 files changed, 55 insertions(+), 37 deletions(-) diff --git a/process-compose/suzuka-full-node/process-compose.setup.yml b/process-compose/suzuka-full-node/process-compose.setup.yml index 7aa531629..ba014cca3 100644 --- a/process-compose/suzuka-full-node/process-compose.setup.yml +++ b/process-compose/suzuka-full-node/process-compose.setup.yml @@ -4,7 +4,7 @@ environment: processes: - suzuka-setup: + setup: command: | suzuka-full-node-setup depends_on: diff --git a/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs b/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs index cf09b50c9..276e7c267 100644 --- a/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs +++ b/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs @@ -1,5 +1,7 @@ use crate::{Error, Verified, VerifierOperations}; -use celestia_rpc::{BlobClient, Client, HeaderClient}; +use celestia_rpc::Client; +#[cfg(feature = "pessimistic")] +use celestia_rpc::{BlobClient, HeaderClient}; use celestia_types::{nmt::Namespace, Blob}; use m1_da_light_node_util::inner_blob::InnerBlob; use std::sync::Arc; @@ -19,41 +21,45 @@ impl Verifier { #[tonic::async_trait] impl VerifierOperations for Verifier { /// Verifies a Celestia Blob as a Valid InnerBlob - async fn verify(&self, blob: Blob, height: u64) -> Result, Error> { - blob.validate().map_err(|e| Error::Validation(e.to_string()))?; - - // wait for the header to be at the correct height - self.client - .header_wait_for_height(height) - .await - .map_err(|e| Error::Internal(e.to_string()))?; - - // get the root - let dah = self - .client - .header_get_by_height(height) - .await - .map_err(|e| Error::Internal(e.to_string()))? - .dah; - let root_hash = dah.row_root(0).ok_or(Error::Validation("No root hash".to_string()))?; - - // get the proof - let proofs = self - .client - .blob_get_proof(height, self.namespace.clone(), blob.commitment) - .await - .map_err(|e| Error::Internal(e.to_string()))?; - - // get the leaves - let leaves = blob.to_shares().map_err(|e| Error::Internal(e.to_string()))?; - - // check if included - for proof in proofs.iter() { - proof - .verify_complete_namespace(&root_hash, &leaves, self.namespace.into()) - .map_err(|_e| { - Error::Validation("failed to verify complete namespace".to_string()) - })?; + async fn verify(&self, blob: Blob, _height: u64) -> Result, Error> { + //@l-monninger: the light node itself does most of the work of verify blobs. The verification under the feature flag below is useful in zero-trust environments. + #[cfg(feature = "pessimistic")] + { + blob.validate().map_err(|e| Error::Validation(e.to_string()))?; + + // wait for the header to be at the correct height + self.client + .header_wait_for_height(height) + .await + .map_err(|e| Error::Internal(e.to_string()))?; + + // get the root + let dah = self + .client + .header_get_by_height(height) + .await + .map_err(|e| Error::Internal(e.to_string()))? + .dah; + let root_hash = dah.row_root(0).ok_or(Error::Validation("No root hash".to_string()))?; + + // get the proof + let proofs = self + .client + .blob_get_proof(height, self.namespace.clone(), blob.commitment) + .await + .map_err(|e| Error::Internal(e.to_string()))?; + + // get the leaves + let leaves = blob.to_shares().map_err(|e| Error::Internal(e.to_string()))?; + + // check if included + for proof in proofs.iter() { + proof + .verify_complete_namespace(&root_hash, &leaves, self.namespace.into()) + .map_err(|_e| { + Error::Validation("failed to verify complete namespace".to_string()) + })?; + } } let inner_blob = InnerBlob::try_from(blob).map_err(|e| Error::Internal(e.to_string()))?; diff --git a/protocol-units/da/m1/util/src/config/common.rs b/protocol-units/da/m1/util/src/config/common.rs index 71a2e41b4..548d3b180 100644 --- a/protocol-units/da/m1/util/src/config/common.rs +++ b/protocol-units/da/m1/util/src/config/common.rs @@ -1,5 +1,17 @@ use alloy::signers::local::PrivateKeySigner; use celestia_types::nmt::Namespace; +/*use ecdsa::{ + elliptic_curve::{ + generic_array::ArrayLength, + ops::Invert, + point::PointCompression, + sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint}, + subtle::CtOption, + AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, Scalar, + }, + hazmat::{DigestPrimitive, SignPrimitive, VerifyPrimitive}, + SignatureSize, +};*/ use godfig::env_default; use std::collections::HashSet; use std::env; From 361822aff99032545bb6817d55a18f374e59eb23 Mon Sep 17 00:00:00 2001 From: Liam Monninger <79056955+l-monninger@users.noreply.github.com> Date: Tue, 17 Sep 2024 11:58:45 +0100 Subject: [PATCH 10/15] fix: signature Co-authored-by: Mikhail Zabaluev --- .../movementlabs/protocol_units/da/m1/light_node/v1beta1.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto/movementlabs/protocol_units/da/m1/light_node/v1beta1.proto b/proto/movementlabs/protocol_units/da/m1/light_node/v1beta1.proto index dc58c8805..30d53a780 100644 --- a/proto/movementlabs/protocol_units/da/m1/light_node/v1beta1.proto +++ b/proto/movementlabs/protocol_units/da/m1/light_node/v1beta1.proto @@ -7,7 +7,7 @@ message Blob { bytes blob_id = 1; bytes data = 2; uint64 height = 3; - bytes signature = 4; // at some point a signature will be added here + bytes signature = 4; uint64 timestamp = 5; bytes signer = 6; } From c1e7945858cf7badcccaf0ba55e8aaacf2adbdc6 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 17 Sep 2024 12:39:54 +0100 Subject: [PATCH 11/15] fix: cleanup inner blob. --- protocol-units/da/m1/util/src/inner_blob.rs | 2 +- protocol-units/settlement/mcr/runner/src/main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/protocol-units/da/m1/util/src/inner_blob.rs b/protocol-units/da/m1/util/src/inner_blob.rs index 574085d41..a2de5d3e6 100644 --- a/protocol-units/da/m1/util/src/inner_blob.rs +++ b/protocol-units/da/m1/util/src/inner_blob.rs @@ -33,7 +33,7 @@ impl InnerSignedBlobV1Data { C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, - AffinePoint: FromEncodedPoint + ToEncodedPoint, + AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: ModulusSize, { let mut hasher = C::Digest::new(); diff --git a/protocol-units/settlement/mcr/runner/src/main.rs b/protocol-units/settlement/mcr/runner/src/main.rs index 9b1d3dd58..f8176d0c1 100644 --- a/protocol-units/settlement/mcr/runner/src/main.rs +++ b/protocol-units/settlement/mcr/runner/src/main.rs @@ -14,7 +14,7 @@ async fn main() -> Result<(), anyhow::Error> { // get the config file let dot_movement = dot_movement::DotMovement::try_from_env()?; - let mut config_file = dot_movement.try_get_or_create_config_file().await?; + let config_file = dot_movement.try_get_or_create_config_file().await?; // get a matching godfig object let godfig: Godfig = From 93de5bdf945dfc554aaf3b0f5d68e4b9f0a54c4d Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Tue, 1 Oct 2024 15:44:19 -0700 Subject: [PATCH 12/15] fix: private key formatting. --- Cargo.lock | 1 + Cargo.toml | 2 +- protocol-units/bridge/contracts/lib/forge-std | 1 + .../contracts/lib/openzeppelin-contracts | 1 + .../lib/openzeppelin-contracts-upgradeable | 1 + .../src/permissioned_signers/mod.rs | 2 +- .../m1/light-node-verifier/src/signed/mod.rs | 11 ++- .../da/m1/light-node/src/v1/passthrough.rs | 4 +- protocol-units/da/m1/util/Cargo.toml | 1 + .../da/m1/util/src/config/common.rs | 23 ----- .../util/src/config/local/m1_da_light_node.rs | 91 +++++++++++++++++-- protocol-units/da/m1/util/src/config/mod.rs | 26 ++++-- protocol-units/da/m1/util/src/inner_blob.rs | 7 +- 13 files changed, 122 insertions(+), 49 deletions(-) create mode 160000 protocol-units/bridge/contracts/lib/forge-std create mode 160000 protocol-units/bridge/contracts/lib/openzeppelin-contracts create mode 160000 protocol-units/bridge/contracts/lib/openzeppelin-contracts-upgradeable diff --git a/Cargo.lock b/Cargo.lock index 4e6c196e9..b4f3a844c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8002,6 +8002,7 @@ dependencies = [ "m1-da-light-node-grpc", "memseq-util", "prost 0.12.6", + "rand 0.8.5", "serde", "serde_derive", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index f73848060..13a42cde0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -292,7 +292,7 @@ rustix = "0.38.34" paste = "1.0.15" uuid = { version = "1.10.0", features = ["v4"] } blake-3 = "1.4.0" -ecdsa = { version = "0.16.9", features = ["signing", "verifying", "der"] } +ecdsa = { version = "0.16.9", features = ["signing", "verifying", "der", "pem", "pkcs8"] } # trying to pin diesel # diesel = "=2.1.1" diff --git a/protocol-units/bridge/contracts/lib/forge-std b/protocol-units/bridge/contracts/lib/forge-std new file mode 160000 index 000000000..5a802d7c1 --- /dev/null +++ b/protocol-units/bridge/contracts/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 5a802d7c10abb4bbfb3e7214c75052ef9e6a06f8 diff --git a/protocol-units/bridge/contracts/lib/openzeppelin-contracts b/protocol-units/bridge/contracts/lib/openzeppelin-contracts new file mode 160000 index 000000000..530179a71 --- /dev/null +++ b/protocol-units/bridge/contracts/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit 530179a71f435e85ae9df9b9f12b5637cf229e5c diff --git a/protocol-units/bridge/contracts/lib/openzeppelin-contracts-upgradeable b/protocol-units/bridge/contracts/lib/openzeppelin-contracts-upgradeable new file mode 160000 index 000000000..f231c5cb8 --- /dev/null +++ b/protocol-units/bridge/contracts/lib/openzeppelin-contracts-upgradeable @@ -0,0 +1 @@ +Subproject commit f231c5cb86c0c045dc0b23d8f3beeaf0d68dccc7 diff --git a/protocol-units/da/m1/light-node-verifier/src/permissioned_signers/mod.rs b/protocol-units/da/m1/light-node-verifier/src/permissioned_signers/mod.rs index 1e9a1f1e4..b3f83a6f6 100644 --- a/protocol-units/da/m1/light-node-verifier/src/permissioned_signers/mod.rs +++ b/protocol-units/da/m1/light-node-verifier/src/permissioned_signers/mod.rs @@ -45,7 +45,7 @@ where pub fn new( celestia_client: Arc, celestia_namespace: Namespace, - known_signers_sec1_bytes: HashSet>, + known_signers_sec1_bytes: HashSet, ) -> Self { Self { celestia: CelestiaVerifier::new(celestia_client, celestia_namespace), diff --git a/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs b/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs index 479e66a3b..ba772b4bd 100644 --- a/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs +++ b/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs @@ -67,7 +67,8 @@ where FieldBytesSize: ModulusSize, { pub inner_verifier: Verifier, - pub known_signers_sec1_bytes: HashSet>, + /// The set of known signers in sec1 bytes hex format. + pub known_signers_sec1_bytes_hex: HashSet, } impl InKnownSignersVerifier @@ -78,8 +79,8 @@ where AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: ModulusSize, { - pub fn new(known_signers_sec1_bytes: HashSet>) -> Self { - Self { inner_verifier: Verifier::new(), known_signers_sec1_bytes } + pub fn new(known_signers_sec1_bytes_hex: HashSet) -> Self { + Self { inner_verifier: Verifier::new(), known_signers_sec1_bytes_hex } } } @@ -95,8 +96,8 @@ where async fn verify(&self, blob: InnerBlob, height: u64) -> Result, Error> { let inner_blob = self.inner_verifier.verify(blob, height).await?; - let signer = inner_blob.inner().signer(); - if !self.known_signers_sec1_bytes.contains(signer) { + let signer = inner_blob.inner().signer_hex(); + if !self.known_signers_sec1_bytes_hex.contains(&signer) { return Err(Error::Validation("signer not in known signers".to_string())); } diff --git a/protocol-units/da/m1/light-node/src/v1/passthrough.rs b/protocol-units/da/m1/light-node/src/v1/passthrough.rs index ef24d8cb1..e32cd3d70 100644 --- a/protocol-units/da/m1/light-node/src/v1/passthrough.rs +++ b/protocol-units/da/m1/light-node/src/v1/passthrough.rs @@ -74,7 +74,9 @@ where let client = Arc::new(config.connect_celestia().await?); let signing_key_str = config.da_signing_key(); - let signing_key = SigningKey::from_bytes(signing_key_str.as_bytes().into()) + let hex_bytes = hex::decode(signing_key_str)?; + + let signing_key = SigningKey::from_bytes(hex_bytes.as_slice().try_into()?) .map_err(|e| anyhow::anyhow!("Failed to create signing key: {}", e))?; Ok(Self { diff --git a/protocol-units/da/m1/util/Cargo.toml b/protocol-units/da/m1/util/Cargo.toml index 8ea928902..aabe4adf2 100644 --- a/protocol-units/da/m1/util/Cargo.toml +++ b/protocol-units/da/m1/util/Cargo.toml @@ -43,6 +43,7 @@ zstd = { workspace = true } bcs = { workspace = true } ecdsa = { workspace = true, features = [ "signing", "verifying", "der"] } k256 = { workspace = true } +rand = { version = "0.8.5" } [dev-dependencies] tempfile = { workspace = true } diff --git a/protocol-units/da/m1/util/src/config/common.rs b/protocol-units/da/m1/util/src/config/common.rs index 548d3b180..d2ab91b7e 100644 --- a/protocol-units/da/m1/util/src/config/common.rs +++ b/protocol-units/da/m1/util/src/config/common.rs @@ -1,4 +1,3 @@ -use alloy::signers::local::PrivateKeySigner; use celestia_types::nmt::Namespace; /*use ecdsa::{ elliptic_curve::{ @@ -13,8 +12,6 @@ use celestia_types::nmt::Namespace; SignatureSize, };*/ use godfig::env_default; -use std::collections::HashSet; -use std::env; // The default hostname for the Celestia RPC env_default!( @@ -140,23 +137,3 @@ pub fn default_celestia_bridge_replace_args() -> Vec { // Whether to use replace args for Celestia bridge env_default!(default_m1_da_light_node_is_initial, "M1_DA_LIGHT_NODE_IS_INITIAL", bool, true); - -/// The default da signing private key -pub fn default_da_signing_private_key() -> String { - let random_wallet = PrivateKeySigner::random(); - let random_wallet_string = random_wallet.to_bytes().to_string(); - env::var("DA_SIGNING_PRIVATE_KEY").unwrap_or(random_wallet_string) -} - -pub fn default_da_signers_sec1_keys() -> HashSet> { - match std::env::var("DA_SIGNERS_SEC1_KEYS") { - Ok(val) => val.split(',').map(|s| s.as_bytes().to_vec()).collect(), - Err(_) => { - // always trust yourself - let mut set = HashSet::new(); - let signer = default_da_signing_private_key(); - set.insert(signer.as_bytes().to_vec()); - set - } - } -} diff --git a/protocol-units/da/m1/util/src/config/local/m1_da_light_node.rs b/protocol-units/da/m1/util/src/config/local/m1_da_light_node.rs index 1ad3e2fc4..41aadcecb 100644 --- a/protocol-units/da/m1/util/src/config/local/m1_da_light_node.rs +++ b/protocol-units/da/m1/util/src/config/local/m1_da_light_node.rs @@ -1,13 +1,90 @@ use crate::config::common::{ default_celestia_rpc_connection_hostname, default_celestia_rpc_connection_port, default_celestia_websocket_connection_hostname, default_celestia_websocket_connection_port, - default_da_signers_sec1_keys, default_da_signing_private_key, default_m1_da_light_node_connection_hostname, default_m1_da_light_node_connection_port, default_m1_da_light_node_listen_hostname, default_m1_da_light_node_listen_port, }; +use ecdsa::SigningKey; +use k256::Secp256k1; use serde::{Deserialize, Serialize}; use std::collections::HashSet; +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct DaSigners { + pub da_signing_private_key_hex: String, + pub da_signers_public_keys_hex: HashSet, +} + +/// The default da signing private key +pub fn default_da_signing_private_key() -> SigningKey { + match std::env::var("DA_SIGNING_PRIVATE_KEY") { + Ok(val) => { + // decode from hex to bytes 32 + let hex_bytes = hex::decode(val).expect("Invalid hex string"); + + // todo: maybe remove the unwrap and catch for a random signing key + let signing_key_bytes: &[u8; 32] = + hex_bytes.as_slice().try_into().expect("Slice with incorrect length"); + SigningKey::from_bytes(signing_key_bytes.into()).unwrap() + } + Err(_) => SigningKey::random( + // rand_core + &mut rand::rngs::OsRng, + ), + } +} + +pub fn default_da_signers_sec1_keys() -> HashSet { + match std::env::var("DA_SIGNERS_SEC1_KEYS") { + Ok(val) => val.split(',').map(|s| s.to_string()).collect(), + Err(_) => HashSet::new(), + } +} + +pub fn default_da_signers() -> DaSigners { + let da_signer = default_da_signing_private_key(); + + // always trust yourself + let mut trusted_signers = HashSet::new(); + let sec1_hex = hex::encode(da_signer.verifying_key().to_sec1_bytes().to_vec()); + trusted_signers.insert(sec1_hex); + + // add the other specified signers + let additional_signers = default_da_signers_sec1_keys(); + trusted_signers.extend(additional_signers); + + DaSigners { + da_signing_private_key_hex: hex::encode(da_signer.to_bytes().as_slice()), + da_signers_public_keys_hex: trusted_signers, + } +} + +#[cfg(test)] +pub mod signers_serialization_test { + + use super::*; + + #[test] + fn test_signing_key() -> Result<(), anyhow::Error> { + let signing_key = SigningKey::::random( + // rand_core + &mut rand::rngs::OsRng, + ); + + let signing_bytes = signing_key.to_bytes(); + + let signing_key_fixed_bytes: &[u8; 32] = + signing_bytes.as_slice().try_into().expect("Slice with incorrect length"); + + let from_bytes = + SigningKey::::from_bytes(&signing_key_fixed_bytes.clone().into())?; + + assert_eq!(signing_key, from_bytes); + + Ok(()) + } +} + /// The inner configuration for the local Celestia Appd Runner #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct Config { @@ -43,12 +120,9 @@ pub struct Config { #[serde(default = "default_m1_da_light_node_connection_port")] pub m1_da_light_node_connection_port: u16, - /// The private key for signing DA messages - #[serde(default = "default_da_signing_private_key")] - pub da_signing_private_key: String, - - #[serde(default = "default_da_signers_sec1_keys")] - pub da_signers_sec1_keys: HashSet>, + /// The DA signers + #[serde(default = "default_da_signers")] + pub da_signers: DaSigners, } impl Default for Config { @@ -63,8 +137,7 @@ impl Default for Config { m1_da_light_node_listen_port: default_m1_da_light_node_listen_port(), m1_da_light_node_connection_hostname: default_m1_da_light_node_connection_hostname(), m1_da_light_node_connection_port: default_m1_da_light_node_connection_port(), - da_signing_private_key: default_da_signing_private_key(), - da_signers_sec1_keys: default_da_signers_sec1_keys(), + da_signers: default_da_signers(), } } } diff --git a/protocol-units/da/m1/util/src/config/mod.rs b/protocol-units/da/m1/util/src/config/mod.rs index d8a828b3f..9b3919f9b 100644 --- a/protocol-units/da/m1/util/src/config/mod.rs +++ b/protocol-units/da/m1/util/src/config/mod.rs @@ -180,18 +180,30 @@ impl Config { /// Gets the da signing key as a string pub fn da_signing_key(&self) -> String { match self { - Config::Local(local) => local.m1_da_light_node.da_signing_private_key.clone(), - Config::Arabica(local) => local.m1_da_light_node.da_signing_private_key.clone(), - Config::Mocha(local) => local.m1_da_light_node.da_signing_private_key.clone(), + Config::Local(local) => { + local.m1_da_light_node.da_signers.da_signing_private_key_hex.clone() + } + Config::Arabica(local) => { + local.m1_da_light_node.da_signers.da_signing_private_key_hex.clone() + } + Config::Mocha(local) => { + local.m1_da_light_node.da_signers.da_signing_private_key_hex.clone() + } } } /// Gets the da signers sec1 keys - pub fn da_signers_sec1_keys(&self) -> HashSet> { + pub fn da_signers_sec1_keys(&self) -> HashSet { match self { - Config::Local(local) => local.m1_da_light_node.da_signers_sec1_keys.clone(), - Config::Arabica(local) => local.m1_da_light_node.da_signers_sec1_keys.clone(), - Config::Mocha(local) => local.m1_da_light_node.da_signers_sec1_keys.clone(), + Config::Local(local) => { + local.m1_da_light_node.da_signers.da_signers_public_keys_hex.clone() + } + Config::Arabica(local) => { + local.m1_da_light_node.da_signers.da_signers_public_keys_hex.clone() + } + Config::Mocha(local) => { + local.m1_da_light_node.da_signers.da_signers_public_keys_hex.clone() + } } } diff --git a/protocol-units/da/m1/util/src/inner_blob.rs b/protocol-units/da/m1/util/src/inner_blob.rs index a2de5d3e6..e3b1ccd91 100644 --- a/protocol-units/da/m1/util/src/inner_blob.rs +++ b/protocol-units/da/m1/util/src/inner_blob.rs @@ -1,4 +1,3 @@ -use ecdsa::signature::digest::Digest; use ecdsa::{ elliptic_curve::{ generic_array::ArrayLength, @@ -9,7 +8,7 @@ use ecdsa::{ AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, Scalar, }, hazmat::{DigestPrimitive, SignPrimitive, VerifyPrimitive}, - signature::DigestVerifier, + signature::{digest::Digest, DigestVerifier}, SignatureSize, SigningKey, VerifyingKey, }; use serde::{Deserialize, Serialize}; @@ -131,6 +130,10 @@ impl InnerBlob { } } + pub fn signer_hex(&self) -> String { + hex::encode(self.signer()) + } + pub fn id(&self) -> &[u8] { match self { InnerBlob::SignedV1(inner) => inner.id.as_slice(), From d64fbc03ca770ba310e1191d010f3608b11a9173 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Fri, 18 Oct 2024 06:30:25 -0700 Subject: [PATCH 13/15] fix: remove file from history. --- .gitignore | 4 +- Cargo.lock | 3 ++ .../src/tasks/execute_settle.rs | 9 +--- .../da/m1/light-node-verifier/Cargo.toml | 3 ++ .../light-node-verifier/src/celestia/mod.rs | 50 ++++++++++++++++--- .../src/permissioned_signers/mod.rs | 14 ++++-- .../m1/light-node-verifier/src/signed/mod.rs | 43 ++++++++++++++-- .../da/m1/light-node/src/v1/passthrough.rs | 20 ++++---- .../da/m1/light-node/src/v1/sequencer.rs | 24 ++++++--- protocol-units/da/m1/util/Cargo.toml | 1 + .../util/src/config/local/m1_da_light_node.rs | 14 +++--- protocol-units/da/m1/util/src/config/mod.rs | 24 +++------ protocol-units/da/m1/util/src/inner_blob.rs | 22 +++++--- .../opt-executor/src/executor/execution.rs | 10 ++-- 14 files changed, 166 insertions(+), 75 deletions(-) diff --git a/.gitignore b/.gitignore index a224bf8d2..0957182bf 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,6 @@ profile.json *.log venv *.pem -*.jot \ No newline at end of file +*.jot.* +*.jot +*.env \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 86738b705..3f86365f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8544,10 +8544,12 @@ dependencies = [ "dot-movement", "ecdsa 0.16.9", "hex", + "k256", "m1-da-light-node-grpc", "m1-da-light-node-setup", "m1-da-light-node-util", "prost 0.12.6", + "rand 0.7.3", "serde_json", "thiserror", "tokio", @@ -8555,6 +8557,7 @@ dependencies = [ "tonic 0.11.0", "tonic-reflection", "tonic-web", + "tracing", ] [[package]] diff --git a/networks/suzuka/suzuka-full-node/src/tasks/execute_settle.rs b/networks/suzuka/suzuka-full-node/src/tasks/execute_settle.rs index f09d072ce..db3437dd5 100644 --- a/networks/suzuka/suzuka-full-node/src/tasks/execute_settle.rs +++ b/networks/suzuka/suzuka-full-node/src/tasks/execute_settle.rs @@ -116,6 +116,7 @@ where info!( block_id = %hex::encode(block_id.clone()), da_height = da_height, + time = block_timestamp, "Processing block from DA" ); @@ -130,13 +131,7 @@ where anyhow::bail!("Invalid DA height: {:?}", da_height); } - // decompress the block bytes - let block = tokio::task::spawn_blocking(move || { - let decompressed_block_bytes = zstd::decode_all(&block_bytes[..])?; - let block: Block = bcs::from_bytes(&decompressed_block_bytes)?; - Ok::(block) - }) - .await??; + let block: Block = bcs::from_bytes(&block_bytes[..])?; // get the transactions let transactions_count = block.transactions().len(); diff --git a/protocol-units/da/m1/light-node-verifier/Cargo.toml b/protocol-units/da/m1/light-node-verifier/Cargo.toml index 4ad124a83..8a5d83ebc 100644 --- a/protocol-units/da/m1/light-node-verifier/Cargo.toml +++ b/protocol-units/da/m1/light-node-verifier/Cargo.toml @@ -29,10 +29,13 @@ async-stream = { workspace = true } thiserror = { workspace = true } serde_json = { workspace = true } ecdsa = { workspace = true, features = [ "signing", "verifying", "der"] } +tracing = { workspace = true } [dev-dependencies] m1-da-light-node-setup = { workspace = true } dot-movement = { workspace = true } +k256 = { workspace = true } +rand = { workspace = true } [lints] workspace = true diff --git a/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs b/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs index 276e7c267..5d52be2e6 100644 --- a/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs +++ b/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs @@ -1,14 +1,14 @@ use crate::{Error, Verified, VerifierOperations}; use celestia_rpc::Client; -#[cfg(feature = "pessimistic")] -use celestia_rpc::{BlobClient, HeaderClient}; use celestia_types::{nmt::Namespace, Blob}; use m1_da_light_node_util::inner_blob::InnerBlob; use std::sync::Arc; #[derive(Clone)] pub struct Verifier { + /// The Celestia RPC client pub client: Arc, + /// The namespace of the Celestia Blob pub namespace: Namespace, } @@ -22,9 +22,42 @@ impl Verifier { impl VerifierOperations for Verifier { /// Verifies a Celestia Blob as a Valid InnerBlob async fn verify(&self, blob: Blob, _height: u64) -> Result, Error> { - //@l-monninger: the light node itself does most of the work of verify blobs. The verification under the feature flag below is useful in zero-trust environments. - #[cfg(feature = "pessimistic")] - { + // Only assert that we can indeed get an InnerBlob from the Blob + let inner_blob = InnerBlob::try_from(blob).map_err(|e| Error::Internal(e.to_string()))?; + + Ok(Verified::new(inner_blob)) + } +} + +pub mod pessimistic { + + use crate::{Error, Verified, VerifierOperations}; + use celestia_rpc::Client; + use celestia_rpc::{BlobClient, HeaderClient}; + use celestia_types::{nmt::Namespace, Blob}; + use m1_da_light_node_util::inner_blob::InnerBlob; + use std::sync::Arc; + + #[derive(Clone)] + pub struct Verifier { + /// The Celestia RPC client + pub client: Arc, + /// The namespace of the Celestia Blob + pub namespace: Namespace, + } + + impl Verifier { + pub fn new(client: Arc, namespace: Namespace) -> Self { + Self { client, namespace } + } + } + + #[tonic::async_trait] + impl VerifierOperations for Verifier { + /// Verifies a Celestia Blob as a Valid InnerBlob + async fn verify(&self, blob: Blob, height: u64) -> Result, Error> { + //@l-monninger: the light node itself does most of the work of verify blobs. The verification under the feature flag below is useful in zero-trust environments. + blob.validate().map_err(|e| Error::Validation(e.to_string()))?; // wait for the header to be at the correct height @@ -60,11 +93,12 @@ impl VerifierOperations for Verifier { Error::Validation("failed to verify complete namespace".to_string()) })?; } - } - let inner_blob = InnerBlob::try_from(blob).map_err(|e| Error::Internal(e.to_string()))?; + let inner_blob = + InnerBlob::try_from(blob).map_err(|e| Error::Internal(e.to_string()))?; - Ok(Verified::new(inner_blob)) + Ok(Verified::new(inner_blob)) + } } } diff --git a/protocol-units/da/m1/light-node-verifier/src/permissioned_signers/mod.rs b/protocol-units/da/m1/light-node-verifier/src/permissioned_signers/mod.rs index b3f83a6f6..9bd0d47c5 100644 --- a/protocol-units/da/m1/light-node-verifier/src/permissioned_signers/mod.rs +++ b/protocol-units/da/m1/light-node-verifier/src/permissioned_signers/mod.rs @@ -18,9 +18,9 @@ use ecdsa::{ SignatureSize, }; use m1_da_light_node_util::inner_blob::InnerBlob; -use std::collections::HashSet; use std::sync::Arc; +/// A verifier of Celestia blobs for permissioned signers #[derive(Clone)] pub struct Verifier where @@ -30,7 +30,9 @@ where AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: ModulusSize, { + /// The Celestia veifier pub celestia: CelestiaVerifier, + /// The verifier for known signers pub known_signers: InKnownSignersVerifier, } @@ -42,11 +44,15 @@ where AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: ModulusSize, { - pub fn new( + pub fn new( celestia_client: Arc, celestia_namespace: Namespace, - known_signers_sec1_bytes: HashSet, - ) -> Self { + known_signers_sec1_bytes: T, + ) -> Self + where + T: IntoIterator, + T::Item: Into, + { Self { celestia: CelestiaVerifier::new(celestia_client, celestia_namespace), known_signers: InKnownSignersVerifier::new(known_signers_sec1_bytes), diff --git a/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs b/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs index ba772b4bd..5453aea58 100644 --- a/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs +++ b/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs @@ -13,7 +13,9 @@ use ecdsa::{ }; use m1_da_light_node_util::inner_blob::InnerBlob; use std::collections::HashSet; +use tracing::info; +/// A verifier that checks the signature of the inner blob. #[derive(Clone)] pub struct Verifier where @@ -79,8 +81,18 @@ where AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: ModulusSize, { - pub fn new(known_signers_sec1_bytes_hex: HashSet) -> Self { - Self { inner_verifier: Verifier::new(), known_signers_sec1_bytes_hex } + pub fn new(known_signers_sec1_bytes_hex: T) -> Self + where + T: IntoIterator, + T::Item: Into, + { + Self { + inner_verifier: Verifier::new(), + known_signers_sec1_bytes_hex: known_signers_sec1_bytes_hex + .into_iter() + .map(Into::into) + .collect(), + } } } @@ -95,7 +107,7 @@ where { async fn verify(&self, blob: InnerBlob, height: u64) -> Result, Error> { let inner_blob = self.inner_verifier.verify(blob, height).await?; - + info!("Verified inner blob"); let signer = inner_blob.inner().signer_hex(); if !self.known_signers_sec1_bytes_hex.contains(&signer) { return Err(Error::Validation("signer not in known signers".to_string())); @@ -104,3 +116,28 @@ where Ok(inner_blob) } } + +#[cfg(test)] +pub mod tests { + /*use ecdsa::SigningKey; + use k256::Secp256k1; + use m1_da_light_node_util::inner_blob::{InnerSignedBlobV1, InnerSignedBlobV1Data, InnerBlob}; + use std::iter::FromIterator; + + #[tokio::test] + async fn test_in_known_signers_verifier() { + let signing_key = SigningKey::::random( + // rand_core + &mut rand::rngs::OsRng, + ); + let blob : InnerBlob = InnerSignedBlobV1::tr + + let inner_blob = create_inner_blob("known_signer"); + let verified = verifier.verify(inner_blob, 0).await.unwrap(); + assert_eq!(verified.inner().signer_hex(), "known_signer"); + + let inner_blob = create_inner_blob("unknown_signer"); + let verified = verifier.verify(inner_blob, 0).await; + assert!(verified.is_err()); + }*/ +} diff --git a/protocol-units/da/m1/light-node/src/v1/passthrough.rs b/protocol-units/da/m1/light-node/src/v1/passthrough.rs index e32cd3d70..39746fcc4 100644 --- a/protocol-units/da/m1/light-node/src/v1/passthrough.rs +++ b/protocol-units/da/m1/light-node/src/v1/passthrough.rs @@ -2,7 +2,7 @@ use m1_da_light_node_util::inner_blob::InnerBlob; use std::fmt::{self, Debug, Formatter}; use std::sync::Arc; use tokio_stream::{Stream, StreamExt}; -use tracing::debug; +use tracing::{error, info}; use celestia_rpc::{BlobClient, Client, HeaderClient}; use celestia_types::{blob::GasPrice, nmt::Namespace, Blob as CelestiaBlob}; @@ -112,8 +112,8 @@ where { /// Creates a new signed blob instance with the provided data. pub fn create_new_celestia_blob(&self, data: Vec) -> Result { - // mark the timestamp as now - let timestamp = chrono::Utc::now().timestamp() as u64; + // mark the timestamp as now in milliseconds + let timestamp = chrono::Utc::now().timestamp_micros() as u64; // sign the blob data and the timestamp let data = InnerSignedBlobV1Data::new(data, timestamp).try_to_sign(&self.signing_key)?; @@ -162,7 +162,7 @@ where let blobs = self.default_client.blob_get_all(height, &[self.celestia_namespace]).await; if let Err(e) = &blobs { - debug!("Error getting blobs: {:?}", e); + error!("Error getting blobs: {:?}", e); } let blobs = blobs.unwrap_or_default(); @@ -171,10 +171,12 @@ where for blob in blobs { match self.verifier.verify(blob, height).await { Ok(verified_blob) => { - verified_blobs.push(verified_blob.into_inner()); + let blob = verified_blob.into_inner(); + info!("Verified blob at height: {} {:?}", height, blob.id()); + verified_blobs.push(blob); } Err(e) => { - debug!("Failed to verify blob: {:?}", e); + error!("Failed to verify blob: {:?}", e,); } } } @@ -244,7 +246,7 @@ where let header = header_res?; let height = header.height().into(); - debug!("Stream got header: {:?}", header.height()); + info!("Stream got header: {:?}", header.height()); // back fetch the blobs if first_flag && (height > start_height) { @@ -253,7 +255,7 @@ where while let Some(blob) = blob_stream.next().await { - debug!("Stream got blob: {:?}", blob); + info!("Stream got blob: {:?}", blob); yield blob?; } @@ -264,7 +266,7 @@ where let blobs = me.get_blobs_at_height(height).await?; for blob in blobs { - debug!("Stream got blob: {:?}", blob); + info!("Stream got blob: {:?}", blob); yield blob; } diff --git a/protocol-units/da/m1/light-node/src/v1/sequencer.rs b/protocol-units/da/m1/light-node/src/v1/sequencer.rs index fca7faf4a..152111c61 100644 --- a/protocol-units/da/m1/light-node/src/v1/sequencer.rs +++ b/protocol-units/da/m1/light-node/src/v1/sequencer.rs @@ -1,3 +1,4 @@ +use block::WrappedBlock; use ecdsa::{ elliptic_curve::{ generic_array::ArrayLength, @@ -160,12 +161,16 @@ where // wrap the blocks in a struct that can be split and compressed // spawn blocking because the compression is blocking and could be slow - let namespace = self.pass_through.celestia_namespace.clone(); + let pass_through = self.pass_through.clone(); let blocks = tokio::task::spawn_blocking(move || { - blocks - .into_iter() - .map(|block| block::WrappedBlock::try_new(block, namespace)) - .collect::, anyhow::Error>>() + let mut wrapped_blocks = Vec::new(); + for block in blocks { + let block_bytes = bcs::to_bytes(&block)?; + let celestia_blob = pass_through.create_new_celestia_blob(block_bytes)?; + let wrapped_block = block::WrappedBlock::new(block, celestia_blob); + wrapped_blocks.push(wrapped_block); + } + Ok::, anyhow::Error>(wrapped_blocks) }) .await??; @@ -472,12 +477,13 @@ where } } -mod block { +pub mod block { use celestia_types::{nmt::Namespace, Blob}; use movement_algs::grouping_heuristic::{binpacking::BinpackingWeighted, splitting::Splitable}; use movement_types::block::Block; + /// A wrapped block that can be used with the binpacking heuristic #[derive(Debug)] pub struct WrappedBlock { pub block: Block, @@ -485,6 +491,12 @@ mod block { } impl WrappedBlock { + /// Create a new wrapped block from a blob and block + pub fn new(block: Block, blob: Blob) -> Self { + Self { block, blob } + } + + /// Create a new wrapped block from a block and a namespace pub fn try_new(block: Block, namespace: Namespace) -> Result { // first serialize the block let block_bytes = bcs::to_bytes(&block)?; diff --git a/protocol-units/da/m1/util/Cargo.toml b/protocol-units/da/m1/util/Cargo.toml index aabe4adf2..efae014f3 100644 --- a/protocol-units/da/m1/util/Cargo.toml +++ b/protocol-units/da/m1/util/Cargo.toml @@ -43,6 +43,7 @@ zstd = { workspace = true } bcs = { workspace = true } ecdsa = { workspace = true, features = [ "signing", "verifying", "der"] } k256 = { workspace = true } +# rand usage conflicts with Aptos. Aptos is on 0.7; ecdsa is on 0.8. rand = { version = "0.8.5" } [dev-dependencies] diff --git a/protocol-units/da/m1/util/src/config/local/m1_da_light_node.rs b/protocol-units/da/m1/util/src/config/local/m1_da_light_node.rs index 891090190..c11bfff46 100644 --- a/protocol-units/da/m1/util/src/config/local/m1_da_light_node.rs +++ b/protocol-units/da/m1/util/src/config/local/m1_da_light_node.rs @@ -12,8 +12,8 @@ use std::collections::HashSet; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct DaSigners { - pub da_signing_private_key_hex: String, - pub da_signers_public_keys_hex: HashSet, + pub private_key_hex: String, + pub public_keys_hex: HashSet, } /// The default da signing private key @@ -28,17 +28,19 @@ pub fn default_da_signing_private_key() -> SigningKey { hex_bytes.as_slice().try_into().expect("Slice with incorrect length"); SigningKey::from_bytes(signing_key_bytes.into()).unwrap() } - Err(_) => SigningKey::random( + Err(std::env::VarError::NotPresent) => SigningKey::random( // rand_core &mut rand::rngs::OsRng, ), + Err(_) => panic!("Invalid DA_SIGNING_PRIVATE_KEY"), } } pub fn default_da_signers_sec1_keys() -> HashSet { match std::env::var("DA_SIGNERS_SEC1_KEYS") { Ok(val) => val.split(',').map(|s| s.to_string()).collect(), - Err(_) => HashSet::new(), + Err(std::env::VarError::NotPresent) => HashSet::new(), + Err(_) => panic!("Invalid DA_SIGNERS_SEC1_KEYS"), } } @@ -55,8 +57,8 @@ pub fn default_da_signers() -> DaSigners { trusted_signers.extend(additional_signers); DaSigners { - da_signing_private_key_hex: hex::encode(da_signer.to_bytes().as_slice()), - da_signers_public_keys_hex: trusted_signers, + private_key_hex: hex::encode(da_signer.to_bytes().as_slice()), + public_keys_hex: trusted_signers, } } diff --git a/protocol-units/da/m1/util/src/config/mod.rs b/protocol-units/da/m1/util/src/config/mod.rs index 49590fb69..48e8cc964 100644 --- a/protocol-units/da/m1/util/src/config/mod.rs +++ b/protocol-units/da/m1/util/src/config/mod.rs @@ -183,30 +183,18 @@ impl Config { /// Gets the da signing key as a string pub fn da_signing_key(&self) -> String { match self { - Config::Local(local) => { - local.m1_da_light_node.da_signers.da_signing_private_key_hex.clone() - } - Config::Arabica(local) => { - local.m1_da_light_node.da_signers.da_signing_private_key_hex.clone() - } - Config::Mocha(local) => { - local.m1_da_light_node.da_signers.da_signing_private_key_hex.clone() - } + Config::Local(local) => local.m1_da_light_node.da_signers.private_key_hex.clone(), + Config::Arabica(local) => local.m1_da_light_node.da_signers.private_key_hex.clone(), + Config::Mocha(local) => local.m1_da_light_node.da_signers.private_key_hex.clone(), } } /// Gets the da signers sec1 keys pub fn da_signers_sec1_keys(&self) -> HashSet { match self { - Config::Local(local) => { - local.m1_da_light_node.da_signers.da_signers_public_keys_hex.clone() - } - Config::Arabica(local) => { - local.m1_da_light_node.da_signers.da_signers_public_keys_hex.clone() - } - Config::Mocha(local) => { - local.m1_da_light_node.da_signers.da_signers_public_keys_hex.clone() - } + Config::Local(local) => local.m1_da_light_node.da_signers.public_keys_hex.clone(), + Config::Arabica(local) => local.m1_da_light_node.da_signers.public_keys_hex.clone(), + Config::Mocha(local) => local.m1_da_light_node.da_signers.public_keys_hex.clone(), } } diff --git a/protocol-units/da/m1/util/src/inner_blob.rs b/protocol-units/da/m1/util/src/inner_blob.rs index e3b1ccd91..7ad1ab580 100644 --- a/protocol-units/da/m1/util/src/inner_blob.rs +++ b/protocol-units/da/m1/util/src/inner_blob.rs @@ -156,9 +156,10 @@ impl InnerBlob { pub mod celestia { - use celestia_types::{nmt::Namespace, Blob as CelestiaBlob}; - use super::InnerBlob; + use anyhow::Context; + use celestia_types::{nmt::Namespace, Blob as CelestiaBlob}; + use tracing::info; impl TryFrom for InnerBlob { type Error = anyhow::Error; @@ -166,11 +167,12 @@ pub mod celestia { // todo: it would be nice to have this be self describing over the compression and serialization format fn try_from(blob: CelestiaBlob) -> Result { // decompress blob.data with zstd - let decompressed = zstd::decode_all(blob.data.as_slice())?; + let decompressed = + zstd::decode_all(blob.data.as_slice()).context("failed to decompress blob")?; - // deserialize the decompressed with bcs - // todo: because this is a simple data structure, bcs might not be the best format - let blob = bcs::from_bytes(decompressed.as_slice())?; + // deserialize the decompressed data with bcs + let blob = + bcs::from_bytes(decompressed.as_slice()).context("failed to deserialize blob")?; Ok(blob) } @@ -178,18 +180,22 @@ pub mod celestia { pub struct CelestiaInnerBlob(pub InnerBlob, pub Namespace); + /// Tries to form a CelestiaBlob from a CelestiaInnerBlob impl TryFrom for CelestiaBlob { type Error = anyhow::Error; fn try_from(inner_blob: CelestiaInnerBlob) -> Result { + info!("converting CelestiaInnerBlob to CelestiaBlob"); + // Extract the inner blob and namespace let CelestiaInnerBlob(inner_blob, namespace) = inner_blob; // Serialize the inner blob with bcs - let serialized_blob = bcs::to_bytes(&inner_blob)?; + let serialized_blob = bcs::to_bytes(&inner_blob).context("failed to serialize blob")?; // Compress the serialized data with zstd - let compressed_blob = zstd::encode_all(serialized_blob.as_slice(), 0)?; + let compressed_blob = zstd::encode_all(serialized_blob.as_slice(), 0) + .context("failed to compress blob")?; // Construct the final CelestiaBlob by assigning the compressed data // and associating it with the provided namespace diff --git a/protocol-units/execution/opt-executor/src/executor/execution.rs b/protocol-units/execution/opt-executor/src/executor/execution.rs index d99f61323..c8d24f105 100644 --- a/protocol-units/execution/opt-executor/src/executor/execution.rs +++ b/protocol-units/execution/opt-executor/src/executor/execution.rs @@ -16,9 +16,10 @@ use aptos_types::{ validator_verifier::{ValidatorConsensusInfo, ValidatorVerifier}, }; use movement_types::block::{BlockCommitment, Commitment, Id}; -use tracing::{debug, info, warn}; +use tracing::{info, warn}; impl Executor { + /// Executes a block and commits it to the storage layer. pub async fn execute_block( &self, block: ExecutableBlock, @@ -64,10 +65,9 @@ impl Executor { }) .await??; - warn!("Block execution compute the following state: {:?}", state_compute); - + info!("Block execution compute the following state: {:?}", state_compute); let version = state_compute.version(); - debug!("Block execution computed the following version: {:?}", version); + info!("Block execution computed the following version: {:?}", version); let (epoch, round) = (block_metadata.epoch(), block_metadata.round()); let ledger_info_with_sigs = self.ledger_info_with_sigs( @@ -366,7 +366,7 @@ mod tests { Transaction::UserTransaction(user_account_creation_tx), Transaction::UserTransaction(mint_tx), ])); - debug!("Number of transactions: {}", transactions.num_transactions()); + info!("Number of transactions: {}", transactions.num_transactions()); let block = ExecutableBlock::new(block_id.clone(), transactions); let block_commitment = executor.execute_block(block).await?; From 860b76b53c04ecbecbbdaccbb7269ffd96caf40e Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Fri, 18 Oct 2024 09:12:49 -0700 Subject: [PATCH 14/15] fix: module separation and comment on verified struct. --- .../light-node-verifier/src/celestia/mod.rs | 74 +------------------ .../src/celestia/pessimistic.rs | 68 +++++++++++++++++ .../da/m1/light-node-verifier/src/lib.rs | 2 +- 3 files changed, 70 insertions(+), 74 deletions(-) create mode 100644 protocol-units/da/m1/light-node-verifier/src/celestia/pessimistic.rs diff --git a/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs b/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs index 5d52be2e6..ce201e7b6 100644 --- a/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs +++ b/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs @@ -29,79 +29,7 @@ impl VerifierOperations for Verifier { } } -pub mod pessimistic { - - use crate::{Error, Verified, VerifierOperations}; - use celestia_rpc::Client; - use celestia_rpc::{BlobClient, HeaderClient}; - use celestia_types::{nmt::Namespace, Blob}; - use m1_da_light_node_util::inner_blob::InnerBlob; - use std::sync::Arc; - - #[derive(Clone)] - pub struct Verifier { - /// The Celestia RPC client - pub client: Arc, - /// The namespace of the Celestia Blob - pub namespace: Namespace, - } - - impl Verifier { - pub fn new(client: Arc, namespace: Namespace) -> Self { - Self { client, namespace } - } - } - - #[tonic::async_trait] - impl VerifierOperations for Verifier { - /// Verifies a Celestia Blob as a Valid InnerBlob - async fn verify(&self, blob: Blob, height: u64) -> Result, Error> { - //@l-monninger: the light node itself does most of the work of verify blobs. The verification under the feature flag below is useful in zero-trust environments. - - blob.validate().map_err(|e| Error::Validation(e.to_string()))?; - - // wait for the header to be at the correct height - self.client - .header_wait_for_height(height) - .await - .map_err(|e| Error::Internal(e.to_string()))?; - - // get the root - let dah = self - .client - .header_get_by_height(height) - .await - .map_err(|e| Error::Internal(e.to_string()))? - .dah; - let root_hash = dah.row_root(0).ok_or(Error::Validation("No root hash".to_string()))?; - - // get the proof - let proofs = self - .client - .blob_get_proof(height, self.namespace.clone(), blob.commitment) - .await - .map_err(|e| Error::Internal(e.to_string()))?; - - // get the leaves - let leaves = blob.to_shares().map_err(|e| Error::Internal(e.to_string()))?; - - // check if included - for proof in proofs.iter() { - proof - .verify_complete_namespace(&root_hash, &leaves, self.namespace.into()) - .map_err(|_e| { - Error::Validation("failed to verify complete namespace".to_string()) - })?; - } - - let inner_blob = - InnerBlob::try_from(blob).map_err(|e| Error::Internal(e.to_string()))?; - - Ok(Verified::new(inner_blob)) - } - } -} - +pub mod pessimistic; #[cfg(all(test, feature = "integration-tests"))] mod tests { use super::*; diff --git a/protocol-units/da/m1/light-node-verifier/src/celestia/pessimistic.rs b/protocol-units/da/m1/light-node-verifier/src/celestia/pessimistic.rs new file mode 100644 index 000000000..1c2817736 --- /dev/null +++ b/protocol-units/da/m1/light-node-verifier/src/celestia/pessimistic.rs @@ -0,0 +1,68 @@ +use crate::{Error, Verified, VerifierOperations}; +use celestia_rpc::Client; +use celestia_rpc::{BlobClient, HeaderClient}; +use celestia_types::{nmt::Namespace, Blob}; +use m1_da_light_node_util::inner_blob::InnerBlob; +use std::sync::Arc; + +#[derive(Clone)] +pub struct Verifier { + /// The Celestia RPC client + pub client: Arc, + /// The namespace of the Celestia Blob + pub namespace: Namespace, +} + +impl Verifier { + pub fn new(client: Arc, namespace: Namespace) -> Self { + Self { client, namespace } + } +} + +#[tonic::async_trait] +impl VerifierOperations for Verifier { + /// Verifies a Celestia Blob as a Valid InnerBlob + async fn verify(&self, blob: Blob, height: u64) -> Result, Error> { + //@l-monninger: the light node itself does most of the work of verify blobs. The verification under the feature flag below is useful in zero-trust environments. + + blob.validate().map_err(|e| Error::Validation(e.to_string()))?; + + // wait for the header to be at the correct height + self.client + .header_wait_for_height(height) + .await + .map_err(|e| Error::Internal(e.to_string()))?; + + // get the root + let dah = self + .client + .header_get_by_height(height) + .await + .map_err(|e| Error::Internal(e.to_string()))? + .dah; + let root_hash = dah.row_root(0).ok_or(Error::Validation("No root hash".to_string()))?; + + // get the proof + let proofs = self + .client + .blob_get_proof(height, self.namespace.clone(), blob.commitment) + .await + .map_err(|e| Error::Internal(e.to_string()))?; + + // get the leaves + let leaves = blob.to_shares().map_err(|e| Error::Internal(e.to_string()))?; + + // check if included + for proof in proofs.iter() { + proof + .verify_complete_namespace(&root_hash, &leaves, self.namespace.into()) + .map_err(|_e| { + Error::Validation("failed to verify complete namespace".to_string()) + })?; + } + + let inner_blob = InnerBlob::try_from(blob).map_err(|e| Error::Internal(e.to_string()))?; + + Ok(Verified::new(inner_blob)) + } +} diff --git a/protocol-units/da/m1/light-node-verifier/src/lib.rs b/protocol-units/da/m1/light-node-verifier/src/lib.rs index c2d7272da..7b4da14ee 100644 --- a/protocol-units/da/m1/light-node-verifier/src/lib.rs +++ b/protocol-units/da/m1/light-node-verifier/src/lib.rs @@ -17,7 +17,7 @@ pub enum Error { /// thiserror for validation and internal errors #[derive(thiserror::Error, Debug)] -/// A verified outcome. Indicates that input of A is verified as valid instance of B, or else invalid instance. +/// A verified outcome. Indicates that input of A (from the trait [VerifierOperations]) is verified as valid instance of B, or else invalid instance. pub struct Verified(B); impl Verified { From 6d69c261bad40e4b20f08dc45756dc1b44616705 Mon Sep 17 00:00:00 2001 From: Liam Monninger Date: Fri, 18 Oct 2024 09:15:25 -0700 Subject: [PATCH 15/15] fix: introduce IntermediateBlobRepresentation. --- .../light-node-verifier/src/celestia/mod.rs | 14 +++--- .../src/celestia/pessimistic.rs | 12 ++--- .../src/permissioned_signers/mod.rs | 6 +-- .../m1/light-node-verifier/src/signed/mod.rs | 28 +++++------ .../da/m1/light-node/src/v1/passthrough.rs | 48 +++++++++++-------- .../m1/util/src/{inner_blob.rs => ir_blob.rs} | 41 ++++++++-------- protocol-units/da/m1/util/src/lib.rs | 2 +- 7 files changed, 80 insertions(+), 71 deletions(-) rename protocol-units/da/m1/util/src/{inner_blob.rs => ir_blob.rs} (78%) diff --git a/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs b/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs index ce201e7b6..423748bc2 100644 --- a/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs +++ b/protocol-units/da/m1/light-node-verifier/src/celestia/mod.rs @@ -1,7 +1,7 @@ use crate::{Error, Verified, VerifierOperations}; use celestia_rpc::Client; use celestia_types::{nmt::Namespace, Blob}; -use m1_da_light_node_util::inner_blob::InnerBlob; +use m1_da_light_node_util::ir_blob::IntermediateBlobRepresentation; use std::sync::Arc; #[derive(Clone)] @@ -19,13 +19,13 @@ impl Verifier { } #[tonic::async_trait] -impl VerifierOperations for Verifier { - /// Verifies a Celestia Blob as a Valid InnerBlob - async fn verify(&self, blob: Blob, _height: u64) -> Result, Error> { - // Only assert that we can indeed get an InnerBlob from the Blob - let inner_blob = InnerBlob::try_from(blob).map_err(|e| Error::Internal(e.to_string()))?; +impl VerifierOperations for Verifier { + /// Verifies a Celestia Blob as a Valid IntermediateBlobRepresentation + async fn verify(&self, blob: Blob, _height: u64) -> Result, Error> { + // Only assert that we can indeed get an IntermediateBlobRepresentation from the Blob + let ir_blob = IntermediateBlobRepresentation::try_from(blob).map_err(|e| Error::Internal(e.to_string()))?; - Ok(Verified::new(inner_blob)) + Ok(Verified::new(ir_blob)) } } diff --git a/protocol-units/da/m1/light-node-verifier/src/celestia/pessimistic.rs b/protocol-units/da/m1/light-node-verifier/src/celestia/pessimistic.rs index 1c2817736..944c700ab 100644 --- a/protocol-units/da/m1/light-node-verifier/src/celestia/pessimistic.rs +++ b/protocol-units/da/m1/light-node-verifier/src/celestia/pessimistic.rs @@ -2,7 +2,7 @@ use crate::{Error, Verified, VerifierOperations}; use celestia_rpc::Client; use celestia_rpc::{BlobClient, HeaderClient}; use celestia_types::{nmt::Namespace, Blob}; -use m1_da_light_node_util::inner_blob::InnerBlob; +use m1_da_light_node_util::ir_blob::IntermediateBlobRepresentation; use std::sync::Arc; #[derive(Clone)] @@ -20,9 +20,9 @@ impl Verifier { } #[tonic::async_trait] -impl VerifierOperations for Verifier { - /// Verifies a Celestia Blob as a Valid InnerBlob - async fn verify(&self, blob: Blob, height: u64) -> Result, Error> { +impl VerifierOperations for Verifier { + /// Verifies a Celestia Blob as a Valid IntermediateBlobRepresentation + async fn verify(&self, blob: Blob, height: u64) -> Result, Error> { //@l-monninger: the light node itself does most of the work of verify blobs. The verification under the feature flag below is useful in zero-trust environments. blob.validate().map_err(|e| Error::Validation(e.to_string()))?; @@ -61,8 +61,8 @@ impl VerifierOperations for Verifier { })?; } - let inner_blob = InnerBlob::try_from(blob).map_err(|e| Error::Internal(e.to_string()))?; + let ir_blob = IntermediateBlobRepresentation::try_from(blob).map_err(|e| Error::Internal(e.to_string()))?; - Ok(Verified::new(inner_blob)) + Ok(Verified::new(ir_blob)) } } diff --git a/protocol-units/da/m1/light-node-verifier/src/permissioned_signers/mod.rs b/protocol-units/da/m1/light-node-verifier/src/permissioned_signers/mod.rs index 9bd0d47c5..d6536148e 100644 --- a/protocol-units/da/m1/light-node-verifier/src/permissioned_signers/mod.rs +++ b/protocol-units/da/m1/light-node-verifier/src/permissioned_signers/mod.rs @@ -17,7 +17,7 @@ use ecdsa::{ hazmat::{DigestPrimitive, SignPrimitive, VerifyPrimitive}, SignatureSize, }; -use m1_da_light_node_util::inner_blob::InnerBlob; +use m1_da_light_node_util::ir_blob::IntermediateBlobRepresentation; use std::sync::Arc; /// A verifier of Celestia blobs for permissioned signers @@ -61,7 +61,7 @@ where } #[tonic::async_trait] -impl VerifierOperations for Verifier +impl VerifierOperations for Verifier where C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, Scalar: Invert>> + SignPrimitive, @@ -69,7 +69,7 @@ where AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: ModulusSize, { - async fn verify(&self, blob: CelestiaBlob, height: u64) -> Result, Error> { + async fn verify(&self, blob: CelestiaBlob, height: u64) -> Result, Error> { let verified_blob = self.celestia.verify(blob, height).await?; self.known_signers.verify(verified_blob.into_inner(), height).await } diff --git a/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs b/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs index 5453aea58..986468a71 100644 --- a/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs +++ b/protocol-units/da/m1/light-node-verifier/src/signed/mod.rs @@ -11,7 +11,7 @@ use ecdsa::{ hazmat::{DigestPrimitive, SignPrimitive, VerifyPrimitive}, SignatureSize, }; -use m1_da_light_node_util::inner_blob::InnerBlob; +use m1_da_light_node_util::ir_blob::IntermediateBlobRepresentation; use std::collections::HashSet; use tracing::info; @@ -42,7 +42,7 @@ where } #[tonic::async_trait] -impl VerifierOperations for Verifier +impl VerifierOperations for Verifier where C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, Scalar: Invert>> + SignPrimitive, @@ -50,7 +50,7 @@ where AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: ModulusSize, { - async fn verify(&self, blob: InnerBlob, _height: u64) -> Result, Error> { + async fn verify(&self, blob: IntermediateBlobRepresentation, _height: u64) -> Result, Error> { blob.verify_signature::().map_err(|e| Error::Validation(e.to_string()))?; Ok(Verified::new(blob)) @@ -97,7 +97,7 @@ where } #[tonic::async_trait] -impl VerifierOperations for InKnownSignersVerifier +impl VerifierOperations for InKnownSignersVerifier where C: PrimeCurve + CurveArithmetic + DigestPrimitive + PointCompression, Scalar: Invert>> + SignPrimitive, @@ -105,15 +105,15 @@ where AffinePoint: FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, FieldBytesSize: ModulusSize, { - async fn verify(&self, blob: InnerBlob, height: u64) -> Result, Error> { - let inner_blob = self.inner_verifier.verify(blob, height).await?; + async fn verify(&self, blob: IntermediateBlobRepresentation, height: u64) -> Result, Error> { + let ir_blob = self.inner_verifier.verify(blob, height).await?; info!("Verified inner blob"); - let signer = inner_blob.inner().signer_hex(); + let signer = ir_blob.inner().signer_hex(); if !self.known_signers_sec1_bytes_hex.contains(&signer) { return Err(Error::Validation("signer not in known signers".to_string())); } - Ok(inner_blob) + Ok(ir_blob) } } @@ -121,7 +121,7 @@ where pub mod tests { /*use ecdsa::SigningKey; use k256::Secp256k1; - use m1_da_light_node_util::inner_blob::{InnerSignedBlobV1, InnerSignedBlobV1Data, InnerBlob}; + use m1_da_light_node_util::ir_blob::{InnerSignedBlobV1, InnerSignedBlobV1Data, IntermediateBlobRepresentation}; use std::iter::FromIterator; #[tokio::test] @@ -130,14 +130,14 @@ pub mod tests { // rand_core &mut rand::rngs::OsRng, ); - let blob : InnerBlob = InnerSignedBlobV1::tr + let blob : IntermediateBlobRepresentation = InnerSignedBlobV1::tr - let inner_blob = create_inner_blob("known_signer"); - let verified = verifier.verify(inner_blob, 0).await.unwrap(); + let ir_blob = create_ir_blob("known_signer"); + let verified = verifier.verify(ir_blob, 0).await.unwrap(); assert_eq!(verified.inner().signer_hex(), "known_signer"); - let inner_blob = create_inner_blob("unknown_signer"); - let verified = verifier.verify(inner_blob, 0).await; + let ir_blob = create_ir_blob("unknown_signer"); + let verified = verifier.verify(ir_blob, 0).await; assert!(verified.is_err()); }*/ } diff --git a/protocol-units/da/m1/light-node/src/v1/passthrough.rs b/protocol-units/da/m1/light-node/src/v1/passthrough.rs index 39746fcc4..cad3829f2 100644 --- a/protocol-units/da/m1/light-node/src/v1/passthrough.rs +++ b/protocol-units/da/m1/light-node/src/v1/passthrough.rs @@ -1,4 +1,4 @@ -use m1_da_light_node_util::inner_blob::InnerBlob; +use m1_da_light_node_util::ir_blob::IntermediateBlobRepresentation; use std::fmt::{self, Debug, Formatter}; use std::sync::Arc; use tokio_stream::{Stream, StreamExt}; @@ -12,7 +12,7 @@ use m1_da_light_node_grpc::light_node_service_server::LightNodeService; use m1_da_light_node_grpc::*; use m1_da_light_node_util::{ config::Config, - inner_blob::{celestia::CelestiaInnerBlob, InnerSignedBlobV1Data}, + ir_blob::{celestia::CelestiaIntermediateBlobRepresentation, InnerSignedBlobV1Data}, }; use m1_da_light_node_verifier::{permissioned_signers::Verifier, VerifierOperations}; @@ -42,7 +42,9 @@ where pub config: Config, pub celestia_namespace: Namespace, pub default_client: Arc, - pub verifier: Arc + Send + Sync>>, + pub verifier: Arc< + Box + Send + Sync>, + >, pub signing_key: SigningKey, } @@ -119,7 +121,8 @@ where let data = InnerSignedBlobV1Data::new(data, timestamp).try_to_sign(&self.signing_key)?; // create the celestia blob - CelestiaInnerBlob(data.into(), self.celestia_namespace.clone()).try_into() + CelestiaIntermediateBlobRepresentation(data.into(), self.celestia_namespace.clone()) + .try_into() } /// Submits a CelestiaBlob to the Celestia node. @@ -155,10 +158,10 @@ where } /// Gets the blobs at a given height. - pub async fn get_inner_blobs_at_height( + pub async fn get_ir_blobs_at_height( &self, height: u64, - ) -> Result, anyhow::Error> { + ) -> Result, anyhow::Error> { let blobs = self.default_client.blob_get_all(height, &[self.celestia_namespace]).await; if let Err(e) = &blobs { @@ -186,10 +189,10 @@ where #[tracing::instrument(target = "movement_timing", level = "debug")] async fn get_blobs_at_height(&self, height: u64) -> Result, anyhow::Error> { - let inner_blobs = self.get_inner_blobs_at_height(height).await?; + let ir_blobs = self.get_ir_blobs_at_height(height).await?; let mut blobs = Vec::new(); - for inner_blob in inner_blobs { - let blob = Self::inner_blob_to_blob(inner_blob, height)?; + for ir_blob in ir_blobs { + let blob = Self::ir_blob_to_blob(ir_blob, height)?; // todo: update logging here blobs.push(blob); } @@ -277,26 +280,29 @@ where as std::pin::Pin> + Send>>) } - pub fn inner_blob_to_blob(inner_blob: InnerBlob, height: u64) -> Result { + pub fn ir_blob_to_blob( + ir_blob: IntermediateBlobRepresentation, + height: u64, + ) -> Result { Ok(Blob { - data: inner_blob.blob().to_vec(), - signature: inner_blob.signature().to_vec(), - timestamp: inner_blob.timestamp(), - signer: inner_blob.signer().to_vec(), - blob_id: inner_blob.id().to_vec(), + data: ir_blob.blob().to_vec(), + signature: ir_blob.signature().to_vec(), + timestamp: ir_blob.timestamp(), + signer: ir_blob.signer().to_vec(), + blob_id: ir_blob.id().to_vec(), height, }) } pub fn celestia_blob_to_blob(blob: CelestiaBlob, height: u64) -> Result { - let inner_blob: InnerBlob = blob.try_into()?; + let ir_blob: IntermediateBlobRepresentation = blob.try_into()?; Ok(Blob { - data: inner_blob.blob().to_vec(), - signature: inner_blob.signature().to_vec(), - timestamp: inner_blob.timestamp(), - signer: inner_blob.signer().to_vec(), - blob_id: inner_blob.id().to_vec(), + data: ir_blob.blob().to_vec(), + signature: ir_blob.signature().to_vec(), + timestamp: ir_blob.timestamp(), + signer: ir_blob.signer().to_vec(), + blob_id: ir_blob.id().to_vec(), height, }) } diff --git a/protocol-units/da/m1/util/src/inner_blob.rs b/protocol-units/da/m1/util/src/ir_blob.rs similarity index 78% rename from protocol-units/da/m1/util/src/inner_blob.rs rename to protocol-units/da/m1/util/src/ir_blob.rs index 7ad1ab580..3226569ed 100644 --- a/protocol-units/da/m1/util/src/inner_blob.rs +++ b/protocol-units/da/m1/util/src/ir_blob.rs @@ -95,38 +95,38 @@ impl InnerSignedBlobV1 { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub enum InnerBlob { +pub enum IntermediateBlobRepresentation { SignedV1(InnerSignedBlobV1), } -impl From for InnerBlob { +impl From for IntermediateBlobRepresentation { fn from(inner: InnerSignedBlobV1) -> Self { - InnerBlob::SignedV1(inner) + IntermediateBlobRepresentation::SignedV1(inner) } } -impl InnerBlob { +impl IntermediateBlobRepresentation { pub fn blob(&self) -> &[u8] { match self { - InnerBlob::SignedV1(inner) => inner.data.blob.as_slice(), + IntermediateBlobRepresentation::SignedV1(inner) => inner.data.blob.as_slice(), } } pub fn signature(&self) -> &[u8] { match self { - InnerBlob::SignedV1(inner) => inner.signature.as_slice(), + IntermediateBlobRepresentation::SignedV1(inner) => inner.signature.as_slice(), } } pub fn timestamp(&self) -> u64 { match self { - InnerBlob::SignedV1(inner) => inner.data.timestamp, + IntermediateBlobRepresentation::SignedV1(inner) => inner.data.timestamp, } } pub fn signer(&self) -> &[u8] { match self { - InnerBlob::SignedV1(inner) => inner.signer.as_slice(), + IntermediateBlobRepresentation::SignedV1(inner) => inner.signer.as_slice(), } } @@ -136,7 +136,7 @@ impl InnerBlob { pub fn id(&self) -> &[u8] { match self { - InnerBlob::SignedV1(inner) => inner.id.as_slice(), + IntermediateBlobRepresentation::SignedV1(inner) => inner.id.as_slice(), } } @@ -149,19 +149,19 @@ impl InnerBlob { FieldBytesSize: ModulusSize, { match self { - InnerBlob::SignedV1(inner) => inner.try_verify::(), + IntermediateBlobRepresentation::SignedV1(inner) => inner.try_verify::(), } } } pub mod celestia { - use super::InnerBlob; + use super::IntermediateBlobRepresentation; use anyhow::Context; use celestia_types::{nmt::Namespace, Blob as CelestiaBlob}; use tracing::info; - impl TryFrom for InnerBlob { + impl TryFrom for IntermediateBlobRepresentation { type Error = anyhow::Error; // todo: it would be nice to have this be self describing over the compression and serialization format @@ -178,20 +178,23 @@ pub mod celestia { } } - pub struct CelestiaInnerBlob(pub InnerBlob, pub Namespace); + pub struct CelestiaIntermediateBlobRepresentation( + pub IntermediateBlobRepresentation, + pub Namespace, + ); - /// Tries to form a CelestiaBlob from a CelestiaInnerBlob - impl TryFrom for CelestiaBlob { + /// Tries to form a CelestiaBlob from a CelestiaIntermediateBlobRepresentation + impl TryFrom for CelestiaBlob { type Error = anyhow::Error; - fn try_from(inner_blob: CelestiaInnerBlob) -> Result { - info!("converting CelestiaInnerBlob to CelestiaBlob"); + fn try_from(ir_blob: CelestiaIntermediateBlobRepresentation) -> Result { + info!("converting CelestiaIntermediateBlobRepresentation to CelestiaBlob"); // Extract the inner blob and namespace - let CelestiaInnerBlob(inner_blob, namespace) = inner_blob; + let CelestiaIntermediateBlobRepresentation(ir_blob, namespace) = ir_blob; // Serialize the inner blob with bcs - let serialized_blob = bcs::to_bytes(&inner_blob).context("failed to serialize blob")?; + let serialized_blob = bcs::to_bytes(&ir_blob).context("failed to serialize blob")?; // Compress the serialized data with zstd let compressed_blob = zstd::encode_all(serialized_blob.as_slice(), 0) diff --git a/protocol-units/da/m1/util/src/lib.rs b/protocol-units/da/m1/util/src/lib.rs index 1ad156f18..32171cd10 100644 --- a/protocol-units/da/m1/util/src/lib.rs +++ b/protocol-units/da/m1/util/src/lib.rs @@ -1,3 +1,3 @@ pub mod config; pub use config::*; -pub mod inner_blob; +pub mod ir_blob;