-
Notifications
You must be signed in to change notification settings - Fork 10
FM-190: Check relayed bottom-up checkpoints #198
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,10 @@ use anyhow::anyhow; | |
use async_trait::async_trait; | ||
|
||
use fendermint_vm_core::chainid::HasChainID; | ||
use fendermint_vm_message::signed::{SignedMessage, SignedMessageError}; | ||
use fendermint_vm_message::signed::{chain_id_bytes, SignedMessage, SignedMessageError}; | ||
use fvm_ipld_encoding::Error as IpldError; | ||
use fvm_shared::{chainid::ChainID, crypto::signature::Signature}; | ||
use serde::Serialize; | ||
|
||
use crate::{ | ||
fvm::{FvmApplyRet, FvmCheckRet, FvmMessage}, | ||
|
@@ -17,6 +20,67 @@ pub struct InvalidSignature(pub String); | |
pub type SignedMessageApplyRet = Result<FvmApplyRet, InvalidSignature>; | ||
pub type SignedMessageCheckRet = Result<FvmCheckRet, InvalidSignature>; | ||
|
||
/// Different kinds of signed messages. | ||
/// | ||
/// This technical construct was introduced so we can have a simple linear interpreter stack | ||
/// where everything flows through all layers, which means to pass something to the FVM we | ||
/// have to go through the signature check. | ||
pub enum VerifiableMessage { | ||
/// A normal message sent by a user. | ||
Signed(SignedMessage), | ||
/// Something we constructed to pass on to the FVM. | ||
Synthetic(SyntheticMessage), | ||
} | ||
|
||
impl VerifiableMessage { | ||
pub fn verify(&self, chain_id: &ChainID) -> Result<(), SignedMessageError> { | ||
match self { | ||
Self::Signed(m) => m.verify(chain_id), | ||
Self::Synthetic(m) => m.verify(chain_id), | ||
} | ||
} | ||
|
||
pub fn into_message(self) -> FvmMessage { | ||
match self { | ||
Self::Signed(m) => m.into_message(), | ||
Self::Synthetic(m) => m.message, | ||
} | ||
} | ||
} | ||
|
||
pub struct SyntheticMessage { | ||
/// The artifical message. | ||
message: FvmMessage, | ||
/// The CID of the original message (assuming here that that's what was signed). | ||
orig_cid: cid::Cid, | ||
/// The signature over the original CID. | ||
signature: Signature, | ||
} | ||
|
||
impl SyntheticMessage { | ||
pub fn new<T: Serialize>( | ||
message: FvmMessage, | ||
orig: &T, | ||
signature: Signature, | ||
) -> Result<Self, IpldError> { | ||
let orig_cid = fendermint_vm_message::cid(orig)?; | ||
Ok(Self { | ||
message, | ||
orig_cid, | ||
signature, | ||
}) | ||
} | ||
|
||
pub fn verify(&self, chain_id: &ChainID) -> Result<(), SignedMessageError> { | ||
let mut data = self.orig_cid.to_bytes(); | ||
data.extend(chain_id_bytes(chain_id).iter()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought the chainID was a prefix instead of a suffix of the message cid. Out of a curiosity, is this Fendermint-specific or Filecoin also does this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have no idea what Filecoin does, I did this in #115 Ethereum seems to play with the signature itself, which seemed so complicated I didn't think we need to replicate it in Fendermint. The |
||
|
||
self.signature | ||
.verify(&data, &self.message.from) | ||
.map_err(SignedMessageError::InvalidSignature) | ||
} | ||
} | ||
|
||
/// Interpreter working on signed messages, validating their signature before sending | ||
/// the unsigned parts on for execution. | ||
#[derive(Clone)] | ||
|
@@ -37,7 +101,7 @@ where | |
S: HasChainID + Send + 'static, | ||
{ | ||
type State = I::State; | ||
type Message = SignedMessage; | ||
type Message = VerifiableMessage; | ||
type BeginOutput = I::BeginOutput; | ||
type DeliverOutput = SignedMessageApplyRet; | ||
type EndOutput = I::EndOutput; | ||
|
@@ -61,7 +125,7 @@ where | |
Ok((state, Err(InvalidSignature(s)))) | ||
} | ||
Ok(()) => { | ||
let (state, ret) = self.inner.deliver(state, msg.message).await?; | ||
let (state, ret) = self.inner.deliver(state, msg.into_message()).await?; | ||
Ok((state, Ok(ret))) | ||
} | ||
} | ||
|
@@ -83,7 +147,7 @@ where | |
S: HasChainID + Send + 'static, | ||
{ | ||
type State = I::State; | ||
type Message = SignedMessage; | ||
type Message = VerifiableMessage; | ||
type Output = SignedMessageCheckRet; | ||
|
||
async fn check( | ||
|
@@ -109,7 +173,10 @@ where | |
Ok((state, Err(InvalidSignature(s)))) | ||
} | ||
Ok(()) => { | ||
let (state, ret) = self.inner.check(state, msg.message, is_recheck).await?; | ||
let (state, ret) = self | ||
.inner | ||
.check(state, msg.into_message(), is_recheck) | ||
.await?; | ||
Ok((state, Ok(ret))) | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
a163497063a16f426f74746f6d55705265736f6c7665a2676d657373616765a3676d657373616765a2676d657373616765a4697375626e65745f6964821b072a749d4e007b3d8256040a0c499bb8f71bfa8a7dc89b21be0050fe01dff50556040a03e75800cbbea43532cc0f8bf76a2f51c23b7d0a666865696768741a550feaaf756e6578745f76616c696461746f725f7365745f69641b01b536ec9cea9bd472626f74746f6d5f75705f6d65737361676573d82a5823001220a73b8d52b312e2e993235783dea2635a70f158f91e5cf1290e1798176ace66416b6365727469666963617465a16a7369676e61747572657381a26976616c696461746f724b00a78594e6f295fcc6f201697369676e61747572654501f9910bec6772656c61796572583103074f77fe3354fbdbc4d846007201386134cca1e7907d85010238c6fb67eceb687ad3017587aa0d9db170c0620089752f6873657175656e63651bf0f091093de904c5697369676e61747572654702bbcafcff3d42 | ||
a163497063a16f426f74746f6d55705265736f6c7665a2676d657373616765a6676d657373616765a2676d657373616765a4697375626e65745f6964821b7c71defc6a386e78834b00f5e3aee78080a0bf9f0156040a761b9e23622b966af8ef2d2d57dcb7856e5e54504a0088d9d5c1a3abc3d351666865696768741ae3e9fc60756e6578745f76616c696461746f725f7365745f69641b11fc1e77ae60cb5072626f74746f6d5f75705f6d65737361676573d82a5823001220469cd40705ee3724f1e8e598b1f50226efa85a5e902e1e1a3a02bceb1205b0656b6365727469666963617465a16a7369676e61747572657383a26976616c696461746f72550224c06e79929caadd2f1f96ff55f701a146058596697369676e617475726542021fa26976616c696461746f725502005c2e87b0e761927a2ab10f0001dcc782ff5fff697369676e6174757265460163b0f80048a26976616c696461746f725502de2d802ebfcc1d7701c5dcdc00018d140f936068697369676e61747572654601d96f8c01b96772656c61796572583103430fcc5b7c76bb455d9a000baccd63f6f4ff99e401f86200211f46783d001cf77ffe8d2c5fe3ff42091a71af1aa15ad26873657175656e63651bd6fb372fc464a1be696761735f6c696d69741b5bf160e4530c90af6b6761735f6665655f636170587d00ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6c31e54ec6bd7d22c46631c1c2f505d00a79229be88bd39d73e5d150b1b8cfaa3ad78d19ea1281c36b6761735f7072656d69756d583100323429ee0653953f35328d8494d19c67967d867390896a46927e881ba2d75b89d436fbd61724c8b013ad3c251aa06d9f697369676e61747572654901005fe047ff3b3b4d |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
Ipc(BottomUpResolve(SignedRelayedMessage { message: RelayedMessage { message: CertifiedMessage { message: BottomUpCheckpoint { subnet_id: SubnetID { root: 516353326254684989, children: [Address { payload: Delegated(DelegatedAddress { namespace: 10, length: 20, buffer: [12, 73, 155, 184, 247, 27, 250, 138, 125, 200, 155, 33, 190, 0, 80, 254, 1, 223, 245, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }) }, Address { payload: Delegated(DelegatedAddress { namespace: 10, length: 20, buffer: [3, 231, 88, 0, 203, 190, 164, 53, 50, 204, 15, 139, 247, 106, 47, 81, 194, 59, 125, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }) }] }, height: 1427106479, next_validator_set_id: 123064954695359444, bottom_up_messages: Cid(QmZbTTpS83pTiqyaUNTWBZotqDcin9p8j434qRVNQX2Z5r) }, certificate: MultiSig { signatures: [ValidatorSignature { validator: Address { payload: ID(17477890364055814823) }, signature: Signature { sig_type: Secp256k1, bytes: [249, 145, 11, 236] } }] } }, relayer: Address { payload: BLS([7, 79, 119, 254, 51, 84, 251, 219, 196, 216, 70, 0, 114, 1, 56, 97, 52, 204, 161, 231, 144, 125, 133, 1, 2, 56, 198, 251, 103, 236, 235, 104, 122, 211, 1, 117, 135, 170, 13, 157, 177, 112, 192, 98, 0, 137, 117, 47]) }, sequence: 17361536032392676549 }, signature: Signature { sig_type: BLS, bytes: [187, 202, 252, 255, 61, 66] } })) | ||
Ipc(BottomUpResolve(SignedRelayedMessage { message: RelayedMessage { message: CertifiedMessage { message: BottomUpCheckpoint { subnet_id: SubnetID { root: 8967193508766576248, children: [Address { payload: ID(11492764036801212917) }, Address { payload: Delegated(DelegatedAddress { namespace: 10, length: 20, buffer: [118, 27, 158, 35, 98, 43, 150, 106, 248, 239, 45, 45, 87, 220, 183, 133, 110, 94, 84, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] }) }, Address { payload: ID(5883686119324085384) }] }, height: 3823762528, next_validator_set_id: 1295944292151380816, bottom_up_messages: Cid(QmT6HwwYSVw1NxZHDFRBVpgXHoP1BZsp8egrUGJLKvBKBz) }, certificate: MultiSig { signatures: [ValidatorSignature { validator: Address { payload: Actor([36, 192, 110, 121, 146, 156, 170, 221, 47, 31, 150, 255, 85, 247, 1, 161, 70, 5, 133, 150]) }, signature: Signature { sig_type: BLS, bytes: [31] } }, ValidatorSignature { validator: Address { payload: Actor([0, 92, 46, 135, 176, 231, 97, 146, 122, 42, 177, 15, 0, 1, 220, 199, 130, 255, 95, 255]) }, signature: Signature { sig_type: Secp256k1, bytes: [99, 176, 248, 0, 72] } }, ValidatorSignature { validator: Address { payload: Actor([222, 45, 128, 46, 191, 204, 29, 119, 1, 197, 220, 220, 0, 1, 141, 20, 15, 147, 96, 104]) }, signature: Signature { sig_type: Secp256k1, bytes: [217, 111, 140, 1, 185] } }] } }, relayer: Address { payload: BLS([67, 15, 204, 91, 124, 118, 187, 69, 93, 154, 0, 11, 172, 205, 99, 246, 244, 255, 153, 228, 1, 248, 98, 0, 33, 31, 70, 120, 61, 0, 28, 247, 127, 254, 141, 44, 95, 227, 255, 66, 9, 26, 113, 175, 26, 161, 90, 210]) }, sequence: 15491036021568872894, gas_limit: 6625183060600852655, gas_fee_cap: TokenAmount(41855804968213567224547853478906320725054875457247406540771499545716837934567817284890561672488119458109166910841919797858872862722356017328064756151166307827869405370407152286801072676024887272960758522802096518222997167129660169469158860048690764605453004790204472697535710106459.730900083939639747), gas_premium: TokenAmount(7727066607978473919987999013363901971034861126626277477615296751585842935993863628627762990636417.915060831698054559) }, signature: Signature { sig_type: Secp256k1, bytes: [0, 95, 224, 71, 255, 59, 59, 77] } })) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to double-check, this is the original signature of the message, right? Should we maybe call it
WrappedMessage
instead ofSyntheticMessage
? (weak opinion weakly held, but synthetic seemed like "artificial" to me :) )There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is an artificial message, that's exactly what I was trying to convey, that it's just a transient technical message to piggy-back on the signature checks and FVM message execution. The construction of the FVM message from the original is synthesising an artificial message that nobody sent.
It's indeed wrapping something but that wouldn't say why; so does the other one after all.
You are correct that
signature
is the signature over the original message, in particular the CID of the original message.We could get rid of this if we used FVM messages, in which case instead of synthesising a message in the
ChainMessageInterpreter
from an IPC specific one, we would parse an FVM message into an IPC message to look for CIDs to resolve.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is fine for now, and now I understand the rationale behind the name. Thanks!