16
16
17
17
use crate :: {
18
18
mock:: * , BeaconConfig , BeaconConfigurationPayload , BeaconInfoResponse , Call , DrandResponseBody ,
19
- Error , Pulse , Pulses , PulsesPayload , RoundNumber ,
19
+ Error , Pulse , Pulses , PulsesPayload , ENDPOINTS , QUICKNET_CHAIN_HASH ,
20
20
} ;
21
21
use codec:: Encode ;
22
22
use frame_support:: {
23
23
assert_noop, assert_ok,
24
24
pallet_prelude:: { InvalidTransaction , TransactionSource } ,
25
25
} ;
26
26
use frame_system:: RawOrigin ;
27
+ use serde_json;
27
28
use sp_runtime:: {
28
29
offchain:: {
29
30
testing:: { PendingRequest , TestOffchainExt } ,
@@ -38,6 +39,7 @@ pub const ROUND_NUMBER: u64 = 1000;
38
39
// Quicknet parameters
39
40
pub const DRAND_PULSE : & str = "{\" round\" :1000,\" randomness\" :\" fe290beca10872ef2fb164d2aa4442de4566183ec51c56ff3cd603d930e54fdd\" ,\" signature\" :\" b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39\" }" ;
40
41
pub const DRAND_INFO_RESPONSE : & str = "{\" public_key\" :\" 83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a\" ,\" period\" :3,\" genesis_time\" :1692803367,\" hash\" :\" 52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971\" ,\" groupHash\" :\" f477d5c89f21a17c863a7f937c6a6d15859414d2be09cd448d4279af331c5d3e\" ,\" schemeID\" :\" bls-unchained-g1-rfc9380\" ,\" metadata\" :{\" beaconID\" :\" quicknet\" }}" ;
42
+ const INVALID_JSON : & str = r#"{"round":1000,"randomness":"not base64??","signature":}"# ;
41
43
42
44
#[ test]
43
45
fn it_can_submit_valid_pulse_when_beacon_config_exists ( ) {
@@ -342,53 +344,45 @@ fn test_not_validate_unsigned_write_pulse_with_no_payload_signature() {
342
344
}
343
345
344
346
#[ test]
345
- #[ ignore]
346
- fn test_validate_unsigned_write_pulse_by_non_authority ( ) {
347
- // TODO: https://github.com/ideal-lab5/pallet-drand/issues/3
348
- todo ! (
349
- "the transaction should be validated even if the signer of the payload is not an authority"
350
- ) ;
351
- }
347
+ fn can_execute_and_handle_valid_http_responses ( ) {
348
+ use serde_json;
352
349
353
- #[ test]
354
- #[ ignore]
355
- fn test_not_validate_unsigned_set_beacon_config_by_non_authority ( ) {
356
- // TODO: https://github.com/ideal-lab5/pallet-drand/issues/3
357
- todo ! (
358
- "the transaction should not be validated if the signer of the payload is not an authority"
359
- ) ;
360
- }
350
+ let expected_pulse: DrandResponseBody = serde_json:: from_str ( DRAND_PULSE ) . unwrap ( ) ;
361
351
362
- #[ test]
363
- fn can_execute_and_handle_valid_http_responses ( ) {
364
352
let ( offchain, state) = TestOffchainExt :: new ( ) ;
365
353
let mut t = sp_io:: TestExternalities :: default ( ) ;
366
354
t. register_extension ( OffchainWorkerExt :: new ( offchain) ) ;
367
355
368
356
{
369
357
let mut state = state. write ( ) ;
370
- state. expect_request ( PendingRequest {
371
- method : "GET" . into ( ) ,
372
- uri : "https://drand.cloudflare.com/52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971/public/1" . into ( ) ,
373
- response : Some ( DRAND_PULSE . as_bytes ( ) . to_vec ( ) ) ,
374
- sent : true ,
375
- ..Default :: default ( )
376
- } ) ;
377
- state. expect_request ( PendingRequest {
378
- method : "GET" . into ( ) ,
379
- uri : "https://drand.cloudflare.com/52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971/public/latest" . into ( ) ,
380
- response : Some ( DRAND_PULSE . as_bytes ( ) . to_vec ( ) ) ,
381
- sent : true ,
382
- ..Default :: default ( )
383
- } ) ;
358
+
359
+ for endpoint in ENDPOINTS . iter ( ) {
360
+ state. expect_request ( PendingRequest {
361
+ method : "GET" . into ( ) ,
362
+ uri : format ! ( "{}/{}/public/1000" , endpoint, QUICKNET_CHAIN_HASH ) ,
363
+ response : Some ( DRAND_PULSE . as_bytes ( ) . to_vec ( ) ) ,
364
+ sent : true ,
365
+ ..Default :: default ( )
366
+ } ) ;
367
+ }
368
+
369
+ for endpoint in ENDPOINTS . iter ( ) {
370
+ state. expect_request ( PendingRequest {
371
+ method : "GET" . into ( ) ,
372
+ uri : format ! ( "{}/{}/public/latest" , endpoint, QUICKNET_CHAIN_HASH ) ,
373
+ response : Some ( DRAND_PULSE . as_bytes ( ) . to_vec ( ) ) ,
374
+ sent : true ,
375
+ ..Default :: default ( )
376
+ } ) ;
377
+ }
384
378
}
385
379
386
380
t. execute_with ( || {
387
- let actual_specific = Drand :: fetch_drand_by_round ( RoundNumber :: from ( 1u64 ) ) . unwrap ( ) ;
388
- assert_eq ! ( actual_specific, DRAND_PULSE ) ;
381
+ let actual_specific = Drand :: fetch_drand_by_round ( 1000u64 ) . unwrap ( ) ;
382
+ assert_eq ! ( actual_specific, expected_pulse ) ;
389
383
390
384
let actual_pulse = Drand :: fetch_drand_latest ( ) . unwrap ( ) ;
391
- assert_eq ! ( actual_pulse, DRAND_PULSE ) ;
385
+ assert_eq ! ( actual_pulse, expected_pulse ) ;
392
386
} ) ;
393
387
}
394
388
@@ -417,3 +411,142 @@ fn validate_unsigned_rejects_future_block_number() {
417
411
assert_noop ! ( validity, InvalidTransaction :: Future ) ;
418
412
} ) ;
419
413
}
414
+
415
+ #[ test]
416
+ fn test_all_endpoints_fail ( ) {
417
+ let ( offchain, state) = TestOffchainExt :: new ( ) ;
418
+ let mut t = sp_io:: TestExternalities :: default ( ) ;
419
+ t. register_extension ( OffchainWorkerExt :: new ( offchain) ) ;
420
+
421
+ {
422
+ let mut state = state. write ( ) ;
423
+ let endpoints = ENDPOINTS ;
424
+
425
+ for endpoint in endpoints. iter ( ) {
426
+ state. expect_request ( PendingRequest {
427
+ method : "GET" . into ( ) ,
428
+ uri : format ! ( "{}/{}/public/1000" , endpoint, QUICKNET_CHAIN_HASH ) ,
429
+ response : Some ( INVALID_JSON . as_bytes ( ) . to_vec ( ) ) ,
430
+ sent : true ,
431
+ ..Default :: default ( )
432
+ } ) ;
433
+ }
434
+ }
435
+
436
+ t. execute_with ( || {
437
+ let result = Drand :: fetch_drand_by_round ( 1000u64 ) ;
438
+ assert ! (
439
+ result. is_err( ) ,
440
+ "All endpoints should fail due to invalid JSON responses"
441
+ ) ;
442
+ } ) ;
443
+ }
444
+
445
+ #[ test]
446
+ fn test_eventual_success ( ) {
447
+ let expected_pulse: DrandResponseBody = serde_json:: from_str ( DRAND_PULSE ) . unwrap ( ) ;
448
+
449
+ let ( offchain, state) = TestOffchainExt :: new ( ) ;
450
+ let mut t = sp_io:: TestExternalities :: default ( ) ;
451
+ t. register_extension ( OffchainWorkerExt :: new ( offchain) ) ;
452
+
453
+ {
454
+ let mut state = state. write ( ) ;
455
+ let endpoints = ENDPOINTS ;
456
+
457
+ // We'll make all endpoints except the last return invalid JSON.
458
+ // Since no meta is provided, these are "200 OK" but invalid JSON, causing decode failures.
459
+ // The last endpoint returns the valid DRAND_PULSE JSON, leading to success.
460
+
461
+ // Endpoint 0: Invalid JSON (decode fail)
462
+ state. expect_request ( PendingRequest {
463
+ method : "GET" . into ( ) ,
464
+ uri : format ! ( "{}/{}/public/1000" , endpoints[ 0 ] , QUICKNET_CHAIN_HASH ) ,
465
+ response : Some ( INVALID_JSON . as_bytes ( ) . to_vec ( ) ) ,
466
+ sent : true ,
467
+ ..Default :: default ( )
468
+ } ) ;
469
+
470
+ // Endpoint 1: Invalid JSON
471
+ state. expect_request ( PendingRequest {
472
+ method : "GET" . into ( ) ,
473
+ uri : format ! ( "{}/{}/public/1000" , endpoints[ 1 ] , QUICKNET_CHAIN_HASH ) ,
474
+ response : Some ( Vec :: new ( ) ) ,
475
+ sent : true ,
476
+ ..Default :: default ( )
477
+ } ) ;
478
+
479
+ // Endpoint 2: Invalid JSON
480
+ state. expect_request ( PendingRequest {
481
+ method : "GET" . into ( ) ,
482
+ uri : format ! ( "{}/{}/public/1000" , endpoints[ 2 ] , QUICKNET_CHAIN_HASH ) ,
483
+ response : Some ( INVALID_JSON . as_bytes ( ) . to_vec ( ) ) ,
484
+ sent : true ,
485
+ ..Default :: default ( )
486
+ } ) ;
487
+
488
+ // Endpoint 3: Invalid JSON
489
+ state. expect_request ( PendingRequest {
490
+ method : "GET" . into ( ) ,
491
+ uri : format ! ( "{}/{}/public/1000" , endpoints[ 3 ] , QUICKNET_CHAIN_HASH ) ,
492
+ response : Some ( INVALID_JSON . as_bytes ( ) . to_vec ( ) ) ,
493
+ sent : true ,
494
+ ..Default :: default ( )
495
+ } ) ;
496
+
497
+ // Endpoint 4: Valid JSON (success)
498
+ state. expect_request ( PendingRequest {
499
+ method : "GET" . into ( ) ,
500
+ uri : format ! ( "{}/{}/public/1000" , endpoints[ 4 ] , QUICKNET_CHAIN_HASH ) ,
501
+ response : Some ( DRAND_PULSE . as_bytes ( ) . to_vec ( ) ) ,
502
+ sent : true ,
503
+ ..Default :: default ( )
504
+ } ) ;
505
+ }
506
+
507
+ t. execute_with ( || {
508
+ let actual = Drand :: fetch_drand_by_round ( 1000u64 ) . unwrap ( ) ;
509
+ assert_eq ! (
510
+ actual, expected_pulse,
511
+ "Should succeed on the last endpoint after failing at the previous ones"
512
+ ) ;
513
+ } ) ;
514
+ }
515
+
516
+ #[ test]
517
+ fn test_invalid_json_then_success ( ) {
518
+ let expected_pulse: DrandResponseBody = serde_json:: from_str ( DRAND_PULSE ) . unwrap ( ) ;
519
+
520
+ let ( offchain, state) = TestOffchainExt :: new ( ) ;
521
+ let mut t = sp_io:: TestExternalities :: default ( ) ;
522
+ t. register_extension ( OffchainWorkerExt :: new ( offchain) ) ;
523
+
524
+ {
525
+ let mut state = state. write ( ) ;
526
+
527
+ let endpoints = ENDPOINTS ;
528
+
529
+ // Endpoint 1: Invalid JSON
530
+ state. expect_request ( PendingRequest {
531
+ method : "GET" . into ( ) ,
532
+ uri : format ! ( "{}/{}/public/1000" , endpoints[ 0 ] , QUICKNET_CHAIN_HASH ) ,
533
+ response : Some ( INVALID_JSON . as_bytes ( ) . to_vec ( ) ) ,
534
+ sent : true ,
535
+ ..Default :: default ( )
536
+ } ) ;
537
+
538
+ // Endpoint 2: Valid response
539
+ state. expect_request ( PendingRequest {
540
+ method : "GET" . into ( ) ,
541
+ uri : format ! ( "{}/{}/public/1000" , endpoints[ 1 ] , QUICKNET_CHAIN_HASH ) ,
542
+ response : Some ( DRAND_PULSE . as_bytes ( ) . to_vec ( ) ) ,
543
+ sent : true ,
544
+ ..Default :: default ( )
545
+ } ) ;
546
+ }
547
+
548
+ t. execute_with ( || {
549
+ let actual = Drand :: fetch_drand_by_round ( 1000u64 ) . unwrap ( ) ;
550
+ assert_eq ! ( actual, expected_pulse) ;
551
+ } ) ;
552
+ }
0 commit comments