use super::{MessageRead, MessageReadWrite};
use crate::eth::EthChainId;
use crate::shim::message::MethodNum;
use crate::shim::{
address::Address,
crypto::{Signature, SignatureType},
econ::TokenAmount,
message::Message,
};
use fvm_ipld_encoding::tuple::*;
use fvm_ipld_encoding::{RawBytes, to_vec};
use get_size2::GetSize;
#[cfg_attr(test, derive(derive_quickcheck_arbitrary::Arbitrary))]
#[derive(PartialEq, Clone, Debug, Serialize_tuple, Deserialize_tuple, Hash, Eq, GetSize)]
pub struct SignedMessage {
pub message: Message,
pub signature: Signature,
}
impl SignedMessage {
pub fn new_from_parts(message: Message, signature: Signature) -> anyhow::Result<SignedMessage> {
signature.verify(&message.cid().to_bytes(), &message.from())?;
Ok(SignedMessage { message, signature })
}
pub fn new_unchecked(message: Message, signature: Signature) -> SignedMessage {
SignedMessage { message, signature }
}
pub fn message(&self) -> &Message {
&self.message
}
pub fn signature(&self) -> &Signature {
&self.signature
}
pub fn into_message(self) -> Message {
self.message
}
pub fn is_bls(&self) -> bool {
self.signature.signature_type() == SignatureType::Bls
}
pub fn is_secp256k1(&self) -> bool {
self.signature.signature_type() == SignatureType::Secp256k1
}
pub fn is_delegated(&self) -> bool {
self.signature.signature_type() == SignatureType::Delegated
}
pub fn verify(&self, eth_chain_id: EthChainId) -> anyhow::Result<()> {
self.signature
.authenticate_msg(eth_chain_id, self, &self.from())
}
pub fn cid(&self) -> cid::Cid {
if self.is_bls() {
self.message.cid()
} else {
use crate::utils::cid::CidCborExt;
cid::Cid::from_cbor_blake2b256(self).expect("message serialization is infallible")
}
}
pub fn chain_length(&self) -> anyhow::Result<usize> {
let serialized = match self.signature.signature_type() {
SignatureType::Bls => {
to_vec(&self.message)?
}
SignatureType::Secp256k1 | SignatureType::Delegated => {
to_vec(&self)?
}
};
Ok(serialized.len())
}
#[cfg(test)]
pub fn mock_bls_signed_message(message: Message) -> SignedMessage {
let signature = Signature::new_bls(vec![0; crate::shim::crypto::BLS_SIG_LEN]);
SignedMessage::new_unchecked(message, signature)
}
}
impl MessageRead for SignedMessage {
fn from(&self) -> Address {
self.message.from()
}
fn to(&self) -> Address {
self.message.to()
}
fn sequence(&self) -> u64 {
self.message.sequence()
}
fn value(&self) -> TokenAmount {
self.message.value()
}
fn method_num(&self) -> MethodNum {
self.message.method_num
}
fn params(&self) -> &RawBytes {
self.message.params()
}
fn gas_limit(&self) -> u64 {
self.message.gas_limit()
}
fn required_funds(&self) -> TokenAmount {
self.message.required_funds()
}
fn gas_fee_cap(&self) -> TokenAmount {
self.message.gas_fee_cap()
}
fn gas_premium(&self) -> TokenAmount {
self.message.gas_premium()
}
}
impl MessageReadWrite for SignedMessage {
fn set_gas_limit(&mut self, token_amount: u64) {
self.message.set_gas_limit(token_amount);
}
fn set_sequence(&mut self, new_sequence: u64) {
self.message.set_sequence(new_sequence);
}
fn set_gas_fee_cap(&mut self, cap: TokenAmount) {
self.message.set_gas_fee_cap(cap)
}
fn set_gas_premium(&mut self, prem: TokenAmount) {
self.message.set_gas_premium(prem)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::shim::{address::Address, crypto::Signature, message::Message};
use fvm_ipld_encoding::to_vec;
#[test]
fn test_chain_length() {
let message = Message {
to: Address::new_id(1),
from: Address::new_id(2),
..Message::default()
};
let bls_sig = Signature::new_bls(vec![0; 96]);
let signed_message_bls = SignedMessage::new_unchecked(message.clone(), bls_sig);
assert_eq!(
signed_message_bls.chain_length().unwrap(),
to_vec(&message).unwrap().len()
);
let secp_sig = Signature::new_secp256k1(vec![0; 65]);
let signed_message_secp = SignedMessage::new_unchecked(message.clone(), secp_sig);
assert_eq!(
signed_message_secp.chain_length().unwrap(),
to_vec(&signed_message_secp).unwrap().len()
);
}
}