Skip to content

Commit

Permalink
EXP-241 bridge relay v2 p1 (#289)
Browse files Browse the repository at this point in the history
* primitives: copied from other branch

* primitives: fix cargo issues

* db, storage: copied from other branch, some reorganization

* rocksdb-store: copied from other branch, some reorganization

* state: added `OperatorKeyProvider` impl to `OperatorTable`

* rpc/api, sequencer: add RPC stubs

* rocksdb-store: added missing column family names to common list
  • Loading branch information
delbonis authored Sep 18, 2024
1 parent cc59743 commit 98ac2eb
Show file tree
Hide file tree
Showing 22 changed files with 807 additions and 4 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

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

16 changes: 16 additions & 0 deletions crates/db/src/interfaces/bridge_relay.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use alpen_express_primitives::relay::types::BridgeMessage;

use crate::DbResult;

/// Interface for storing and retrieving bridge messages.
#[cfg_attr(feature = "mocks", automock)]
pub trait BridgeMessageDb {
/// Stores a bridge message
fn write_msg(&self, id: u128, msg: BridgeMessage) -> DbResult<()>;

/// Deletes messages by their UNIX epoch.
fn delete_msgs_before_timestamp(&self, msg_ids: u128) -> DbResult<()>;

/// Retrieves messages by their scope.
fn get_msgs_by_scope(&self, scope: &[u8]) -> DbResult<Vec<BridgeMessage>>;
}
1 change: 1 addition & 0 deletions crates/db/src/interfaces/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod bridge_relay;
1 change: 1 addition & 0 deletions crates/db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
pub mod bridge;
pub mod database;
pub mod errors;
pub mod interfaces;
pub mod traits;
pub mod types;

Expand Down
9 changes: 8 additions & 1 deletion crates/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,18 @@ bitcoin = { workspace = true }
borsh = { workspace = true }
digest = { workspace = true }
hex = { workspace = true }
rand = { workspace = true, optional = true }
reth-primitives = { workspace = true }
secp256k1 = { workspace = true, optional = true }
serde = { workspace = true }
serde_json = { workspace = true }
sha2 = { workspace = true }
thiserror = { workspace = true }

[dev-dependencies]
rand = { workspace = true }
alpen-test-utils = { workspace = true }

[features]
default = ["std", "rand"]
std = ["dep:secp256k1"]
rand = ["std", "dep:rand"]
3 changes: 2 additions & 1 deletion crates/primitives/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
//! Collection of generic internal data types that are used widely.
// TODO import hash types, routines
// TODO import address types
// TODO import generic account types

Expand All @@ -11,6 +10,8 @@ pub mod errors;
pub mod evm_exec;
pub mod hash;
pub mod l1;
pub mod operator;
pub mod params;
pub mod prelude;
pub mod relay;
pub mod utils;
30 changes: 30 additions & 0 deletions crates/primitives/src/operator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use super::bridge::OperatorIdx;
use crate::prelude::Buf32;

/// Some type that can provide operator keys.
pub trait OperatorKeyProvider {
/// Returns the operator's signing pubkey, if it exists in the table.
fn get_operator_signing_pk(&self, idx: OperatorIdx) -> Option<Buf32>;
}

/// Stub key provider that can be used for testing.
pub struct StubOpKeyProv {
expected_idx: OperatorIdx,
pk: Buf32,
}

impl StubOpKeyProv {
pub fn new(expected_idx: OperatorIdx, pk: Buf32) -> Self {
Self { expected_idx, pk }
}
}

impl OperatorKeyProvider for StubOpKeyProv {
fn get_operator_signing_pk(&self, idx: OperatorIdx) -> Option<Buf32> {
if idx == self.expected_idx {
Some(self.pk)
} else {
None
}
}
}
2 changes: 2 additions & 0 deletions crates/primitives/src/relay/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod types;
pub mod util;
192 changes: 192 additions & 0 deletions crates/primitives/src/relay/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#![allow(dead_code)]

use core::fmt;

use arbitrary::Arbitrary;
use borsh::{io, BorshDeserialize, BorshSerialize};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};

use crate::buf::{Buf32, Buf64};

/// Message container used to direct payloads depending on the context between parties.
#[derive(Clone, Debug, Eq, PartialEq, BorshDeserialize, BorshSerialize, Deserialize, Serialize)]
pub struct BridgeMessage {
/// Operator ID
pub(crate) source_id: u32,

/// Schnorr signature of the message
pub(crate) sig: Buf64,

/// Purpose of the message.
pub(crate) scope: Vec<u8>,

/// serialized message
pub(crate) payload: Vec<u8>,
}

