@@ -2,7 +2,7 @@ use std::{fmt, sync::Arc};
2
2
3
3
use crate :: key:: Keypair ;
4
4
use anyhow:: Context ;
5
- use subxt:: { config:: Header as _, rpc_params, utils:: H256 } ;
5
+ use subxt:: { config:: Header as _, error :: BlockError , rpc_params, utils:: H256 } ;
6
6
use sugondat_nmt:: Namespace ;
7
7
use sugondat_subxt:: {
8
8
sugondat:: runtime_types:: pallet_sugondat_blobs:: namespace_param:: UnvalidatedNamespace , Header ,
@@ -63,6 +63,7 @@ impl Client {
63
63
/// the block hash of the block at the given height.
64
64
#[ tracing:: instrument( level = Level :: DEBUG , skip( self ) ) ]
65
65
pub async fn wait_finalized_height ( & self , height : u64 ) -> [ u8 ; 32 ] {
66
+ tracing:: info!( "Waiting for block at height: {}" , height) ;
66
67
loop {
67
68
let conn = self . connector . ensure_connected ( ) . await ;
68
69
match conn. finalized . wait_until_finalized ( self , height) . await {
@@ -79,7 +80,7 @@ impl Client {
79
80
///
80
81
/// If there is no block at the given height, returns `None`.
81
82
#[ tracing:: instrument( level = Level :: DEBUG , skip( self ) ) ]
82
- pub async fn block_hash ( & self , height : u64 ) -> anyhow:: Result < Option < H256 > > {
83
+ pub async fn block_hash ( & self , height : u64 ) -> anyhow:: Result < Option < [ u8 ; 32 ] > > {
83
84
loop {
84
85
let conn = self . connector . ensure_connected ( ) . await ;
85
86
let block_hash: Option < H256 > = match conn
@@ -102,43 +103,75 @@ impl Client {
102
103
// at the given height.
103
104
Ok ( None )
104
105
}
105
- Some ( block_hash) => Ok ( Some ( block_hash) ) ,
106
+ Some ( block_hash) => Ok ( Some ( block_hash. into ( ) ) ) ,
106
107
} ;
107
108
}
108
109
}
109
110
110
- /// Returns the header and the body of the block with the given hash, automatically retrying
111
- /// until it succeeds. `None` indicates the best block.
111
+ /// Returns the block hash of the block at the given height,
112
+ /// automatically retrying until it succeeds.
113
+ /// `None` indicates the best block.
114
+ pub async fn wait_block_hash ( & self , height : u64 ) -> [ u8 ; 32 ] {
115
+ loop {
116
+ match self . block_hash ( height) . await {
117
+ Ok ( Some ( res) ) => break res,
118
+ Ok ( None ) => {
119
+ tracing:: info!( "Block hash not available yet" ) ;
120
+ tokio:: time:: sleep ( std:: time:: Duration :: from_secs ( 1 ) ) . await ;
121
+ }
122
+ Err ( e) => {
123
+ // any other error is treated as a failure, resulting in the connection being reset
124
+ tracing:: error!( ?e, "failed to query block hash" ) ;
125
+ self . connector . reset ( ) . await ;
126
+ }
127
+ }
128
+ }
129
+ }
130
+
131
+ /// Returns the header and body of the block with the given hash.
132
+ /// If it is not available, it returns the reason of the failure.
133
+ /// `None` indicates the best block.
112
134
async fn get_header_and_extrinsics (
113
135
& self ,
114
136
block_hash : Option < [ u8 ; 32 ] > ,
115
- ) -> anyhow :: Result < ( Header , Vec < sugondat_subxt:: ExtrinsicDetails > ) > {
137
+ ) -> Result < ( Header , Vec < sugondat_subxt:: ExtrinsicDetails > ) , subxt :: error :: Error > {
116
138
let block_hash = block_hash. map ( H256 :: from) ;
139
+
140
+ let conn = self . connector . ensure_connected ( ) . await ;
141
+ let res = match block_hash {
142
+ Some ( h) => conn. subxt . blocks ( ) . at ( h) . await ,
143
+ None => conn. subxt . blocks ( ) . at_latest ( ) . await ,
144
+ } ?;
145
+
146
+ let header = res. header ( ) ;
147
+ let body = res
148
+ . extrinsics ( )
149
+ . await ?
150
+ . iter ( )
151
+ . collect :: < Result < Vec < _ > , _ > > ( ) ?;
152
+ Ok ( ( header. clone ( ) , body) )
153
+ }
154
+
155
+ /// Returns the header and the body of the block with the given hash,
156
+ /// automatically retrying until it succeeds.
157
+ /// `None` indicates the best block.
158
+ async fn wait_header_and_extrinsics (
159
+ & self ,
160
+ block_hash : Option < [ u8 ; 32 ] > ,
161
+ ) -> ( Header , Vec < sugondat_subxt:: ExtrinsicDetails > ) {
117
162
loop {
118
- let conn = self . connector . ensure_connected ( ) . await ;
119
- let res = match block_hash {
120
- Some ( h) => conn. subxt . blocks ( ) . at ( h) . await ,
121
- None => conn. subxt . blocks ( ) . at_latest ( ) . await ,
122
- } ;
123
- let err = match res {
124
- Ok ( it) => {
125
- let header = it. header ( ) ;
126
- let body = match it. extrinsics ( ) . await {
127
- Ok ( it) => it,
128
- Err ( err) => {
129
- tracing:: error!( ?err, "failed to query block" ) ;
130
- self . connector . reset ( ) . await ;
131
- continue ;
132
- }
133
- }
134
- . iter ( )
135
- . collect :: < Result < Vec < _ > , _ > > ( ) ?;
136
- return Ok ( ( header. clone ( ) , body) ) ;
163
+ match self . get_header_and_extrinsics ( block_hash) . await {
164
+ Ok ( res) => break res,
165
+ Err ( subxt:: Error :: Block ( BlockError :: NotFound ( _hash) ) ) => {
166
+ tracing:: info!( "Block not available yet" ) ;
167
+ tokio:: time:: sleep ( std:: time:: Duration :: from_secs ( 1 ) ) . await ;
137
168
}
138
- Err ( err) => err,
139
- } ;
140
- tracing:: error!( ?err, "failed to query block" ) ;
141
- self . connector . reset ( ) . await ;
169
+ Err ( e) => {
170
+ // any other error is treated as a failure, resulting in the connection being reset
171
+ tracing:: error!( ?e, "failed to query block" ) ;
172
+ self . connector . reset ( ) . await ;
173
+ }
174
+ }
142
175
}
143
176
}
144
177
@@ -148,19 +181,16 @@ impl Client {
148
181
/// `None` indicates that the best block should be used.
149
182
#[ tracing:: instrument( level = Level :: DEBUG , skip( self ) ) ]
150
183
pub async fn get_block_at ( & self , block_hash : Option < [ u8 ; 32 ] > ) -> anyhow:: Result < Block > {
151
- let ( header, extrinsics) = self . get_header_and_extrinsics ( block_hash) . await ?;
152
- let tree_root = tree_root ( & header) . ok_or_else ( err:: no_tree_root) ?;
153
- let timestamp = extract_timestamp ( & extrinsics) ?;
154
- let blobs = extract_blobs ( extrinsics) ;
155
- tracing:: debug!( ?blobs, "found {} blobs in block" , blobs. len( ) ) ;
156
- Ok ( Block {
157
- number : header. number as u64 ,
158
- hash : header. hash ( ) . 0 ,
159
- parent_hash : header. parent_hash . 0 ,
160
- tree_root,
161
- timestamp,
162
- blobs,
163
- } )
184
+ Block :: from_header_and_extrinsics ( self . get_header_and_extrinsics ( block_hash) . await ?)
185
+ }
186
+
187
+ /// Returns the data of the block identified by the given block hash.
188
+ /// It retries until the block is present
189
+ ///
190
+ /// `None` indicates that the best block should be used.
191
+ #[ tracing:: instrument( level = Level :: DEBUG , skip( self ) ) ]
192
+ pub async fn wait_block_at ( & self , block_hash : Option < [ u8 ; 32 ] > ) -> anyhow:: Result < Block > {
193
+ Block :: from_header_and_extrinsics ( self . wait_header_and_extrinsics ( block_hash) . await )
164
194
}
165
195
166
196
/// Submit a blob with the given namespace and signed with the given key. The block is submitted
@@ -319,7 +349,7 @@ impl FinalizedHeadWatcher {
319
349
// TODO: throttle the retries. // TODO: return an error
320
350
continue ;
321
351
} ;
322
- break Some ( block_hash. 0 ) ;
352
+ break Some ( block_hash) ;
323
353
}
324
354
}
325
355
}
@@ -347,6 +377,26 @@ pub struct Block {
347
377
pub blobs : Vec < Blob > ,
348
378
}
349
379
380
+ impl Block {
381
+ fn from_header_and_extrinsics (
382
+ value : ( Header , Vec < sugondat_subxt:: ExtrinsicDetails > ) ,
383
+ ) -> anyhow:: Result < Self > {
384
+ let ( header, extrinsics) = value;
385
+ let tree_root = tree_root ( & header) . ok_or_else ( err:: no_tree_root) ?;
386
+ let timestamp = extract_timestamp ( & extrinsics) ?;
387
+ let blobs = extract_blobs ( extrinsics) ;
388
+ tracing:: debug!( ?blobs, "found {} blobs in block" , blobs. len( ) ) ;
389
+ Ok ( Block {
390
+ number : header. number as u64 ,
391
+ hash : header. hash ( ) . 0 ,
392
+ parent_hash : header. parent_hash . 0 ,
393
+ tree_root,
394
+ timestamp,
395
+ blobs,
396
+ } )
397
+ }
398
+ }
399
+
350
400
/// Represents a blob in a sugondat block.
351
401
pub struct Blob {
352
402
pub extrinsic_index : u32 ,
0 commit comments