Skip to content

Commit

Permalink
Better logging of checkpoint and its associated structs (#743)
Browse files Browse the repository at this point in the history
Co-authored-by: Akosh Farkash <[email protected]>
  • Loading branch information
2 people authored and fridrik01 committed May 8, 2024
1 parent 6935ede commit 3440ead
Show file tree
Hide file tree
Showing 15 changed files with 224 additions and 60 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions ipc/api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ serde_tuple = { workspace = true }
strum = { workspace = true }
thiserror = { workspace = true }
ethers = { workspace = true }
tracing = { workspace = true }
serde_with = { workspace = true, features = ["hex"] }

ipc_actors_abis = { workspace = true }
ipc-types = { workspace = true }
Expand Down
22 changes: 22 additions & 0 deletions ipc/api/src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// SPDX-License-Identifier: MIT
use crate::error::Error;
use crate::subnet_id::SubnetID;
use crate::{deserialize_human_readable_str, HumanReadable};
use fvm_shared::address::{Address, Protocol};
use serde::ser::Error as SerializeError;
use serde_tuple::{Deserialize_tuple, Serialize_tuple};
use std::{fmt, str::FromStr};

Expand Down Expand Up @@ -98,6 +100,26 @@ impl FromStr for IPCAddress {
}
}

impl serde_with::SerializeAs<IPCAddress> for HumanReadable {
fn serialize_as<S>(address: &IPCAddress, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
address
.to_string()
.map_err(|e| {
S::Error::custom(format!("cannot convert ipc address to string: {e}"))
})?
.serialize(serializer)
} else {
address.serialize(serializer)
}
}
}

deserialize_human_readable_str!(IPCAddress);

