1
1
use {
2
- crate :: api:: ChainId ,
2
+ crate :: {
3
+ api:: ChainId ,
4
+ keeper:: keeper_metrics:: { AccountLabel , KeeperMetrics } ,
5
+ } ,
3
6
anyhow:: { ensure, Result } ,
4
7
ethers:: types:: Address ,
5
8
sha3:: { Digest , Keccak256 } ,
9
+ std:: sync:: Arc ,
6
10
tokio:: task:: spawn_blocking,
7
11
} ;
8
12
@@ -127,11 +131,22 @@ impl PebbleHashChain {
127
131
/// which requires tracking multiple hash chains here.
128
132
pub struct HashChainState {
129
133
// The sequence number where the hash chain starts. Must be stored in sorted order.
130
- pub offsets : Vec < usize > ,
131
- pub hash_chains : Vec < PebbleHashChain > ,
134
+ offsets : Vec < usize > ,
135
+ hash_chains : Vec < PebbleHashChain > ,
132
136
}
133
137
134
138
impl HashChainState {
139
+ pub fn new ( offsets : Vec < usize > , hash_chains : Vec < PebbleHashChain > ) -> Result < HashChainState > {
140
+ if offsets. len ( ) != hash_chains. len ( ) {
141
+ return Err ( anyhow:: anyhow!(
142
+ "Offsets and hash chains must have the same length."
143
+ ) ) ;
144
+ }
145
+ Ok ( HashChainState {
146
+ offsets,
147
+ hash_chains,
148
+ } )
149
+ }
135
150
pub fn from_chain_at_offset ( offset : usize , chain : PebbleHashChain ) -> HashChainState {
136
151
HashChainState {
137
152
offsets : vec ! [ offset] ,
@@ -152,12 +167,54 @@ impl HashChainState {
152
167
}
153
168
}
154
169
170
+ pub struct MonitoredHashChainState {
171
+ hash_chain_state : Arc < HashChainState > ,
172
+ metrics : Arc < KeeperMetrics > ,
173
+ account_label : AccountLabel ,
174
+ }
175
+ impl MonitoredHashChainState {
176
+ pub fn new (
177
+ hash_chain_state : Arc < HashChainState > ,
178
+ metrics : Arc < KeeperMetrics > ,
179
+ chain_id : ChainId ,
180
+ provider_address : Address ,
181
+ ) -> Self {
182
+ Self {
183
+ hash_chain_state,
184
+ metrics,
185
+ account_label : AccountLabel {
186
+ chain_id,
187
+ address : provider_address. to_string ( ) ,
188
+ } ,
189
+ }
190
+ }
191
+
192
+ pub fn reveal ( & self , sequence_number : u64 ) -> Result < [ u8 ; 32 ] > {
193
+ let res = self . hash_chain_state . reveal ( sequence_number) ;
194
+ if res. is_ok ( ) {
195
+ let metric = self
196
+ . metrics
197
+ . highest_revealed_sequence_number
198
+ . get_or_create ( & self . account_label ) ;
199
+ if metric. get ( ) < sequence_number as i64 {
200
+ metric. set ( sequence_number as i64 ) ;
201
+ }
202
+ }
203
+ res
204
+ }
205
+ }
206
+
155
207
#[ cfg( test) ]
156
208
mod test {
157
209
use {
158
- crate :: state:: { HashChainState , PebbleHashChain } ,
210
+ crate :: {
211
+ keeper:: keeper_metrics:: { AccountLabel , KeeperMetrics } ,
212
+ state:: { HashChainState , MonitoredHashChainState , PebbleHashChain } ,
213
+ } ,
159
214
anyhow:: Result ,
215
+ ethers:: types:: Address ,
160
216
sha3:: { Digest , Keccak256 } ,
217
+ std:: { sync:: Arc , vec} ,
161
218
} ;
162
219
163
220
fn run_hash_chain_test ( secret : [ u8 ; 32 ] , length : usize , sample_interval : usize ) {
@@ -294,4 +351,65 @@ mod test {
294
351
295
352
Ok ( ( ) )
296
353
}
354
+ #[ test]
355
+ fn test_inconsistent_lengths ( ) -> Result < ( ) > {
356
+ let chain1 = PebbleHashChain :: new ( [ 0u8 ; 32 ] , 10 , 1 ) ;
357
+ let chain2 = PebbleHashChain :: new ( [ 1u8 ; 32 ] , 10 , 1 ) ;
358
+
359
+ let hash_chain_state = HashChainState :: new ( vec ! [ 5 ] , vec ! [ chain1. clone( ) , chain2. clone( ) ] ) ;
360
+ assert ! ( hash_chain_state. is_err( ) ) ;
361
+ let hash_chain_state = HashChainState :: new ( vec ! [ 5 , 10 ] , vec ! [ chain1. clone( ) ] ) ;
362
+ assert ! ( hash_chain_state. is_err( ) ) ;
363
+ let hash_chain_state =
364
+ HashChainState :: new ( vec ! [ 5 , 10 ] , vec ! [ chain1. clone( ) , chain2. clone( ) ] ) ;
365
+ assert ! ( hash_chain_state. is_ok( ) ) ;
366
+
367
+ Ok ( ( ) )
368
+ }
369
+
370
+ #[ test]
371
+ fn test_highest_revealed_sequence_number ( ) {
372
+ let chain = PebbleHashChain :: new ( [ 0u8 ; 32 ] , 100 , 1 ) ;
373
+ let hash_chain_state = HashChainState :: new ( vec ! [ 0 ] , vec ! [ chain] ) . unwrap ( ) ;
374
+ let metrics = Arc :: new ( KeeperMetrics :: default ( ) ) ;
375
+ let provider = Address :: random ( ) ;
376
+ let monitored = MonitoredHashChainState :: new (
377
+ Arc :: new ( hash_chain_state) ,
378
+ metrics. clone ( ) ,
379
+ "ethereum" . to_string ( ) ,
380
+ provider,
381
+ ) ;
382
+ let label = AccountLabel {
383
+ chain_id : "ethereum" . to_string ( ) ,
384
+ address : provider. to_string ( ) ,
385
+ } ;
386
+
387
+ assert ! ( monitored. reveal( 5 ) . is_ok( ) ) ;
388
+ let current = metrics
389
+ . highest_revealed_sequence_number
390
+ . get_or_create ( & label)
391
+ . get ( ) ;
392
+ assert_eq ! ( current, 5 ) ;
393
+
394
+ assert ! ( monitored. reveal( 15 ) . is_ok( ) ) ;
395
+ let current = metrics
396
+ . highest_revealed_sequence_number
397
+ . get_or_create ( & label)
398
+ . get ( ) ;
399
+ assert_eq ! ( current, 15 ) ;
400
+
401
+ assert ! ( monitored. reveal( 10 ) . is_ok( ) ) ;
402
+ let current = metrics
403
+ . highest_revealed_sequence_number
404
+ . get_or_create ( & label)
405
+ . get ( ) ;
406
+ assert_eq ! ( current, 15 ) ;
407
+
408
+ assert ! ( monitored. reveal( 1000 ) . is_err( ) ) ;
409
+ let current = metrics
410
+ . highest_revealed_sequence_number
411
+ . get_or_create ( & label)
412
+ . get ( ) ;
413
+ assert_eq ! ( current, 15 ) ;
414
+ }
297
415
}
0 commit comments