use super::*;
use crate::message::signed_message::SignedMessage;
use crate::shim::address::Protocol;
use crate::shim::crypto::{SECP_SIG_LEN, Signature, SignatureType};
use crate::shim::{address::Address, econ::TokenAmount, message::Message};
use ambassador::Delegate;
use get_size2::GetSize;
use serde::{Deserialize, Serialize};
use spire_enum::prelude::delegated_enum;
use std::sync::Arc;
#[delegated_enum]
#[derive(
Clone, Debug, Serialize, Deserialize, Hash, Eq, PartialEq, GetSize, derive_more::From, Delegate,
)]
#[delegate(MessageRead)]
#[serde(untagged)]
pub enum ChainMessage {
Unsigned(Arc<Message>),
Signed(Arc<SignedMessage>),
}
impl From<Message> for ChainMessage {
fn from(msg: Message) -> Self {
Arc::new(msg).into()
}
}
impl From<SignedMessage> for ChainMessage {
fn from(msg: SignedMessage) -> Self {
Arc::new(msg).into()
}
}
impl ChainMessage {
pub fn message(&self) -> &Message {
match self {
Self::Unsigned(m) => m,
Self::Signed(sm) => sm.message(),
}
}
pub fn cid(&self) -> cid::Cid {
delegate_chain_message!(self.cid())
}
pub fn encoded_len(&self) -> usize {
match self {
Self::Unsigned(msg) => msg.encoded_len(),
Self::Signed(msg) => msg.signed_encoded_len(),
}
}
pub fn equal_call(&self, other: &Self) -> bool {
self.message().equal_call(other.message())
}
pub fn for_gas_estimation(msg: Message, from_protocol: Protocol) -> Self {
match from_protocol {
Protocol::Secp256k1 => {
SignedMessage::new_unchecked(msg, Signature::new_secp256k1(vec![0; SECP_SIG_LEN]))
.into()
}
Protocol::Delegated => SignedMessage::new_unchecked(
msg,
Signature::new(SignatureType::Delegated, vec![0; SECP_SIG_LEN]),
)
.into(),
_ => msg.into(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::shim::crypto::BLS_SIG_LEN;
use crate::utils::encoding::calc_encoded_len;
use quickcheck_macros::quickcheck;
#[quickcheck]
fn computing_the_memo_is_invisible(msg: SignedMessage) -> bool {
use std::hash::Hasher as _;
let hash_of = |value: &ChainMessage| {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
std::hash::Hash::hash(value, &mut hasher);
hasher.finish()
};
let cold: ChainMessage = msg.clone().into();
let warm: ChainMessage = msg.into();
let _ = warm.encoded_len();
warm == cold && hash_of(&warm) == hash_of(&cold)
}
#[quickcheck]
fn encoded_len_measures_the_serialized_bytes(msg: Message, sig_bytes: Vec<u8>) -> bool {
let signatures = [
Signature::new_secp256k1(sig_bytes.clone()),
Signature::new(SignatureType::Delegated, sig_bytes),
Signature::new_bls(vec![0; BLS_SIG_LEN]),
];
let unsigned: ChainMessage = msg.clone().into();
let mut candidates = std::iter::once(unsigned).chain(
signatures
.into_iter()
.map(|sig| SignedMessage::new_unchecked(msg.clone(), sig).into()),
);
candidates.all(|chain_msg: ChainMessage| {
chain_msg.encoded_len() == calc_encoded_len(&chain_msg).unwrap()
})
}
fn dummy_msg() -> Message {
Message::builder()
.from(Address::new_id(2))
.to(Address::new_id(1))
.build()
}
#[track_caller]
fn assert_signed_with_zero_sig(
chain_msg: ChainMessage,
sig_type: SignatureType,
msg: &Message,
) {
let ChainMessage::Signed(signed) = chain_msg else {
panic!("expected Signed variant");
};
assert_eq!(signed.signature().signature_type(), sig_type);
assert_eq!(signed.signature().bytes(), &vec![0; SECP_SIG_LEN]);
assert_eq!(signed.message(), msg);
}
#[track_caller]
fn assert_unsigned(chain_msg: ChainMessage, msg: &Message) {
let ChainMessage::Unsigned(m) = chain_msg else {
panic!("expected Unsigned variant");
};
assert_eq!(&*m, msg);
}
#[test]
fn test_for_gas_estimation_secp256k1() {
let msg = dummy_msg();
let chain_msg = ChainMessage::for_gas_estimation(msg.clone(), Protocol::Secp256k1);
assert_signed_with_zero_sig(chain_msg, SignatureType::Secp256k1, &msg);
}
#[test]
fn test_for_gas_estimation_delegated() {
let msg = dummy_msg();
let chain_msg = ChainMessage::for_gas_estimation(msg.clone(), Protocol::Delegated);
assert_signed_with_zero_sig(chain_msg, SignatureType::Delegated, &msg);
}
#[test]
fn test_for_gas_estimation_bls() {
let msg = dummy_msg();
let chain_msg = ChainMessage::for_gas_estimation(msg.clone(), Protocol::BLS);
assert_unsigned(chain_msg, &msg);
}
#[test]
fn test_for_gas_estimation_id() {
let msg = dummy_msg();
let chain_msg = ChainMessage::for_gas_estimation(msg.clone(), Protocol::ID);
assert_unsigned(chain_msg, &msg);
}
#[test]
fn test_for_gas_estimation_actor() {
let msg = dummy_msg();
let chain_msg = ChainMessage::for_gas_estimation(msg.clone(), Protocol::Actor);
assert_unsigned(chain_msg, &msg);
}
}