use crate::{deserialize_sig_bytes_or_string, Address};
use derive_builder::Builder;
use ethers_core::types::Signature as ElectrumSignature;
use schemars::JsonSchema;
use secp256k1::{
ecdsa::{RecoverableSignature as Signature, RecoveryId},
Message, PublicKey,
};
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;
fn serialize_as_hex<S>(bytes: &[u8; 32], serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let hex_string = hex::encode(bytes);
serializer.serialize_str(&format!("0x{}", hex_string))
}
#[derive(
Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
pub struct RecoverableSignature {
#[serde(
serialize_with = "serialize_as_hex",
deserialize_with = "deserialize_sig_bytes_or_string"
)]
r: [u8; 32],
#[serde(
serialize_with = "serialize_as_hex",
deserialize_with = "deserialize_sig_bytes_or_string"
)]
s: [u8; 32],
v: i32,
}
impl RecoverableSignature {
pub fn recover(&self, message_bytes: &[u8]) -> Result<Address, secp256k1::Error> {
let r_hex = hex::encode(self.get_r());
let s_hex = hex::encode(self.get_s());
tracing::warn!("attempting to recover from r: {}, s: {}", r_hex, s_hex);
tracing::warn!("MessageBytes: {}", hex::encode(message_bytes));
if self.v >= 27 && self.v <= 28 {
tracing::warn!("v is >= 27, <= 28, using electrum signature");
let esig = ElectrumSignature {
r: ethers_core::abi::ethereum_types::U256::from(self.get_r()),
s: ethers_core::abi::ethereum_types::U256::from(self.get_s()),
v: self.get_v() as u64,
};
let eaddr = esig
.recover(message_bytes)
.map_err(|_| secp256k1::Error::InvalidSignature)?;
tracing::warn!("{:?}", eaddr);
Ok(Address::from(eaddr))
} else {
let message = Message::from_digest_slice(message_bytes)?;
let secp = secp256k1::Secp256k1::new();
let recoverable_sig = Signature::try_from(self)?;
let pk = secp.recover_ecdsa(&message, &recoverable_sig)?;
Ok(Address::from(pk))
}
}
pub fn verify(&self, message: &[u8], pk: PublicKey) -> Result<(), secp256k1::Error> {
tracing::info!("attemting to recover signature");
let sig = Signature::try_from(self)?.to_standard();
tracing::info!("reconstructing message: {}", hex::encode(message));
let msg = Message::from_digest_slice(message)?;
tracing::info!(
"verifying message: {} with pubkey: {}",
hex::encode(message),
pk.to_string()
);
sig.verify(&msg, &pk)
}
pub fn to_vec(&self) -> Vec<u8> {
let mut bytes: Vec<u8> = Vec::new();
bytes.extend(self.r);
bytes.extend(self.s);
bytes.extend(self.v.to_le_bytes());
bytes
}
pub fn serialize(&self) -> Result<Vec<u8>, serde_json::Error> {
Ok(serde_json::to_string(&self)?.as_bytes().to_vec())
}
pub fn deserialize(bytes: &[u8]) -> Result<Self, serde_json::Error> {
serde_json::from_str(&String::from_utf8_lossy(bytes))
}
pub fn get_r(&self) -> [u8; 32] {
self.r
}
pub fn get_s(&self) -> [u8; 32] {
self.s
}
pub fn get_v(&self) -> i32 {
self.v
}
pub fn v_into_bytes(&self) -> [u8; 4] {
self.v.to_le_bytes()
}
}
impl From<Signature> for RecoverableSignature {
fn from(value: Signature) -> Self {
let (v, rs) = value.serialize_compact();
let mut r = [0u8; 32];
let mut s = [0u8; 32];
r.copy_from_slice(&rs[0..32]);
s.copy_from_slice(&rs[32..]);
Self {
r,
s,
v: v.to_i32(),
}
}
}
impl TryFrom<RecoverableSignature> for Signature {
type Error = secp256k1::Error;
fn try_from(value: RecoverableSignature) -> Result<Signature, Self::Error> {
let mut data = Vec::new();
let mut recovery_id = value.get_v();
if (0..=3).contains(&recovery_id) {
tracing::info!("using r: {:?} and s: {:?} with recovery_id: {:?} to convert to secp256k1 signature", &value.get_r(), value.get_s(), value.get_v());
data.extend_from_slice(&value.get_r());
data.extend_from_slice(&value.get_s());
Signature::from_compact(&data, RecoveryId::from_i32(recovery_id)?)
} else if (27..=30).contains(&recovery_id) {
recovery_id -= 27;
tracing::info!("using r: {:?} and s: {:?} with recovery_id: {:?} to convert to secp256k1 signature", &value.get_r(), value.get_s(), value.get_v());
data.extend_from_slice(&value.get_r());
data.extend_from_slice(&value.get_s());
Signature::from_compact(&data, RecoveryId::from_i32(recovery_id)?)
} else if (35..=38).contains(&recovery_id) {
recovery_id -= 35;
tracing::info!("using r: {:?} and s: {:?} with recovery_id: {:?} to convert to secp256k1 signature", &value.get_r(), value.get_s(), value.get_v());
data.extend_from_slice(&value.get_r());
data.extend_from_slice(&value.get_s());
Signature::from_compact(&data, RecoveryId::from_i32(recovery_id)?)
} else {
tracing::info!("using r: {:?} and s: {:?} with recovery_id: {:?} to convert to secp256k1 signature", &value.get_r(), value.get_s(), value.get_v());
data.extend_from_slice(&value.get_r());
data.extend_from_slice(&value.get_s());
Signature::from_compact(&data, RecoveryId::from_i32(recovery_id)?)
}
}
}
impl TryFrom<&RecoverableSignature> for Signature {
type Error = secp256k1::Error;
fn try_from(value: &RecoverableSignature) -> Result<Signature, Self::Error> {
let mut data = Vec::new();
let mut recovery_id = value.get_v();
if (0..=3).contains(&recovery_id) {
tracing::warn!("using r: {:?} and s: {:?} with recovery_id: {:?} to convert to secp256k1 signature", &value.get_r(), value.get_s(), value.get_v());
data.extend_from_slice(&value.get_r());
data.extend_from_slice(&value.get_s());
Signature::from_compact(&data, RecoveryId::from_i32(recovery_id)?)
} else if (27..=30).contains(&recovery_id) {
recovery_id -= 27;
tracing::warn!("using r: {:?} and s: {:?} with recovery_id: {:?} to convert to secp256k1 signature", &value.get_r(), value.get_s(), value.get_v());
data.extend_from_slice(&value.get_r());
data.extend_from_slice(&value.get_s());
Signature::from_compact(&data, RecoveryId::from_i32(recovery_id)?)
} else if (35..=38).contains(&recovery_id) {
recovery_id -= 35;
tracing::warn!("using r: {:?} and s: {:?} with recovery_id: {:?} to convert to secp256k1 signature", &value.get_r(), value.get_s(), value.get_v());
data.extend_from_slice(&value.get_r());
data.extend_from_slice(&value.get_s());
Signature::from_compact(&data, RecoveryId::from_i32(recovery_id)?)
} else {
tracing::info!("using r: {:?} and s: {:?} with recovery_id: {:?} to convert to secp256k1 signature", &value.get_r(), value.get_s(), value.get_v());
data.extend_from_slice(&value.get_r());
data.extend_from_slice(&value.get_s());
Signature::from_compact(&data, RecoveryId::from_i32(recovery_id)?)
}
}
}
#[derive(
Builder, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, PartialOrd, Ord, Hash,
)]
pub struct Certificate {
quorum_id: [u8; 20],
quorum_sigs: BTreeSet<RecoverableSignature>,
}
impl Certificate {
pub fn to_vec(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend(self.quorum_id);
let sigs: &BTreeSet<RecoverableSignature> = &self.quorum_sigs;
bytes.extend(
&sigs
.iter()
.map(|sig| sig.to_vec())
.collect::<Vec<Vec<u8>>>()
.into_iter()
.flatten()
.collect::<Vec<u8>>(),
);
bytes
}
pub fn serialize(&self) -> Result<Vec<u8>, serde_json::Error> {
Ok(serde_json::to_string(&self)?.as_bytes().to_vec())
}
pub fn deserialize(bytes: &[u8]) -> Result<Self, serde_json::Error> {
serde_json::from_str(&String::from_utf8_lossy(bytes))
}
}