@@ -8,6 +8,7 @@ use anyhow::{anyhow, Context};
8
8
use async_trait:: async_trait;
9
9
use pallas_hardano:: storage:: immutable:: chunk:: { read_blocks, Reader } ;
10
10
use pallas_traverse:: MultiEraBlock ;
11
+ use slog:: { error, Logger } ;
11
12
use std:: path:: Path ;
12
13
use tokio:: sync:: RwLock ;
13
14
@@ -87,35 +88,43 @@ struct Block {
87
88
}
88
89
89
90
impl Block {
90
- fn try_convert (
91
- multi_era_block : MultiEraBlock ,
92
- immutable_file_number : ImmutableFileNumber ,
93
- ) -> StdResult < Self > {
91
+ fn convert ( multi_era_block : MultiEraBlock , immutable_file_number : ImmutableFileNumber ) -> Self {
94
92
let mut transactions = Vec :: new ( ) ;
95
93
for tx in & multi_era_block. txs ( ) {
96
94
transactions. push ( tx. hash ( ) . to_string ( ) ) ;
97
95
}
98
- let block = Block {
96
+
97
+ Block {
99
98
block_number : multi_era_block. number ( ) ,
100
99
immutable_file_number,
101
100
transactions,
102
- } ;
103
-
104
- Ok ( block)
101
+ }
105
102
}
106
103
}
107
104
108
105
/// Cardano transaction parser
109
- pub struct CardanoTransactionParser { }
106
+ pub struct CardanoTransactionParser {
107
+ logger : Logger ,
108
+ /// When set to true, no error is returned in case of unparsable block, and an error log is written instead.
109
+ /// This can occur when the crate 'pallas-hardano' doesn't support some non final encoding for a Cardano era.
110
+ /// This situation should only happen on the test networks and not on the mainnet.
111
+ allow_unparsable_block : bool ,
112
+ }
110
113
111
114
impl CardanoTransactionParser {
112
115
/// Factory
113
- pub fn new ( ) -> Self {
114
- Self { }
116
+ pub fn new ( logger : Logger , allow_unparsable_block : bool ) -> Self {
117
+ Self {
118
+ logger,
119
+ allow_unparsable_block,
120
+ }
115
121
}
116
122
117
123
/// Read blocks from immutable file
118
- fn read_blocks_from_immutable_file ( immutable_file : & ImmutableFile ) -> StdResult < Vec < Block > > {
124
+ fn read_blocks_from_immutable_file (
125
+ & self ,
126
+ immutable_file : & ImmutableFile ,
127
+ ) -> StdResult < Vec < Block > > {
119
128
let cardano_blocks_reader =
120
129
CardanoTransactionParser :: cardano_blocks_reader ( immutable_file) ?;
121
130
@@ -127,10 +136,19 @@ impl CardanoTransactionParser {
127
136
immutable_file. path
128
137
)
129
138
} ) ?;
130
- blocks. push ( CardanoTransactionParser :: convert_to_block (
131
- & block,
132
- immutable_file,
133
- ) ?) ;
139
+ match CardanoTransactionParser :: convert_to_block ( & block, immutable_file) {
140
+ Ok ( convert_to_block) => {
141
+ blocks. push ( convert_to_block) ;
142
+ }
143
+ Err ( err) if self . allow_unparsable_block => {
144
+ error ! (
145
+ self . logger,
146
+ "The cbor encoded block could not be parsed" ;
147
+ "error" => ?err, "immutable_file_number" => immutable_file. number
148
+ ) ;
149
+ }
150
+ Err ( e) => return Err ( e) ,
151
+ }
134
152
}
135
153
Ok ( blocks)
136
154
}
@@ -143,12 +161,7 @@ impl CardanoTransactionParser {
143
161
)
144
162
} ) ?;
145
163
146
- Block :: try_convert ( multi_era_block, immutable_file. number ) . with_context ( || {
147
- format ! (
148
- "CardanoTransactionParser could not read data from block in immutable file: {:?}" ,
149
- immutable_file. path
150
- )
151
- } )
164
+ Ok ( Block :: convert ( multi_era_block, immutable_file. number ) )
152
165
}
153
166
154
167
fn cardano_blocks_reader ( immutable_file : & ImmutableFile ) -> StdResult < Reader > {
@@ -169,12 +182,6 @@ impl CardanoTransactionParser {
169
182
}
170
183
}
171
184
172
- impl Default for CardanoTransactionParser {
173
- fn default ( ) -> Self {
174
- Self :: new ( )
175
- }
176
- }
177
-
178
185
#[ async_trait]
179
186
impl TransactionParser for CardanoTransactionParser {
180
187
async fn parse ( & self , dirpath : & Path , beacon : & Beacon ) -> StdResult < Vec < CardanoTransaction > > {
@@ -186,8 +193,9 @@ impl TransactionParser for CardanoTransactionParser {
186
193
let mut transactions: Vec < CardanoTransaction > = vec ! [ ] ;
187
194
188
195
for immutable_file in & immutable_chunks {
189
- let blocks =
190
- Self :: read_blocks_from_immutable_file ( immutable_file) . with_context ( || {
196
+ let blocks = self
197
+ . read_blocks_from_immutable_file ( immutable_file)
198
+ . with_context ( || {
191
199
format ! (
192
200
"CardanoTransactionParser could read blocks from immutable file: '{}'." ,
193
201
immutable_file. path. display( )
@@ -219,6 +227,11 @@ mod tests {
219
227
220
228
use super :: * ;
221
229
230
+ use crate :: test_utils:: TempDir ;
231
+
232
+ use slog:: Drain ;
233
+ use std:: { fs:: File , sync:: Arc } ;
234
+
222
235
fn get_number_of_immutable_chunk_in_dir ( dir : & Path ) -> usize {
223
236
ImmutableFile :: list_completed_in_dir ( dir)
224
237
. unwrap ( )
@@ -227,6 +240,15 @@ mod tests {
227
240
. len ( )
228
241
}
229
242
243
+ fn create_file_logger ( filepath : & Path ) -> slog:: Logger {
244
+ let writer = File :: create ( filepath) . unwrap ( ) ;
245
+ let decorator = slog_term:: PlainDecorator :: new ( writer) ;
246
+ let drain = slog_term:: CompactFormat :: new ( decorator) . build ( ) . fuse ( ) ;
247
+ let drain = slog_async:: Async :: new ( drain) . build ( ) . fuse ( ) ;
248
+
249
+ Logger :: root ( Arc :: new ( drain) , slog:: o!( ) )
250
+ }
251
+
230
252
#[ tokio:: test]
231
253
async fn test_parse_expected_number_of_transactions ( ) {
232
254
// We known the number of transactions in those prebuilt immutables
@@ -239,7 +261,8 @@ mod tests {
239
261
..Beacon :: default ( )
240
262
} ;
241
263
let tx_count: usize = immutable_files. iter ( ) . map ( |( _, count) | * count) . sum ( ) ;
242
- let cardano_transaction_parser = CardanoTransactionParser :: new ( ) ;
264
+ let cardano_transaction_parser =
265
+ CardanoTransactionParser :: new ( Logger :: root ( slog:: Discard , slog:: o!( ) ) , false ) ;
243
266
244
267
let transactions = cardano_transaction_parser
245
268
. parse ( db_path, & beacon)
@@ -249,6 +272,48 @@ mod tests {
249
272
assert_eq ! ( transactions. len( ) , tx_count) ;
250
273
}
251
274
275
+ #[ tokio:: test]
276
+ async fn test_parse_should_error_with_unparsable_block_format ( ) {
277
+ let db_path = Path :: new ( "../mithril-test-lab/test_data/parsing_error/immutable/" ) ;
278
+ let beacon = Beacon {
279
+ immutable_file_number : 4831 ,
280
+ ..Beacon :: default ( )
281
+ } ;
282
+ let cardano_transaction_parser =
283
+ CardanoTransactionParser :: new ( Logger :: root ( slog:: Discard , slog:: o!( ) ) , false ) ;
284
+
285
+ let result = cardano_transaction_parser. parse ( db_path, & beacon) . await ;
286
+
287
+ assert ! ( result. is_err( ) ) ;
288
+ }
289
+
290
+ #[ tokio:: test]
291
+ async fn test_parse_should_log_error_with_unparsable_block_format ( ) {
292
+ let temp_dir = TempDir :: create (
293
+ "cardano_transaction_parser" ,
294
+ "test_parse_should_log_error_with_unparsable_block_format" ,
295
+ ) ;
296
+ let filepath = temp_dir. join ( "test.log" ) ;
297
+ let db_path = Path :: new ( "../mithril-test-lab/test_data/parsing_error/immutable/" ) ;
298
+ let beacon = Beacon {
299
+ immutable_file_number : 4831 ,
300
+ ..Beacon :: default ( )
301
+ } ;
302
+ // We create a block to drop the logger and force a flush before we read the log file.
303
+ {
304
+ let cardano_transaction_parser =
305
+ CardanoTransactionParser :: new ( create_file_logger ( & filepath) , true ) ;
306
+
307
+ cardano_transaction_parser
308
+ . parse ( db_path, & beacon)
309
+ . await
310
+ . unwrap ( ) ;
311
+ }
312
+
313
+ let log_file = std:: fs:: read_to_string ( & filepath) . unwrap ( ) ;
314
+ assert ! ( log_file. contains( "The cbor encoded block could not be parsed" ) ) ;
315
+ }
316
+
252
317
#[ tokio:: test]
253
318
async fn test_parse_up_to_given_beacon ( ) {
254
319
// We known the number of transactions in those prebuilt immutables
@@ -261,7 +326,8 @@ mod tests {
261
326
..Beacon :: default ( )
262
327
} ;
263
328
let tx_count: usize = immutable_files. iter ( ) . map ( |( _, count) | * count) . sum ( ) ;
264
- let cardano_transaction_parser = CardanoTransactionParser :: new ( ) ;
329
+ let cardano_transaction_parser =
330
+ CardanoTransactionParser :: new ( Logger :: root ( slog:: Discard , slog:: o!( ) ) , false ) ;
265
331
266
332
let transactions = cardano_transaction_parser
267
333
. parse ( db_path, & beacon)
0 commit comments