Skip to content

Commit

Permalink
feat(primitives)!: improve usability of BridgeMessage
Browse files Browse the repository at this point in the history
  • Loading branch information
storopoli committed Sep 25, 2024
1 parent 4a6830d commit c7e34c0
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 20 deletions.
14 changes: 14 additions & 0 deletions crates/primitives/src/buf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use arbitrary::Arbitrary;
use bitcoin::{hashes::Hash, secp256k1::schnorr, BlockHash, Txid};
use borsh::{BorshDeserialize, BorshSerialize};
use reth_primitives::alloy_primitives::FixedBytes;
use secp256k1::SecretKey;
use serde::{Deserialize, Serialize};

// 20-byte buf
Expand Down Expand Up @@ -93,6 +94,19 @@ impl From<Buf32> for Txid {
}
}

impl From<SecretKey> for Buf32 {
fn from(value: SecretKey) -> Self {
let bytes: [u8; 32] = value.secret_bytes();
bytes.into()
}
}

impl From<Buf32> for SecretKey {
fn from(value: Buf32) -> Self {
SecretKey::from_slice(value.0.as_slice()).expect("could not convert Buf32 into SecretKey")
}
}

impl AsRef<[u8; 32]> for Buf32 {
fn as_ref(&self) -> &[u8; 32] {
&self.0
Expand Down
121 changes: 112 additions & 9 deletions crates/primitives/src/relay/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,34 @@ use borsh::{io, BorshDeserialize, BorshSerialize};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};

use crate::buf::{Buf32, Buf64};
use crate::{
bridge::OperatorIdx,
buf::{Buf32, Buf64},
prelude::BitcoinTxid,
};

/// Message container used to direct payloads depending on the context between parties.
///
/// # Caution
///
/// Users should not construct a [`BridgeMessage`] directly,
/// instead construct a [`MessageSigner`](super::util::MessageSigner) by
/// calling [`MessageSigner::new`](super::util::MessageSigner::new),
/// followed by [`sign_raw`](super::util::MessageSigner::sign_raw)
/// or [`sign_scope`](super::util::MessageSigner::sign_scope)
/// depending on the use case.
#[derive(Clone, Debug, Eq, PartialEq, BorshDeserialize, BorshSerialize, Deserialize, Serialize)]
pub struct BridgeMessage {
/// Operator ID
pub(crate) source_id: u32,
pub(crate) source_id: OperatorIdx,

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

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

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

Expand Down Expand Up @@ -84,6 +97,52 @@ impl BridgeMessage {
}
}

impl TryFrom<Vec<u8>> for BridgeMessage {
type Error = io::Error;

fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
let result = borsh::from_slice(value.as_ref())?;
Ok(result)
}
}

impl TryInto<Vec<u8>> for BridgeMessage {
type Error = io::Error;

fn try_into(self) -> Result<Vec<u8>, Self::Error> {
borsh::to_vec(&self)
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Serialization error"))
}
}

impl TryFrom<&[u8]> for BridgeMessage {
type Error = io::Error;

fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
let result = borsh::from_slice(value)?;
Ok(result)
}
}

impl TryFrom<Box<[u8]>> for BridgeMessage {
type Error = io::Error;

fn try_from(value: Box<[u8]>) -> Result<Self, Self::Error> {
let result = borsh::from_slice(value.as_ref())?;
Ok(result)
}
}

impl TryInto<Box<[u8]>> for BridgeMessage {
type Error = io::Error;

fn try_into(self) -> Result<Box<[u8]>, Self::Error> {
let serialized_vec = borsh::to_vec(&self)
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Serialization error"))?;
Ok(serialized_vec.into_boxed_slice()) // Convert Vec<u8> to Box<[u8]>
}
}

/// Scope of the [`BridgeMessage`]
#[derive(Clone, Debug, Eq, PartialEq, BorshDeserialize, BorshSerialize, Deserialize, Serialize)]
pub enum Scope {
Expand All @@ -92,20 +151,64 @@ pub enum Scope {

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

/// Deposit MuSig public nonce
V0DepositPubNonce(BitcoinTxid),

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

/// Withdrawal MuSig public nonce
V0WithdrawalPubNonce(BitcoinTxid),
}

impl TryFrom<Vec<u8>> for Scope {
type Error = io::Error;

fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
let result = borsh::from_slice(value.as_ref())?;
Ok(result)
}
}

impl TryInto<Vec<u8>> for Scope {
type Error = io::Error;

fn try_into(self) -> Result<Vec<u8>, Self::Error> {
borsh::to_vec(&self)
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Serialization error"))
}
}

