@@ -3,14 +3,16 @@ use std::fmt::Debug;
33use std:: sync:: Arc ;
44
55use async_trait:: async_trait;
6+ use ethers:: utils:: keccak256;
67use futures_util:: future:: join_all;
7- use tracing:: info;
8+ use serde:: Serialize ;
9+ use tracing:: { info, warn} ;
810use url:: Url ;
911
1012use hyperlane_base:: settings:: ChainConnectionConf ;
11- use hyperlane_base:: CoreMetrics ;
13+ use hyperlane_base:: { CheckpointSyncer , CoreMetrics } ;
1214use hyperlane_core:: rpc_clients:: call_and_retry_indefinitely;
13- use hyperlane_core:: { HyperlaneDomain , MerkleTreeHook , ReorgPeriod } ;
15+ use hyperlane_core:: { CheckpointAtBlock , HyperlaneDomain , MerkleTreeHook , ReorgPeriod , H256 } ;
1416use hyperlane_ethereum:: RpcConnectionConf ;
1517
1618use crate :: settings:: ValidatorSettings ;
@@ -26,9 +28,49 @@ pub struct LatestCheckpointReorgReporter {
2628 merkle_tree_hooks : HashMap < Url , Arc < dyn MerkleTreeHook > > ,
2729}
2830
31+ #[ derive( Serialize ) ]
32+ struct ReorgReportRpcResponse {
33+ rpc_url_hash : H256 ,
34+ rpc_host_hash : H256 ,
35+ height : Option < u64 > ,
36+ reorg_period : Option < ReorgPeriod > ,
37+ merkle_root_index : u32 ,
38+ merkle_root_hash : H256 ,
39+ timestamp : String ,
40+ }
41+
42+ impl ReorgReportRpcResponse {
43+ fn new (
44+ url : Url ,
45+ latest_checkpoint : CheckpointAtBlock ,
46+ height : Option < u64 > ,
47+ reorg_period : Option < ReorgPeriod > ,
48+ ) -> Self {
49+ ReorgReportRpcResponse {
50+ rpc_host_hash : H256 :: from_slice ( & keccak256 ( url. host_str ( ) . unwrap_or ( "" ) . as_bytes ( ) ) ) ,
51+ rpc_url_hash : H256 :: from_slice ( & keccak256 ( url. as_str ( ) . as_bytes ( ) ) ) ,
52+ height,
53+ reorg_period,
54+ merkle_root_hash : latest_checkpoint. checkpoint . root ,
55+ merkle_root_index : latest_checkpoint. checkpoint . index ,
56+ timestamp : chrono:: Utc :: now ( ) . to_rfc3339 ( ) ,
57+ }
58+ }
59+ }
60+
2961#[ async_trait]
3062impl ReorgReporter for LatestCheckpointReorgReporter {
3163 async fn report_at_block ( & self , height : u64 ) {
64+ self . report_at_block ( height) . await ;
65+ }
66+
67+ async fn report_with_reorg_period ( & self , reorg_period : & ReorgPeriod ) {
68+ self . report_with_reorg_period ( reorg_period) . await ;
69+ }
70+ }
71+
72+ impl LatestCheckpointReorgReporter {
73+ async fn report_at_block ( & self , height : u64 ) -> Vec < ReorgReportRpcResponse > {
3274 info ! ( ?height, "Reporting latest checkpoint on reorg" ) ;
3375 let mut futures = vec ! [ ] ;
3476 for ( url, merkle_tree_hook) in & self . merkle_tree_hooks {
@@ -42,15 +84,19 @@ impl ReorgReporter for LatestCheckpointReorgReporter {
4284 . await ;
4385
4486 info ! ( url = ?url. clone( ) , ?height, ?latest_checkpoint, "Report latest checkpoint on reorg" ) ;
87+ ReorgReportRpcResponse :: new ( url. clone ( ) , latest_checkpoint, Some ( height) , None )
4588 } ;
4689
4790 futures. push ( future) ;
4891 }
4992
50- join_all ( futures) . await ;
93+ join_all ( futures) . await
5194 }
5295
53- async fn report_with_reorg_period ( & self , reorg_period : & ReorgPeriod ) {
96+ async fn report_with_reorg_period (
97+ & self ,
98+ reorg_period : & ReorgPeriod ,
99+ ) -> Vec < ReorgReportRpcResponse > {
54100 info ! ( ?reorg_period, "Reporting latest checkpoint on reorg" ) ;
55101 let mut futures = vec ! [ ] ;
56102 for ( url, merkle_tree_hook) in & self . merkle_tree_hooks {
@@ -63,12 +109,18 @@ impl ReorgReporter for LatestCheckpointReorgReporter {
63109 . await ;
64110
65111 info ! ( url = ?url. clone( ) , ?reorg_period, ?latest_checkpoint, "Report latest checkpoint on reorg" ) ;
112+ ReorgReportRpcResponse :: new (
113+ url. clone ( ) ,
114+ latest_checkpoint,
115+ None ,
116+ Some ( reorg_period. clone ( ) ) ,
117+ )
66118 } ;
67119
68120 futures. push ( future) ;
69121 }
70122
71- join_all ( futures) . await ;
123+ join_all ( futures) . await
72124 }
73125}
74126
@@ -142,13 +194,11 @@ impl LatestCheckpointReorgReporter {
142194 Starknet ( updated_conn)
143195 } )
144196 }
145- Radix ( conn) => {
146- Self :: map_urls_to_connections ( conn. gateway . clone ( ) , conn, |conn, url| {
147- let mut updated_conn = conn. clone ( ) ;
148- updated_conn. gateway = vec ! [ url] ;
149- Radix ( updated_conn)
150- } )
151- }
197+ Radix ( conn) => Self :: map_urls_to_connections ( conn. core . clone ( ) , conn, |conn, url| {
198+ let mut updated_conn = conn. clone ( ) ;
199+ updated_conn. core = vec ! [ url] ;
200+ Radix ( updated_conn)
201+ } ) ,
152202 } ;
153203
154204 chain_conn_confs
@@ -180,3 +230,62 @@ impl LatestCheckpointReorgReporter {
180230 . collect ( )
181231 }
182232}
233+
234+ #[ derive( Debug ) ]
235+ pub struct LatestCheckpointReorgReporterWithStorageWriter {
236+ /// `LatestCheckpointReorgReporterWithStorageWriter` is an extension to
237+ /// `LatestCheckpointReorgReporter`
238+ latest_checkpoint_reorg_reporter : LatestCheckpointReorgReporter ,
239+
240+ /// Currently, the storage abstraction is tied to the checkpoint syncer, which is why
241+ /// it is used here.
242+ storage_writer : Arc < dyn CheckpointSyncer > ,
243+ }
244+
245+ #[ async_trait]
246+ impl ReorgReporter for LatestCheckpointReorgReporterWithStorageWriter {
247+ async fn report_at_block ( & self , height : u64 ) {
248+ let logs = self
249+ . latest_checkpoint_reorg_reporter
250+ . report_at_block ( height)
251+ . await ;
252+ self . submit_to_storage_writer ( & logs) . await ;
253+ }
254+
255+ async fn report_with_reorg_period ( & self , reorg_period : & ReorgPeriod ) {
256+ let logs = self
257+ . latest_checkpoint_reorg_reporter
258+ . report_with_reorg_period ( reorg_period)
259+ . await ;
260+ self . submit_to_storage_writer ( & logs) . await ;
261+ }
262+ }
263+
264+ impl LatestCheckpointReorgReporterWithStorageWriter {
265+ pub ( crate ) async fn from_settings_with_storage_writer (
266+ settings : & ValidatorSettings ,
267+ metrics : & CoreMetrics ,
268+ storage_writer : Arc < dyn CheckpointSyncer > ,
269+ ) -> eyre:: Result < Self > {
270+ Ok ( LatestCheckpointReorgReporterWithStorageWriter {
271+ latest_checkpoint_reorg_reporter : LatestCheckpointReorgReporter :: from_settings (
272+ settings, metrics,
273+ )
274+ . await ?,
275+ storage_writer,
276+ } )
277+ }
278+
279+ async fn submit_to_storage_writer ( & self , storage_logs_entries : & Vec < ReorgReportRpcResponse > ) {
280+ let json_string = serde_json:: to_string_pretty ( storage_logs_entries) . unwrap_or_else ( |e| {
281+ warn ! ( "Error serializing json: {}" , e) ;
282+ String :: from ( "{\" error\" : \" Error formatting the string\" }" )
283+ } ) ;
284+ self . storage_writer
285+ . write_reorg_rpc_responses ( json_string)
286+ . await
287+ . unwrap_or_else ( |e| {
288+ warn ! ( "Error writing checkpoint syncer to reorg log: {}" , e) ;
289+ } ) ;
290+ }
291+ }
0 commit comments