Skip to content

Commit

Permalink
feat: add timestamp to all event protos (#4722)
Browse files Browse the repository at this point in the history
this makes a non-consensus-breaking change, adding a timestamp field to
the EventBlockRoot proto.
This will be used to keep track of the timestamp of the block for use in
pindexer.

I tested this by running a local devnet with the changes to core. Pd
runs fine and the timestamps are correctly propagated to the raw events
db.

prerequisite for #4675

- [x] If this code contains consensus-breaking changes, I have added the
"consensus-breaking" label. Otherwise, I declare my belief that there
are not consensus-breaking changes, for the following reason:

- only adds a field
- adds this field to an Event proto
  • Loading branch information
vacekj authored and cronokirby committed Jul 22, 2024
1 parent 5cf137e commit 741eed2
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 6 deletions.
12 changes: 9 additions & 3 deletions crates/core/component/sct/src/component/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,23 +72,28 @@ pub trait SctManager: StateWrite {
epoch_root: Option<epoch::Root>,
) {
let sct_anchor = sct.root();
let block_timestamp = self
.get_current_block_timestamp()
.await
.map(|t| t.unix_timestamp())
.unwrap_or(0);

// Write the anchor as a key, so we can check claimed anchors...
self.put_proto(state_key::tree::anchor_lookup(sct_anchor), height);
// ... and as a value, so we can check SCT consistency.
// TODO: can we move this out to NV storage?
self.put(state_key::tree::anchor_by_height(height), sct_anchor);

self.record_proto(event::anchor(height, sct_anchor));
self.record_proto(event::block_root(height, block_root));
self.record_proto(event::anchor(height, sct_anchor, block_timestamp));
self.record_proto(event::block_root(height, block_root, block_timestamp));
// Only record an epoch root event if we are ending the epoch.
if let Some(epoch_root) = epoch_root {
let index = self
.get_current_epoch()
.await
.expect("epoch must be set")
.index;
self.record_proto(event::epoch_root(index, epoch_root));
self.record_proto(event::epoch_root(index, epoch_root, block_timestamp));
}

self.write_sct_cache(sct);
Expand Down Expand Up @@ -238,4 +243,5 @@ pub trait VerificationExt: StateRead {
Ok(())
}
}

impl<T: StateRead + ?Sized> VerificationExt for T {}
19 changes: 16 additions & 3 deletions crates/core/component/sct/src/event.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,41 @@
use pbjson_types::Timestamp;
use penumbra_tct as tct;
use tct::builder::{block, epoch};

use penumbra_proto::core::component::sct::v1 as pb;

use crate::CommitmentSource;

pub fn anchor(height: u64, anchor: tct::Root) -> pb::EventAnchor {
pub fn anchor(height: u64, anchor: tct::Root, timestamp: i64) -> pb::EventAnchor {
pb::EventAnchor {
height,
anchor: Some(anchor.into()),
timestamp: Some(Timestamp {
seconds: timestamp,
nanos: 0,
}),
}
}

pub fn block_root(height: u64, root: block::Root) -> pb::EventBlockRoot {
pub fn block_root(height: u64, root: block::Root, timestamp: i64) -> pb::EventBlockRoot {
pb::EventBlockRoot {
height,
root: Some(root.into()),
timestamp: Some(Timestamp {
seconds: timestamp,
nanos: 0,
}),
}
}

pub fn epoch_root(index: u64, root: epoch::Root) -> pb::EventEpochRoot {
pub fn epoch_root(index: u64, root: epoch::Root, timestamp: i64) -> pb::EventEpochRoot {
pb::EventEpochRoot {
index,
root: Some(root.into()),
timestamp: Some(Timestamp {
seconds: timestamp,
nanos: 0,
}),
}
}

Expand Down
6 changes: 6 additions & 0 deletions crates/proto/src/gen/penumbra.core.component.sct.v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ pub struct EventAnchor {
>,
#[prost(uint64, tag = "2")]
pub height: u64,
#[prost(message, optional, tag = "3")]
pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>,
}
impl ::prost::Name for EventAnchor {
const NAME: &'static str = "EventAnchor";
Expand All @@ -247,6 +249,8 @@ pub struct EventEpochRoot {
>,
#[prost(uint64, tag = "2")]
pub index: u64,
#[prost(message, optional, tag = "3")]
pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>,
}
impl ::prost::Name for EventEpochRoot {
const NAME: &'static str = "EventEpochRoot";
Expand All @@ -265,6 +269,8 @@ pub struct EventBlockRoot {
>,
#[prost(uint64, tag = "2")]
pub height: u64,
#[prost(message, optional, tag = "3")]
pub timestamp: ::core::option::Option<::pbjson_types::Timestamp>,
}
impl ::prost::Name for EventBlockRoot {
const NAME: &'static str = "EventBlockRoot";
Expand Down
51 changes: 51 additions & 0 deletions crates/proto/src/gen/penumbra.core.component.sct.v1.serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1147,6 +1147,9 @@ impl serde::Serialize for EventAnchor {
if self.height != 0 {
len += 1;
}
if self.timestamp.is_some() {
len += 1;
}
let mut struct_ser = serializer.serialize_struct("penumbra.core.component.sct.v1.EventAnchor", len)?;
if let Some(v) = self.anchor.as_ref() {
struct_ser.serialize_field("anchor", v)?;
Expand All @@ -1155,6 +1158,9 @@ impl serde::Serialize for EventAnchor {
#[allow(clippy::needless_borrow)]
struct_ser.serialize_field("height", ToString::to_string(&self.height).as_str())?;
}
if let Some(v) = self.timestamp.as_ref() {
struct_ser.serialize_field("timestamp", v)?;
}
struct_ser.end()
}
}
Expand All @@ -1167,12 +1173,14 @@ impl<'de> serde::Deserialize<'de> for EventAnchor {
const FIELDS: &[&str] = &[
"anchor",
"height",
"timestamp",
];

#[allow(clippy::enum_variant_names)]
enum GeneratedField {
Anchor,
Height,
Timestamp,
__SkipField__,
}
impl<'de> serde::Deserialize<'de> for GeneratedField {
Expand All @@ -1197,6 +1205,7 @@ impl<'de> serde::Deserialize<'de> for EventAnchor {
match value {
"anchor" => Ok(GeneratedField::Anchor),
"height" => Ok(GeneratedField::Height),
"timestamp" => Ok(GeneratedField::Timestamp),
_ => Ok(GeneratedField::__SkipField__),
}
}
Expand All @@ -1218,6 +1227,7 @@ impl<'de> serde::Deserialize<'de> for EventAnchor {
{
let mut anchor__ = None;
let mut height__ = None;
let mut timestamp__ = None;
while let Some(k) = map_.next_key()? {
match k {
GeneratedField::Anchor => {
Expand All @@ -1234,6 +1244,12 @@ impl<'de> serde::Deserialize<'de> for EventAnchor {
Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0)
;
}
GeneratedField::Timestamp => {
if timestamp__.is_some() {
return Err(serde::de::Error::duplicate_field("timestamp"));
}
timestamp__ = map_.next_value()?;
}
GeneratedField::__SkipField__ => {
let _ = map_.next_value::<serde::de::IgnoredAny>()?;
}
Expand All @@ -1242,6 +1258,7 @@ impl<'de> serde::Deserialize<'de> for EventAnchor {
Ok(EventAnchor {
anchor: anchor__,
height: height__.unwrap_or_default(),
timestamp: timestamp__,
})
}
}
Expand All @@ -1262,6 +1279,9 @@ impl serde::Serialize for EventBlockRoot {
if self.height != 0 {
len += 1;
}
if self.timestamp.is_some() {
len += 1;
}
let mut struct_ser = serializer.serialize_struct("penumbra.core.component.sct.v1.EventBlockRoot", len)?;
if let Some(v) = self.root.as_ref() {
struct_ser.serialize_field("root", v)?;
Expand All @@ -1270,6 +1290,9 @@ impl serde::Serialize for EventBlockRoot {
#[allow(clippy::needless_borrow)]
struct_ser.serialize_field("height", ToString::to_string(&self.height).as_str())?;
}
if let Some(v) = self.timestamp.as_ref() {
struct_ser.serialize_field("timestamp", v)?;
}
struct_ser.end()
}
}
Expand All @@ -1282,12 +1305,14 @@ impl<'de> serde::Deserialize<'de> for EventBlockRoot {
const FIELDS: &[&str] = &[
"root",
"height",
"timestamp",
];

#[allow(clippy::enum_variant_names)]
enum GeneratedField {
Root,
Height,
Timestamp,
__SkipField__,
}
impl<'de> serde::Deserialize<'de> for GeneratedField {
Expand All @@ -1312,6 +1337,7 @@ impl<'de> serde::Deserialize<'de> for EventBlockRoot {
match value {
"root" => Ok(GeneratedField::Root),
"height" => Ok(GeneratedField::Height),
"timestamp" => Ok(GeneratedField::Timestamp),
_ => Ok(GeneratedField::__SkipField__),
}
}
Expand All @@ -1333,6 +1359,7 @@ impl<'de> serde::Deserialize<'de> for EventBlockRoot {
{
let mut root__ = None;
let mut height__ = None;
let mut timestamp__ = None;
while let Some(k) = map_.next_key()? {
match k {
GeneratedField::Root => {
Expand All @@ -1349,6 +1376,12 @@ impl<'de> serde::Deserialize<'de> for EventBlockRoot {
Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0)
;
}
GeneratedField::Timestamp => {
if timestamp__.is_some() {
return Err(serde::de::Error::duplicate_field("timestamp"));
}
timestamp__ = map_.next_value()?;
}
GeneratedField::__SkipField__ => {
let _ = map_.next_value::<serde::de::IgnoredAny>()?;
}
Expand All @@ -1357,6 +1390,7 @@ impl<'de> serde::Deserialize<'de> for EventBlockRoot {
Ok(EventBlockRoot {
root: root__,
height: height__.unwrap_or_default(),
timestamp: timestamp__,
})
}
}
Expand Down Expand Up @@ -1509,6 +1543,9 @@ impl serde::Serialize for EventEpochRoot {
if self.index != 0 {
len += 1;
}
if self.timestamp.is_some() {
len += 1;
}
let mut struct_ser = serializer.serialize_struct("penumbra.core.component.sct.v1.EventEpochRoot", len)?;
if let Some(v) = self.root.as_ref() {
struct_ser.serialize_field("root", v)?;
Expand All @@ -1517,6 +1554,9 @@ impl serde::Serialize for EventEpochRoot {
#[allow(clippy::needless_borrow)]
struct_ser.serialize_field("index", ToString::to_string(&self.index).as_str())?;
}
if let Some(v) = self.timestamp.as_ref() {
struct_ser.serialize_field("timestamp", v)?;
}
struct_ser.end()
}
}
Expand All @@ -1529,12 +1569,14 @@ impl<'de> serde::Deserialize<'de> for EventEpochRoot {
const FIELDS: &[&str] = &[
"root",
"index",
"timestamp",
];

#[allow(clippy::enum_variant_names)]
enum GeneratedField {
Root,
Index,
Timestamp,
__SkipField__,
}
impl<'de> serde::Deserialize<'de> for GeneratedField {
Expand All @@ -1559,6 +1601,7 @@ impl<'de> serde::Deserialize<'de> for EventEpochRoot {
match value {
"root" => Ok(GeneratedField::Root),
"index" => Ok(GeneratedField::Index),
"timestamp" => Ok(GeneratedField::Timestamp),
_ => Ok(GeneratedField::__SkipField__),
}
}
Expand All @@ -1580,6 +1623,7 @@ impl<'de> serde::Deserialize<'de> for EventEpochRoot {
{
let mut root__ = None;
let mut index__ = None;
let mut timestamp__ = None;
while let Some(k) = map_.next_key()? {
match k {
GeneratedField::Root => {
Expand All @@ -1596,6 +1640,12 @@ impl<'de> serde::Deserialize<'de> for EventEpochRoot {
Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0)
;
}
GeneratedField::Timestamp => {
if timestamp__.is_some() {
return Err(serde::de::Error::duplicate_field("timestamp"));
}
timestamp__ = map_.next_value()?;
}
GeneratedField::__SkipField__ => {
let _ = map_.next_value::<serde::de::IgnoredAny>()?;
}
Expand All @@ -1604,6 +1654,7 @@ impl<'de> serde::Deserialize<'de> for EventEpochRoot {
Ok(EventEpochRoot {
root: root__,
index: index__.unwrap_or_default(),
timestamp: timestamp__,
})
}
}
Expand Down
4 changes: 4 additions & 0 deletions proto/penumbra/penumbra/core/component/sct/v1/sct.proto
Original file line number Diff line number Diff line change
Expand Up @@ -89,19 +89,23 @@ message EventCommitment {
message EventAnchor {
crypto.tct.v1.MerkleRoot anchor = 1;
uint64 height = 2;
google.protobuf.Timestamp timestamp = 3;
}

// Event recording an SCT epoch root.
message EventEpochRoot {
crypto.tct.v1.MerkleRoot root = 1;
uint64 index = 2;
google.protobuf.Timestamp timestamp = 3;
}

// Event recording an SCT block root.
message EventBlockRoot {
crypto.tct.v1.MerkleRoot root = 1;
uint64 height = 2;
google.protobuf.Timestamp timestamp = 3;
}

message EpochByHeightRequest {
uint64 height = 1;
}
Expand Down

0 comments on commit 741eed2

Please sign in to comment.