impl Scope {
/// Tries to parse the scope from a slice.
pub fn try_from_slice(raw: &[u8]) -> Result<Scope, io::Error> {
let result = borsh::from_slice(raw)?;
impl TryFrom<&[u8]> for Scope {
type Error = io::Error;

fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
let result = borsh::from_slice(value)?;
Ok(result)
}
}

impl TryFrom<Box<[u8]>> for Scope {
type Error = io::Error;

fn try_from(value: Box<[u8]>) -> Result<Self, Self::Error> {
let result = borsh::from_slice(value.as_ref())?;
Ok(result)
}
}

impl TryInto<Box<[u8]>> for Scope {
type Error = io::Error;

fn try_into(self) -> Result<Box<[u8]>, Self::Error> {
let serialized_vec = borsh::to_vec(&self)
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Serialization error"))?;
Ok(serialized_vec.into_boxed_slice()) // Convert Vec<u8> to Box<[u8]>
}
}

/// ID of a [``BridgeMessage``] computed from the sender ID, scope, and payload.
#[derive(
Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Arbitrary, BorshDeserialize, BorshSerialize,
Expand Down
39 changes: 28 additions & 11 deletions crates/primitives/src/relay/util.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::sync::Arc;
use std::{io, sync::Arc};

use borsh::BorshSerialize;
use rand::rngs::OsRng;
use secp256k1::{schnorr::Signature, All, Keypair, Message, Secp256k1, SecretKey, XOnlyPublicKey};
use thiserror::Error;
Expand All @@ -10,7 +11,7 @@ use crate::{
operator::OperatorKeyProvider,
};

/// Contains data needed to construct bridge messages.
/// Contains data needed to construct [`BridgeMessage`]s.
#[derive(Clone)]
pub struct MessageSigner {
operator_idx: u32,
Expand All @@ -19,6 +20,13 @@ pub struct MessageSigner {
}

impl MessageSigner {
/// Creates a new [`MessageSigner`].
///
/// # Notes
///
/// In order to get a [`BridgeMessage`], call [`sign_raw`](Self::sign_raw)
/// or [`sign_scope`](Self::sign_scope) on this [`MessageSigner`]
/// depending on the use case.
pub fn new(operator_idx: u32, msg_signing_sk: Buf32, secp: Arc<Secp256k1<All>>) -> Self {
Self {
operator_idx,
Expand All @@ -38,25 +46,34 @@ impl MessageSigner {
}

/// Signs a message using a raw scope and payload.
pub fn sign_raw(&self, scope: Vec<u8>, payload: Vec<u8>) -> BridgeMessage {
pub fn sign_raw<T: BorshSerialize>(
&self,
scope: &T,
payload: &T,
) -> Result<BridgeMessage, io::Error> {
let mut tmp_m = BridgeMessage {
source_id: self.operator_idx,
sig: Buf64::zero(),
scope,
payload,
scope: borsh::to_vec(scope)?,
payload: borsh::to_vec(payload)?,
};

let id: Buf32 = tmp_m.compute_id().into();
let sig = sign_msg_hash(&self.msg_signing_sk, &id, self.secp.as_ref());
tmp_m.sig = sig;

tmp_m
Ok(tmp_m)
}

/// Signs a message with some particular typed scope.
pub fn sign_scope(&self, scope: &Scope, payload: Vec<u8>) -> BridgeMessage {
let raw_scope = borsh::to_vec(scope).unwrap();
self.sign_raw(raw_scope, payload)
pub fn sign_scope<T: BorshSerialize>(
&self,
scope: &Scope,
payload: &T,
) -> Result<BridgeMessage, io::Error> {
let raw_scope = borsh::to_vec(scope)?;
let payload: Vec<u8> = borsh::to_vec(&payload)?;
self.sign_raw(&raw_scope, &payload)
}
}

Expand Down Expand Up @@ -152,7 +169,7 @@ mod tests {
let pk = signer.get_pubkey();

let payload = vec![1, 2, 3, 4, 5];
let m = signer.sign_scope(&Scope::Misc, payload);
let m = signer.sign_scope(&Scope::Misc, &payload).unwrap();

let stub_prov = StubOpKeyProv::new(idx, pk);
assert!(verify_bridge_msg_sig(&m, &stub_prov).is_ok());
Expand All @@ -168,7 +185,7 @@ mod tests {
let pk = signer.get_pubkey();

let payload = vec![1, 2, 3, 4, 5];
let mut m = signer.sign_scope(&Scope::Misc, payload);
let mut m = signer.sign_scope(&Scope::Misc, &payload).unwrap();
m.sig = Buf64::zero();

let stub_prov = StubOpKeyProv::new(idx, pk);
Expand Down
24 changes: 24 additions & 0 deletions crates/rpc/types/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,30 @@ impl HexBytes {
}
}

impl From<Vec<u8>> for HexBytes {
fn from(value: Vec<u8>) -> Self {
HexBytes(value)
}
}

impl From<&[u8]> for HexBytes {
fn from(value: &[u8]) -> Self {
HexBytes(value.to_vec())
}
}

impl From<Box<[u8]>> for HexBytes {
fn from(value: Box<[u8]>) -> Self {
HexBytes(value.into_vec())
}
}

impl From<HexBytes> for Vec<u8> {
fn from(value: HexBytes) -> Self {
value.0
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HexBytes32(#[serde(with = "hex::serde")] pub [u8; 32]);

Expand Down

0 comments on commit c7e34c0

Please sign in to comment.