impl<'a> Arbitrary<'a> for BridgeMessage {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let source_id = u32::arbitrary(u)?;
let sig = Buf64::arbitrary(u)?;
let scope = borsh::to_vec(&Scope::Misc).unwrap();
let mut payload = vec![0; 20];
u.fill_buffer(&mut payload)?;

Ok(Self {
source_id,
sig,
scope,
payload,
})
}
}

impl BridgeMessage {
/// Source ID.
pub fn source_id(&self) -> u32 {
self.source_id
}

/// Signature.
pub fn signature(&self) -> &Buf64 {
&self.sig
}

/// Raw scope.
pub fn scope(&self) -> &[u8] {
&self.scope
}

/// Raw payload
pub fn payload(&self) -> &[u8] {
&self.payload
}

/// Tries to parse the scope buf as a typed scope.
pub fn try_parse_scope(&self) -> Option<Scope> {
Scope::try_from_slice(self.scope()).ok()
}

/// Computes a msg ID based on the .
pub fn compute_id(&self) -> BridgeMsgId {
// No signature because it might be malleable and it doesn't have any
// useful data in it we'd want to inspect.
let mut digest = Sha256::default();
digest.update(&self.source_id.to_be_bytes());
digest.update(&(self.scope.len() as u64).to_be_bytes());
digest.update(&self.scope);
digest.update(&(self.payload.len() as u64).to_be_bytes());
digest.update(&self.payload);

let hash: [u8; 32] = digest.finalize().into();
BridgeMsgId::from(Buf32::from(hash))
}
}

/// Scope of the [`BridgeMessage`]
#[derive(Clone, Debug, Eq, PartialEq, BorshDeserialize, BorshSerialize, Deserialize, Serialize)]
pub enum Scope {
/// Used for debugging purposes.
Misc,

/// Deposit Signature with Outpoint.
// TODO make this contain the outpoint
V0DepositSig(u32),

/// Withdrawal Signature with Deposit index.
V0WithdrawalSig(u32),
}

impl Scope {
/// Tries to parse the scope from a slice.
pub fn try_from_slice(raw: &[u8]) -> Result<Scope, io::Error> {
Ok(borsh::from_slice(raw)?)
}
}

/// ID of a [``BridgeMessage``] computed from the sender ID, scope, and payload.
#[derive(
Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Arbitrary, BorshDeserialize, BorshSerialize,
)]
pub struct BridgeMsgId(Buf32);

impl BridgeMsgId {
pub fn inner(&self) -> &Buf32 {
&self.0
}

pub fn into_inner(self) -> Buf32 {
self.0
}
}

impl From<Buf32> for BridgeMsgId {
fn from(value: Buf32) -> Self {
Self(value)
}
}

impl From<BridgeMsgId> for Buf32 {
fn from(value: BridgeMsgId) -> Self {
value.0
}
}

impl AsRef<[u8; 32]> for BridgeMsgId {
fn as_ref(&self) -> &[u8; 32] {
self.0.as_ref()
}
}

impl fmt::Debug for BridgeMsgId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}

impl fmt::Display for BridgeMsgId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}

#[derive(Copy, Clone, Deserialize, Debug)]
pub struct RelayerConfig {
/// Time we check for purgeable messages.
pub refresh_interval: u64,

/// Age after which we'll start to re-relay a message if we recv it again.
pub stale_duration: u64,

/// Relay misc messages that don't check signatures.
pub relay_misc: bool,
}

#[cfg(test)]
mod tests {
use alpen_test_utils::ArbitraryGenerator;

use super::{BridgeMessage, Scope};
use crate::buf::Buf64;

fn get_arb_bridge_msg() -> BridgeMessage {
let msg: BridgeMessage = ArbitraryGenerator::new().generate();
msg
}

fn make_bridge_msg() -> BridgeMessage {
BridgeMessage {
source_id: 1,
sig: Buf64::from([0; 64]),
scope: borsh::to_vec(&Scope::Misc).unwrap(),
payload: vec![1, 2, 3, 4, 5],
}
}

#[test]
fn test_get_scope_raw() {
let msg = make_bridge_msg();
assert_eq!(msg.scope(), vec![0])
}
}
Loading

0 comments on commit 98ac2eb

Please sign in to comment.