#[cfg(test)]
mod tests {
use crate::address::IPCAddress;
Expand Down
70 changes: 69 additions & 1 deletion ipc/api/src/checkpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use crate::cross::IpcEnvelope;
use crate::subnet_id::SubnetID;
use crate::HumanReadable;
use cid::multihash::Code;
use cid::multihash::MultihashDigest;
use cid::Cid;
Expand All @@ -13,7 +14,9 @@ use fvm_shared::address::Address;
use fvm_shared::clock::ChainEpoch;
use fvm_shared::econ::TokenAmount;
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
use serde::ser::SerializeSeq;
use serde::{Deserialize, Serialize, Serializer};
use serde_with::serde_as;
use std::fmt::{Display, Formatter};

lazy_static! {
Expand Down Expand Up @@ -49,26 +52,31 @@ impl Display for QuorumReachedEvent {
}

/// The collection of items for the bottom up checkpoint submission
#[serde_as]
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
pub struct BottomUpCheckpointBundle {
pub checkpoint: BottomUpCheckpoint,
/// The list of signatures that have signed the checkpoint hash
#[serde_as(as = "Vec<HumanReadable>")]
pub signatures: Vec<Signature>,
/// The list of addresses that have signed the checkpoint hash
pub signatories: Vec<Address>,
}

/// The collection of items for the bottom up checkpoint submission
#[serde_as]
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
pub struct BottomUpMsgBatch {
/// Child subnet ID, for replay protection from other subnets where the exact same validators operate
#[serde_as(as = "HumanReadable")]
pub subnet_id: SubnetID,
/// The height of the child subnet at which the batch was cut
pub block_height: ChainEpoch,
/// Batch of messages to execute
pub msgs: Vec<IpcEnvelope>,
}

#[serde_as]
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
pub struct BottomUpCheckpoint {
/// Child subnet ID, for replay protection from other subnets where the exact same validators operate.
Expand All @@ -78,6 +86,7 @@ pub struct BottomUpCheckpoint {
/// Has to follow the previous checkpoint by checkpoint period.
pub block_height: ChainEpoch,
/// The hash of the block.
#[serde_as(as = "HumanReadable")]
pub block_hash: Vec<u8>,
/// The number of the membership (validator set) which is going to sign the next checkpoint.
/// This one expected to be signed by the validators from the membership reported in the previous checkpoint.
Expand All @@ -86,3 +95,62 @@ pub struct BottomUpCheckpoint {
/// The list of messages for execution
pub msgs: Vec<IpcEnvelope>,
}

pub fn serialize_vec_bytes_to_vec_hex<T: AsRef<[u8]>, S>(
data: &[T],
s: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = s.serialize_seq(Some(data.len()))?;
for element in data {
seq.serialize_element(&hex::encode(element))?;
}
seq.end()
}

#[cfg(test)]
mod tests {
use crate::address::IPCAddress;
use crate::checkpoint::Signature;
use crate::subnet_id::SubnetID;
use crate::HumanReadable;
use fvm_shared::address::Address;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use std::str::FromStr;

#[test]
fn test_serialization_vec_vec_u8() {
#[serde_as]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct T {
#[serde_as(as = "Vec<HumanReadable>")]
d: Vec<Signature>,
#[serde_as(as = "HumanReadable")]
subnet_id: SubnetID,
#[serde_as(as = "HumanReadable")]
ipc_address: IPCAddress,
}

let subnet_id =
SubnetID::from_str("/r31415926/f2xwzbdu7z5sam6hc57xxwkctciuaz7oe5omipwbq").unwrap();
let ipc_address = IPCAddress::new(&subnet_id, &Address::new_id(101)).unwrap();

let t = T {
d: vec![vec![1; 30], vec![2; 30]],
subnet_id,
ipc_address,
};
let s = serde_json::to_string(&t).unwrap();
assert_eq!(
s,
r#"{"d":["010101010101010101010101010101010101010101010101010101010101","020202020202020202020202020202020202020202020202020202020202"],"subnet_id":"/r31415926/f2xwzbdu7z5sam6hc57xxwkctciuaz7oe5omipwbq","ipc_address":"/r31415926/f2xwzbdu7z5sam6hc57xxwkctciuaz7oe5omipwbq:f0101"}"#
);

let r: T = serde_json::from_str(&s).unwrap();

assert_eq!(r, t);
}
}
7 changes: 6 additions & 1 deletion ipc/api/src/cross.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
use crate::address::IPCAddress;
use crate::subnet_id::SubnetID;
use crate::HumanReadable;
use anyhow::anyhow;
use fvm_shared::address::Address;
use fvm_shared::econ::TokenAmount;
use serde::{Deserialize, Serialize};
use serde_tuple::{Deserialize_tuple, Serialize_tuple};
use serde_with::serde_as;

#[serde_as]
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
pub struct IpcEnvelope {
/// Type of message being propagated.
Expand All @@ -18,12 +21,14 @@ pub struct IpcEnvelope {
/// It makes sense to extract from the encoded message
/// all shared fields required by all message, so they
/// can be inspected without having to decode the message.
#[serde_as(as = "HumanReadable")]
pub to: IPCAddress,
/// Value included in the envelope
pub value: TokenAmount,
/// address sending the message
pub from: IPCAddress,
/// abi.encoded message
#[serde_as(as = "HumanReadable")]
pub message: Vec<u8>,
/// outgoing nonce for the envelope.
/// This nonce is set by the gateway when committing the message for propagation
Expand Down Expand Up @@ -104,7 +109,7 @@ impl IpcEnvelope {
}

/// Type of cross-net messages currently supported
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize, strum::Display)]
#[repr(u8)]
pub enum IpcMsgKind {
/// for cross-net messages that move native token, i.e. fund/release.
Expand Down
83 changes: 83 additions & 0 deletions ipc/api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// Copyright 2022-2024 Protocol Labs
// SPDX-License-Identifier: MIT
use ethers::utils::hex;
use fvm_shared::{address::Address, econ::TokenAmount};
use ipc_types::EthAddress;
use serde::de::Error as SerdeError;
use serde::{Deserialize, Serialize, Serializer};
use std::str::FromStr;

pub mod address;
Expand Down Expand Up @@ -31,3 +34,83 @@ pub fn ethers_address_to_fil_address(addr: &ethers::types::Address) -> anyhow::R
let eth_addr = EthAddress::from_str(&raw_addr)?;
Ok(Address::from(eth_addr))
}

/// Marker type for serialising data to/from string
pub struct HumanReadable;

#[macro_export]
macro_rules! serialise_human_readable_str {
($typ:ty) => {
impl serde_with::SerializeAs<$typ> for $crate::HumanReadable {
fn serialize_as<S>(value: &$typ, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if serializer.is_human_readable() {
value.to_string().serialize(serializer)
} else {
value.serialize(serializer)
}
}
}
};
}

#[macro_export]
macro_rules! deserialize_human_readable_str {
($typ:ty) => {
use serde::de::Error as DeserializeError;
use serde::{Deserialize, Serialize};

impl<'de> serde_with::DeserializeAs<'de, $typ> for $crate::HumanReadable {
fn deserialize_as<D>(deserializer: D) -> Result<$typ, D::Error>
where
D: serde::de::Deserializer<'de>,
{
if deserializer.is_human_readable() {
let s = String::deserialize(deserializer)?;
<$typ>::from_str(&s).map_err(|_| {
D::Error::custom(format!(
"cannot parse from str {}",
core::any::type_name::<$typ>()
))
})
} else {
<$typ>::deserialize(deserializer)
}
}
}
};
}

#[macro_export]
macro_rules! as_human_readable_str {
($typ:ty) => {
$crate::serialise_human_readable_str!($typ);
$crate::deserialize_human_readable_str!($typ);
};
}

impl serde_with::SerializeAs<Vec<u8>> for HumanReadable {
fn serialize_as<S>(source: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
hex::encode(source).serialize(serializer)
}
}

impl<'de> serde_with::DeserializeAs<'de, Vec<u8>> for HumanReadable {
fn deserialize_as<D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
if deserializer.is_human_readable() {
let s = String::deserialize(deserializer)?;
Ok(hex::decode(s)
.map_err(|e| D::Error::custom(format!("cannot parse from str {e}")))?)
} else {
Vec::<u8>::deserialize(deserializer)
}
}
}
4 changes: 4 additions & 0 deletions ipc/api/src/subnet_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use std::fmt::Write;
use std::hash::{Hash, Hasher};
use std::str::FromStr;

use crate::as_human_readable_str;

use crate::error::Error;

/// MaxChainID is the maximum chain ID value
Expand All @@ -26,6 +28,8 @@ pub struct SubnetID {
children: Vec<Address>,
}

as_human_readable_str!(SubnetID);

lazy_static! {
pub static ref UNDEF: SubnetID = SubnetID {
root: 0,
Expand Down
12 changes: 6 additions & 6 deletions ipc/cli/src/commands/checkpoint/bottomup_bundles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ impl CommandLineHandler for GetBottomUpBundles {
let subnet = SubnetID::from_str(&arguments.subnet)?;

for h in arguments.from_epoch..=arguments.to_epoch {
let bundle = provider.get_bottom_up_bundle(&subnet, h).await?;
println!(
"checkpoint: {:?}, signatures: {:?}, signatories: {:?}",
bundle.checkpoint, bundle.signatures, bundle.signatories,
);
println!("{bundle:?}");
let Some(bundle) = provider.get_bottom_up_bundle(&subnet, h).await? else {
continue;
};

println!("bottom up checkpoint bundle at height: {}", h);
println!("{}", serde_json::to_string(&bundle)?);
}

Ok(())
Expand Down
35 changes: 0 additions & 35 deletions ipc/cli/src/commands/checkpoint/list_checkpoints.rs

This file was deleted.

Loading

0 comments on commit 3440ead

Please sign in to comment.