Newer
Older
assert_ok!(submit_finality_proof(5));
})
}
#[test]
fn importing_header_enacts_new_authority_set() {
run_test(|| {
initialize_substrate_bridge();
let next_set_id = 2;
let next_authorities = vec![(ALICE.into(), 1), (BOB.into(), 1)];
// Need to update the header digest to indicate that our header signals an authority set
// change. The change will be enacted when we import our header.
let mut header = test_header(2);
header.digest = change_log(0);
// Create a valid justification for the header
let justification = make_default_justification(&header);
// Let's import our test header
let result = Pallet::<TestRuntime>::submit_finality_proof(
RuntimeOrigin::signed(1),
Box::new(header.clone()),
justification,
assert_ok!(result);
assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::No);
// Make sure that our header is the best finalized
Svyatoslav Nikolsky
committed
assert_eq!(<BestFinalized<TestRuntime>>::get().unwrap().1, header.hash());
assert!(<ImportedHeaders<TestRuntime>>::contains_key(header.hash()));
// Make sure that the authority set actually changed upon importing our header
assert_eq!(
<CurrentAuthoritySet<TestRuntime>>::get(),
Svyatoslav Nikolsky
committed
StoredAuthoritySet::<TestRuntime, ()>::try_new(next_authorities, next_set_id)
.unwrap(),
);
})
}
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
#[test]
fn relayer_pays_tx_fee_when_submitting_huge_mandatory_header() {
run_test(|| {
initialize_substrate_bridge();
// let's prepare a huge authorities change header, which is definitely above size limits
let mut header = test_header(2);
header.digest = change_log(0);
header.digest.push(DigestItem::Other(vec![42u8; 1024 * 1024]));
let justification = make_default_justification(&header);
// without large digest item ^^^ the relayer would have paid zero transaction fee
// (`Pays::No`)
let result = Pallet::<TestRuntime>::submit_finality_proof(
RuntimeOrigin::signed(1),
Box::new(header.clone()),
justification,
);
assert_ok!(result);
assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes);
// Make sure that our header is the best finalized
assert_eq!(<BestFinalized<TestRuntime>>::get().unwrap().1, header.hash());
assert!(<ImportedHeaders<TestRuntime>>::contains_key(header.hash()));
})
}
#[test]
fn relayer_pays_tx_fee_when_submitting_justification_with_long_ancestry_votes() {
run_test(|| {
initialize_substrate_bridge();
// let's prepare a huge authorities change header, which is definitely above weight
// limits
let mut header = test_header(2);
header.digest = change_log(0);
let justification = make_justification_for_header(JustificationGeneratorParams {
header: header.clone(),
ancestors: TestBridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY + 1,
..Default::default()
});
// without many headers in votes ancestries ^^^ the relayer would have paid zero
// transaction fee (`Pays::No`)
let result = Pallet::<TestRuntime>::submit_finality_proof(
RuntimeOrigin::signed(1),
Box::new(header.clone()),
justification,
);
assert_ok!(result);
assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes);
// Make sure that our header is the best finalized
assert_eq!(<BestFinalized<TestRuntime>>::get().unwrap().1, header.hash());
assert!(<ImportedHeaders<TestRuntime>>::contains_key(header.hash()));
})
}
#[test]
fn importing_header_rejects_header_with_scheduled_change_delay() {
run_test(|| {
initialize_substrate_bridge();
// Need to update the header digest to indicate that our header signals an authority set
// change. However, the change doesn't happen until the next block.
let mut header = test_header(2);
header.digest = change_log(1);
// Create a valid justification for the header
let justification = make_default_justification(&header);
// Should not be allowed to import this header
assert_err!(
RuntimeOrigin::signed(1),
<Error<TestRuntime>>::UnsupportedScheduledChange
);
})
}
#[test]
fn importing_header_rejects_header_with_forced_changes() {
run_test(|| {
initialize_substrate_bridge();
// Need to update the header digest to indicate that it signals a forced authority set
// change.
let mut header = test_header(2);
header.digest = forced_change_log(0);
// Create a valid justification for the header
let justification = make_default_justification(&header);
// Should not be allowed to import this header
assert_err!(
RuntimeOrigin::signed(1),
<Error<TestRuntime>>::UnsupportedScheduledChange
);
})
}
Svyatoslav Nikolsky
committed
#[test]
fn importing_header_rejects_header_with_too_many_authorities() {
run_test(|| {
initialize_substrate_bridge();
// Need to update the header digest to indicate that our header signals an authority set
// change. However, the change doesn't happen until the next block.
let mut header = test_header(2);
header.digest = many_authorities_log();
// Create a valid justification for the header
let justification = make_default_justification(&header);
// Should not be allowed to import this header
assert_err!(
Pallet::<TestRuntime>::submit_finality_proof(
RuntimeOrigin::signed(1),
Svyatoslav Nikolsky
committed
Box::new(header),
justification
),
<Error<TestRuntime>>::TooManyAuthoritiesInSet
);
});
}
#[test]
fn parse_finalized_storage_proof_rejects_proof_on_unknown_header() {
run_test(|| {
assert_noop!(
Pallet::<TestRuntime>::storage_proof_checker(Default::default(), vec![],)
.map(|_| ()),
bp_header_chain::HeaderChainError::UnknownHeader,
);
});
}
#[test]
fn parse_finalized_storage_accepts_valid_proof() {
run_test(|| {
let (state_root, storage_proof) = bp_runtime::craft_valid_storage_proof();
let mut header = test_header(2);
header.set_state_root(state_root);
let hash = header.hash();
<BestFinalized<TestRuntime>>::put(HeaderId(2, hash));
<ImportedHeaders<TestRuntime>>::insert(hash, header.build());
Pallet::<TestRuntime>::storage_proof_checker(hash, storage_proof).map(|_| ())
);
});
}
Svyatoslav Nikolsky
committed
fn rate_limiter_disallows_free_imports_once_limit_is_hit_in_single_block() {
run_test(|| {
initialize_substrate_bridge();
Svyatoslav Nikolsky
committed
let result = submit_mandatory_finality_proof(1, 1);
assert_eq!(result.expect("call failed").pays_fee, Pays::No);
let result = submit_mandatory_finality_proof(2, 2);
assert_eq!(result.expect("call failed").pays_fee, Pays::No);
let result = submit_mandatory_finality_proof(3, 3);
assert_eq!(result.expect("call failed").pays_fee, Pays::Yes);
})
}
#[test]
fn rate_limiter_invalid_requests_do_not_count_towards_request_count() {
run_test(|| {
let submit_invalid_request = || {
Svyatoslav Nikolsky
committed
let mut header = test_header(1);
header.digest = change_log(0);
let mut invalid_justification = make_default_justification(&header);
invalid_justification.round = 42;
RuntimeOrigin::signed(1),
Box::new(header),
invalid_justification,
)
};
initialize_substrate_bridge();
Svyatoslav Nikolsky
committed
for _ in 0..<TestRuntime as Config>::MaxFreeMandatoryHeadersPerBlock::get() + 1 {
assert_err!(submit_invalid_request(), <Error<TestRuntime>>::InvalidJustification);
}
Svyatoslav Nikolsky
committed
// Can still submit free mandatory headers afterwards
let result = submit_mandatory_finality_proof(1, 1);
assert_eq!(result.expect("call failed").pays_fee, Pays::No);
let result = submit_mandatory_finality_proof(2, 2);
assert_eq!(result.expect("call failed").pays_fee, Pays::No);
let result = submit_mandatory_finality_proof(3, 3);
assert_eq!(result.expect("call failed").pays_fee, Pays::Yes);
fn rate_limiter_allows_request_after_new_block_has_started() {
run_test(|| {
initialize_substrate_bridge();
Svyatoslav Nikolsky
committed
let result = submit_mandatory_finality_proof(1, 1);
assert_eq!(result.expect("call failed").pays_fee, Pays::No);
Svyatoslav Nikolsky
committed
let result = submit_mandatory_finality_proof(2, 2);
assert_eq!(result.expect("call failed").pays_fee, Pays::No);
let result = submit_mandatory_finality_proof(3, 3);
assert_eq!(result.expect("call failed").pays_fee, Pays::Yes);
Svyatoslav Nikolsky
committed
let result = submit_mandatory_finality_proof(4, 4);
assert_eq!(result.expect("call failed").pays_fee, Pays::No);
let result = submit_mandatory_finality_proof(5, 5);
assert_eq!(result.expect("call failed").pays_fee, Pays::No);
let result = submit_mandatory_finality_proof(6, 6);
assert_eq!(result.expect("call failed").pays_fee, Pays::Yes);
Svyatoslav Nikolsky
committed
fn rate_limiter_ignores_non_mandatory_headers() {
run_test(|| {
initialize_substrate_bridge();
Svyatoslav Nikolsky
committed
let result = submit_finality_proof(1);
assert_eq!(result.expect("call failed").pays_fee, Pays::Yes);
Svyatoslav Nikolsky
committed
let result = submit_mandatory_finality_proof(2, 1);
assert_eq!(result.expect("call failed").pays_fee, Pays::No);
let result = submit_finality_proof_with_set_id(3, 2);
assert_eq!(result.expect("call failed").pays_fee, Pays::Yes);
let result = submit_mandatory_finality_proof(4, 2);
assert_eq!(result.expect("call failed").pays_fee, Pays::No);
let result = submit_finality_proof_with_set_id(5, 3);
assert_eq!(result.expect("call failed").pays_fee, Pays::Yes);
let result = submit_mandatory_finality_proof(6, 3);
assert_eq!(result.expect("call failed").pays_fee, Pays::Yes);
#[test]
fn should_prune_headers_over_headers_to_keep_parameter() {
run_test(|| {
initialize_substrate_bridge();
assert_ok!(submit_finality_proof(1));
let first_header_hash = Pallet::<TestRuntime>::best_finalized().unwrap().hash();
next_block();
assert_ok!(submit_finality_proof(2));
next_block();
assert_ok!(submit_finality_proof(3));
next_block();
assert_ok!(submit_finality_proof(4));
next_block();
assert_ok!(submit_finality_proof(5));
next_block();
assert_ok!(submit_finality_proof(6));
assert!(
!ImportedHeaders::<TestRuntime, ()>::contains_key(first_header_hash),
"First header should be pruned.",
Svyatoslav Nikolsky
committed
#[test]
fn storage_keys_computed_properly() {
Svyatoslav Nikolsky
committed
assert_eq!(
PalletOperatingMode::<TestRuntime>::storage_value_final_key().to_vec(),
bp_header_chain::storage_keys::pallet_operating_mode_key("Grandpa").0,
Svyatoslav Nikolsky
committed
);
assert_eq!(
CurrentAuthoritySet::<TestRuntime>::storage_value_final_key().to_vec(),
bp_header_chain::storage_keys::current_authority_set_key("Grandpa").0,
);
Svyatoslav Nikolsky
committed
assert_eq!(
BestFinalized::<TestRuntime>::storage_value_final_key().to_vec(),
Svyatoslav Nikolsky
committed
bp_header_chain::storage_keys::best_finalized_key("Grandpa").0,
Svyatoslav Nikolsky
committed
);
}
#[test]
fn test_bridge_grandpa_call_is_correctly_defined() {
let header = test_header(0);
let init_data = InitializationData {
header: Box::new(header.clone()),
authority_list: authority_list(),
set_id: 1,
operating_mode: BasicOperatingMode::Normal,
};
let justification = make_default_justification(&header);
let direct_initialize_call =
Call::<TestRuntime>::initialize { init_data: init_data.clone() };
let indirect_initialize_call = BridgeGrandpaCall::<TestHeader>::initialize { init_data };
assert_eq!(direct_initialize_call.encode(), indirect_initialize_call.encode());
let direct_submit_finality_proof_call = Call::<TestRuntime>::submit_finality_proof {
finality_target: Box::new(header.clone()),
justification: justification.clone(),
};
let indirect_submit_finality_proof_call =
BridgeGrandpaCall::<TestHeader>::submit_finality_proof {
finality_target: Box::new(header),
justification,
};
assert_eq!(
direct_submit_finality_proof_call.encode(),
indirect_submit_finality_proof_call.encode()
);
}
generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted);
#[test]
fn maybe_headers_to_keep_returns_correct_value() {
assert_eq!(MaybeHeadersToKeep::<TestRuntime, ()>::get(), Some(mock::HeadersToKeep::get()));
}
Svyatoslav Nikolsky
committed
#[test]
fn submit_finality_proof_requires_signed_origin() {
run_test(|| {
initialize_substrate_bridge();
let header = test_header(1);
let justification = make_default_justification(&header);
assert_noop!(
Pallet::<TestRuntime>::submit_finality_proof(
RuntimeOrigin::root(),
Box::new(header),
justification,
),
DispatchError::BadOrigin,
);
})
}