mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-25 08:15:44 +00:00
Kill the light client, CHTs and change tries. (#10080)
* Remove light client, change tries and CHTs * Update tests * fmt * Restore changes_root * Fixed benches * Cargo fmt * fmt * fmt
This commit is contained in:
Generated
-20
@@ -8209,23 +8209,6 @@ dependencies = [
|
|||||||
"tempfile",
|
"tempfile",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sc-light"
|
|
||||||
version = "4.0.0-dev"
|
|
||||||
dependencies = [
|
|
||||||
"hash-db",
|
|
||||||
"parity-scale-codec",
|
|
||||||
"parking_lot 0.11.1",
|
|
||||||
"sc-client-api",
|
|
||||||
"sc-executor",
|
|
||||||
"sp-api",
|
|
||||||
"sp-blockchain",
|
|
||||||
"sp-core",
|
|
||||||
"sp-externalities",
|
|
||||||
"sp-runtime",
|
|
||||||
"sp-state-machine",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sc-network"
|
name = "sc-network"
|
||||||
version = "0.10.0-dev"
|
version = "0.10.0-dev"
|
||||||
@@ -8558,7 +8541,6 @@ dependencies = [
|
|||||||
"sc-client-db",
|
"sc-client-db",
|
||||||
"sc-consensus",
|
"sc-consensus",
|
||||||
"sc-executor",
|
"sc-executor",
|
||||||
"sc-light",
|
|
||||||
"sc-network",
|
"sc-network",
|
||||||
"sc-service",
|
"sc-service",
|
||||||
"sc-transaction-pool-api",
|
"sc-transaction-pool-api",
|
||||||
@@ -10176,7 +10158,6 @@ dependencies = [
|
|||||||
"sc-client-db",
|
"sc-client-db",
|
||||||
"sc-consensus",
|
"sc-consensus",
|
||||||
"sc-executor",
|
"sc-executor",
|
||||||
"sc-light",
|
|
||||||
"sc-offchain",
|
"sc-offchain",
|
||||||
"sc-service",
|
"sc-service",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -10245,7 +10226,6 @@ dependencies = [
|
|||||||
"sc-block-builder",
|
"sc-block-builder",
|
||||||
"sc-client-api",
|
"sc-client-api",
|
||||||
"sc-consensus",
|
"sc-consensus",
|
||||||
"sc-light",
|
|
||||||
"sp-api",
|
"sp-api",
|
||||||
"sp-blockchain",
|
"sp-blockchain",
|
||||||
"sp-consensus",
|
"sp-consensus",
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ members = [
|
|||||||
"client/finality-grandpa",
|
"client/finality-grandpa",
|
||||||
"client/informant",
|
"client/informant",
|
||||||
"client/keystore",
|
"client/keystore",
|
||||||
"client/light",
|
|
||||||
"client/network",
|
"client/network",
|
||||||
"client/network-gossip",
|
"client/network-gossip",
|
||||||
"client/network/test",
|
"client/network/test",
|
||||||
|
|||||||
@@ -134,7 +134,6 @@ fn testnet_genesis(
|
|||||||
system: SystemConfig {
|
system: SystemConfig {
|
||||||
// Add Wasm runtime to storage.
|
// Add Wasm runtime to storage.
|
||||||
code: wasm_binary.to_vec(),
|
code: wasm_binary.to_vec(),
|
||||||
changes_trie_config: Default::default(),
|
|
||||||
},
|
},
|
||||||
balances: BalancesConfig {
|
balances: BalancesConfig {
|
||||||
// Configure endowed accounts with initial balance of 1 << 60.
|
// Configure endowed accounts with initial balance of 1 << 60.
|
||||||
|
|||||||
@@ -194,7 +194,6 @@ pub fn new_full(mut config: Configuration) -> Result<TaskManager, ServiceError>
|
|||||||
transaction_pool: transaction_pool.clone(),
|
transaction_pool: transaction_pool.clone(),
|
||||||
spawn_handle: task_manager.spawn_handle(),
|
spawn_handle: task_manager.spawn_handle(),
|
||||||
import_queue,
|
import_queue,
|
||||||
on_demand: None,
|
|
||||||
block_announce_validator_builder: None,
|
block_announce_validator_builder: None,
|
||||||
warp_sync: Some(warp_sync),
|
warp_sync: Some(warp_sync),
|
||||||
})?;
|
})?;
|
||||||
@@ -234,8 +233,6 @@ pub fn new_full(mut config: Configuration) -> Result<TaskManager, ServiceError>
|
|||||||
task_manager: &mut task_manager,
|
task_manager: &mut task_manager,
|
||||||
transaction_pool: transaction_pool.clone(),
|
transaction_pool: transaction_pool.clone(),
|
||||||
rpc_extensions_builder,
|
rpc_extensions_builder,
|
||||||
on_demand: None,
|
|
||||||
remote_blockchain: None,
|
|
||||||
backend,
|
backend,
|
||||||
system_rpc_tx,
|
system_rpc_tx,
|
||||||
config,
|
config,
|
||||||
|
|||||||
@@ -295,10 +295,7 @@ pub fn testnet_genesis(
|
|||||||
const STASH: Balance = ENDOWMENT / 1000;
|
const STASH: Balance = ENDOWMENT / 1000;
|
||||||
|
|
||||||
GenesisConfig {
|
GenesisConfig {
|
||||||
system: SystemConfig {
|
system: SystemConfig { code: wasm_binary_unwrap().to_vec() },
|
||||||
code: wasm_binary_unwrap().to_vec(),
|
|
||||||
changes_trie_config: Default::default(),
|
|
||||||
},
|
|
||||||
balances: BalancesConfig {
|
balances: BalancesConfig {
|
||||||
balances: endowed_accounts.iter().cloned().map(|x| (x, ENDOWMENT)).collect(),
|
balances: endowed_accounts.iter().cloned().map(|x| (x, ENDOWMENT)).collect(),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -338,7 +338,6 @@ pub fn new_full_base(
|
|||||||
transaction_pool: transaction_pool.clone(),
|
transaction_pool: transaction_pool.clone(),
|
||||||
spawn_handle: task_manager.spawn_handle(),
|
spawn_handle: task_manager.spawn_handle(),
|
||||||
import_queue,
|
import_queue,
|
||||||
on_demand: None,
|
|
||||||
block_announce_validator_builder: None,
|
block_announce_validator_builder: None,
|
||||||
warp_sync: Some(warp_sync),
|
warp_sync: Some(warp_sync),
|
||||||
})?;
|
})?;
|
||||||
@@ -369,8 +368,6 @@ pub fn new_full_base(
|
|||||||
rpc_extensions_builder: Box::new(rpc_extensions_builder),
|
rpc_extensions_builder: Box::new(rpc_extensions_builder),
|
||||||
transaction_pool: transaction_pool.clone(),
|
transaction_pool: transaction_pool.clone(),
|
||||||
task_manager: &mut task_manager,
|
task_manager: &mut task_manager,
|
||||||
on_demand: None,
|
|
||||||
remote_blockchain: None,
|
|
||||||
system_rpc_tx,
|
system_rpc_tx,
|
||||||
telemetry: telemetry.as_mut(),
|
telemetry: telemetry.as_mut(),
|
||||||
})?;
|
})?;
|
||||||
@@ -542,7 +539,7 @@ mod tests {
|
|||||||
use sc_service_test::TestNetNode;
|
use sc_service_test::TestNetNode;
|
||||||
use sc_transaction_pool_api::{ChainEvent, MaintainedTransactionPool};
|
use sc_transaction_pool_api::{ChainEvent, MaintainedTransactionPool};
|
||||||
use sp_consensus::{BlockOrigin, Environment, Proposer};
|
use sp_consensus::{BlockOrigin, Environment, Proposer};
|
||||||
use sp_core::{crypto::Pair as CryptoPair, Public, H256};
|
use sp_core::{crypto::Pair as CryptoPair, Public};
|
||||||
use sp_inherents::InherentDataProvider;
|
use sp_inherents::InherentDataProvider;
|
||||||
use sp_keyring::AccountKeyring;
|
use sp_keyring::AccountKeyring;
|
||||||
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
|
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
|
||||||
@@ -621,7 +618,7 @@ mod tests {
|
|||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut digest = Digest::<H256>::default();
|
let mut digest = Digest::default();
|
||||||
|
|
||||||
// even though there's only one authority some slots might be empty,
|
// even though there's only one authority some slots might be empty,
|
||||||
// so we must keep trying the next slots until we can claim one.
|
// so we must keep trying the next slots until we can claim one.
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ const SPEC_VERSION: u32 = node_runtime::VERSION.spec_version;
|
|||||||
|
|
||||||
const HEAP_PAGES: u64 = 20;
|
const HEAP_PAGES: u64 = 20;
|
||||||
|
|
||||||
type TestExternalities<H> = CoreTestExternalities<H, u64>;
|
type TestExternalities<H> = CoreTestExternalities<H>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum ExecutionMethod {
|
enum ExecutionMethod {
|
||||||
@@ -188,7 +188,7 @@ fn bench_execute_block(c: &mut Criterion) {
|
|||||||
|
|
||||||
for strategy in execution_methods {
|
for strategy in execution_methods {
|
||||||
group.bench_function(format!("{:?}", strategy), |b| {
|
group.bench_function(format!("{:?}", strategy), |b| {
|
||||||
let genesis_config = node_testing::genesis::config(false, Some(compact_code_unwrap()));
|
let genesis_config = node_testing::genesis::config(Some(compact_code_unwrap()));
|
||||||
let (use_native, wasm_method) = match strategy {
|
let (use_native, wasm_method) = match strategy {
|
||||||
ExecutionMethod::Native => (true, WasmExecutionMethod::Interpreted),
|
ExecutionMethod::Native => (true, WasmExecutionMethod::Interpreted),
|
||||||
ExecutionMethod::Wasm(wasm_method) => (false, wasm_method),
|
ExecutionMethod::Wasm(wasm_method) => (false, wasm_method),
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ use sp_runtime::{
|
|||||||
use node_primitives::{Balance, Hash};
|
use node_primitives::{Balance, Hash};
|
||||||
use node_runtime::{
|
use node_runtime::{
|
||||||
constants::{currency::*, time::SLOT_DURATION},
|
constants::{currency::*, time::SLOT_DURATION},
|
||||||
Balances, Block, Call, CheckedExtrinsic, Event, Header, Runtime, System, TransactionPayment,
|
Balances, Call, CheckedExtrinsic, Event, Header, Runtime, System, TransactionPayment,
|
||||||
UncheckedExtrinsic,
|
UncheckedExtrinsic,
|
||||||
};
|
};
|
||||||
use node_testing::keyring::*;
|
use node_testing::keyring::*;
|
||||||
@@ -78,7 +78,7 @@ fn set_heap_pages<E: Externalities>(ext: &mut E, heap_pages: u64) {
|
|||||||
fn changes_trie_block() -> (Vec<u8>, Hash) {
|
fn changes_trie_block() -> (Vec<u8>, Hash) {
|
||||||
let time = 42 * 1000;
|
let time = 42 * 1000;
|
||||||
construct_block(
|
construct_block(
|
||||||
&mut new_test_ext(compact_code_unwrap(), true),
|
&mut new_test_ext(compact_code_unwrap()),
|
||||||
1,
|
1,
|
||||||
GENESIS_HASH.into(),
|
GENESIS_HASH.into(),
|
||||||
vec![
|
vec![
|
||||||
@@ -102,7 +102,7 @@ fn changes_trie_block() -> (Vec<u8>, Hash) {
|
|||||||
/// are not guaranteed to be deterministic) and to ensure that the correct state is propagated
|
/// are not guaranteed to be deterministic) and to ensure that the correct state is propagated
|
||||||
/// from block1's execution to block2 to derive the correct storage_root.
|
/// from block1's execution to block2 to derive the correct storage_root.
|
||||||
fn blocks() -> ((Vec<u8>, Hash), (Vec<u8>, Hash)) {
|
fn blocks() -> ((Vec<u8>, Hash), (Vec<u8>, Hash)) {
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), false);
|
let mut t = new_test_ext(compact_code_unwrap());
|
||||||
let time1 = 42 * 1000;
|
let time1 = 42 * 1000;
|
||||||
let block1 = construct_block(
|
let block1 = construct_block(
|
||||||
&mut t,
|
&mut t,
|
||||||
@@ -160,7 +160,7 @@ fn blocks() -> ((Vec<u8>, Hash), (Vec<u8>, Hash)) {
|
|||||||
|
|
||||||
fn block_with_size(time: u64, nonce: u32, size: usize) -> (Vec<u8>, Hash) {
|
fn block_with_size(time: u64, nonce: u32, size: usize) -> (Vec<u8>, Hash) {
|
||||||
construct_block(
|
construct_block(
|
||||||
&mut new_test_ext(compact_code_unwrap(), false),
|
&mut new_test_ext(compact_code_unwrap()),
|
||||||
1,
|
1,
|
||||||
GENESIS_HASH.into(),
|
GENESIS_HASH.into(),
|
||||||
vec![
|
vec![
|
||||||
@@ -179,7 +179,7 @@ fn block_with_size(time: u64, nonce: u32, size: usize) -> (Vec<u8>, Hash) {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn panic_execution_with_foreign_code_gives_error() {
|
fn panic_execution_with_foreign_code_gives_error() {
|
||||||
let mut t = new_test_ext(bloaty_code_unwrap(), false);
|
let mut t = new_test_ext(bloaty_code_unwrap());
|
||||||
t.insert(
|
t.insert(
|
||||||
<frame_system::Account<Runtime>>::hashed_key_for(alice()),
|
<frame_system::Account<Runtime>>::hashed_key_for(alice()),
|
||||||
(69u128, 0u32, 0u128, 0u128, 0u128).encode(),
|
(69u128, 0u32, 0u128, 0u128, 0u128).encode(),
|
||||||
@@ -211,7 +211,7 @@ fn panic_execution_with_foreign_code_gives_error() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bad_extrinsic_with_native_equivalent_code_gives_error() {
|
fn bad_extrinsic_with_native_equivalent_code_gives_error() {
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), false);
|
let mut t = new_test_ext(compact_code_unwrap());
|
||||||
t.insert(
|
t.insert(
|
||||||
<frame_system::Account<Runtime>>::hashed_key_for(alice()),
|
<frame_system::Account<Runtime>>::hashed_key_for(alice()),
|
||||||
(0u32, 0u32, 0u32, 69u128, 0u128, 0u128, 0u128).encode(),
|
(0u32, 0u32, 0u32, 69u128, 0u128, 0u128, 0u128).encode(),
|
||||||
@@ -243,7 +243,7 @@ fn bad_extrinsic_with_native_equivalent_code_gives_error() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn successful_execution_with_native_equivalent_code_gives_ok() {
|
fn successful_execution_with_native_equivalent_code_gives_ok() {
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), false);
|
let mut t = new_test_ext(compact_code_unwrap());
|
||||||
t.insert(
|
t.insert(
|
||||||
<frame_system::Account<Runtime>>::hashed_key_for(alice()),
|
<frame_system::Account<Runtime>>::hashed_key_for(alice()),
|
||||||
AccountInfo::<<Runtime as frame_system::Config>::Index, _> {
|
AccountInfo::<<Runtime as frame_system::Config>::Index, _> {
|
||||||
@@ -296,7 +296,7 @@ fn successful_execution_with_native_equivalent_code_gives_ok() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn successful_execution_with_foreign_code_gives_ok() {
|
fn successful_execution_with_foreign_code_gives_ok() {
|
||||||
let mut t = new_test_ext(bloaty_code_unwrap(), false);
|
let mut t = new_test_ext(bloaty_code_unwrap());
|
||||||
t.insert(
|
t.insert(
|
||||||
<frame_system::Account<Runtime>>::hashed_key_for(alice()),
|
<frame_system::Account<Runtime>>::hashed_key_for(alice()),
|
||||||
AccountInfo::<<Runtime as frame_system::Config>::Index, _> {
|
AccountInfo::<<Runtime as frame_system::Config>::Index, _> {
|
||||||
@@ -349,7 +349,7 @@ fn successful_execution_with_foreign_code_gives_ok() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn full_native_block_import_works() {
|
fn full_native_block_import_works() {
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), false);
|
let mut t = new_test_ext(compact_code_unwrap());
|
||||||
|
|
||||||
let (block1, block2) = blocks();
|
let (block1, block2) = blocks();
|
||||||
|
|
||||||
@@ -529,7 +529,7 @@ fn full_native_block_import_works() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn full_wasm_block_import_works() {
|
fn full_wasm_block_import_works() {
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), false);
|
let mut t = new_test_ext(compact_code_unwrap());
|
||||||
|
|
||||||
let (block1, block2) = blocks();
|
let (block1, block2) = blocks();
|
||||||
|
|
||||||
@@ -679,7 +679,7 @@ fn deploying_wasm_contract_should_work() {
|
|||||||
|
|
||||||
let time = 42 * 1000;
|
let time = 42 * 1000;
|
||||||
let b = construct_block(
|
let b = construct_block(
|
||||||
&mut new_test_ext(compact_code_unwrap(), false),
|
&mut new_test_ext(compact_code_unwrap()),
|
||||||
1,
|
1,
|
||||||
GENESIS_HASH.into(),
|
GENESIS_HASH.into(),
|
||||||
vec![
|
vec![
|
||||||
@@ -712,7 +712,7 @@ fn deploying_wasm_contract_should_work() {
|
|||||||
(time / SLOT_DURATION).into(),
|
(time / SLOT_DURATION).into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), false);
|
let mut t = new_test_ext(compact_code_unwrap());
|
||||||
|
|
||||||
executor_call::<NeverNativeValue, fn() -> _>(&mut t, "Core_execute_block", &b.0, false, None)
|
executor_call::<NeverNativeValue, fn() -> _>(&mut t, "Core_execute_block", &b.0, false, None)
|
||||||
.0
|
.0
|
||||||
@@ -727,7 +727,7 @@ fn deploying_wasm_contract_should_work() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn wasm_big_block_import_fails() {
|
fn wasm_big_block_import_fails() {
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), false);
|
let mut t = new_test_ext(compact_code_unwrap());
|
||||||
|
|
||||||
set_heap_pages(&mut t.ext(), 4);
|
set_heap_pages(&mut t.ext(), 4);
|
||||||
|
|
||||||
@@ -744,7 +744,7 @@ fn wasm_big_block_import_fails() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn native_big_block_import_succeeds() {
|
fn native_big_block_import_succeeds() {
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), false);
|
let mut t = new_test_ext(compact_code_unwrap());
|
||||||
|
|
||||||
executor_call::<NeverNativeValue, fn() -> _>(
|
executor_call::<NeverNativeValue, fn() -> _>(
|
||||||
&mut t,
|
&mut t,
|
||||||
@@ -759,7 +759,7 @@ fn native_big_block_import_succeeds() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn native_big_block_import_fails_on_fallback() {
|
fn native_big_block_import_fails_on_fallback() {
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), false);
|
let mut t = new_test_ext(compact_code_unwrap());
|
||||||
|
|
||||||
// We set the heap pages to 8 because we know that should give an OOM in WASM with the given
|
// We set the heap pages to 8 because we know that should give an OOM in WASM with the given
|
||||||
// block.
|
// block.
|
||||||
@@ -778,7 +778,7 @@ fn native_big_block_import_fails_on_fallback() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn panic_execution_gives_error() {
|
fn panic_execution_gives_error() {
|
||||||
let mut t = new_test_ext(bloaty_code_unwrap(), false);
|
let mut t = new_test_ext(bloaty_code_unwrap());
|
||||||
t.insert(
|
t.insert(
|
||||||
<frame_system::Account<Runtime>>::hashed_key_for(alice()),
|
<frame_system::Account<Runtime>>::hashed_key_for(alice()),
|
||||||
AccountInfo::<<Runtime as frame_system::Config>::Index, _> {
|
AccountInfo::<<Runtime as frame_system::Config>::Index, _> {
|
||||||
@@ -815,7 +815,7 @@ fn panic_execution_gives_error() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn successful_execution_gives_ok() {
|
fn successful_execution_gives_ok() {
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), false);
|
let mut t = new_test_ext(compact_code_unwrap());
|
||||||
t.insert(
|
t.insert(
|
||||||
<frame_system::Account<Runtime>>::hashed_key_for(alice()),
|
<frame_system::Account<Runtime>>::hashed_key_for(alice()),
|
||||||
AccountInfo::<<Runtime as frame_system::Config>::Index, _> {
|
AccountInfo::<<Runtime as frame_system::Config>::Index, _> {
|
||||||
@@ -874,44 +874,6 @@ fn successful_execution_gives_ok() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn full_native_block_import_works_with_changes_trie() {
|
|
||||||
let block1 = changes_trie_block();
|
|
||||||
let block_data = block1.0;
|
|
||||||
let block = Block::decode(&mut &block_data[..]).unwrap();
|
|
||||||
|
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), true);
|
|
||||||
executor_call::<NeverNativeValue, fn() -> _>(
|
|
||||||
&mut t,
|
|
||||||
"Core_execute_block",
|
|
||||||
&block.encode(),
|
|
||||||
true,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.0
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(t.ext().storage_changes_root(&GENESIS_HASH).unwrap().is_some());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn full_wasm_block_import_works_with_changes_trie() {
|
|
||||||
let block1 = changes_trie_block();
|
|
||||||
|
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), true);
|
|
||||||
executor_call::<NeverNativeValue, fn() -> _>(
|
|
||||||
&mut t,
|
|
||||||
"Core_execute_block",
|
|
||||||
&block1.0,
|
|
||||||
false,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.0
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(t.ext().storage_changes_root(&GENESIS_HASH).unwrap().is_some());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_import_block_with_test_client() {
|
fn should_import_block_with_test_client() {
|
||||||
use node_testing::client::{
|
use node_testing::client::{
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ pub const SPEC_VERSION: u32 = node_runtime::VERSION.spec_version;
|
|||||||
|
|
||||||
pub const TRANSACTION_VERSION: u32 = node_runtime::VERSION.transaction_version;
|
pub const TRANSACTION_VERSION: u32 = node_runtime::VERSION.transaction_version;
|
||||||
|
|
||||||
pub type TestExternalities<H> = CoreTestExternalities<H, u64>;
|
pub type TestExternalities<H> = CoreTestExternalities<H>;
|
||||||
|
|
||||||
pub fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic {
|
pub fn sign(xt: CheckedExtrinsic) -> UncheckedExtrinsic {
|
||||||
node_testing::keyring::sign(xt, SPEC_VERSION, TRANSACTION_VERSION, GENESIS_HASH)
|
node_testing::keyring::sign(xt, SPEC_VERSION, TRANSACTION_VERSION, GENESIS_HASH)
|
||||||
@@ -123,14 +123,11 @@ pub fn executor_call<
|
|||||||
executor().call::<R, NC>(&mut t, &runtime_code, method, data, use_native, native_call)
|
executor().call::<R, NC>(&mut t, &runtime_code, method, data, use_native, native_call)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_test_ext(code: &[u8], support_changes_trie: bool) -> TestExternalities<BlakeTwo256> {
|
pub fn new_test_ext(code: &[u8]) -> TestExternalities<BlakeTwo256> {
|
||||||
let mut ext = TestExternalities::new_with_code(
|
let ext = TestExternalities::new_with_code(
|
||||||
code,
|
code,
|
||||||
node_testing::genesis::config(support_changes_trie, Some(code))
|
node_testing::genesis::config(Some(code)).build_storage().unwrap(),
|
||||||
.build_storage()
|
|
||||||
.unwrap(),
|
|
||||||
);
|
);
|
||||||
ext.changes_trie_storage().insert(0, GENESIS_HASH.into(), Default::default());
|
|
||||||
ext
|
ext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ use self::common::{sign, *};
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fee_multiplier_increases_and_decreases_on_big_weight() {
|
fn fee_multiplier_increases_and_decreases_on_big_weight() {
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), false);
|
let mut t = new_test_ext(compact_code_unwrap());
|
||||||
|
|
||||||
// initial fee multiplier must be one.
|
// initial fee multiplier must be one.
|
||||||
let mut prev_multiplier = Multiplier::one();
|
let mut prev_multiplier = Multiplier::one();
|
||||||
@@ -45,7 +45,7 @@ fn fee_multiplier_increases_and_decreases_on_big_weight() {
|
|||||||
assert_eq!(TransactionPayment::next_fee_multiplier(), prev_multiplier);
|
assert_eq!(TransactionPayment::next_fee_multiplier(), prev_multiplier);
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut tt = new_test_ext(compact_code_unwrap(), false);
|
let mut tt = new_test_ext(compact_code_unwrap());
|
||||||
|
|
||||||
let time1 = 42 * 1000;
|
let time1 = 42 * 1000;
|
||||||
// big one in terms of weight.
|
// big one in terms of weight.
|
||||||
@@ -151,7 +151,7 @@ fn transaction_fee_is_correct() {
|
|||||||
// - 1 MILLICENTS in substrate node.
|
// - 1 MILLICENTS in substrate node.
|
||||||
// - 1 milli-dot based on current polkadot runtime.
|
// - 1 milli-dot based on current polkadot runtime.
|
||||||
// (this baed on assigning 0.1 CENT to the cheapest tx with `weight = 100`)
|
// (this baed on assigning 0.1 CENT to the cheapest tx with `weight = 100`)
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), false);
|
let mut t = new_test_ext(compact_code_unwrap());
|
||||||
t.insert(<frame_system::Account<Runtime>>::hashed_key_for(alice()), new_account_info(100));
|
t.insert(<frame_system::Account<Runtime>>::hashed_key_for(alice()), new_account_info(100));
|
||||||
t.insert(<frame_system::Account<Runtime>>::hashed_key_for(bob()), new_account_info(10));
|
t.insert(<frame_system::Account<Runtime>>::hashed_key_for(bob()), new_account_info(10));
|
||||||
t.insert(
|
t.insert(
|
||||||
@@ -226,9 +226,9 @@ fn block_weight_capacity_report() {
|
|||||||
use node_primitives::Index;
|
use node_primitives::Index;
|
||||||
|
|
||||||
// execution ext.
|
// execution ext.
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), false);
|
let mut t = new_test_ext(compact_code_unwrap());
|
||||||
// setup ext.
|
// setup ext.
|
||||||
let mut tt = new_test_ext(compact_code_unwrap(), false);
|
let mut tt = new_test_ext(compact_code_unwrap());
|
||||||
|
|
||||||
let factor = 50;
|
let factor = 50;
|
||||||
let mut time = 10;
|
let mut time = 10;
|
||||||
@@ -303,9 +303,9 @@ fn block_length_capacity_report() {
|
|||||||
use node_primitives::Index;
|
use node_primitives::Index;
|
||||||
|
|
||||||
// execution ext.
|
// execution ext.
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), false);
|
let mut t = new_test_ext(compact_code_unwrap());
|
||||||
// setup ext.
|
// setup ext.
|
||||||
let mut tt = new_test_ext(compact_code_unwrap(), false);
|
let mut tt = new_test_ext(compact_code_unwrap());
|
||||||
|
|
||||||
let factor = 256 * 1024;
|
let factor = 256 * 1024;
|
||||||
let mut time = 10;
|
let mut time = 10;
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ use self::common::*;
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_submit_unsigned_transaction() {
|
fn should_submit_unsigned_transaction() {
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), false);
|
let mut t = new_test_ext(compact_code_unwrap());
|
||||||
let (pool, state) = TestTransactionPoolExt::new();
|
let (pool, state) = TestTransactionPoolExt::new();
|
||||||
t.register_extension(TransactionPoolExt::new(pool));
|
t.register_extension(TransactionPoolExt::new(pool));
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ const PHRASE: &str = "news slush supreme milk chapter athlete soap sausage put c
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_submit_signed_transaction() {
|
fn should_submit_signed_transaction() {
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), false);
|
let mut t = new_test_ext(compact_code_unwrap());
|
||||||
let (pool, state) = TestTransactionPoolExt::new();
|
let (pool, state) = TestTransactionPoolExt::new();
|
||||||
t.register_extension(TransactionPoolExt::new(pool));
|
t.register_extension(TransactionPoolExt::new(pool));
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ fn should_submit_signed_transaction() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_submit_signed_twice_from_the_same_account() {
|
fn should_submit_signed_twice_from_the_same_account() {
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), false);
|
let mut t = new_test_ext(compact_code_unwrap());
|
||||||
let (pool, state) = TestTransactionPoolExt::new();
|
let (pool, state) = TestTransactionPoolExt::new();
|
||||||
t.register_extension(TransactionPoolExt::new(pool));
|
t.register_extension(TransactionPoolExt::new(pool));
|
||||||
|
|
||||||
@@ -156,7 +156,7 @@ fn should_submit_signed_twice_from_the_same_account() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_submit_signed_twice_from_all_accounts() {
|
fn should_submit_signed_twice_from_all_accounts() {
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), false);
|
let mut t = new_test_ext(compact_code_unwrap());
|
||||||
let (pool, state) = TestTransactionPoolExt::new();
|
let (pool, state) = TestTransactionPoolExt::new();
|
||||||
t.register_extension(TransactionPoolExt::new(pool));
|
t.register_extension(TransactionPoolExt::new(pool));
|
||||||
|
|
||||||
@@ -220,7 +220,7 @@ fn submitted_transaction_should_be_valid() {
|
|||||||
transaction_validity::{TransactionSource, TransactionTag},
|
transaction_validity::{TransactionSource, TransactionTag},
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), false);
|
let mut t = new_test_ext(compact_code_unwrap());
|
||||||
let (pool, state) = TestTransactionPoolExt::new();
|
let (pool, state) = TestTransactionPoolExt::new();
|
||||||
t.register_extension(TransactionPoolExt::new(pool));
|
t.register_extension(TransactionPoolExt::new(pool));
|
||||||
|
|
||||||
@@ -249,7 +249,7 @@ fn submitted_transaction_should_be_valid() {
|
|||||||
// check that transaction is valid, but reset environment storage,
|
// check that transaction is valid, but reset environment storage,
|
||||||
// since CreateTransaction increments the nonce
|
// since CreateTransaction increments the nonce
|
||||||
let tx0 = state.read().transactions[0].clone();
|
let tx0 = state.read().transactions[0].clone();
|
||||||
let mut t = new_test_ext(compact_code_unwrap(), false);
|
let mut t = new_test_ext(compact_code_unwrap());
|
||||||
t.execute_with(|| {
|
t.execute_with(|| {
|
||||||
let source = TransactionSource::External;
|
let source = TransactionSource::External;
|
||||||
let extrinsic = UncheckedExtrinsic::decode(&mut &*tx0).unwrap();
|
let extrinsic = UncheckedExtrinsic::decode(&mut &*tx0).unwrap();
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ pub type Hash = sp_core::H256;
|
|||||||
pub type Timestamp = u64;
|
pub type Timestamp = u64;
|
||||||
|
|
||||||
/// Digest item type.
|
/// Digest item type.
|
||||||
pub type DigestItem = generic::DigestItem<Hash>;
|
pub type DigestItem = generic::DigestItem;
|
||||||
/// Header type.
|
/// Header type.
|
||||||
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||||
/// Block type.
|
/// Block type.
|
||||||
|
|||||||
@@ -51,18 +51,6 @@ use sp_consensus::SelectChain;
|
|||||||
use sp_consensus_babe::BabeApi;
|
use sp_consensus_babe::BabeApi;
|
||||||
use sp_keystore::SyncCryptoStorePtr;
|
use sp_keystore::SyncCryptoStorePtr;
|
||||||
|
|
||||||
/// Light client extra dependencies.
|
|
||||||
pub struct LightDeps<C, F, P> {
|
|
||||||
/// The client instance to use.
|
|
||||||
pub client: Arc<C>,
|
|
||||||
/// Transaction pool instance.
|
|
||||||
pub pool: Arc<P>,
|
|
||||||
/// Remote access to the blockchain (async).
|
|
||||||
pub remote_blockchain: Arc<dyn sc_client_api::light::RemoteBlockchain<Block>>,
|
|
||||||
/// Fetcher instance.
|
|
||||||
pub fetcher: Arc<F>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extra dependencies for BABE.
|
/// Extra dependencies for BABE.
|
||||||
pub struct BabeDeps {
|
pub struct BabeDeps {
|
||||||
/// BABE protocol config.
|
/// BABE protocol config.
|
||||||
@@ -183,26 +171,3 @@ where
|
|||||||
|
|
||||||
Ok(io)
|
Ok(io)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Instantiate all Light RPC extensions.
|
|
||||||
pub fn create_light<C, P, M, F>(deps: LightDeps<C, F, P>) -> jsonrpc_core::IoHandler<M>
|
|
||||||
where
|
|
||||||
C: sp_blockchain::HeaderBackend<Block>,
|
|
||||||
C: Send + Sync + 'static,
|
|
||||||
F: sc_client_api::light::Fetcher<Block> + 'static,
|
|
||||||
P: TransactionPool + 'static,
|
|
||||||
M: jsonrpc_core::Metadata + Default,
|
|
||||||
{
|
|
||||||
use substrate_frame_rpc_system::{LightSystem, SystemApi};
|
|
||||||
|
|
||||||
let LightDeps { client, pool, remote_blockchain, fetcher } = deps;
|
|
||||||
let mut io = jsonrpc_core::IoHandler::default();
|
|
||||||
io.extend_with(SystemApi::<Hash, AccountId, Index>::to_delegate(LightSystem::new(
|
|
||||||
client,
|
|
||||||
remote_blockchain,
|
|
||||||
fetcher,
|
|
||||||
pool,
|
|
||||||
)));
|
|
||||||
|
|
||||||
io
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -591,7 +591,6 @@ impl BenchKeyring {
|
|||||||
/// Generate genesis with accounts from this keyring endowed with some balance.
|
/// Generate genesis with accounts from this keyring endowed with some balance.
|
||||||
pub fn generate_genesis(&self) -> node_runtime::GenesisConfig {
|
pub fn generate_genesis(&self) -> node_runtime::GenesisConfig {
|
||||||
crate::genesis::config_endowed(
|
crate::genesis::config_endowed(
|
||||||
false,
|
|
||||||
Some(node_runtime::wasm_binary_unwrap()),
|
Some(node_runtime::wasm_binary_unwrap()),
|
||||||
self.collect_account_ids(),
|
self.collect_account_ids(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -42,13 +42,11 @@ pub type Transaction = sc_client_api::backend::TransactionFor<Backend, node_prim
|
|||||||
|
|
||||||
/// Genesis configuration parameters for `TestClient`.
|
/// Genesis configuration parameters for `TestClient`.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct GenesisParameters {
|
pub struct GenesisParameters;
|
||||||
support_changes_trie: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl substrate_test_client::GenesisInit for GenesisParameters {
|
impl substrate_test_client::GenesisInit for GenesisParameters {
|
||||||
fn genesis_storage(&self) -> Storage {
|
fn genesis_storage(&self) -> Storage {
|
||||||
crate::genesis::config(self.support_changes_trie, None).build_storage().unwrap()
|
crate::genesis::config(None).build_storage().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,22 +24,17 @@ use node_runtime::{
|
|||||||
GenesisConfig, GrandpaConfig, IndicesConfig, SessionConfig, SocietyConfig, StakerStatus,
|
GenesisConfig, GrandpaConfig, IndicesConfig, SessionConfig, SocietyConfig, StakerStatus,
|
||||||
StakingConfig, SystemConfig, BABE_GENESIS_EPOCH_CONFIG,
|
StakingConfig, SystemConfig, BABE_GENESIS_EPOCH_CONFIG,
|
||||||
};
|
};
|
||||||
use sp_core::ChangesTrieConfiguration;
|
|
||||||
use sp_keyring::{Ed25519Keyring, Sr25519Keyring};
|
use sp_keyring::{Ed25519Keyring, Sr25519Keyring};
|
||||||
use sp_runtime::Perbill;
|
use sp_runtime::Perbill;
|
||||||
|
|
||||||
/// Create genesis runtime configuration for tests.
|
/// Create genesis runtime configuration for tests.
|
||||||
pub fn config(support_changes_trie: bool, code: Option<&[u8]>) -> GenesisConfig {
|
pub fn config(code: Option<&[u8]>) -> GenesisConfig {
|
||||||
config_endowed(support_changes_trie, code, Default::default())
|
config_endowed(code, Default::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create genesis runtime configuration for tests with some extra
|
/// Create genesis runtime configuration for tests with some extra
|
||||||
/// endowed accounts.
|
/// endowed accounts.
|
||||||
pub fn config_endowed(
|
pub fn config_endowed(code: Option<&[u8]>, extra_endowed: Vec<AccountId>) -> GenesisConfig {
|
||||||
support_changes_trie: bool,
|
|
||||||
code: Option<&[u8]>,
|
|
||||||
extra_endowed: Vec<AccountId>,
|
|
||||||
) -> GenesisConfig {
|
|
||||||
let mut endowed = vec![
|
let mut endowed = vec![
|
||||||
(alice(), 111 * DOLLARS),
|
(alice(), 111 * DOLLARS),
|
||||||
(bob(), 100 * DOLLARS),
|
(bob(), 100 * DOLLARS),
|
||||||
@@ -53,11 +48,6 @@ pub fn config_endowed(
|
|||||||
|
|
||||||
GenesisConfig {
|
GenesisConfig {
|
||||||
system: SystemConfig {
|
system: SystemConfig {
|
||||||
changes_trie_config: if support_changes_trie {
|
|
||||||
Some(ChangesTrieConfiguration { digest_interval: 2, digest_levels: 2 })
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
code: code.map(|x| x.to_vec()).unwrap_or_else(|| wasm_binary_unwrap().to_vec()),
|
code: code.map(|x| x.to_vec()).unwrap_or_else(|| wasm_binary_unwrap().to_vec()),
|
||||||
},
|
},
|
||||||
indices: IndicesConfig { indices: vec![] },
|
indices: IndicesConfig { indices: vec![] },
|
||||||
|
|||||||
@@ -20,27 +20,22 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
blockchain::{well_known_cache_keys, Backend as BlockchainBackend},
|
blockchain::{well_known_cache_keys, Backend as BlockchainBackend},
|
||||||
light::RemoteBlockchain,
|
|
||||||
UsageInfo,
|
UsageInfo,
|
||||||
};
|
};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use sp_blockchain;
|
use sp_blockchain;
|
||||||
use sp_consensus::BlockOrigin;
|
use sp_consensus::BlockOrigin;
|
||||||
use sp_core::{offchain::OffchainStorage, ChangesTrieConfigurationRange};
|
use sp_core::offchain::OffchainStorage;
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::BlockId,
|
generic::BlockId,
|
||||||
traits::{Block as BlockT, HashFor, NumberFor},
|
traits::{Block as BlockT, HashFor, NumberFor},
|
||||||
Justification, Justifications, Storage,
|
Justification, Justifications, Storage,
|
||||||
};
|
};
|
||||||
use sp_state_machine::{
|
use sp_state_machine::{
|
||||||
ChangesTrieState, ChangesTrieStorage as StateChangesTrieStorage, ChangesTrieTransaction,
|
|
||||||
ChildStorageCollection, IndexOperation, OffchainChangesCollection, StorageCollection,
|
ChildStorageCollection, IndexOperation, OffchainChangesCollection, StorageCollection,
|
||||||
};
|
};
|
||||||
use sp_storage::{ChildInfo, PrefixedStorageKey, StorageData, StorageKey};
|
use sp_storage::{ChildInfo, StorageData, StorageKey};
|
||||||
use std::{
|
use std::collections::{HashMap, HashSet};
|
||||||
collections::{HashMap, HashSet},
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use sp_state_machine::{Backend as StateBackend, KeyValueStates};
|
pub use sp_state_machine::{Backend as StateBackend, KeyValueStates};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
@@ -191,12 +186,6 @@ pub trait BlockImportOperation<Block: BlockT> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inject changes trie data into the database.
|
|
||||||
fn update_changes_trie(
|
|
||||||
&mut self,
|
|
||||||
update: ChangesTrieTransaction<HashFor<Block>, NumberFor<Block>>,
|
|
||||||
) -> sp_blockchain::Result<()>;
|
|
||||||
|
|
||||||
/// Insert auxiliary keys.
|
/// Insert auxiliary keys.
|
||||||
///
|
///
|
||||||
/// Values are `None` if should be deleted.
|
/// Values are `None` if should be deleted.
|
||||||
@@ -418,28 +407,6 @@ pub trait StorageProvider<Block: BlockT, B: Backend<Block>> {
|
|||||||
child_info: &ChildInfo,
|
child_info: &ChildInfo,
|
||||||
key: &StorageKey,
|
key: &StorageKey,
|
||||||
) -> sp_blockchain::Result<Option<Block::Hash>>;
|
) -> sp_blockchain::Result<Option<Block::Hash>>;
|
||||||
|
|
||||||
/// Get longest range within [first; last] that is possible to use in `key_changes`
|
|
||||||
/// and `key_changes_proof` calls.
|
|
||||||
/// Range could be shortened from the beginning if some changes tries have been pruned.
|
|
||||||
/// Returns Ok(None) if changes tries are not supported.
|
|
||||||
fn max_key_changes_range(
|
|
||||||
&self,
|
|
||||||
first: NumberFor<Block>,
|
|
||||||
last: BlockId<Block>,
|
|
||||||
) -> sp_blockchain::Result<Option<(NumberFor<Block>, BlockId<Block>)>>;
|
|
||||||
|
|
||||||
/// Get pairs of (block, extrinsic) where key has been changed at given blocks range.
|
|
||||||
/// Works only for runtimes that are supporting changes tries.
|
|
||||||
///
|
|
||||||
/// Changes are returned in descending order (i.e. last block comes first).
|
|
||||||
fn key_changes(
|
|
||||||
&self,
|
|
||||||
first: NumberFor<Block>,
|
|
||||||
last: BlockId<Block>,
|
|
||||||
storage_key: Option<&PrefixedStorageKey>,
|
|
||||||
key: &StorageKey,
|
|
||||||
) -> sp_blockchain::Result<Vec<(NumberFor<Block>, u32)>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Client backend.
|
/// Client backend.
|
||||||
@@ -504,9 +471,6 @@ pub trait Backend<Block: BlockT>: AuxStore + Send + Sync {
|
|||||||
/// Returns current usage statistics.
|
/// Returns current usage statistics.
|
||||||
fn usage_info(&self) -> Option<UsageInfo>;
|
fn usage_info(&self) -> Option<UsageInfo>;
|
||||||
|
|
||||||
/// Returns reference to changes trie storage.
|
|
||||||
fn changes_trie_storage(&self) -> Option<&dyn PrunableStateChangesTrieStorage<Block>>;
|
|
||||||
|
|
||||||
/// Returns a handle to offchain storage.
|
/// Returns a handle to offchain storage.
|
||||||
fn offchain_storage(&self) -> Option<Self::OffchainStorage>;
|
fn offchain_storage(&self) -> Option<Self::OffchainStorage>;
|
||||||
|
|
||||||
@@ -561,72 +525,5 @@ pub trait Backend<Block: BlockT>: AuxStore + Send + Sync {
|
|||||||
fn get_import_lock(&self) -> &RwLock<()>;
|
fn get_import_lock(&self) -> &RwLock<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Changes trie storage that supports pruning.
|
|
||||||
pub trait PrunableStateChangesTrieStorage<Block: BlockT>:
|
|
||||||
StateChangesTrieStorage<HashFor<Block>, NumberFor<Block>>
|
|
||||||
{
|
|
||||||
/// Get reference to StateChangesTrieStorage.
|
|
||||||
fn storage(&self) -> &dyn StateChangesTrieStorage<HashFor<Block>, NumberFor<Block>>;
|
|
||||||
/// Get configuration at given block.
|
|
||||||
fn configuration_at(
|
|
||||||
&self,
|
|
||||||
at: &BlockId<Block>,
|
|
||||||
) -> sp_blockchain::Result<ChangesTrieConfigurationRange<NumberFor<Block>, Block::Hash>>;
|
|
||||||
/// Get end block (inclusive) of oldest pruned max-level (or skewed) digest trie blocks range.
|
|
||||||
/// It is guaranteed that we have no any changes tries before (and including) this block.
|
|
||||||
/// It is guaranteed that all existing changes tries after this block are not yet pruned (if
|
|
||||||
/// created).
|
|
||||||
fn oldest_pruned_digest_range_end(&self) -> NumberFor<Block>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mark for all Backend implementations, that are making use of state data, stored locally.
|
/// Mark for all Backend implementations, that are making use of state data, stored locally.
|
||||||
pub trait LocalBackend<Block: BlockT>: Backend<Block> {}
|
pub trait LocalBackend<Block: BlockT>: Backend<Block> {}
|
||||||
|
|
||||||
/// Mark for all Backend implementations, that are fetching required state data from remote nodes.
|
|
||||||
pub trait RemoteBackend<Block: BlockT>: Backend<Block> {
|
|
||||||
/// Returns true if the state for given block is available locally.
|
|
||||||
fn is_local_state_available(&self, block: &BlockId<Block>) -> bool;
|
|
||||||
|
|
||||||
/// Returns reference to blockchain backend.
|
|
||||||
///
|
|
||||||
/// Returned backend either resolves blockchain data
|
|
||||||
/// locally, or prepares request to fetch that data from remote node.
|
|
||||||
fn remote_blockchain(&self) -> Arc<dyn RemoteBlockchain<Block>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return changes tries state at given block.
|
|
||||||
pub fn changes_tries_state_at_block<'a, Block: BlockT>(
|
|
||||||
block: &BlockId<Block>,
|
|
||||||
maybe_storage: Option<&'a dyn PrunableStateChangesTrieStorage<Block>>,
|
|
||||||
) -> sp_blockchain::Result<Option<ChangesTrieState<'a, HashFor<Block>, NumberFor<Block>>>> {
|
|
||||||
let storage = match maybe_storage {
|
|
||||||
Some(storage) => storage,
|
|
||||||
None => return Ok(None),
|
|
||||||
};
|
|
||||||
|
|
||||||
let config_range = storage.configuration_at(block)?;
|
|
||||||
match config_range.config {
|
|
||||||
Some(config) =>
|
|
||||||
Ok(Some(ChangesTrieState::new(config, config_range.zero.0, storage.storage()))),
|
|
||||||
None => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Provide CHT roots. These are stored on a light client and generated dynamically on a full
|
|
||||||
/// client.
|
|
||||||
pub trait ProvideChtRoots<Block: BlockT> {
|
|
||||||
/// Get headers CHT root for given block. Returns None if the block is not a part of any CHT.
|
|
||||||
fn header_cht_root(
|
|
||||||
&self,
|
|
||||||
cht_size: NumberFor<Block>,
|
|
||||||
block: NumberFor<Block>,
|
|
||||||
) -> sp_blockchain::Result<Option<Block::Hash>>;
|
|
||||||
|
|
||||||
/// Get changes trie CHT root for given block. Returns None if the block is not a part of any
|
|
||||||
/// CHT.
|
|
||||||
fn changes_trie_cht_root(
|
|
||||||
&self,
|
|
||||||
cht_size: NumberFor<Block>,
|
|
||||||
block: NumberFor<Block>,
|
|
||||||
) -> sp_blockchain::Result<Option<Block::Hash>>;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,474 +0,0 @@
|
|||||||
// This file is part of Substrate.
|
|
||||||
|
|
||||||
// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Canonical hash trie definitions and helper functions.
|
|
||||||
//!
|
|
||||||
//! Each CHT is a trie mapping block numbers to canonical hash.
|
|
||||||
//! One is generated for every `SIZE` blocks, allowing us to discard those blocks in
|
|
||||||
//! favor of the trie root. When the "ancient" blocks need to be accessed, we simply
|
|
||||||
//! request an inclusion proof of a specific block number against the trie with the
|
|
||||||
//! root hash. A correct proof implies that the claimed block is identical to the one
|
|
||||||
//! we discarded.
|
|
||||||
|
|
||||||
use codec::Encode;
|
|
||||||
use hash_db;
|
|
||||||
use sp_trie;
|
|
||||||
|
|
||||||
use sp_core::{convert_hash, H256};
|
|
||||||
use sp_runtime::traits::{AtLeast32Bit, Header as HeaderT, One, Zero};
|
|
||||||
use sp_state_machine::{
|
|
||||||
prove_read_on_trie_backend, read_proof_check, read_proof_check_on_proving_backend,
|
|
||||||
Backend as StateBackend, InMemoryBackend, MemoryDB, StorageProof, TrieBackend,
|
|
||||||
};
|
|
||||||
|
|
||||||
use sp_blockchain::{Error as ClientError, Result as ClientResult};
|
|
||||||
|
|
||||||
/// The size of each CHT. This value is passed to every CHT-related function from
|
|
||||||
/// production code. Other values are passed from tests.
|
|
||||||
const SIZE: u32 = 2048;
|
|
||||||
|
|
||||||
/// Gets default CHT size.
|
|
||||||
pub fn size<N: From<u32>>() -> N {
|
|
||||||
SIZE.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns Some(cht_number) if CHT is need to be built when the block with given number is
|
|
||||||
/// canonized.
|
|
||||||
pub fn is_build_required<N>(cht_size: N, block_num: N) -> Option<N>
|
|
||||||
where
|
|
||||||
N: Clone + AtLeast32Bit,
|
|
||||||
{
|
|
||||||
let block_cht_num = block_to_cht_number(cht_size.clone(), block_num.clone())?;
|
|
||||||
let two = N::one() + N::one();
|
|
||||||
if block_cht_num < two {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
let cht_start = start_number(cht_size, block_cht_num.clone());
|
|
||||||
if cht_start != block_num {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(block_cht_num - two)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns Some(max_cht_number) if CHT has ever been built given maximal canonical block number.
|
|
||||||
pub fn max_cht_number<N>(cht_size: N, max_canonical_block: N) -> Option<N>
|
|
||||||
where
|
|
||||||
N: Clone + AtLeast32Bit,
|
|
||||||
{
|
|
||||||
let max_cht_number = block_to_cht_number(cht_size, max_canonical_block)?;
|
|
||||||
let two = N::one() + N::one();
|
|
||||||
if max_cht_number < two {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
Some(max_cht_number - two)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compute a CHT root from an iterator of block hashes. Fails if shorter than
|
|
||||||
/// SIZE items. The items are assumed to proceed sequentially from `start_number(cht_num)`.
|
|
||||||
/// Discards the trie's nodes.
|
|
||||||
pub fn compute_root<Header, Hasher, I>(
|
|
||||||
cht_size: Header::Number,
|
|
||||||
cht_num: Header::Number,
|
|
||||||
hashes: I,
|
|
||||||
) -> ClientResult<Hasher::Out>
|
|
||||||
where
|
|
||||||
Header: HeaderT,
|
|
||||||
Hasher: hash_db::Hasher,
|
|
||||||
Hasher::Out: Ord,
|
|
||||||
I: IntoIterator<Item = ClientResult<Option<Header::Hash>>>,
|
|
||||||
{
|
|
||||||
use sp_trie::TrieConfiguration;
|
|
||||||
Ok(sp_trie::trie_types::Layout::<Hasher>::trie_root(build_pairs::<Header, I>(
|
|
||||||
cht_size, cht_num, hashes,
|
|
||||||
)?))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build CHT-based header proof.
|
|
||||||
pub fn build_proof<Header, Hasher, BlocksI, HashesI>(
|
|
||||||
cht_size: Header::Number,
|
|
||||||
cht_num: Header::Number,
|
|
||||||
blocks: BlocksI,
|
|
||||||
hashes: HashesI,
|
|
||||||
) -> ClientResult<StorageProof>
|
|
||||||
where
|
|
||||||
Header: HeaderT,
|
|
||||||
Hasher: hash_db::Hasher,
|
|
||||||
Hasher::Out: Ord + codec::Codec,
|
|
||||||
BlocksI: IntoIterator<Item = Header::Number>,
|
|
||||||
HashesI: IntoIterator<Item = ClientResult<Option<Header::Hash>>>,
|
|
||||||
{
|
|
||||||
let transaction = build_pairs::<Header, _>(cht_size, cht_num, hashes)?
|
|
||||||
.into_iter()
|
|
||||||
.map(|(k, v)| (k, Some(v)))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let storage = InMemoryBackend::<Hasher>::default().update(vec![(None, transaction)]);
|
|
||||||
let trie_storage = storage
|
|
||||||
.as_trie_backend()
|
|
||||||
.expect("InMemoryState::as_trie_backend always returns Some; qed");
|
|
||||||
prove_read_on_trie_backend(
|
|
||||||
trie_storage,
|
|
||||||
blocks.into_iter().map(|number| encode_cht_key(number)),
|
|
||||||
)
|
|
||||||
.map_err(ClientError::from_state)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check CHT-based header proof.
|
|
||||||
pub fn check_proof<Header, Hasher>(
|
|
||||||
local_root: Header::Hash,
|
|
||||||
local_number: Header::Number,
|
|
||||||
remote_hash: Header::Hash,
|
|
||||||
remote_proof: StorageProof,
|
|
||||||
) -> ClientResult<()>
|
|
||||||
where
|
|
||||||
Header: HeaderT,
|
|
||||||
Hasher: hash_db::Hasher,
|
|
||||||
Hasher::Out: Ord + codec::Codec,
|
|
||||||
{
|
|
||||||
do_check_proof::<Header, Hasher, _>(
|
|
||||||
local_root,
|
|
||||||
local_number,
|
|
||||||
remote_hash,
|
|
||||||
move |local_root, local_cht_key| {
|
|
||||||
read_proof_check::<Hasher, _>(
|
|
||||||
local_root,
|
|
||||||
remote_proof,
|
|
||||||
::std::iter::once(local_cht_key),
|
|
||||||
)
|
|
||||||
.map(|mut map| map.remove(local_cht_key).expect("checked proof of local_cht_key; qed"))
|
|
||||||
.map_err(ClientError::from_state)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check CHT-based header proof on pre-created proving backend.
|
|
||||||
pub fn check_proof_on_proving_backend<Header, Hasher>(
|
|
||||||
local_root: Header::Hash,
|
|
||||||
local_number: Header::Number,
|
|
||||||
remote_hash: Header::Hash,
|
|
||||||
proving_backend: &TrieBackend<MemoryDB<Hasher>, Hasher>,
|
|
||||||
) -> ClientResult<()>
|
|
||||||
where
|
|
||||||
Header: HeaderT,
|
|
||||||
Hasher: hash_db::Hasher,
|
|
||||||
Hasher::Out: Ord + codec::Codec,
|
|
||||||
{
|
|
||||||
do_check_proof::<Header, Hasher, _>(
|
|
||||||
local_root,
|
|
||||||
local_number,
|
|
||||||
remote_hash,
|
|
||||||
|_, local_cht_key| {
|
|
||||||
read_proof_check_on_proving_backend::<Hasher>(proving_backend, local_cht_key)
|
|
||||||
.map_err(ClientError::from_state)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check CHT-based header proof using passed checker function.
|
|
||||||
fn do_check_proof<Header, Hasher, F>(
|
|
||||||
local_root: Header::Hash,
|
|
||||||
local_number: Header::Number,
|
|
||||||
remote_hash: Header::Hash,
|
|
||||||
checker: F,
|
|
||||||
) -> ClientResult<()>
|
|
||||||
where
|
|
||||||
Header: HeaderT,
|
|
||||||
Hasher: hash_db::Hasher,
|
|
||||||
Hasher::Out: Ord,
|
|
||||||
F: FnOnce(Hasher::Out, &[u8]) -> ClientResult<Option<Vec<u8>>>,
|
|
||||||
{
|
|
||||||
let root: Hasher::Out = convert_hash(&local_root);
|
|
||||||
let local_cht_key = encode_cht_key(local_number);
|
|
||||||
let local_cht_value = checker(root, &local_cht_key)?;
|
|
||||||
let local_cht_value = local_cht_value.ok_or_else(|| ClientError::InvalidCHTProof)?;
|
|
||||||
let local_hash =
|
|
||||||
decode_cht_value(&local_cht_value).ok_or_else(|| ClientError::InvalidCHTProof)?;
|
|
||||||
match &local_hash[..] == remote_hash.as_ref() {
|
|
||||||
true => Ok(()),
|
|
||||||
false => Err(ClientError::InvalidCHTProof.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Group ordered blocks by CHT number and call functor with blocks of each group.
|
|
||||||
pub fn for_each_cht_group<Header, I, F, P>(
|
|
||||||
cht_size: Header::Number,
|
|
||||||
blocks: I,
|
|
||||||
mut functor: F,
|
|
||||||
mut functor_param: P,
|
|
||||||
) -> ClientResult<()>
|
|
||||||
where
|
|
||||||
Header: HeaderT,
|
|
||||||
I: IntoIterator<Item = Header::Number>,
|
|
||||||
F: FnMut(P, Header::Number, Vec<Header::Number>) -> ClientResult<P>,
|
|
||||||
{
|
|
||||||
let mut current_cht_num = None;
|
|
||||||
let mut current_cht_blocks = Vec::new();
|
|
||||||
for block in blocks {
|
|
||||||
let new_cht_num = block_to_cht_number(cht_size, block).ok_or_else(|| {
|
|
||||||
ClientError::Backend(format!("Cannot compute CHT root for the block #{}", block))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let advance_to_next_cht = current_cht_num.is_some() && current_cht_num != Some(new_cht_num);
|
|
||||||
if advance_to_next_cht {
|
|
||||||
let current_cht_num = current_cht_num.expect(
|
|
||||||
"advance_to_next_cht is true;
|
|
||||||
it is true only when current_cht_num is Some; qed",
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
new_cht_num > current_cht_num,
|
|
||||||
"for_each_cht_group only supports ordered iterators"
|
|
||||||
);
|
|
||||||
|
|
||||||
functor_param =
|
|
||||||
functor(functor_param, current_cht_num, std::mem::take(&mut current_cht_blocks))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
current_cht_blocks.push(block);
|
|
||||||
current_cht_num = Some(new_cht_num);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(current_cht_num) = current_cht_num {
|
|
||||||
functor(functor_param, current_cht_num, std::mem::take(&mut current_cht_blocks))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build pairs for computing CHT.
|
|
||||||
fn build_pairs<Header, I>(
|
|
||||||
cht_size: Header::Number,
|
|
||||||
cht_num: Header::Number,
|
|
||||||
hashes: I,
|
|
||||||
) -> ClientResult<Vec<(Vec<u8>, Vec<u8>)>>
|
|
||||||
where
|
|
||||||
Header: HeaderT,
|
|
||||||
I: IntoIterator<Item = ClientResult<Option<Header::Hash>>>,
|
|
||||||
{
|
|
||||||
let start_num = start_number(cht_size, cht_num);
|
|
||||||
let mut pairs = Vec::new();
|
|
||||||
let mut hash_index = Header::Number::zero();
|
|
||||||
for hash in hashes.into_iter() {
|
|
||||||
let hash =
|
|
||||||
hash?.ok_or_else(|| ClientError::from(ClientError::MissingHashRequiredForCHT))?;
|
|
||||||
pairs.push((encode_cht_key(start_num + hash_index).to_vec(), encode_cht_value(hash)));
|
|
||||||
hash_index += Header::Number::one();
|
|
||||||
if hash_index == cht_size {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if hash_index == cht_size {
|
|
||||||
Ok(pairs)
|
|
||||||
} else {
|
|
||||||
Err(ClientError::MissingHashRequiredForCHT)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the starting block of a given CHT.
|
|
||||||
/// CHT 0 includes block 1...SIZE,
|
|
||||||
/// CHT 1 includes block SIZE + 1 ... 2*SIZE
|
|
||||||
/// More generally: CHT N includes block (1 + N*SIZE)...((N+1)*SIZE).
|
|
||||||
/// This is because the genesis hash is assumed to be known
|
|
||||||
/// and including it would be redundant.
|
|
||||||
pub fn start_number<N: AtLeast32Bit>(cht_size: N, cht_num: N) -> N {
|
|
||||||
(cht_num * cht_size) + N::one()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the ending block of a given CHT.
|
|
||||||
pub fn end_number<N: AtLeast32Bit>(cht_size: N, cht_num: N) -> N {
|
|
||||||
(cht_num + N::one()) * cht_size
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert a block number to a CHT number.
|
|
||||||
/// Returns `None` for `block_num` == 0, `Some` otherwise.
|
|
||||||
pub fn block_to_cht_number<N: AtLeast32Bit>(cht_size: N, block_num: N) -> Option<N> {
|
|
||||||
if block_num == N::zero() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some((block_num - N::one()) / cht_size)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert header number into CHT key.
|
|
||||||
pub fn encode_cht_key<N: Encode>(number: N) -> Vec<u8> {
|
|
||||||
number.encode()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert header hash into CHT value.
|
|
||||||
fn encode_cht_value<Hash: AsRef<[u8]>>(hash: Hash) -> Vec<u8> {
|
|
||||||
hash.as_ref().to_vec()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert CHT value into block header hash.
|
|
||||||
pub fn decode_cht_value(value: &[u8]) -> Option<H256> {
|
|
||||||
match value.len() {
|
|
||||||
32 => Some(H256::from_slice(&value[0..32])),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use sp_runtime::{generic, traits::BlakeTwo256};
|
|
||||||
|
|
||||||
type Header = generic::Header<u64, BlakeTwo256>;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn is_build_required_works() {
|
|
||||||
assert_eq!(is_build_required(SIZE, 0u32.into()), None);
|
|
||||||
assert_eq!(is_build_required(SIZE, 1u32.into()), None);
|
|
||||||
assert_eq!(is_build_required(SIZE, SIZE), None);
|
|
||||||
assert_eq!(is_build_required(SIZE, SIZE + 1), None);
|
|
||||||
assert_eq!(is_build_required(SIZE, 2 * SIZE), None);
|
|
||||||
assert_eq!(is_build_required(SIZE, 2 * SIZE + 1), Some(0));
|
|
||||||
assert_eq!(is_build_required(SIZE, 2 * SIZE + 2), None);
|
|
||||||
assert_eq!(is_build_required(SIZE, 3 * SIZE), None);
|
|
||||||
assert_eq!(is_build_required(SIZE, 3 * SIZE + 1), Some(1));
|
|
||||||
assert_eq!(is_build_required(SIZE, 3 * SIZE + 2), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn max_cht_number_works() {
|
|
||||||
assert_eq!(max_cht_number(SIZE, 0u32.into()), None);
|
|
||||||
assert_eq!(max_cht_number(SIZE, 1u32.into()), None);
|
|
||||||
assert_eq!(max_cht_number(SIZE, SIZE), None);
|
|
||||||
assert_eq!(max_cht_number(SIZE, SIZE + 1), None);
|
|
||||||
assert_eq!(max_cht_number(SIZE, 2 * SIZE), None);
|
|
||||||
assert_eq!(max_cht_number(SIZE, 2 * SIZE + 1), Some(0));
|
|
||||||
assert_eq!(max_cht_number(SIZE, 2 * SIZE + 2), Some(0));
|
|
||||||
assert_eq!(max_cht_number(SIZE, 3 * SIZE), Some(0));
|
|
||||||
assert_eq!(max_cht_number(SIZE, 3 * SIZE + 1), Some(1));
|
|
||||||
assert_eq!(max_cht_number(SIZE, 3 * SIZE + 2), Some(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn start_number_works() {
|
|
||||||
assert_eq!(start_number(SIZE, 0u32), 1u32);
|
|
||||||
assert_eq!(start_number(SIZE, 1u32), SIZE + 1);
|
|
||||||
assert_eq!(start_number(SIZE, 2u32), SIZE + SIZE + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn end_number_works() {
|
|
||||||
assert_eq!(end_number(SIZE, 0u32), SIZE);
|
|
||||||
assert_eq!(end_number(SIZE, 1u32), SIZE + SIZE);
|
|
||||||
assert_eq!(end_number(SIZE, 2u32), SIZE + SIZE + SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn build_pairs_fails_when_no_enough_blocks() {
|
|
||||||
assert!(build_pairs::<Header, _>(
|
|
||||||
SIZE as _,
|
|
||||||
0,
|
|
||||||
::std::iter::repeat_with(|| Ok(Some(H256::from_low_u64_be(1)))).take(SIZE as usize / 2)
|
|
||||||
)
|
|
||||||
.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn build_pairs_fails_when_missing_block() {
|
|
||||||
assert!(build_pairs::<Header, _>(
|
|
||||||
SIZE as _,
|
|
||||||
0,
|
|
||||||
::std::iter::repeat_with(|| Ok(Some(H256::from_low_u64_be(1))))
|
|
||||||
.take(SIZE as usize / 2)
|
|
||||||
.chain(::std::iter::once(Ok(None)))
|
|
||||||
.chain(
|
|
||||||
::std::iter::repeat_with(|| Ok(Some(H256::from_low_u64_be(2))))
|
|
||||||
.take(SIZE as usize / 2 - 1)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn compute_root_works() {
|
|
||||||
assert!(compute_root::<Header, BlakeTwo256, _>(
|
|
||||||
SIZE as _,
|
|
||||||
42,
|
|
||||||
::std::iter::repeat_with(|| Ok(Some(H256::from_low_u64_be(1)))).take(SIZE as usize)
|
|
||||||
)
|
|
||||||
.is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn build_proof_panics_when_querying_wrong_block() {
|
|
||||||
assert!(build_proof::<Header, BlakeTwo256, _, _>(
|
|
||||||
SIZE as _,
|
|
||||||
0,
|
|
||||||
vec![(SIZE * 1000) as u64],
|
|
||||||
::std::iter::repeat_with(|| Ok(Some(H256::from_low_u64_be(1)))).take(SIZE as usize)
|
|
||||||
)
|
|
||||||
.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn build_proof_works() {
|
|
||||||
assert!(build_proof::<Header, BlakeTwo256, _, _>(
|
|
||||||
SIZE as _,
|
|
||||||
0,
|
|
||||||
vec![(SIZE / 2) as u64],
|
|
||||||
::std::iter::repeat_with(|| Ok(Some(H256::from_low_u64_be(1)))).take(SIZE as usize)
|
|
||||||
)
|
|
||||||
.is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic]
|
|
||||||
fn for_each_cht_group_panics() {
|
|
||||||
let cht_size = SIZE as u64;
|
|
||||||
let _ = for_each_cht_group::<Header, _, _, _>(
|
|
||||||
cht_size,
|
|
||||||
vec![cht_size * 5, cht_size * 2],
|
|
||||||
|_, _, _| Ok(()),
|
|
||||||
(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn for_each_cht_group_works() {
|
|
||||||
let cht_size = SIZE as u64;
|
|
||||||
let _ = for_each_cht_group::<Header, _, _, _>(
|
|
||||||
cht_size,
|
|
||||||
vec![
|
|
||||||
cht_size * 2 + 1,
|
|
||||||
cht_size * 2 + 2,
|
|
||||||
cht_size * 2 + 5,
|
|
||||||
cht_size * 4 + 1,
|
|
||||||
cht_size * 4 + 7,
|
|
||||||
cht_size * 6 + 1,
|
|
||||||
],
|
|
||||||
|_, cht_num, blocks| {
|
|
||||||
match cht_num {
|
|
||||||
2 => assert_eq!(
|
|
||||||
blocks,
|
|
||||||
vec![cht_size * 2 + 1, cht_size * 2 + 2, cht_size * 2 + 5]
|
|
||||||
),
|
|
||||||
4 => assert_eq!(blocks, vec![cht_size * 4 + 1, cht_size * 4 + 7]),
|
|
||||||
6 => assert_eq!(blocks, vec![cht_size * 6 + 1]),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -29,8 +29,8 @@ use sp_runtime::{
|
|||||||
Justification, Justifications, Storage,
|
Justification, Justifications, Storage,
|
||||||
};
|
};
|
||||||
use sp_state_machine::{
|
use sp_state_machine::{
|
||||||
Backend as StateBackend, ChangesTrieTransaction, ChildStorageCollection, InMemoryBackend,
|
Backend as StateBackend, ChildStorageCollection, InMemoryBackend, IndexOperation,
|
||||||
IndexOperation, StorageCollection,
|
StorageCollection,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
@@ -39,10 +39,10 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::{self, NewBlockState, ProvideChtRoots},
|
backend::{self, NewBlockState},
|
||||||
blockchain::{self, well_known_cache_keys::Id as CacheKeyId, BlockStatus, HeaderBackend},
|
blockchain::{self, well_known_cache_keys::Id as CacheKeyId, BlockStatus, HeaderBackend},
|
||||||
leaves::LeafSet,
|
leaves::LeafSet,
|
||||||
light, UsageInfo,
|
UsageInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PendingBlock<B: BlockT> {
|
struct PendingBlock<B: BlockT> {
|
||||||
@@ -109,7 +109,6 @@ struct BlockchainStorage<Block: BlockT> {
|
|||||||
finalized_number: NumberFor<Block>,
|
finalized_number: NumberFor<Block>,
|
||||||
genesis_hash: Block::Hash,
|
genesis_hash: Block::Hash,
|
||||||
header_cht_roots: HashMap<NumberFor<Block>, Block::Hash>,
|
header_cht_roots: HashMap<NumberFor<Block>, Block::Hash>,
|
||||||
changes_trie_cht_roots: HashMap<NumberFor<Block>, Block::Hash>,
|
|
||||||
leaves: LeafSet<Block::Hash, NumberFor<Block>>,
|
leaves: LeafSet<Block::Hash, NumberFor<Block>>,
|
||||||
aux: HashMap<Vec<u8>, Vec<u8>>,
|
aux: HashMap<Vec<u8>, Vec<u8>>,
|
||||||
}
|
}
|
||||||
@@ -152,7 +151,6 @@ impl<Block: BlockT> Blockchain<Block> {
|
|||||||
finalized_number: Zero::zero(),
|
finalized_number: Zero::zero(),
|
||||||
genesis_hash: Default::default(),
|
genesis_hash: Default::default(),
|
||||||
header_cht_roots: HashMap::new(),
|
header_cht_roots: HashMap::new(),
|
||||||
changes_trie_cht_roots: HashMap::new(),
|
|
||||||
leaves: LeafSet::new(),
|
leaves: LeafSet::new(),
|
||||||
aux: HashMap::new(),
|
aux: HashMap::new(),
|
||||||
}));
|
}));
|
||||||
@@ -442,10 +440,6 @@ impl<Block: BlockT> blockchain::Backend<Block> for Blockchain<Block> {
|
|||||||
Ok(self.storage.read().finalized_hash.clone())
|
Ok(self.storage.read().finalized_hash.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cache(&self) -> Option<Arc<dyn blockchain::Cache<Block>>> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn leaves(&self) -> sp_blockchain::Result<Vec<Block::Hash>> {
|
fn leaves(&self) -> sp_blockchain::Result<Vec<Block::Hash>> {
|
||||||
Ok(self.storage.read().leaves.hashes())
|
Ok(self.storage.read().leaves.hashes())
|
||||||
}
|
}
|
||||||
@@ -466,12 +460,6 @@ impl<Block: BlockT> blockchain::Backend<Block> for Blockchain<Block> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Block: BlockT> blockchain::ProvideCache<Block> for Blockchain<Block> {
|
|
||||||
fn cache(&self) -> Option<Arc<dyn blockchain::Cache<Block>>> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Block: BlockT> backend::AuxStore for Blockchain<Block> {
|
impl<Block: BlockT> backend::AuxStore for Blockchain<Block> {
|
||||||
fn insert_aux<
|
fn insert_aux<
|
||||||
'a,
|
'a,
|
||||||
@@ -499,82 +487,6 @@ impl<Block: BlockT> backend::AuxStore for Blockchain<Block> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Block: BlockT> light::Storage<Block> for Blockchain<Block>
|
|
||||||
where
|
|
||||||
Block::Hash: From<[u8; 32]>,
|
|
||||||
{
|
|
||||||
fn import_header(
|
|
||||||
&self,
|
|
||||||
header: Block::Header,
|
|
||||||
_cache: HashMap<CacheKeyId, Vec<u8>>,
|
|
||||||
state: NewBlockState,
|
|
||||||
aux_ops: Vec<(Vec<u8>, Option<Vec<u8>>)>,
|
|
||||||
) -> sp_blockchain::Result<()> {
|
|
||||||
let hash = header.hash();
|
|
||||||
self.insert(hash, header, None, None, state)?;
|
|
||||||
|
|
||||||
self.write_aux(aux_ops);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_head(&self, id: BlockId<Block>) -> sp_blockchain::Result<()> {
|
|
||||||
Blockchain::set_head(self, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn last_finalized(&self) -> sp_blockchain::Result<Block::Hash> {
|
|
||||||
Ok(self.storage.read().finalized_hash.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finalize_header(&self, id: BlockId<Block>) -> sp_blockchain::Result<()> {
|
|
||||||
Blockchain::finalize_header(self, id, None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cache(&self) -> Option<Arc<dyn blockchain::Cache<Block>>> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage_info(&self) -> Option<UsageInfo> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Block: BlockT> ProvideChtRoots<Block> for Blockchain<Block> {
|
|
||||||
fn header_cht_root(
|
|
||||||
&self,
|
|
||||||
_cht_size: NumberFor<Block>,
|
|
||||||
block: NumberFor<Block>,
|
|
||||||
) -> sp_blockchain::Result<Option<Block::Hash>> {
|
|
||||||
self.storage
|
|
||||||
.read()
|
|
||||||
.header_cht_roots
|
|
||||||
.get(&block)
|
|
||||||
.cloned()
|
|
||||||
.ok_or_else(|| {
|
|
||||||
sp_blockchain::Error::Backend(format!("Header CHT for block {} not exists", block))
|
|
||||||
})
|
|
||||||
.map(Some)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn changes_trie_cht_root(
|
|
||||||
&self,
|
|
||||||
_cht_size: NumberFor<Block>,
|
|
||||||
block: NumberFor<Block>,
|
|
||||||
) -> sp_blockchain::Result<Option<Block::Hash>> {
|
|
||||||
self.storage
|
|
||||||
.read()
|
|
||||||
.changes_trie_cht_roots
|
|
||||||
.get(&block)
|
|
||||||
.cloned()
|
|
||||||
.ok_or_else(|| {
|
|
||||||
sp_blockchain::Error::Backend(format!(
|
|
||||||
"Changes trie CHT for block {} not exists",
|
|
||||||
block
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.map(Some)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// In-memory operation.
|
/// In-memory operation.
|
||||||
pub struct BlockImportOperation<Block: BlockT> {
|
pub struct BlockImportOperation<Block: BlockT> {
|
||||||
pending_block: Option<PendingBlock<Block>>,
|
pending_block: Option<PendingBlock<Block>>,
|
||||||
@@ -650,13 +562,6 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_changes_trie(
|
|
||||||
&mut self,
|
|
||||||
_update: ChangesTrieTransaction<HashFor<Block>, NumberFor<Block>>,
|
|
||||||
) -> sp_blockchain::Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_genesis_state(
|
fn set_genesis_state(
|
||||||
&mut self,
|
&mut self,
|
||||||
storage: Storage,
|
storage: Storage,
|
||||||
@@ -846,10 +751,6 @@ where
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn changes_trie_storage(&self) -> Option<&dyn backend::PrunableStateChangesTrieStorage<Block>> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn offchain_storage(&self) -> Option<Self::OffchainStorage> {
|
fn offchain_storage(&self) -> Option<Self::OffchainStorage> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -885,22 +786,6 @@ where
|
|||||||
|
|
||||||
impl<Block: BlockT> backend::LocalBackend<Block> for Backend<Block> where Block::Hash: Ord {}
|
impl<Block: BlockT> backend::LocalBackend<Block> for Backend<Block> where Block::Hash: Ord {}
|
||||||
|
|
||||||
impl<Block: BlockT> backend::RemoteBackend<Block> for Backend<Block>
|
|
||||||
where
|
|
||||||
Block::Hash: Ord,
|
|
||||||
{
|
|
||||||
fn is_local_state_available(&self, block: &BlockId<Block>) -> bool {
|
|
||||||
self.blockchain
|
|
||||||
.expect_block_number_from_id(block)
|
|
||||||
.map(|num| num.is_zero())
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remote_blockchain(&self) -> Arc<dyn light::RemoteBlockchain<Block>> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check that genesis storage is valid.
|
/// Check that genesis storage is valid.
|
||||||
pub fn check_genesis_storage(storage: &Storage) -> sp_blockchain::Result<()> {
|
pub fn check_genesis_storage(storage: &Storage) -> sp_blockchain::Result<()> {
|
||||||
if storage.top.iter().any(|(k, _)| well_known_keys::is_child_storage_key(k)) {
|
if storage.top.iter().any(|(k, _)| well_known_keys::is_child_storage_key(k)) {
|
||||||
|
|||||||
@@ -21,19 +21,16 @@
|
|||||||
|
|
||||||
pub mod backend;
|
pub mod backend;
|
||||||
pub mod call_executor;
|
pub mod call_executor;
|
||||||
pub mod cht;
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod execution_extensions;
|
pub mod execution_extensions;
|
||||||
pub mod in_mem;
|
pub mod in_mem;
|
||||||
pub mod leaves;
|
pub mod leaves;
|
||||||
pub mod light;
|
|
||||||
pub mod notifications;
|
pub mod notifications;
|
||||||
pub mod proof_provider;
|
pub mod proof_provider;
|
||||||
|
|
||||||
pub use backend::*;
|
pub use backend::*;
|
||||||
pub use call_executor::*;
|
pub use call_executor::*;
|
||||||
pub use client::*;
|
pub use client::*;
|
||||||
pub use light::*;
|
|
||||||
pub use notifications::*;
|
pub use notifications::*;
|
||||||
pub use proof_provider::*;
|
pub use proof_provider::*;
|
||||||
pub use sp_blockchain as blockchain;
|
pub use sp_blockchain as blockchain;
|
||||||
|
|||||||
@@ -1,372 +0,0 @@
|
|||||||
// This file is part of Substrate.
|
|
||||||
|
|
||||||
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Substrate light client interfaces
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
collections::{BTreeMap, HashMap},
|
|
||||||
future::Future,
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
backend::{AuxStore, NewBlockState},
|
|
||||||
ProvideChtRoots, UsageInfo,
|
|
||||||
};
|
|
||||||
use sp_blockchain::{
|
|
||||||
well_known_cache_keys, Cache as BlockchainCache, Error as ClientError, HeaderBackend,
|
|
||||||
HeaderMetadata, Result as ClientResult,
|
|
||||||
};
|
|
||||||
use sp_core::{storage::PrefixedStorageKey, ChangesTrieConfigurationRange};
|
|
||||||
use sp_runtime::{
|
|
||||||
generic::BlockId,
|
|
||||||
traits::{Block as BlockT, Header as HeaderT, NumberFor},
|
|
||||||
};
|
|
||||||
use sp_state_machine::StorageProof;
|
|
||||||
|
|
||||||
/// Remote call request.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct RemoteCallRequest<Header: HeaderT> {
|
|
||||||
/// Call at state of given block.
|
|
||||||
pub block: Header::Hash,
|
|
||||||
/// Header of block at which call is performed.
|
|
||||||
pub header: Header,
|
|
||||||
/// Method to call.
|
|
||||||
pub method: String,
|
|
||||||
/// Call data.
|
|
||||||
pub call_data: Vec<u8>,
|
|
||||||
/// Number of times to retry request. None means that default RETRY_COUNT is used.
|
|
||||||
pub retry_count: Option<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remote canonical header request.
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
|
|
||||||
pub struct RemoteHeaderRequest<Header: HeaderT> {
|
|
||||||
/// The root of CHT this block is included in.
|
|
||||||
pub cht_root: Header::Hash,
|
|
||||||
/// Number of the header to query.
|
|
||||||
pub block: Header::Number,
|
|
||||||
/// Number of times to retry request. None means that default RETRY_COUNT is used.
|
|
||||||
pub retry_count: Option<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remote storage read request.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct RemoteReadRequest<Header: HeaderT> {
|
|
||||||
/// Read at state of given block.
|
|
||||||
pub block: Header::Hash,
|
|
||||||
/// Header of block at which read is performed.
|
|
||||||
pub header: Header,
|
|
||||||
/// Storage key to read.
|
|
||||||
pub keys: Vec<Vec<u8>>,
|
|
||||||
/// Number of times to retry request. None means that default RETRY_COUNT is used.
|
|
||||||
pub retry_count: Option<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remote storage read child request.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct RemoteReadChildRequest<Header: HeaderT> {
|
|
||||||
/// Read at state of given block.
|
|
||||||
pub block: Header::Hash,
|
|
||||||
/// Header of block at which read is performed.
|
|
||||||
pub header: Header,
|
|
||||||
/// Storage key for child.
|
|
||||||
pub storage_key: PrefixedStorageKey,
|
|
||||||
/// Child storage key to read.
|
|
||||||
pub keys: Vec<Vec<u8>>,
|
|
||||||
/// Number of times to retry request. None means that default RETRY_COUNT is used.
|
|
||||||
pub retry_count: Option<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remote key changes read request.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct RemoteChangesRequest<Header: HeaderT> {
|
|
||||||
/// All changes trie configurations that are valid within [first_block; last_block].
|
|
||||||
pub changes_trie_configs: Vec<ChangesTrieConfigurationRange<Header::Number, Header::Hash>>,
|
|
||||||
/// Query changes from range of blocks, starting (and including) with this hash...
|
|
||||||
pub first_block: (Header::Number, Header::Hash),
|
|
||||||
/// ...ending (and including) with this hash. Should come after first_block and
|
|
||||||
/// be the part of the same fork.
|
|
||||||
pub last_block: (Header::Number, Header::Hash),
|
|
||||||
/// Only use digests from blocks up to this hash. Should be last_block OR come
|
|
||||||
/// after this block and be the part of the same fork.
|
|
||||||
pub max_block: (Header::Number, Header::Hash),
|
|
||||||
/// Known changes trie roots for the range of blocks [tries_roots.0..max_block].
|
|
||||||
/// Proofs for roots of ascendants of tries_roots.0 are provided by the remote node.
|
|
||||||
pub tries_roots: (Header::Number, Header::Hash, Vec<Header::Hash>),
|
|
||||||
/// Optional Child Storage key to read.
|
|
||||||
pub storage_key: Option<PrefixedStorageKey>,
|
|
||||||
/// Storage key to read.
|
|
||||||
pub key: Vec<u8>,
|
|
||||||
/// Number of times to retry request. None means that default RETRY_COUNT is used.
|
|
||||||
pub retry_count: Option<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Key changes read proof.
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
pub struct ChangesProof<Header: HeaderT> {
|
|
||||||
/// Max block that has been used in changes query.
|
|
||||||
pub max_block: Header::Number,
|
|
||||||
/// All touched nodes of all changes tries.
|
|
||||||
pub proof: Vec<Vec<u8>>,
|
|
||||||
/// All changes tries roots that have been touched AND are missing from
|
|
||||||
/// the requester' node. It is a map of block number => changes trie root.
|
|
||||||
pub roots: BTreeMap<Header::Number, Header::Hash>,
|
|
||||||
/// The proofs for all changes tries roots that have been touched AND are
|
|
||||||
/// missing from the requester' node. It is a map of CHT number => proof.
|
|
||||||
pub roots_proof: StorageProof,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remote block body request
|
|
||||||
#[derive(Clone, Default, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct RemoteBodyRequest<Header: HeaderT> {
|
|
||||||
/// Header of the requested block body
|
|
||||||
pub header: Header,
|
|
||||||
/// Number of times to retry request. None means that default RETRY_COUNT is used.
|
|
||||||
pub retry_count: Option<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Light client data fetcher. Implementations of this trait must check if remote data
|
|
||||||
/// is correct (see FetchedDataChecker) and return already checked data.
|
|
||||||
pub trait Fetcher<Block: BlockT>: Send + Sync {
|
|
||||||
/// Remote header future.
|
|
||||||
type RemoteHeaderResult: Future<Output = Result<Block::Header, ClientError>>
|
|
||||||
+ Unpin
|
|
||||||
+ Send
|
|
||||||
+ 'static;
|
|
||||||
/// Remote storage read future.
|
|
||||||
type RemoteReadResult: Future<Output = Result<HashMap<Vec<u8>, Option<Vec<u8>>>, ClientError>>
|
|
||||||
+ Unpin
|
|
||||||
+ Send
|
|
||||||
+ 'static;
|
|
||||||
/// Remote call result future.
|
|
||||||
type RemoteCallResult: Future<Output = Result<Vec<u8>, ClientError>> + Unpin + Send + 'static;
|
|
||||||
/// Remote changes result future.
|
|
||||||
type RemoteChangesResult: Future<Output = Result<Vec<(NumberFor<Block>, u32)>, ClientError>>
|
|
||||||
+ Unpin
|
|
||||||
+ Send
|
|
||||||
+ 'static;
|
|
||||||
/// Remote block body result future.
|
|
||||||
type RemoteBodyResult: Future<Output = Result<Vec<Block::Extrinsic>, ClientError>>
|
|
||||||
+ Unpin
|
|
||||||
+ Send
|
|
||||||
+ 'static;
|
|
||||||
|
|
||||||
/// Fetch remote header.
|
|
||||||
fn remote_header(
|
|
||||||
&self,
|
|
||||||
request: RemoteHeaderRequest<Block::Header>,
|
|
||||||
) -> Self::RemoteHeaderResult;
|
|
||||||
/// Fetch remote storage value.
|
|
||||||
fn remote_read(&self, request: RemoteReadRequest<Block::Header>) -> Self::RemoteReadResult;
|
|
||||||
/// Fetch remote storage child value.
|
|
||||||
fn remote_read_child(
|
|
||||||
&self,
|
|
||||||
request: RemoteReadChildRequest<Block::Header>,
|
|
||||||
) -> Self::RemoteReadResult;
|
|
||||||
/// Fetch remote call result.
|
|
||||||
fn remote_call(&self, request: RemoteCallRequest<Block::Header>) -> Self::RemoteCallResult;
|
|
||||||
/// Fetch remote changes ((block number, extrinsic index)) where given key has been changed
|
|
||||||
/// at a given blocks range.
|
|
||||||
fn remote_changes(
|
|
||||||
&self,
|
|
||||||
request: RemoteChangesRequest<Block::Header>,
|
|
||||||
) -> Self::RemoteChangesResult;
|
|
||||||
/// Fetch remote block body
|
|
||||||
fn remote_body(&self, request: RemoteBodyRequest<Block::Header>) -> Self::RemoteBodyResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Light client remote data checker.
|
|
||||||
///
|
|
||||||
/// Implementations of this trait should not use any prunable blockchain data
|
|
||||||
/// except that is passed to its methods.
|
|
||||||
pub trait FetchChecker<Block: BlockT>: Send + Sync {
|
|
||||||
/// Check remote header proof.
|
|
||||||
fn check_header_proof(
|
|
||||||
&self,
|
|
||||||
request: &RemoteHeaderRequest<Block::Header>,
|
|
||||||
header: Option<Block::Header>,
|
|
||||||
remote_proof: StorageProof,
|
|
||||||
) -> ClientResult<Block::Header>;
|
|
||||||
/// Check remote storage read proof.
|
|
||||||
fn check_read_proof(
|
|
||||||
&self,
|
|
||||||
request: &RemoteReadRequest<Block::Header>,
|
|
||||||
remote_proof: StorageProof,
|
|
||||||
) -> ClientResult<HashMap<Vec<u8>, Option<Vec<u8>>>>;
|
|
||||||
/// Check remote storage read proof.
|
|
||||||
fn check_read_child_proof(
|
|
||||||
&self,
|
|
||||||
request: &RemoteReadChildRequest<Block::Header>,
|
|
||||||
remote_proof: StorageProof,
|
|
||||||
) -> ClientResult<HashMap<Vec<u8>, Option<Vec<u8>>>>;
|
|
||||||
/// Check remote method execution proof.
|
|
||||||
fn check_execution_proof(
|
|
||||||
&self,
|
|
||||||
request: &RemoteCallRequest<Block::Header>,
|
|
||||||
remote_proof: StorageProof,
|
|
||||||
) -> ClientResult<Vec<u8>>;
|
|
||||||
/// Check remote changes query proof.
|
|
||||||
fn check_changes_proof(
|
|
||||||
&self,
|
|
||||||
request: &RemoteChangesRequest<Block::Header>,
|
|
||||||
proof: ChangesProof<Block::Header>,
|
|
||||||
) -> ClientResult<Vec<(NumberFor<Block>, u32)>>;
|
|
||||||
/// Check remote body proof.
|
|
||||||
fn check_body_proof(
|
|
||||||
&self,
|
|
||||||
request: &RemoteBodyRequest<Block::Header>,
|
|
||||||
body: Vec<Block::Extrinsic>,
|
|
||||||
) -> ClientResult<Vec<Block::Extrinsic>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Light client blockchain storage.
|
|
||||||
pub trait Storage<Block: BlockT>:
|
|
||||||
AuxStore
|
|
||||||
+ HeaderBackend<Block>
|
|
||||||
+ HeaderMetadata<Block, Error = ClientError>
|
|
||||||
+ ProvideChtRoots<Block>
|
|
||||||
{
|
|
||||||
/// Store new header. Should refuse to revert any finalized blocks.
|
|
||||||
///
|
|
||||||
/// Takes new authorities, the leaf state of the new block, and
|
|
||||||
/// any auxiliary storage updates to place in the same operation.
|
|
||||||
fn import_header(
|
|
||||||
&self,
|
|
||||||
header: Block::Header,
|
|
||||||
cache: HashMap<well_known_cache_keys::Id, Vec<u8>>,
|
|
||||||
state: NewBlockState,
|
|
||||||
aux_ops: Vec<(Vec<u8>, Option<Vec<u8>>)>,
|
|
||||||
) -> ClientResult<()>;
|
|
||||||
|
|
||||||
/// Set an existing block as new best block.
|
|
||||||
fn set_head(&self, block: BlockId<Block>) -> ClientResult<()>;
|
|
||||||
|
|
||||||
/// Mark historic header as finalized.
|
|
||||||
fn finalize_header(&self, block: BlockId<Block>) -> ClientResult<()>;
|
|
||||||
|
|
||||||
/// Get last finalized header.
|
|
||||||
fn last_finalized(&self) -> ClientResult<Block::Hash>;
|
|
||||||
|
|
||||||
/// Get storage cache.
|
|
||||||
fn cache(&self) -> Option<Arc<dyn BlockchainCache<Block>>>;
|
|
||||||
|
|
||||||
/// Get storage usage statistics.
|
|
||||||
fn usage_info(&self) -> Option<UsageInfo>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remote header.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum LocalOrRemote<Data, Request> {
|
|
||||||
/// When data is available locally, it is returned.
|
|
||||||
Local(Data),
|
|
||||||
/// When data is unavailable locally, the request to fetch it from remote node is returned.
|
|
||||||
Remote(Request),
|
|
||||||
/// When data is unknown.
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Futures-based blockchain backend that either resolves blockchain data
|
|
||||||
/// locally, or fetches required data from remote node.
|
|
||||||
pub trait RemoteBlockchain<Block: BlockT>: Send + Sync {
|
|
||||||
/// Get block header.
|
|
||||||
fn header(
|
|
||||||
&self,
|
|
||||||
id: BlockId<Block>,
|
|
||||||
) -> ClientResult<LocalOrRemote<Block::Header, RemoteHeaderRequest<Block::Header>>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns future that resolves header either locally, or remotely.
|
|
||||||
pub fn future_header<Block: BlockT, F: Fetcher<Block>>(
|
|
||||||
blockchain: &dyn RemoteBlockchain<Block>,
|
|
||||||
fetcher: &F,
|
|
||||||
id: BlockId<Block>,
|
|
||||||
) -> impl Future<Output = Result<Option<Block::Header>, ClientError>> {
|
|
||||||
use futures::future::{ready, Either, FutureExt};
|
|
||||||
|
|
||||||
match blockchain.header(id) {
|
|
||||||
Ok(LocalOrRemote::Remote(request)) =>
|
|
||||||
Either::Left(fetcher.remote_header(request).then(|header| ready(header.map(Some)))),
|
|
||||||
Ok(LocalOrRemote::Unknown) => Either::Right(ready(Ok(None))),
|
|
||||||
Ok(LocalOrRemote::Local(local_header)) => Either::Right(ready(Ok(Some(local_header)))),
|
|
||||||
Err(err) => Either::Right(ready(Err(err))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod tests {
|
|
||||||
use super::*;
|
|
||||||
use futures::future::Ready;
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use sp_blockchain::Error as ClientError;
|
|
||||||
use sp_test_primitives::{Block, Extrinsic, Header};
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
#[error("Not implemented on test node")]
|
|
||||||
struct MockError;
|
|
||||||
|
|
||||||
impl Into<ClientError> for MockError {
|
|
||||||
fn into(self) -> ClientError {
|
|
||||||
ClientError::Application(Box::new(self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type OkCallFetcher = Mutex<Vec<u8>>;
|
|
||||||
|
|
||||||
fn not_implemented_in_tests<T>() -> Ready<Result<T, ClientError>> {
|
|
||||||
futures::future::ready(Err(MockError.into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Fetcher<Block> for OkCallFetcher {
|
|
||||||
type RemoteHeaderResult = Ready<Result<Header, ClientError>>;
|
|
||||||
type RemoteReadResult = Ready<Result<HashMap<Vec<u8>, Option<Vec<u8>>>, ClientError>>;
|
|
||||||
type RemoteCallResult = Ready<Result<Vec<u8>, ClientError>>;
|
|
||||||
type RemoteChangesResult = Ready<Result<Vec<(NumberFor<Block>, u32)>, ClientError>>;
|
|
||||||
type RemoteBodyResult = Ready<Result<Vec<Extrinsic>, ClientError>>;
|
|
||||||
|
|
||||||
fn remote_header(&self, _request: RemoteHeaderRequest<Header>) -> Self::RemoteHeaderResult {
|
|
||||||
not_implemented_in_tests()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remote_read(&self, _request: RemoteReadRequest<Header>) -> Self::RemoteReadResult {
|
|
||||||
not_implemented_in_tests()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remote_read_child(
|
|
||||||
&self,
|
|
||||||
_request: RemoteReadChildRequest<Header>,
|
|
||||||
) -> Self::RemoteReadResult {
|
|
||||||
not_implemented_in_tests()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remote_call(&self, _request: RemoteCallRequest<Header>) -> Self::RemoteCallResult {
|
|
||||||
futures::future::ready(Ok((*self.lock()).clone()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remote_changes(
|
|
||||||
&self,
|
|
||||||
_request: RemoteChangesRequest<Header>,
|
|
||||||
) -> Self::RemoteChangesResult {
|
|
||||||
not_implemented_in_tests()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remote_body(&self, _request: RemoteBodyRequest<Header>) -> Self::RemoteBodyResult {
|
|
||||||
not_implemented_in_tests()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,10 +17,10 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! Proof utilities
|
//! Proof utilities
|
||||||
use crate::{ChangesProof, CompactProof, StorageProof};
|
use crate::{CompactProof, StorageProof};
|
||||||
use sp_runtime::{generic::BlockId, traits::Block as BlockT};
|
use sp_runtime::{generic::BlockId, traits::Block as BlockT};
|
||||||
use sp_state_machine::{KeyValueStates, KeyValueStorageLevel};
|
use sp_state_machine::{KeyValueStates, KeyValueStorageLevel};
|
||||||
use sp_storage::{ChildInfo, PrefixedStorageKey, StorageKey};
|
use sp_storage::ChildInfo;
|
||||||
|
|
||||||
/// Interface for providing block proving utilities.
|
/// Interface for providing block proving utilities.
|
||||||
pub trait ProofProvider<Block: BlockT> {
|
pub trait ProofProvider<Block: BlockT> {
|
||||||
@@ -50,27 +50,6 @@ pub trait ProofProvider<Block: BlockT> {
|
|||||||
method: &str,
|
method: &str,
|
||||||
call_data: &[u8],
|
call_data: &[u8],
|
||||||
) -> sp_blockchain::Result<(Vec<u8>, StorageProof)>;
|
) -> sp_blockchain::Result<(Vec<u8>, StorageProof)>;
|
||||||
/// Reads given header and generates CHT-based header proof.
|
|
||||||
fn header_proof(
|
|
||||||
&self,
|
|
||||||
id: &BlockId<Block>,
|
|
||||||
) -> sp_blockchain::Result<(Block::Header, StorageProof)>;
|
|
||||||
|
|
||||||
/// Get proof for computation of (block, extrinsic) pairs where key has been changed at given
|
|
||||||
/// blocks range. `min` is the hash of the first block, which changes trie root is known to the
|
|
||||||
/// requester - when we're using changes tries from ascendants of this block, we should provide
|
|
||||||
/// proofs for changes tries roots `max` is the hash of the last block known to the requester -
|
|
||||||
/// we can't use changes tries from descendants of this block.
|
|
||||||
/// Works only for runtimes that are supporting changes tries.
|
|
||||||
fn key_changes_proof(
|
|
||||||
&self,
|
|
||||||
first: Block::Hash,
|
|
||||||
last: Block::Hash,
|
|
||||||
min: Block::Hash,
|
|
||||||
max: Block::Hash,
|
|
||||||
storage_key: Option<&PrefixedStorageKey>,
|
|
||||||
key: &StorageKey,
|
|
||||||
) -> sp_blockchain::Result<ChangesProof<Block::Header>>;
|
|
||||||
|
|
||||||
/// Given a `BlockId` iterate over all storage values starting at `start_keys`.
|
/// Given a `BlockId` iterate over all storage values starting at `start_keys`.
|
||||||
/// Last `start_keys` element contains last accessed key value.
|
/// Last `start_keys` element contains last accessed key value.
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ use sp_core::traits::SpawnNamed;
|
|||||||
use sp_inherents::InherentData;
|
use sp_inherents::InherentData;
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::BlockId,
|
generic::BlockId,
|
||||||
traits::{BlakeTwo256, Block as BlockT, DigestFor, Hash as HashT, Header as HeaderT},
|
traits::{BlakeTwo256, Block as BlockT, Hash as HashT, Header as HeaderT},
|
||||||
|
Digest,
|
||||||
};
|
};
|
||||||
use std::{marker::PhantomData, pin::Pin, sync::Arc, time};
|
use std::{marker::PhantomData, pin::Pin, sync::Arc, time};
|
||||||
|
|
||||||
@@ -261,7 +262,7 @@ where
|
|||||||
fn propose(
|
fn propose(
|
||||||
self,
|
self,
|
||||||
inherent_data: InherentData,
|
inherent_data: InherentData,
|
||||||
inherent_digests: DigestFor<Block>,
|
inherent_digests: Digest,
|
||||||
max_duration: time::Duration,
|
max_duration: time::Duration,
|
||||||
block_size_limit: Option<usize>,
|
block_size_limit: Option<usize>,
|
||||||
) -> Self::Proposal {
|
) -> Self::Proposal {
|
||||||
@@ -310,7 +311,7 @@ where
|
|||||||
async fn propose_with(
|
async fn propose_with(
|
||||||
self,
|
self,
|
||||||
inherent_data: InherentData,
|
inherent_data: InherentData,
|
||||||
inherent_digests: DigestFor<Block>,
|
inherent_digests: Digest,
|
||||||
deadline: time::Instant,
|
deadline: time::Instant,
|
||||||
block_size_limit: Option<usize>,
|
block_size_limit: Option<usize>,
|
||||||
) -> Result<Proposal<Block, backend::TransactionFor<B, Block>, PR::Proof>, sp_blockchain::Error>
|
) -> Result<Proposal<Block, backend::TransactionFor<B, Block>, PR::Proof>, sp_blockchain::Error>
|
||||||
@@ -690,13 +691,8 @@ mod tests {
|
|||||||
api.execute_block(&block_id, proposal.block).unwrap();
|
api.execute_block(&block_id, proposal.block).unwrap();
|
||||||
|
|
||||||
let state = backend.state_at(block_id).unwrap();
|
let state = backend.state_at(block_id).unwrap();
|
||||||
let changes_trie_state =
|
|
||||||
backend::changes_tries_state_at_block(&block_id, backend.changes_trie_storage())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let storage_changes = api
|
let storage_changes = api.into_storage_changes(&state, genesis_hash).unwrap();
|
||||||
.into_storage_changes(&state, changes_trie_state.as_ref(), genesis_hash)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
proposal.storage_changes.transaction_storage_root,
|
proposal.storage_changes.transaction_storage_root,
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ use sp_blockchain::{ApplyExtrinsicFailed, Error};
|
|||||||
use sp_core::ExecutionContext;
|
use sp_core::ExecutionContext;
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::BlockId,
|
generic::BlockId,
|
||||||
traits::{Block as BlockT, DigestFor, Hash, HashFor, Header as HeaderT, NumberFor, One},
|
traits::{Block as BlockT, Hash, HashFor, Header as HeaderT, NumberFor, One},
|
||||||
|
Digest,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use sp_block_builder::BlockBuilder as BlockBuilderApi;
|
pub use sp_block_builder::BlockBuilder as BlockBuilderApi;
|
||||||
@@ -119,14 +120,14 @@ where
|
|||||||
fn new_block_at<R: Into<RecordProof>>(
|
fn new_block_at<R: Into<RecordProof>>(
|
||||||
&self,
|
&self,
|
||||||
parent: &BlockId<Block>,
|
parent: &BlockId<Block>,
|
||||||
inherent_digests: DigestFor<Block>,
|
inherent_digests: Digest,
|
||||||
record_proof: R,
|
record_proof: R,
|
||||||
) -> sp_blockchain::Result<BlockBuilder<Block, RA, B>>;
|
) -> sp_blockchain::Result<BlockBuilder<Block, RA, B>>;
|
||||||
|
|
||||||
/// Create a new block, built on the head of the chain.
|
/// Create a new block, built on the head of the chain.
|
||||||
fn new_block(
|
fn new_block(
|
||||||
&self,
|
&self,
|
||||||
inherent_digests: DigestFor<Block>,
|
inherent_digests: Digest,
|
||||||
) -> sp_blockchain::Result<BlockBuilder<Block, RA, B>>;
|
) -> sp_blockchain::Result<BlockBuilder<Block, RA, B>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +160,7 @@ where
|
|||||||
parent_hash: Block::Hash,
|
parent_hash: Block::Hash,
|
||||||
parent_number: NumberFor<Block>,
|
parent_number: NumberFor<Block>,
|
||||||
record_proof: RecordProof,
|
record_proof: RecordProof,
|
||||||
inherent_digests: DigestFor<Block>,
|
inherent_digests: Digest,
|
||||||
backend: &'a B,
|
backend: &'a B,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let header = <<Block as BlockT>::Header as HeaderT>::new(
|
let header = <<Block as BlockT>::Header as HeaderT>::new(
|
||||||
@@ -237,15 +238,11 @@ where
|
|||||||
let proof = self.api.extract_proof();
|
let proof = self.api.extract_proof();
|
||||||
|
|
||||||
let state = self.backend.state_at(self.block_id)?;
|
let state = self.backend.state_at(self.block_id)?;
|
||||||
let changes_trie_state = backend::changes_tries_state_at_block(
|
|
||||||
&self.block_id,
|
|
||||||
self.backend.changes_trie_storage(),
|
|
||||||
)?;
|
|
||||||
let parent_hash = self.parent_hash;
|
let parent_hash = self.parent_hash;
|
||||||
|
|
||||||
let storage_changes = self
|
let storage_changes = self
|
||||||
.api
|
.api
|
||||||
.into_storage_changes(&state, changes_trie_state.as_ref(), parent_hash)
|
.into_storage_changes(&state, parent_hash)
|
||||||
.map_err(|e| sp_blockchain::Error::StorageChanges(e))?;
|
.map_err(|e| sp_blockchain::Error::StorageChanges(e))?;
|
||||||
|
|
||||||
Ok(BuiltBlock {
|
Ok(BuiltBlock {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ use sp_api::{ApiExt, ProvideRuntimeApi};
|
|||||||
use sp_block_builder::BlockBuilder as BlockBuilderApi;
|
use sp_block_builder::BlockBuilder as BlockBuilderApi;
|
||||||
use sp_blockchain::{
|
use sp_blockchain::{
|
||||||
well_known_cache_keys::{self, Id as CacheKeyId},
|
well_known_cache_keys::{self, Id as CacheKeyId},
|
||||||
HeaderBackend, ProvideCache,
|
HeaderBackend,
|
||||||
};
|
};
|
||||||
use sp_consensus::{CanAuthorWith, Error as ConsensusError};
|
use sp_consensus::{CanAuthorWith, Error as ConsensusError};
|
||||||
use sp_consensus_aura::{
|
use sp_consensus_aura::{
|
||||||
@@ -45,7 +45,8 @@ use sp_core::{crypto::Pair, ExecutionContext};
|
|||||||
use sp_inherents::{CreateInherentDataProviders, InherentDataProvider as _};
|
use sp_inherents::{CreateInherentDataProviders, InherentDataProvider as _};
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::{BlockId, OpaqueDigestItemId},
|
generic::{BlockId, OpaqueDigestItemId},
|
||||||
traits::{Block as BlockT, DigestItemFor, Header},
|
traits::{Block as BlockT, Header},
|
||||||
|
DigestItem,
|
||||||
};
|
};
|
||||||
use std::{fmt::Debug, hash::Hash, marker::PhantomData, sync::Arc};
|
use std::{fmt::Debug, hash::Hash, marker::PhantomData, sync::Arc};
|
||||||
|
|
||||||
@@ -61,9 +62,8 @@ fn check_header<C, B: BlockT, P: Pair>(
|
|||||||
hash: B::Hash,
|
hash: B::Hash,
|
||||||
authorities: &[AuthorityId<P>],
|
authorities: &[AuthorityId<P>],
|
||||||
check_for_equivocation: CheckForEquivocation,
|
check_for_equivocation: CheckForEquivocation,
|
||||||
) -> Result<CheckedHeader<B::Header, (Slot, DigestItemFor<B>)>, Error<B>>
|
) -> Result<CheckedHeader<B::Header, (Slot, DigestItem)>, Error<B>>
|
||||||
where
|
where
|
||||||
DigestItemFor<B>: CompatibleDigestItem<P::Signature>,
|
|
||||||
P::Signature: Codec,
|
P::Signature: Codec,
|
||||||
C: sc_client_api::backend::AuxStore,
|
C: sc_client_api::backend::AuxStore,
|
||||||
P::Public: Encode + Decode + PartialEq + Clone,
|
P::Public: Encode + Decode + PartialEq + Clone,
|
||||||
@@ -189,14 +189,8 @@ where
|
|||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl<B: BlockT, C, P, CAW, CIDP> Verifier<B> for AuraVerifier<C, P, CAW, CIDP>
|
impl<B: BlockT, C, P, CAW, CIDP> Verifier<B> for AuraVerifier<C, P, CAW, CIDP>
|
||||||
where
|
where
|
||||||
C: ProvideRuntimeApi<B>
|
C: ProvideRuntimeApi<B> + Send + Sync + sc_client_api::backend::AuxStore + BlockOf,
|
||||||
+ Send
|
|
||||||
+ Sync
|
|
||||||
+ sc_client_api::backend::AuxStore
|
|
||||||
+ ProvideCache<B>
|
|
||||||
+ BlockOf,
|
|
||||||
C::Api: BlockBuilderApi<B> + AuraApi<B, AuthorityId<P>> + ApiExt<B>,
|
C::Api: BlockBuilderApi<B> + AuraApi<B, AuthorityId<P>> + ApiExt<B>,
|
||||||
DigestItemFor<B>: CompatibleDigestItem<P::Signature>,
|
|
||||||
P: Pair + Send + Sync + 'static,
|
P: Pair + Send + Sync + 'static,
|
||||||
P::Public: Send + Sync + Hash + Eq + Clone + Decode + Encode + Debug + 'static,
|
P::Public: Send + Sync + Hash + Eq + Clone + Decode + Encode + Debug + 'static,
|
||||||
P::Signature: Encode + Decode,
|
P::Signature: Encode + Decode,
|
||||||
@@ -385,7 +379,6 @@ where
|
|||||||
C: 'static
|
C: 'static
|
||||||
+ ProvideRuntimeApi<Block>
|
+ ProvideRuntimeApi<Block>
|
||||||
+ BlockOf
|
+ BlockOf
|
||||||
+ ProvideCache<Block>
|
|
||||||
+ Send
|
+ Send
|
||||||
+ Sync
|
+ Sync
|
||||||
+ AuxStore
|
+ AuxStore
|
||||||
@@ -395,7 +388,6 @@ where
|
|||||||
+ Send
|
+ Send
|
||||||
+ Sync
|
+ Sync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
DigestItemFor<Block>: CompatibleDigestItem<P::Signature>,
|
|
||||||
P: Pair + Send + Sync + 'static,
|
P: Pair + Send + Sync + 'static,
|
||||||
P::Public: Clone + Eq + Send + Sync + Hash + Debug + Encode + Decode,
|
P::Public: Clone + Eq + Send + Sync + Hash + Debug + Encode + Decode,
|
||||||
P::Signature: Encode + Decode,
|
P::Signature: Encode + Decode,
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ use sc_consensus_slots::{
|
|||||||
use sc_telemetry::TelemetryHandle;
|
use sc_telemetry::TelemetryHandle;
|
||||||
use sp_api::ProvideRuntimeApi;
|
use sp_api::ProvideRuntimeApi;
|
||||||
use sp_application_crypto::{AppKey, AppPublic};
|
use sp_application_crypto::{AppKey, AppPublic};
|
||||||
use sp_blockchain::{HeaderBackend, ProvideCache, Result as CResult};
|
use sp_blockchain::{HeaderBackend, Result as CResult};
|
||||||
use sp_consensus::{
|
use sp_consensus::{
|
||||||
BlockOrigin, CanAuthorWith, Environment, Error as ConsensusError, Proposer, SelectChain,
|
BlockOrigin, CanAuthorWith, Environment, Error as ConsensusError, Proposer, SelectChain,
|
||||||
};
|
};
|
||||||
@@ -62,7 +62,8 @@ use sp_inherents::CreateInherentDataProviders;
|
|||||||
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
|
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::BlockId,
|
generic::BlockId,
|
||||||
traits::{Block as BlockT, DigestItemFor, Header, Member, NumberFor, Zero},
|
traits::{Block as BlockT, Header, Member, NumberFor, Zero},
|
||||||
|
DigestItem,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod import_queue;
|
mod import_queue;
|
||||||
@@ -178,7 +179,7 @@ where
|
|||||||
P::Public: AppPublic + Hash + Member + Encode + Decode,
|
P::Public: AppPublic + Hash + Member + Encode + Decode,
|
||||||
P::Signature: TryFrom<Vec<u8>> + Hash + Member + Encode + Decode,
|
P::Signature: TryFrom<Vec<u8>> + Hash + Member + Encode + Decode,
|
||||||
B: BlockT,
|
B: BlockT,
|
||||||
C: ProvideRuntimeApi<B> + BlockOf + ProvideCache<B> + AuxStore + HeaderBackend<B> + Send + Sync,
|
C: ProvideRuntimeApi<B> + BlockOf + AuxStore + HeaderBackend<B> + Send + Sync,
|
||||||
C::Api: AuraApi<B, AuthorityId<P>>,
|
C::Api: AuraApi<B, AuthorityId<P>>,
|
||||||
SC: SelectChain<B>,
|
SC: SelectChain<B>,
|
||||||
I: BlockImport<B, Transaction = sp_api::TransactionFor<C, B>> + Send + Sync + 'static,
|
I: BlockImport<B, Transaction = sp_api::TransactionFor<C, B>> + Send + Sync + 'static,
|
||||||
@@ -267,7 +268,7 @@ pub fn build_aura_worker<P, B, C, PF, I, SO, L, BS, Error>(
|
|||||||
) -> impl sc_consensus_slots::SlotWorker<B, <PF::Proposer as Proposer<B>>::Proof>
|
) -> impl sc_consensus_slots::SlotWorker<B, <PF::Proposer as Proposer<B>>::Proof>
|
||||||
where
|
where
|
||||||
B: BlockT,
|
B: BlockT,
|
||||||
C: ProvideRuntimeApi<B> + BlockOf + ProvideCache<B> + AuxStore + HeaderBackend<B> + Send + Sync,
|
C: ProvideRuntimeApi<B> + BlockOf + AuxStore + HeaderBackend<B> + Send + Sync,
|
||||||
C::Api: AuraApi<B, AuthorityId<P>>,
|
C::Api: AuraApi<B, AuthorityId<P>>,
|
||||||
PF: Environment<B, Error = Error> + Send + Sync + 'static,
|
PF: Environment<B, Error = Error> + Send + Sync + 'static,
|
||||||
PF::Proposer: Proposer<B, Error = Error, Transaction = sp_api::TransactionFor<C, B>>,
|
PF::Proposer: Proposer<B, Error = Error, Transaction = sp_api::TransactionFor<C, B>>,
|
||||||
@@ -316,7 +317,7 @@ impl<B, C, E, I, P, Error, SO, L, BS> sc_consensus_slots::SimpleSlotWorker<B>
|
|||||||
for AuraWorker<C, E, I, P, SO, L, BS>
|
for AuraWorker<C, E, I, P, SO, L, BS>
|
||||||
where
|
where
|
||||||
B: BlockT,
|
B: BlockT,
|
||||||
C: ProvideRuntimeApi<B> + BlockOf + ProvideCache<B> + HeaderBackend<B> + Sync,
|
C: ProvideRuntimeApi<B> + BlockOf + HeaderBackend<B> + Sync,
|
||||||
C::Api: AuraApi<B, AuthorityId<P>>,
|
C::Api: AuraApi<B, AuthorityId<P>>,
|
||||||
E: Environment<B, Error = Error> + Send + Sync,
|
E: Environment<B, Error = Error> + Send + Sync,
|
||||||
E::Proposer: Proposer<B, Error = Error, Transaction = sp_api::TransactionFor<C, B>>,
|
E::Proposer: Proposer<B, Error = Error, Transaction = sp_api::TransactionFor<C, B>>,
|
||||||
@@ -377,12 +378,8 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_digest_data(
|
fn pre_digest_data(&self, slot: Slot, _claim: &Self::Claim) -> Vec<sp_runtime::DigestItem> {
|
||||||
&self,
|
vec![<DigestItem as CompatibleDigestItem<P::Signature>>::aura_pre_digest(slot)]
|
||||||
slot: Slot,
|
|
||||||
_claim: &Self::Claim,
|
|
||||||
) -> Vec<sp_runtime::DigestItem<B::Hash>> {
|
|
||||||
vec![<DigestItemFor<B> as CompatibleDigestItem<P::Signature>>::aura_pre_digest(slot)]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_import_params(
|
fn block_import_params(
|
||||||
@@ -426,7 +423,7 @@ where
|
|||||||
.map_err(|_| sp_consensus::Error::InvalidSignature(signature, public))?;
|
.map_err(|_| sp_consensus::Error::InvalidSignature(signature, public))?;
|
||||||
|
|
||||||
let signature_digest_item =
|
let signature_digest_item =
|
||||||
<DigestItemFor<B> as CompatibleDigestItem<P::Signature>>::aura_seal(signature);
|
<DigestItem as CompatibleDigestItem<P::Signature>>::aura_seal(signature);
|
||||||
|
|
||||||
let mut import_block = BlockImportParams::new(BlockOrigin::Own, header);
|
let mut import_block = BlockImportParams::new(BlockOrigin::Own, header);
|
||||||
import_block.post_digests.push(signature_digest_item);
|
import_block.post_digests.push(signature_digest_item);
|
||||||
@@ -545,7 +542,7 @@ fn authorities<A, B, C>(client: &C, at: &BlockId<B>) -> Result<Vec<A>, Consensus
|
|||||||
where
|
where
|
||||||
A: Codec + Debug,
|
A: Codec + Debug,
|
||||||
B: BlockT,
|
B: BlockT,
|
||||||
C: ProvideRuntimeApi<B> + BlockOf + ProvideCache<B>,
|
C: ProvideRuntimeApi<B> + BlockOf,
|
||||||
C::Api: AuraApi<B, A>,
|
C::Api: AuraApi<B, A>,
|
||||||
{
|
{
|
||||||
client
|
client
|
||||||
@@ -574,7 +571,10 @@ mod tests {
|
|||||||
use sp_consensus_aura::sr25519::AuthorityPair;
|
use sp_consensus_aura::sr25519::AuthorityPair;
|
||||||
use sp_inherents::InherentData;
|
use sp_inherents::InherentData;
|
||||||
use sp_keyring::sr25519::Keyring;
|
use sp_keyring::sr25519::Keyring;
|
||||||
use sp_runtime::traits::{Block as BlockT, DigestFor, Header as _};
|
use sp_runtime::{
|
||||||
|
traits::{Block as BlockT, Header as _},
|
||||||
|
Digest,
|
||||||
|
};
|
||||||
use sp_timestamp::InherentDataProvider as TimestampInherentDataProvider;
|
use sp_timestamp::InherentDataProvider as TimestampInherentDataProvider;
|
||||||
use std::{
|
use std::{
|
||||||
task::Poll,
|
task::Poll,
|
||||||
@@ -611,7 +611,7 @@ mod tests {
|
|||||||
fn propose(
|
fn propose(
|
||||||
self,
|
self,
|
||||||
_: InherentData,
|
_: InherentData,
|
||||||
digests: DigestFor<TestBlock>,
|
digests: Digest,
|
||||||
_: Duration,
|
_: Duration,
|
||||||
_: Option<usize>,
|
_: Option<usize>,
|
||||||
) -> Self::Proposal {
|
) -> Self::Proposal {
|
||||||
@@ -661,29 +661,25 @@ mod tests {
|
|||||||
_cfg: &ProtocolConfig,
|
_cfg: &ProtocolConfig,
|
||||||
_peer_data: &(),
|
_peer_data: &(),
|
||||||
) -> Self::Verifier {
|
) -> Self::Verifier {
|
||||||
match client {
|
let client = client.as_client();
|
||||||
PeersClient::Full(client, _) => {
|
let slot_duration = slot_duration(&*client).expect("slot duration available");
|
||||||
let slot_duration = slot_duration(&*client).expect("slot duration available");
|
|
||||||
|
|
||||||
assert_eq!(slot_duration.slot_duration().as_millis() as u64, SLOT_DURATION);
|
assert_eq!(slot_duration.slot_duration().as_millis() as u64, SLOT_DURATION);
|
||||||
import_queue::AuraVerifier::new(
|
import_queue::AuraVerifier::new(
|
||||||
client,
|
client,
|
||||||
Box::new(|_, _| async {
|
Box::new(|_, _| async {
|
||||||
let timestamp = TimestampInherentDataProvider::from_system_time();
|
let timestamp = TimestampInherentDataProvider::from_system_time();
|
||||||
let slot = InherentDataProvider::from_timestamp_and_duration(
|
let slot = InherentDataProvider::from_timestamp_and_duration(
|
||||||
*timestamp,
|
*timestamp,
|
||||||
Duration::from_secs(6),
|
Duration::from_secs(6),
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok((timestamp, slot))
|
Ok((timestamp, slot))
|
||||||
}),
|
}),
|
||||||
AlwaysCanAuthor,
|
AlwaysCanAuthor,
|
||||||
CheckForEquivocation::Yes,
|
CheckForEquivocation::Yes,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
},
|
|
||||||
PeersClient::Light(_, _) => unreachable!("No (yet) tests for light client + Aura"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_block_import(
|
fn make_block_import(
|
||||||
@@ -724,7 +720,7 @@ mod tests {
|
|||||||
for (peer_id, key) in peers {
|
for (peer_id, key) in peers {
|
||||||
let mut net = net.lock();
|
let mut net = net.lock();
|
||||||
let peer = net.peer(*peer_id);
|
let peer = net.peer(*peer_id);
|
||||||
let client = peer.client().as_full().expect("full clients are created").clone();
|
let client = peer.client().as_client();
|
||||||
let select_chain = peer.select_chain().expect("full client has a select chain");
|
let select_chain = peer.select_chain().expect("full client has a select chain");
|
||||||
let keystore_path = tempfile::tempdir().expect("Creates keystore path");
|
let keystore_path = tempfile::tempdir().expect("Creates keystore path");
|
||||||
let keystore = Arc::new(
|
let keystore = Arc::new(
|
||||||
@@ -823,7 +819,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut net = net.lock();
|
let mut net = net.lock();
|
||||||
let peer = net.peer(3);
|
let peer = net.peer(3);
|
||||||
let client = peer.client().as_full().expect("full clients are created").clone();
|
let client = peer.client().as_client();
|
||||||
let environ = DummyFactory(client.clone());
|
let environ = DummyFactory(client.clone());
|
||||||
|
|
||||||
let worker = AuraWorker {
|
let worker = AuraWorker {
|
||||||
@@ -875,7 +871,7 @@ mod tests {
|
|||||||
|
|
||||||
let mut net = net.lock();
|
let mut net = net.lock();
|
||||||
let peer = net.peer(3);
|
let peer = net.peer(3);
|
||||||
let client = peer.client().as_full().expect("full clients are created").clone();
|
let client = peer.client().as_client();
|
||||||
let environ = DummyFactory(client.clone());
|
let environ = DummyFactory(client.clone());
|
||||||
|
|
||||||
let mut worker = AuraWorker {
|
let mut worker = AuraWorker {
|
||||||
|
|||||||
@@ -103,9 +103,7 @@ use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_TRACE}
|
|||||||
use sp_api::{ApiExt, NumberFor, ProvideRuntimeApi};
|
use sp_api::{ApiExt, NumberFor, ProvideRuntimeApi};
|
||||||
use sp_application_crypto::AppKey;
|
use sp_application_crypto::AppKey;
|
||||||
use sp_block_builder::BlockBuilder as BlockBuilderApi;
|
use sp_block_builder::BlockBuilder as BlockBuilderApi;
|
||||||
use sp_blockchain::{
|
use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata, Result as ClientResult};
|
||||||
Error as ClientError, HeaderBackend, HeaderMetadata, ProvideCache, Result as ClientResult,
|
|
||||||
};
|
|
||||||
use sp_consensus::{
|
use sp_consensus::{
|
||||||
BlockOrigin, CacheKeyId, CanAuthorWith, Environment, Error as ConsensusError, Proposer,
|
BlockOrigin, CacheKeyId, CanAuthorWith, Environment, Error as ConsensusError, Proposer,
|
||||||
SelectChain, SlotData,
|
SelectChain, SlotData,
|
||||||
@@ -117,7 +115,8 @@ use sp_inherents::{CreateInherentDataProviders, InherentData, InherentDataProvid
|
|||||||
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
|
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::{BlockId, OpaqueDigestItemId},
|
generic::{BlockId, OpaqueDigestItemId},
|
||||||
traits::{Block as BlockT, DigestItemFor, Header, Zero},
|
traits::{Block as BlockT, Header, Zero},
|
||||||
|
DigestItem,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use sc_consensus_slots::SlotProportion;
|
pub use sc_consensus_slots::SlotProportion;
|
||||||
@@ -465,7 +464,6 @@ pub fn start_babe<B, C, SC, E, I, SO, CIDP, BS, CAW, L, Error>(
|
|||||||
where
|
where
|
||||||
B: BlockT,
|
B: BlockT,
|
||||||
C: ProvideRuntimeApi<B>
|
C: ProvideRuntimeApi<B>
|
||||||
+ ProvideCache<B>
|
|
||||||
+ ProvideUncles<B>
|
+ ProvideUncles<B>
|
||||||
+ BlockchainEvents<B>
|
+ BlockchainEvents<B>
|
||||||
+ HeaderBackend<B>
|
+ HeaderBackend<B>
|
||||||
@@ -539,7 +537,6 @@ async fn answer_requests<B: BlockT, C>(
|
|||||||
epoch_changes: SharedEpochChanges<B, Epoch>,
|
epoch_changes: SharedEpochChanges<B, Epoch>,
|
||||||
) where
|
) where
|
||||||
C: ProvideRuntimeApi<B>
|
C: ProvideRuntimeApi<B>
|
||||||
+ ProvideCache<B>
|
|
||||||
+ ProvideUncles<B>
|
+ ProvideUncles<B>
|
||||||
+ BlockchainEvents<B>
|
+ BlockchainEvents<B>
|
||||||
+ HeaderBackend<B>
|
+ HeaderBackend<B>
|
||||||
@@ -677,10 +674,7 @@ impl<B, C, E, I, Error, SO, L, BS> sc_consensus_slots::SimpleSlotWorker<B>
|
|||||||
for BabeSlotWorker<B, C, E, I, SO, L, BS>
|
for BabeSlotWorker<B, C, E, I, SO, L, BS>
|
||||||
where
|
where
|
||||||
B: BlockT,
|
B: BlockT,
|
||||||
C: ProvideRuntimeApi<B>
|
C: ProvideRuntimeApi<B> + HeaderBackend<B> + HeaderMetadata<B, Error = ClientError>,
|
||||||
+ ProvideCache<B>
|
|
||||||
+ HeaderBackend<B>
|
|
||||||
+ HeaderMetadata<B, Error = ClientError>,
|
|
||||||
C::Api: BabeApi<B>,
|
C::Api: BabeApi<B>,
|
||||||
E: Environment<B, Error = Error> + Sync,
|
E: Environment<B, Error = Error> + Sync,
|
||||||
E::Proposer: Proposer<B, Error = Error, Transaction = sp_api::TransactionFor<C, B>>,
|
E::Proposer: Proposer<B, Error = Error, Transaction = sp_api::TransactionFor<C, B>>,
|
||||||
@@ -774,12 +768,8 @@ where
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_digest_data(
|
fn pre_digest_data(&self, _slot: Slot, claim: &Self::Claim) -> Vec<sp_runtime::DigestItem> {
|
||||||
&self,
|
vec![<DigestItem as CompatibleDigestItem>::babe_pre_digest(claim.0.clone())]
|
||||||
_slot: Slot,
|
|
||||||
claim: &Self::Claim,
|
|
||||||
) -> Vec<sp_runtime::DigestItem<B::Hash>> {
|
|
||||||
vec![<DigestItemFor<B> as CompatibleDigestItem>::babe_pre_digest(claim.0.clone())]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block_import_params(
|
fn block_import_params(
|
||||||
@@ -820,8 +810,7 @@ where
|
|||||||
.clone()
|
.clone()
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| sp_consensus::Error::InvalidSignature(signature, public))?;
|
.map_err(|_| sp_consensus::Error::InvalidSignature(signature, public))?;
|
||||||
let digest_item =
|
let digest_item = <DigestItem as CompatibleDigestItem>::babe_seal(signature.into());
|
||||||
<DigestItemFor<B> as CompatibleDigestItem>::babe_seal(signature.into());
|
|
||||||
|
|
||||||
let mut import_block = BlockImportParams::new(BlockOrigin::Own, header);
|
let mut import_block = BlockImportParams::new(BlockOrigin::Own, header);
|
||||||
import_block.post_digests.push(digest_item);
|
import_block.post_digests.push(digest_item);
|
||||||
@@ -921,10 +910,7 @@ pub fn find_pre_digest<B: BlockT>(header: &B::Header) -> Result<PreDigest, Error
|
|||||||
/// Extract the BABE epoch change digest from the given header, if it exists.
|
/// Extract the BABE epoch change digest from the given header, if it exists.
|
||||||
fn find_next_epoch_digest<B: BlockT>(
|
fn find_next_epoch_digest<B: BlockT>(
|
||||||
header: &B::Header,
|
header: &B::Header,
|
||||||
) -> Result<Option<NextEpochDescriptor>, Error<B>>
|
) -> Result<Option<NextEpochDescriptor>, Error<B>> {
|
||||||
where
|
|
||||||
DigestItemFor<B>: CompatibleDigestItem,
|
|
||||||
{
|
|
||||||
let mut epoch_digest: Option<_> = None;
|
let mut epoch_digest: Option<_> = None;
|
||||||
for log in header.digest().logs() {
|
for log in header.digest().logs() {
|
||||||
trace!(target: "babe", "Checking log {:?}, looking for epoch change digest.", log);
|
trace!(target: "babe", "Checking log {:?}, looking for epoch change digest.", log);
|
||||||
@@ -943,10 +929,7 @@ where
|
|||||||
/// Extract the BABE config change digest from the given header, if it exists.
|
/// Extract the BABE config change digest from the given header, if it exists.
|
||||||
fn find_next_config_digest<B: BlockT>(
|
fn find_next_config_digest<B: BlockT>(
|
||||||
header: &B::Header,
|
header: &B::Header,
|
||||||
) -> Result<Option<NextConfigDescriptor>, Error<B>>
|
) -> Result<Option<NextConfigDescriptor>, Error<B>> {
|
||||||
where
|
|
||||||
DigestItemFor<B>: CompatibleDigestItem,
|
|
||||||
{
|
|
||||||
let mut config_digest: Option<_> = None;
|
let mut config_digest: Option<_> = None;
|
||||||
for log in header.digest().logs() {
|
for log in header.digest().logs() {
|
||||||
trace!(target: "babe", "Checking log {:?}, looking for epoch change digest.", log);
|
trace!(target: "babe", "Checking log {:?}, looking for epoch change digest.", log);
|
||||||
@@ -1132,8 +1115,7 @@ where
|
|||||||
+ ProvideRuntimeApi<Block>
|
+ ProvideRuntimeApi<Block>
|
||||||
+ Send
|
+ Send
|
||||||
+ Sync
|
+ Sync
|
||||||
+ AuxStore
|
+ AuxStore,
|
||||||
+ ProvideCache<Block>,
|
|
||||||
Client::Api: BlockBuilderApi<Block> + BabeApi<Block>,
|
Client::Api: BlockBuilderApi<Block> + BabeApi<Block>,
|
||||||
SelectChain: sp_consensus::SelectChain<Block>,
|
SelectChain: sp_consensus::SelectChain<Block>,
|
||||||
CAW: CanAuthorWith<Block> + Send + Sync,
|
CAW: CanAuthorWith<Block> + Send + Sync,
|
||||||
@@ -1332,7 +1314,6 @@ where
|
|||||||
+ HeaderMetadata<Block, Error = sp_blockchain::Error>
|
+ HeaderMetadata<Block, Error = sp_blockchain::Error>
|
||||||
+ AuxStore
|
+ AuxStore
|
||||||
+ ProvideRuntimeApi<Block>
|
+ ProvideRuntimeApi<Block>
|
||||||
+ ProvideCache<Block>
|
|
||||||
+ Send
|
+ Send
|
||||||
+ Sync,
|
+ Sync,
|
||||||
Client::Api: BabeApi<Block> + ApiExt<Block>,
|
Client::Api: BabeApi<Block> + ApiExt<Block>,
|
||||||
@@ -1399,7 +1380,6 @@ where
|
|||||||
+ HeaderMetadata<Block, Error = sp_blockchain::Error>
|
+ HeaderMetadata<Block, Error = sp_blockchain::Error>
|
||||||
+ AuxStore
|
+ AuxStore
|
||||||
+ ProvideRuntimeApi<Block>
|
+ ProvideRuntimeApi<Block>
|
||||||
+ ProvideCache<Block>
|
|
||||||
+ Send
|
+ Send
|
||||||
+ Sync,
|
+ Sync,
|
||||||
Client::Api: BabeApi<Block> + ApiExt<Block>,
|
Client::Api: BabeApi<Block> + ApiExt<Block>,
|
||||||
@@ -1756,7 +1736,6 @@ where
|
|||||||
+ Sync
|
+ Sync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
Client: ProvideRuntimeApi<Block>
|
Client: ProvideRuntimeApi<Block>
|
||||||
+ ProvideCache<Block>
|
|
||||||
+ HeaderBackend<Block>
|
+ HeaderBackend<Block>
|
||||||
+ HeaderMetadata<Block, Error = sp_blockchain::Error>
|
+ HeaderMetadata<Block, Error = sp_blockchain::Error>
|
||||||
+ AuxStore
|
+ AuxStore
|
||||||
|
|||||||
@@ -43,13 +43,13 @@ use sp_consensus_babe::{
|
|||||||
use sp_core::crypto::Pair;
|
use sp_core::crypto::Pair;
|
||||||
use sp_keystore::{vrf::make_transcript as transcript_from_data, SyncCryptoStore};
|
use sp_keystore::{vrf::make_transcript as transcript_from_data, SyncCryptoStore};
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::DigestItem,
|
generic::{Digest, DigestItem},
|
||||||
traits::{Block as BlockT, DigestFor},
|
traits::Block as BlockT,
|
||||||
};
|
};
|
||||||
use sp_timestamp::InherentDataProvider as TimestampInherentDataProvider;
|
use sp_timestamp::InherentDataProvider as TimestampInherentDataProvider;
|
||||||
use std::{cell::RefCell, task::Poll, time::Duration};
|
use std::{cell::RefCell, task::Poll, time::Duration};
|
||||||
|
|
||||||
type Item = DigestItem<Hash>;
|
type Item = DigestItem;
|
||||||
|
|
||||||
type Error = sp_blockchain::Error;
|
type Error = sp_blockchain::Error;
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ impl Environment<TestBlock> for DummyFactory {
|
|||||||
impl DummyProposer {
|
impl DummyProposer {
|
||||||
fn propose_with(
|
fn propose_with(
|
||||||
&mut self,
|
&mut self,
|
||||||
pre_digests: DigestFor<TestBlock>,
|
pre_digests: Digest,
|
||||||
) -> future::Ready<
|
) -> future::Ready<
|
||||||
Result<
|
Result<
|
||||||
Proposal<
|
Proposal<
|
||||||
@@ -181,7 +181,7 @@ impl Proposer<TestBlock> for DummyProposer {
|
|||||||
fn propose(
|
fn propose(
|
||||||
mut self,
|
mut self,
|
||||||
_: InherentData,
|
_: InherentData,
|
||||||
pre_digests: DigestFor<TestBlock>,
|
pre_digests: Digest,
|
||||||
_: Duration,
|
_: Duration,
|
||||||
_: Option<usize>,
|
_: Option<usize>,
|
||||||
) -> Self::Proposal {
|
) -> Self::Proposal {
|
||||||
@@ -295,7 +295,7 @@ impl TestNetFactory for BabeTestNet {
|
|||||||
Option<BoxJustificationImport<Block>>,
|
Option<BoxJustificationImport<Block>>,
|
||||||
Option<PeerData>,
|
Option<PeerData>,
|
||||||
) {
|
) {
|
||||||
let client = client.as_full().expect("only full clients are tested");
|
let client = client.as_client();
|
||||||
|
|
||||||
let config = Config::get_or_compute(&*client).expect("config available");
|
let config = Config::get_or_compute(&*client).expect("config available");
|
||||||
let (block_import, link) = crate::block_import(config, client.clone(), client.clone())
|
let (block_import, link) = crate::block_import(config, client.clone(), client.clone())
|
||||||
@@ -320,7 +320,7 @@ impl TestNetFactory for BabeTestNet {
|
|||||||
) -> Self::Verifier {
|
) -> Self::Verifier {
|
||||||
use substrate_test_runtime_client::DefaultTestClientBuilderExt;
|
use substrate_test_runtime_client::DefaultTestClientBuilderExt;
|
||||||
|
|
||||||
let client = client.as_full().expect("only full clients are used in test");
|
let client = client.as_client();
|
||||||
trace!(target: "babe", "Creating a verifier");
|
trace!(target: "babe", "Creating a verifier");
|
||||||
|
|
||||||
// ensure block import and verifier are linked correctly.
|
// ensure block import and verifier are linked correctly.
|
||||||
@@ -395,7 +395,7 @@ fn run_one_test(mutator: impl Fn(&mut TestHeader, Stage) + Send + Sync + 'static
|
|||||||
for (peer_id, seed) in peers {
|
for (peer_id, seed) in peers {
|
||||||
let mut net = net.lock();
|
let mut net = net.lock();
|
||||||
let peer = net.peer(*peer_id);
|
let peer = net.peer(*peer_id);
|
||||||
let client = peer.client().as_full().expect("Only full clients are used in tests").clone();
|
let client = peer.client().as_client();
|
||||||
let select_chain = peer.select_chain().expect("Full client has select_chain");
|
let select_chain = peer.select_chain().expect("Full client has select_chain");
|
||||||
|
|
||||||
let keystore_path = tempfile::tempdir().expect("Creates keystore path");
|
let keystore_path = tempfile::tempdir().expect("Creates keystore path");
|
||||||
@@ -679,7 +679,7 @@ fn importing_block_one_sets_genesis_epoch() {
|
|||||||
|
|
||||||
let peer = net.peer(0);
|
let peer = net.peer(0);
|
||||||
let data = peer.data.as_ref().expect("babe link set up during initialization");
|
let data = peer.data.as_ref().expect("babe link set up during initialization");
|
||||||
let client = peer.client().as_full().expect("Only full clients are used in tests").clone();
|
let client = peer.client().as_client();
|
||||||
|
|
||||||
let mut proposer_factory = DummyFactory {
|
let mut proposer_factory = DummyFactory {
|
||||||
client: client.clone(),
|
client: client.clone(),
|
||||||
@@ -721,7 +721,7 @@ fn importing_epoch_change_block_prunes_tree() {
|
|||||||
let peer = net.peer(0);
|
let peer = net.peer(0);
|
||||||
let data = peer.data.as_ref().expect("babe link set up during initialization");
|
let data = peer.data.as_ref().expect("babe link set up during initialization");
|
||||||
|
|
||||||
let client = peer.client().as_full().expect("Only full clients are used in tests").clone();
|
let client = peer.client().as_client();
|
||||||
let mut block_import = data.block_import.lock().take().expect("import set up during init");
|
let mut block_import = data.block_import.lock().take().expect("import set up during init");
|
||||||
let epoch_changes = data.link.epoch_changes.clone();
|
let epoch_changes = data.link.epoch_changes.clone();
|
||||||
|
|
||||||
@@ -836,7 +836,7 @@ fn verify_slots_are_strictly_increasing() {
|
|||||||
let peer = net.peer(0);
|
let peer = net.peer(0);
|
||||||
let data = peer.data.as_ref().expect("babe link set up during initialization");
|
let data = peer.data.as_ref().expect("babe link set up during initialization");
|
||||||
|
|
||||||
let client = peer.client().as_full().expect("Only full clients are used in tests").clone();
|
let client = peer.client().as_client();
|
||||||
let mut block_import = data.block_import.lock().take().expect("import set up during init");
|
let mut block_import = data.block_import.lock().take().expect("import set up during init");
|
||||||
|
|
||||||
let mut proposer_factory = DummyFactory {
|
let mut proposer_factory = DummyFactory {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ use sp_consensus_babe::{
|
|||||||
};
|
};
|
||||||
use sp_consensus_slots::Slot;
|
use sp_consensus_slots::Slot;
|
||||||
use sp_core::{Pair, Public};
|
use sp_core::{Pair, Public};
|
||||||
use sp_runtime::traits::{DigestItemFor, Header};
|
use sp_runtime::{traits::Header, DigestItem};
|
||||||
|
|
||||||
/// BABE verification parameters
|
/// BABE verification parameters
|
||||||
pub(super) struct VerificationParams<'a, B: 'a + BlockT> {
|
pub(super) struct VerificationParams<'a, B: 'a + BlockT> {
|
||||||
@@ -61,10 +61,7 @@ pub(super) struct VerificationParams<'a, B: 'a + BlockT> {
|
|||||||
/// with each having different validation logic.
|
/// with each having different validation logic.
|
||||||
pub(super) fn check_header<B: BlockT + Sized>(
|
pub(super) fn check_header<B: BlockT + Sized>(
|
||||||
params: VerificationParams<B>,
|
params: VerificationParams<B>,
|
||||||
) -> Result<CheckedHeader<B::Header, VerifiedHeaderInfo<B>>, Error<B>>
|
) -> Result<CheckedHeader<B::Header, VerifiedHeaderInfo>, Error<B>> {
|
||||||
where
|
|
||||||
DigestItemFor<B>: CompatibleDigestItem,
|
|
||||||
{
|
|
||||||
let VerificationParams { mut header, pre_digest, slot_now, epoch } = params;
|
let VerificationParams { mut header, pre_digest, slot_now, epoch } = params;
|
||||||
|
|
||||||
let authorities = &epoch.authorities;
|
let authorities = &epoch.authorities;
|
||||||
@@ -137,9 +134,9 @@ where
|
|||||||
Ok(CheckedHeader::Checked(header, info))
|
Ok(CheckedHeader::Checked(header, info))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct VerifiedHeaderInfo<B: BlockT> {
|
pub(super) struct VerifiedHeaderInfo {
|
||||||
pub(super) pre_digest: DigestItemFor<B>,
|
pub(super) pre_digest: DigestItem,
|
||||||
pub(super) seal: DigestItemFor<B>,
|
pub(super) seal: DigestItem,
|
||||||
pub(super) author: AuthorityId,
|
pub(super) author: AuthorityId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,8 @@
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
traits::{Block as BlockT, DigestItemFor, HashFor, Header as HeaderT, NumberFor},
|
traits::{Block as BlockT, HashFor, Header as HeaderT, NumberFor},
|
||||||
Justification, Justifications,
|
DigestItem, Justification, Justifications,
|
||||||
};
|
};
|
||||||
use std::{any::Any, borrow::Cow, collections::HashMap, sync::Arc};
|
use std::{any::Any, borrow::Cow, collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
@@ -122,7 +122,7 @@ pub struct BlockCheckParams<Block: BlockT> {
|
|||||||
/// Precomputed storage.
|
/// Precomputed storage.
|
||||||
pub enum StorageChanges<Block: BlockT, Transaction> {
|
pub enum StorageChanges<Block: BlockT, Transaction> {
|
||||||
/// Changes coming from block execution.
|
/// Changes coming from block execution.
|
||||||
Changes(sp_state_machine::StorageChanges<Transaction, HashFor<Block>, NumberFor<Block>>),
|
Changes(sp_state_machine::StorageChanges<Transaction, HashFor<Block>>),
|
||||||
/// Whole new state.
|
/// Whole new state.
|
||||||
Import(ImportedState<Block>),
|
Import(ImportedState<Block>),
|
||||||
}
|
}
|
||||||
@@ -175,7 +175,7 @@ pub struct BlockImportParams<Block: BlockT, Transaction> {
|
|||||||
pub justifications: Option<Justifications>,
|
pub justifications: Option<Justifications>,
|
||||||
/// Digest items that have been added after the runtime for external
|
/// Digest items that have been added after the runtime for external
|
||||||
/// work, like a consensus signature.
|
/// work, like a consensus signature.
|
||||||
pub post_digests: Vec<DigestItemFor<Block>>,
|
pub post_digests: Vec<DigestItem>,
|
||||||
/// The body of the block.
|
/// The body of the block.
|
||||||
pub body: Option<Vec<Block::Extrinsic>>,
|
pub body: Option<Vec<Block::Extrinsic>>,
|
||||||
/// Indexed transaction body of the block.
|
/// Indexed transaction body of the block.
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ use super::Error;
|
|||||||
|
|
||||||
use sc_consensus::BlockImportParams;
|
use sc_consensus::BlockImportParams;
|
||||||
use sp_inherents::InherentData;
|
use sp_inherents::InherentData;
|
||||||
use sp_runtime::traits::{Block as BlockT, DigestFor};
|
use sp_runtime::{traits::Block as BlockT, Digest};
|
||||||
|
|
||||||
pub mod babe;
|
pub mod babe;
|
||||||
|
|
||||||
@@ -32,11 +32,7 @@ pub trait ConsensusDataProvider<B: BlockT>: Send + Sync {
|
|||||||
type Transaction;
|
type Transaction;
|
||||||
|
|
||||||
/// Attempt to create a consensus digest.
|
/// Attempt to create a consensus digest.
|
||||||
fn create_digest(
|
fn create_digest(&self, parent: &B::Header, inherents: &InherentData) -> Result<Digest, Error>;
|
||||||
&self,
|
|
||||||
parent: &B::Header,
|
|
||||||
inherents: &InherentData,
|
|
||||||
) -> Result<DigestFor<B>, Error>;
|
|
||||||
|
|
||||||
/// set up the neccessary import params.
|
/// set up the neccessary import params.
|
||||||
fn append_block_import(
|
fn append_block_import(
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ use sp_consensus_slots::Slot;
|
|||||||
use sp_inherents::{InherentData, InherentDataProvider, InherentIdentifier};
|
use sp_inherents::{InherentData, InherentDataProvider, InherentIdentifier};
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::{BlockId, Digest},
|
generic::{BlockId, Digest},
|
||||||
traits::{Block as BlockT, DigestFor, DigestItemFor, Header, Zero},
|
traits::{Block as BlockT, Header, Zero},
|
||||||
|
DigestItem,
|
||||||
};
|
};
|
||||||
use sp_timestamp::{InherentType, TimestampInherentData, INHERENT_IDENTIFIER};
|
use sp_timestamp::{InherentType, TimestampInherentData, INHERENT_IDENTIFIER};
|
||||||
|
|
||||||
@@ -193,11 +194,7 @@ where
|
|||||||
{
|
{
|
||||||
type Transaction = TransactionFor<C, B>;
|
type Transaction = TransactionFor<C, B>;
|
||||||
|
|
||||||
fn create_digest(
|
fn create_digest(&self, parent: &B::Header, inherents: &InherentData) -> Result<Digest, Error> {
|
||||||
&self,
|
|
||||||
parent: &B::Header,
|
|
||||||
inherents: &InherentData,
|
|
||||||
) -> Result<DigestFor<B>, Error> {
|
|
||||||
let slot = inherents
|
let slot = inherents
|
||||||
.babe_inherent_data()?
|
.babe_inherent_data()?
|
||||||
.ok_or_else(|| Error::StringError("No babe inherent data".into()))?;
|
.ok_or_else(|| Error::StringError("No babe inherent data".into()))?;
|
||||||
@@ -207,7 +204,7 @@ where
|
|||||||
let logs = if let Some((predigest, _)) =
|
let logs = if let Some((predigest, _)) =
|
||||||
authorship::claim_slot(slot, &epoch, &self.keystore)
|
authorship::claim_slot(slot, &epoch, &self.keystore)
|
||||||
{
|
{
|
||||||
vec![<DigestItemFor<B> as CompatibleDigestItem>::babe_pre_digest(predigest)]
|
vec![<DigestItem as CompatibleDigestItem>::babe_pre_digest(predigest)]
|
||||||
} else {
|
} else {
|
||||||
// well we couldn't claim a slot because this is an existing chain and we're not in the
|
// well we couldn't claim a slot because this is an existing chain and we're not in the
|
||||||
// authorities. we need to tell BabeBlockImport that the epoch has changed, and we put
|
// authorities. we need to tell BabeBlockImport that the epoch has changed, and we put
|
||||||
@@ -244,13 +241,13 @@ where
|
|||||||
});
|
});
|
||||||
|
|
||||||
vec![
|
vec![
|
||||||
DigestItemFor::<B>::PreRuntime(BABE_ENGINE_ID, predigest.encode()),
|
DigestItem::PreRuntime(BABE_ENGINE_ID, predigest.encode()),
|
||||||
DigestItemFor::<B>::Consensus(BABE_ENGINE_ID, next_epoch.encode()),
|
DigestItem::Consensus(BABE_ENGINE_ID, next_epoch.encode()),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
ViableEpochDescriptor::UnimportedGenesis(_) => {
|
ViableEpochDescriptor::UnimportedGenesis(_) => {
|
||||||
// since this is the genesis, secondary predigest works for now.
|
// since this is the genesis, secondary predigest works for now.
|
||||||
vec![DigestItemFor::<B>::PreRuntime(BABE_ENGINE_ID, predigest.encode())]
|
vec![DigestItem::PreRuntime(BABE_ENGINE_ID, predigest.encode())]
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ use sc_consensus::{
|
|||||||
};
|
};
|
||||||
use sp_api::ProvideRuntimeApi;
|
use sp_api::ProvideRuntimeApi;
|
||||||
use sp_block_builder::BlockBuilder as BlockBuilderApi;
|
use sp_block_builder::BlockBuilder as BlockBuilderApi;
|
||||||
use sp_blockchain::{well_known_cache_keys::Id as CacheKeyId, HeaderBackend, ProvideCache};
|
use sp_blockchain::{well_known_cache_keys::Id as CacheKeyId, HeaderBackend};
|
||||||
use sp_consensus::{
|
use sp_consensus::{
|
||||||
CanAuthorWith, Environment, Error as ConsensusError, Proposer, SelectChain, SyncOracle,
|
CanAuthorWith, Environment, Error as ConsensusError, Proposer, SelectChain, SyncOracle,
|
||||||
};
|
};
|
||||||
@@ -240,7 +240,7 @@ where
|
|||||||
B: BlockT,
|
B: BlockT,
|
||||||
I: BlockImport<B, Transaction = sp_api::TransactionFor<C, B>> + Send + Sync,
|
I: BlockImport<B, Transaction = sp_api::TransactionFor<C, B>> + Send + Sync,
|
||||||
I::Error: Into<ConsensusError>,
|
I::Error: Into<ConsensusError>,
|
||||||
C: ProvideRuntimeApi<B> + Send + Sync + HeaderBackend<B> + AuxStore + ProvideCache<B> + BlockOf,
|
C: ProvideRuntimeApi<B> + Send + Sync + HeaderBackend<B> + AuxStore + BlockOf,
|
||||||
C::Api: BlockBuilderApi<B>,
|
C::Api: BlockBuilderApi<B>,
|
||||||
Algorithm: PowAlgorithm<B>,
|
Algorithm: PowAlgorithm<B>,
|
||||||
CAW: CanAuthorWith<B>,
|
CAW: CanAuthorWith<B>,
|
||||||
@@ -319,7 +319,7 @@ where
|
|||||||
I: BlockImport<B, Transaction = sp_api::TransactionFor<C, B>> + Send + Sync,
|
I: BlockImport<B, Transaction = sp_api::TransactionFor<C, B>> + Send + Sync,
|
||||||
I::Error: Into<ConsensusError>,
|
I::Error: Into<ConsensusError>,
|
||||||
S: SelectChain<B>,
|
S: SelectChain<B>,
|
||||||
C: ProvideRuntimeApi<B> + Send + Sync + HeaderBackend<B> + AuxStore + ProvideCache<B> + BlockOf,
|
C: ProvideRuntimeApi<B> + Send + Sync + HeaderBackend<B> + AuxStore + BlockOf,
|
||||||
C::Api: BlockBuilderApi<B>,
|
C::Api: BlockBuilderApi<B>,
|
||||||
Algorithm: PowAlgorithm<B> + Send + Sync,
|
Algorithm: PowAlgorithm<B> + Send + Sync,
|
||||||
Algorithm::Difficulty: 'static + Send,
|
Algorithm::Difficulty: 'static + Send,
|
||||||
@@ -425,10 +425,7 @@ impl<B: BlockT, Algorithm> PowVerifier<B, Algorithm> {
|
|||||||
Self { algorithm, _marker: PhantomData }
|
Self { algorithm, _marker: PhantomData }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_header(
|
fn check_header(&self, mut header: B::Header) -> Result<(B::Header, DigestItem), Error<B>>
|
||||||
&self,
|
|
||||||
mut header: B::Header,
|
|
||||||
) -> Result<(B::Header, DigestItem<B::Hash>), Error<B>>
|
|
||||||
where
|
where
|
||||||
Algorithm: PowAlgorithm<B>,
|
Algorithm: PowAlgorithm<B>,
|
||||||
{
|
{
|
||||||
@@ -630,7 +627,7 @@ where
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut inherent_digest = Digest::<Block::Hash>::default();
|
let mut inherent_digest = Digest::default();
|
||||||
if let Some(pre_runtime) = &pre_runtime {
|
if let Some(pre_runtime) = &pre_runtime {
|
||||||
inherent_digest.push(DigestItem::PreRuntime(POW_ENGINE_ID, pre_runtime.to_vec()));
|
inherent_digest.push(DigestItem::PreRuntime(POW_ENGINE_ID, pre_runtime.to_vec()));
|
||||||
}
|
}
|
||||||
@@ -702,10 +699,7 @@ fn find_pre_digest<B: BlockT>(header: &B::Header) -> Result<Option<Vec<u8>>, Err
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch PoW seal.
|
/// Fetch PoW seal.
|
||||||
fn fetch_seal<B: BlockT>(
|
fn fetch_seal<B: BlockT>(digest: Option<&DigestItem>, hash: B::Hash) -> Result<Vec<u8>, Error<B>> {
|
||||||
digest: Option<&DigestItem<B::Hash>>,
|
|
||||||
hash: B::Hash,
|
|
||||||
) -> Result<Vec<u8>, Error<B>> {
|
|
||||||
match digest {
|
match digest {
|
||||||
Some(DigestItem::Seal(id, seal)) =>
|
Some(DigestItem::Seal(id, seal)) =>
|
||||||
if id == &POW_ENGINE_ID {
|
if id == &POW_ENGINE_ID {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ use sp_consensus_slots::Slot;
|
|||||||
use sp_inherents::CreateInherentDataProviders;
|
use sp_inherents::CreateInherentDataProviders;
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::BlockId,
|
generic::BlockId,
|
||||||
traits::{Block as BlockT, HashFor, Header as HeaderT, NumberFor},
|
traits::{Block as BlockT, HashFor, Header as HeaderT},
|
||||||
};
|
};
|
||||||
use sp_timestamp::Timestamp;
|
use sp_timestamp::Timestamp;
|
||||||
use std::{fmt::Debug, ops::Deref, time::Duration};
|
use std::{fmt::Debug, ops::Deref, time::Duration};
|
||||||
@@ -54,7 +54,7 @@ use std::{fmt::Debug, ops::Deref, time::Duration};
|
|||||||
///
|
///
|
||||||
/// See [`sp_state_machine::StorageChanges`] for more information.
|
/// See [`sp_state_machine::StorageChanges`] for more information.
|
||||||
pub type StorageChanges<Transaction, Block> =
|
pub type StorageChanges<Transaction, Block> =
|
||||||
sp_state_machine::StorageChanges<Transaction, HashFor<Block>, NumberFor<Block>>;
|
sp_state_machine::StorageChanges<Transaction, HashFor<Block>>;
|
||||||
|
|
||||||
/// The result of [`SlotWorker::on_slot`].
|
/// The result of [`SlotWorker::on_slot`].
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -141,11 +141,7 @@ pub trait SimpleSlotWorker<B: BlockT> {
|
|||||||
fn notify_slot(&self, _header: &B::Header, _slot: Slot, _epoch_data: &Self::EpochData) {}
|
fn notify_slot(&self, _header: &B::Header, _slot: Slot, _epoch_data: &Self::EpochData) {}
|
||||||
|
|
||||||
/// Return the pre digest data to include in a block authored with the given claim.
|
/// Return the pre digest data to include in a block authored with the given claim.
|
||||||
fn pre_digest_data(
|
fn pre_digest_data(&self, slot: Slot, claim: &Self::Claim) -> Vec<sp_runtime::DigestItem>;
|
||||||
&self,
|
|
||||||
slot: Slot,
|
|
||||||
claim: &Self::Claim,
|
|
||||||
) -> Vec<sp_runtime::DigestItem<B::Hash>>;
|
|
||||||
|
|
||||||
/// Returns a function which produces a `BlockImportParams`.
|
/// Returns a function which produces a `BlockImportParams`.
|
||||||
fn block_import_params(
|
fn block_import_params(
|
||||||
|
|||||||
-2351
File diff suppressed because it is too large
Load Diff
-187
@@ -1,187 +0,0 @@
|
|||||||
// This file is part of Substrate.
|
|
||||||
|
|
||||||
// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! List-cache storage entries.
|
|
||||||
|
|
||||||
use codec::{Decode, Encode};
|
|
||||||
use sp_blockchain::Result as ClientResult;
|
|
||||||
use sp_runtime::traits::{Block as BlockT, NumberFor};
|
|
||||||
|
|
||||||
use crate::cache::{list_storage::Storage, CacheItemT, ComplexBlockId};
|
|
||||||
|
|
||||||
/// Single list-based cache entry.
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
|
||||||
pub struct Entry<Block: BlockT, T> {
|
|
||||||
/// first block, when this value became actual.
|
|
||||||
pub valid_from: ComplexBlockId<Block>,
|
|
||||||
/// Value stored at this entry.
|
|
||||||
pub value: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Internal representation of the single list-based cache entry. The entry points to the
|
|
||||||
/// previous entry in the cache, allowing us to traverse back in time in list-style.
|
|
||||||
#[derive(Debug, Encode, Decode)]
|
|
||||||
#[cfg_attr(test, derive(Clone, PartialEq))]
|
|
||||||
pub struct StorageEntry<Block: BlockT, T: CacheItemT> {
|
|
||||||
/// None if valid from the beginning.
|
|
||||||
pub prev_valid_from: Option<ComplexBlockId<Block>>,
|
|
||||||
/// Value stored at this entry.
|
|
||||||
pub value: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Block: BlockT, T: CacheItemT> Entry<Block, T> {
|
|
||||||
/// Returns Some if the entry should be updated with the new value.
|
|
||||||
pub fn try_update(&self, value: Option<T>) -> Option<StorageEntry<Block, T>> {
|
|
||||||
match value {
|
|
||||||
Some(value) => match self.value == value {
|
|
||||||
true => None,
|
|
||||||
false =>
|
|
||||||
Some(StorageEntry { prev_valid_from: Some(self.valid_from.clone()), value }),
|
|
||||||
},
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wrapper that calls search_before to get range where the given block fits.
|
|
||||||
pub fn search_best_range_before<S: Storage<Block, T>>(
|
|
||||||
&self,
|
|
||||||
storage: &S,
|
|
||||||
block: NumberFor<Block>,
|
|
||||||
) -> ClientResult<Option<(ComplexBlockId<Block>, Option<ComplexBlockId<Block>>)>> {
|
|
||||||
Ok(self
|
|
||||||
.search_best_before(storage, block)?
|
|
||||||
.map(|(entry, next)| (entry.valid_from, next)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Searches the list, ending with THIS entry for the best entry preceding (or at)
|
|
||||||
/// given block number.
|
|
||||||
/// If the entry is found, result is the entry and the block id of next entry (if exists).
|
|
||||||
/// NOTE that this function does not check that the passed block is actually linked to
|
|
||||||
/// the blocks it found.
|
|
||||||
pub fn search_best_before<S: Storage<Block, T>>(
|
|
||||||
&self,
|
|
||||||
storage: &S,
|
|
||||||
block: NumberFor<Block>,
|
|
||||||
) -> ClientResult<Option<(Entry<Block, T>, Option<ComplexBlockId<Block>>)>> {
|
|
||||||
// we're looking for the best value
|
|
||||||
let mut next = None;
|
|
||||||
let mut current = self.valid_from.clone();
|
|
||||||
if block >= self.valid_from.number {
|
|
||||||
let value = self.value.clone();
|
|
||||||
return Ok(Some((Entry { valid_from: current, value }, next)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// else - travel back in time
|
|
||||||
loop {
|
|
||||||
let entry = storage.require_entry(¤t)?;
|
|
||||||
if block >= current.number {
|
|
||||||
return Ok(Some((Entry { valid_from: current, value: entry.value }, next)))
|
|
||||||
}
|
|
||||||
|
|
||||||
next = Some(current);
|
|
||||||
current = match entry.prev_valid_from {
|
|
||||||
Some(prev_valid_from) => prev_valid_from,
|
|
||||||
None => return Ok(None),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Block: BlockT, T: CacheItemT> StorageEntry<Block, T> {
|
|
||||||
/// Converts storage entry into an entry, valid from given block.
|
|
||||||
pub fn into_entry(self, valid_from: ComplexBlockId<Block>) -> Entry<Block, T> {
|
|
||||||
Entry { valid_from, value: self.value }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::cache::list_storage::tests::{DummyStorage, FaultyStorage};
|
|
||||||
use substrate_test_runtime_client::runtime::{Block, H256};
|
|
||||||
|
|
||||||
fn test_id(number: u64) -> ComplexBlockId<Block> {
|
|
||||||
ComplexBlockId::new(H256::from_low_u64_be(number), number)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn entry_try_update_works() {
|
|
||||||
// when trying to update with None value
|
|
||||||
assert_eq!(Entry::<_, u64> { valid_from: test_id(1), value: 42 }.try_update(None), None);
|
|
||||||
// when trying to update with the same Some value
|
|
||||||
assert_eq!(Entry { valid_from: test_id(1), value: 1 }.try_update(Some(1)), None);
|
|
||||||
// when trying to update with different Some value
|
|
||||||
assert_eq!(
|
|
||||||
Entry { valid_from: test_id(1), value: 1 }.try_update(Some(2)),
|
|
||||||
Some(StorageEntry { prev_valid_from: Some(test_id(1)), value: 2 })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn entry_search_best_before_fails() {
|
|
||||||
// when storage returns error
|
|
||||||
assert!(Entry::<_, u64> { valid_from: test_id(100), value: 42 }
|
|
||||||
.search_best_before(&FaultyStorage, 50)
|
|
||||||
.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn entry_search_best_before_works() {
|
|
||||||
// when block is better than our best block
|
|
||||||
assert_eq!(
|
|
||||||
Entry::<_, u64> { valid_from: test_id(100), value: 100 }
|
|
||||||
.search_best_before(&DummyStorage::new(), 150)
|
|
||||||
.unwrap(),
|
|
||||||
Some((Entry::<_, u64> { valid_from: test_id(100), value: 100 }, None))
|
|
||||||
);
|
|
||||||
// when block is found between two entries
|
|
||||||
assert_eq!(
|
|
||||||
Entry::<_, u64> { valid_from: test_id(100), value: 100 }
|
|
||||||
.search_best_before(
|
|
||||||
&DummyStorage::new()
|
|
||||||
.with_entry(
|
|
||||||
test_id(100),
|
|
||||||
StorageEntry { prev_valid_from: Some(test_id(50)), value: 100 }
|
|
||||||
)
|
|
||||||
.with_entry(
|
|
||||||
test_id(50),
|
|
||||||
StorageEntry { prev_valid_from: Some(test_id(30)), value: 50 }
|
|
||||||
),
|
|
||||||
75
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
Some((Entry::<_, u64> { valid_from: test_id(50), value: 50 }, Some(test_id(100))))
|
|
||||||
);
|
|
||||||
// when block is not found
|
|
||||||
assert_eq!(
|
|
||||||
Entry::<_, u64> { valid_from: test_id(100), value: 100 }
|
|
||||||
.search_best_before(
|
|
||||||
&DummyStorage::new()
|
|
||||||
.with_entry(
|
|
||||||
test_id(100),
|
|
||||||
StorageEntry { prev_valid_from: Some(test_id(50)), value: 100 }
|
|
||||||
)
|
|
||||||
.with_entry(test_id(50), StorageEntry { prev_valid_from: None, value: 50 }),
|
|
||||||
30
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
None
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-441
@@ -1,441 +0,0 @@
|
|||||||
// This file is part of Substrate.
|
|
||||||
|
|
||||||
// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! List-cache storage definition and implementation.
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use crate::utils::{self, meta_keys};
|
|
||||||
use codec::{Decode, Encode};
|
|
||||||
use sp_blockchain::{Error as ClientError, Result as ClientResult};
|
|
||||||
use sp_database::{Database, Transaction};
|
|
||||||
use sp_runtime::{
|
|
||||||
generic::BlockId,
|
|
||||||
traits::{Block as BlockT, Header as HeaderT, NumberFor},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
cache::{
|
|
||||||
list_cache::{CommitOperation, Fork},
|
|
||||||
list_entry::{Entry, StorageEntry},
|
|
||||||
CacheItemT, ComplexBlockId,
|
|
||||||
},
|
|
||||||
DbHash,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Single list-cache metadata.
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[cfg_attr(test, derive(Clone, PartialEq))]
|
|
||||||
pub struct Metadata<Block: BlockT> {
|
|
||||||
/// Block at which best finalized entry is stored.
|
|
||||||
pub finalized: Option<ComplexBlockId<Block>>,
|
|
||||||
/// A set of blocks at which best unfinalized entries are stored.
|
|
||||||
pub unfinalized: Vec<ComplexBlockId<Block>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Readonly list-cache storage trait.
|
|
||||||
pub trait Storage<Block: BlockT, T: CacheItemT> {
|
|
||||||
/// Reads hash of the block at given number.
|
|
||||||
fn read_id(&self, at: NumberFor<Block>) -> ClientResult<Option<Block::Hash>>;
|
|
||||||
|
|
||||||
/// Reads header of the block with given hash.
|
|
||||||
fn read_header(&self, at: &Block::Hash) -> ClientResult<Option<Block::Header>>;
|
|
||||||
|
|
||||||
/// Reads cache metadata: best finalized entry (if some) and the list.
|
|
||||||
fn read_meta(&self) -> ClientResult<Metadata<Block>>;
|
|
||||||
|
|
||||||
/// Reads cache entry from the storage.
|
|
||||||
fn read_entry(
|
|
||||||
&self,
|
|
||||||
at: &ComplexBlockId<Block>,
|
|
||||||
) -> ClientResult<Option<StorageEntry<Block, T>>>;
|
|
||||||
|
|
||||||
/// Reads referenced (and thus existing) cache entry from the storage.
|
|
||||||
fn require_entry(&self, at: &ComplexBlockId<Block>) -> ClientResult<StorageEntry<Block, T>> {
|
|
||||||
self.read_entry(at).and_then(|entry| {
|
|
||||||
entry.ok_or_else(|| {
|
|
||||||
ClientError::from(ClientError::Backend(format!(
|
|
||||||
"Referenced cache entry at {:?} is not found",
|
|
||||||
at
|
|
||||||
)))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// List-cache storage transaction.
|
|
||||||
pub trait StorageTransaction<Block: BlockT, T: CacheItemT> {
|
|
||||||
/// Insert storage entry at given block.
|
|
||||||
fn insert_storage_entry(&mut self, at: &ComplexBlockId<Block>, entry: &StorageEntry<Block, T>);
|
|
||||||
|
|
||||||
/// Delete storage entry at given block.
|
|
||||||
fn remove_storage_entry(&mut self, at: &ComplexBlockId<Block>);
|
|
||||||
|
|
||||||
/// Update metadata of the cache.
|
|
||||||
fn update_meta(
|
|
||||||
&mut self,
|
|
||||||
best_finalized_entry: Option<&Entry<Block, T>>,
|
|
||||||
unfinalized: &[Fork<Block, T>],
|
|
||||||
operation: &CommitOperation<Block, T>,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A set of columns used by the DbStorage.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct DbColumns {
|
|
||||||
/// Column holding cache meta.
|
|
||||||
pub meta: u32,
|
|
||||||
/// Column holding the mapping of { block number => block hash } for blocks of the best chain.
|
|
||||||
pub key_lookup: u32,
|
|
||||||
/// Column holding the mapping of { block hash => block header }.
|
|
||||||
pub header: u32,
|
|
||||||
/// Column holding cache entries.
|
|
||||||
pub cache: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Database-backed list cache storage.
|
|
||||||
pub struct DbStorage {
|
|
||||||
name: Vec<u8>,
|
|
||||||
meta_key: Vec<u8>,
|
|
||||||
db: Arc<dyn Database<DbHash>>,
|
|
||||||
columns: DbColumns,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DbStorage {
|
|
||||||
/// Create new database-backed list cache storage.
|
|
||||||
pub fn new(name: Vec<u8>, db: Arc<dyn Database<DbHash>>, columns: DbColumns) -> Self {
|
|
||||||
let meta_key = meta::key(&name);
|
|
||||||
DbStorage { name, meta_key, db, columns }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get reference to the database.
|
|
||||||
pub fn db(&self) -> &Arc<dyn Database<DbHash>> {
|
|
||||||
&self.db
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get reference to the database columns.
|
|
||||||
pub fn columns(&self) -> &DbColumns {
|
|
||||||
&self.columns
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Encode block id for storing as a key in cache column.
|
|
||||||
/// We append prefix to the actual encoding to allow several caches
|
|
||||||
/// store entries in the same column.
|
|
||||||
pub fn encode_block_id<Block: BlockT>(&self, block: &ComplexBlockId<Block>) -> Vec<u8> {
|
|
||||||
let mut encoded = self.name.clone();
|
|
||||||
encoded.extend(block.hash.as_ref());
|
|
||||||
encoded
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Block: BlockT, T: CacheItemT> Storage<Block, T> for DbStorage {
|
|
||||||
fn read_id(&self, at: NumberFor<Block>) -> ClientResult<Option<Block::Hash>> {
|
|
||||||
utils::read_header::<Block>(
|
|
||||||
&*self.db,
|
|
||||||
self.columns.key_lookup,
|
|
||||||
self.columns.header,
|
|
||||||
BlockId::Number(at),
|
|
||||||
)
|
|
||||||
.map(|maybe_header| maybe_header.map(|header| header.hash()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_header(&self, at: &Block::Hash) -> ClientResult<Option<Block::Header>> {
|
|
||||||
utils::read_header::<Block>(
|
|
||||||
&*self.db,
|
|
||||||
self.columns.key_lookup,
|
|
||||||
self.columns.header,
|
|
||||||
BlockId::Hash(*at),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_meta(&self) -> ClientResult<Metadata<Block>> {
|
|
||||||
match self.db.get(self.columns.meta, &self.meta_key) {
|
|
||||||
Some(meta) => meta::decode(&*meta),
|
|
||||||
None => Ok(Metadata { finalized: None, unfinalized: Vec::new() }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_entry(
|
|
||||||
&self,
|
|
||||||
at: &ComplexBlockId<Block>,
|
|
||||||
) -> ClientResult<Option<StorageEntry<Block, T>>> {
|
|
||||||
match self.db.get(self.columns.cache, &self.encode_block_id(at)) {
|
|
||||||
Some(entry) => StorageEntry::<Block, T>::decode(&mut &entry[..])
|
|
||||||
.map_err(|_| ClientError::Backend("Failed to decode cache entry".into()))
|
|
||||||
.map(Some),
|
|
||||||
None => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Database-backed list cache storage transaction.
|
|
||||||
pub struct DbStorageTransaction<'a> {
|
|
||||||
storage: &'a DbStorage,
|
|
||||||
tx: &'a mut Transaction<DbHash>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> DbStorageTransaction<'a> {
|
|
||||||
/// Create new database transaction.
|
|
||||||
pub fn new(storage: &'a DbStorage, tx: &'a mut Transaction<DbHash>) -> Self {
|
|
||||||
DbStorageTransaction { storage, tx }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Block: BlockT, T: CacheItemT> StorageTransaction<Block, T> for DbStorageTransaction<'a> {
|
|
||||||
fn insert_storage_entry(&mut self, at: &ComplexBlockId<Block>, entry: &StorageEntry<Block, T>) {
|
|
||||||
self.tx.set_from_vec(
|
|
||||||
self.storage.columns.cache,
|
|
||||||
&self.storage.encode_block_id(at),
|
|
||||||
entry.encode(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_storage_entry(&mut self, at: &ComplexBlockId<Block>) {
|
|
||||||
self.tx.remove(self.storage.columns.cache, &self.storage.encode_block_id(at));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_meta(
|
|
||||||
&mut self,
|
|
||||||
best_finalized_entry: Option<&Entry<Block, T>>,
|
|
||||||
unfinalized: &[Fork<Block, T>],
|
|
||||||
operation: &CommitOperation<Block, T>,
|
|
||||||
) {
|
|
||||||
self.tx.set_from_vec(
|
|
||||||
self.storage.columns.meta,
|
|
||||||
&self.storage.meta_key,
|
|
||||||
meta::encode(best_finalized_entry, unfinalized, operation),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Metadata related functions.
|
|
||||||
mod meta {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
/// Convert cache name into cache metadata key.
|
|
||||||
pub fn key(name: &[u8]) -> Vec<u8> {
|
|
||||||
let mut key_name = meta_keys::CACHE_META_PREFIX.to_vec();
|
|
||||||
key_name.extend_from_slice(name);
|
|
||||||
key_name
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Encode cache metadata 'applying' commit operation before encoding.
|
|
||||||
pub fn encode<Block: BlockT, T: CacheItemT>(
|
|
||||||
best_finalized_entry: Option<&Entry<Block, T>>,
|
|
||||||
unfinalized: &[Fork<Block, T>],
|
|
||||||
op: &CommitOperation<Block, T>,
|
|
||||||
) -> Vec<u8> {
|
|
||||||
let mut finalized = best_finalized_entry.as_ref().map(|entry| &entry.valid_from);
|
|
||||||
let mut unfinalized =
|
|
||||||
unfinalized.iter().map(|fork| &fork.head().valid_from).collect::<Vec<_>>();
|
|
||||||
|
|
||||||
match op {
|
|
||||||
CommitOperation::AppendNewBlock(_, _) => (),
|
|
||||||
CommitOperation::AppendNewEntry(index, ref entry) => {
|
|
||||||
unfinalized[*index] = &entry.valid_from;
|
|
||||||
},
|
|
||||||
CommitOperation::AddNewFork(ref entry) => {
|
|
||||||
unfinalized.push(&entry.valid_from);
|
|
||||||
},
|
|
||||||
CommitOperation::BlockFinalized(_, ref finalizing_entry, ref forks) => {
|
|
||||||
if let Some(finalizing_entry) = finalizing_entry.as_ref() {
|
|
||||||
finalized = Some(&finalizing_entry.valid_from);
|
|
||||||
}
|
|
||||||
for fork_index in forks.iter().rev() {
|
|
||||||
unfinalized.remove(*fork_index);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
CommitOperation::BlockReverted(ref forks) => {
|
|
||||||
for (fork_index, updated_fork) in forks.iter().rev() {
|
|
||||||
match updated_fork {
|
|
||||||
Some(updated_fork) =>
|
|
||||||
unfinalized[*fork_index] = &updated_fork.head().valid_from,
|
|
||||||
None => {
|
|
||||||
unfinalized.remove(*fork_index);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
(finalized, unfinalized).encode()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Decode meta information.
|
|
||||||
pub fn decode<Block: BlockT>(encoded: &[u8]) -> ClientResult<Metadata<Block>> {
|
|
||||||
let input = &mut &*encoded;
|
|
||||||
let finalized: Option<ComplexBlockId<Block>> = Decode::decode(input).map_err(|_| {
|
|
||||||
ClientError::from(ClientError::Backend("Error decoding cache meta".into()))
|
|
||||||
})?;
|
|
||||||
let unfinalized: Vec<ComplexBlockId<Block>> = Decode::decode(input).map_err(|_| {
|
|
||||||
ClientError::from(ClientError::Backend("Error decoding cache meta".into()))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(Metadata { finalized, unfinalized })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod tests {
|
|
||||||
use super::*;
|
|
||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
|
|
||||||
pub struct FaultyStorage;
|
|
||||||
|
|
||||||
impl<Block: BlockT, T: CacheItemT> Storage<Block, T> for FaultyStorage {
|
|
||||||
fn read_id(&self, _at: NumberFor<Block>) -> ClientResult<Option<Block::Hash>> {
|
|
||||||
Err(ClientError::Backend("TestError".into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_header(&self, _at: &Block::Hash) -> ClientResult<Option<Block::Header>> {
|
|
||||||
Err(ClientError::Backend("TestError".into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_meta(&self) -> ClientResult<Metadata<Block>> {
|
|
||||||
Err(ClientError::Backend("TestError".into()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_entry(
|
|
||||||
&self,
|
|
||||||
_at: &ComplexBlockId<Block>,
|
|
||||||
) -> ClientResult<Option<StorageEntry<Block, T>>> {
|
|
||||||
Err(ClientError::Backend("TestError".into()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DummyStorage<Block: BlockT, T: CacheItemT> {
|
|
||||||
meta: Metadata<Block>,
|
|
||||||
ids: HashMap<NumberFor<Block>, Block::Hash>,
|
|
||||||
headers: HashMap<Block::Hash, Block::Header>,
|
|
||||||
entries: HashMap<Block::Hash, StorageEntry<Block, T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Block: BlockT, T: CacheItemT> DummyStorage<Block, T> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
DummyStorage {
|
|
||||||
meta: Metadata { finalized: None, unfinalized: Vec::new() },
|
|
||||||
ids: HashMap::new(),
|
|
||||||
headers: HashMap::new(),
|
|
||||||
entries: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_meta(
|
|
||||||
mut self,
|
|
||||||
finalized: Option<ComplexBlockId<Block>>,
|
|
||||||
unfinalized: Vec<ComplexBlockId<Block>>,
|
|
||||||
) -> Self {
|
|
||||||
self.meta.finalized = finalized;
|
|
||||||
self.meta.unfinalized = unfinalized;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_id(mut self, at: NumberFor<Block>, id: Block::Hash) -> Self {
|
|
||||||
self.ids.insert(at, id);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_header(mut self, header: Block::Header) -> Self {
|
|
||||||
self.headers.insert(header.hash(), header);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_entry(
|
|
||||||
mut self,
|
|
||||||
at: ComplexBlockId<Block>,
|
|
||||||
entry: StorageEntry<Block, T>,
|
|
||||||
) -> Self {
|
|
||||||
self.entries.insert(at.hash, entry);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Block: BlockT, T: CacheItemT> Storage<Block, T> for DummyStorage<Block, T> {
|
|
||||||
fn read_id(&self, at: NumberFor<Block>) -> ClientResult<Option<Block::Hash>> {
|
|
||||||
Ok(self.ids.get(&at).cloned())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_header(&self, at: &Block::Hash) -> ClientResult<Option<Block::Header>> {
|
|
||||||
Ok(self.headers.get(&at).cloned())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_meta(&self) -> ClientResult<Metadata<Block>> {
|
|
||||||
Ok(self.meta.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_entry(
|
|
||||||
&self,
|
|
||||||
at: &ComplexBlockId<Block>,
|
|
||||||
) -> ClientResult<Option<StorageEntry<Block, T>>> {
|
|
||||||
Ok(self.entries.get(&at.hash).cloned())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DummyTransaction<Block: BlockT> {
|
|
||||||
updated_meta: Option<Metadata<Block>>,
|
|
||||||
inserted_entries: HashSet<Block::Hash>,
|
|
||||||
removed_entries: HashSet<Block::Hash>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Block: BlockT> DummyTransaction<Block> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
DummyTransaction {
|
|
||||||
updated_meta: None,
|
|
||||||
inserted_entries: HashSet::new(),
|
|
||||||
removed_entries: HashSet::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inserted_entries(&self) -> &HashSet<Block::Hash> {
|
|
||||||
&self.inserted_entries
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn removed_entries(&self) -> &HashSet<Block::Hash> {
|
|
||||||
&self.removed_entries
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn updated_meta(&self) -> &Option<Metadata<Block>> {
|
|
||||||
&self.updated_meta
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Block: BlockT, T: CacheItemT> StorageTransaction<Block, T> for DummyTransaction<Block> {
|
|
||||||
fn insert_storage_entry(
|
|
||||||
&mut self,
|
|
||||||
at: &ComplexBlockId<Block>,
|
|
||||||
_entry: &StorageEntry<Block, T>,
|
|
||||||
) {
|
|
||||||
self.inserted_entries.insert(at.hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_storage_entry(&mut self, at: &ComplexBlockId<Block>) {
|
|
||||||
self.removed_entries.insert(at.hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_meta(
|
|
||||||
&mut self,
|
|
||||||
best_finalized_entry: Option<&Entry<Block, T>>,
|
|
||||||
unfinalized: &[Fork<Block, T>],
|
|
||||||
operation: &CommitOperation<Block, T>,
|
|
||||||
) {
|
|
||||||
self.updated_meta = Some(
|
|
||||||
meta::decode(&meta::encode(best_finalized_entry, unfinalized, operation)).unwrap(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Vendored
-413
@@ -1,413 +0,0 @@
|
|||||||
// This file is part of Substrate.
|
|
||||||
|
|
||||||
// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! DB-backed cache of blockchain data.
|
|
||||||
|
|
||||||
use parking_lot::RwLock;
|
|
||||||
use std::{
|
|
||||||
collections::{hash_map::Entry, HashMap},
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
utils::{self, COLUMN_META},
|
|
||||||
DbHash,
|
|
||||||
};
|
|
||||||
use codec::{Decode, Encode};
|
|
||||||
use sc_client_api::blockchain::{
|
|
||||||
well_known_cache_keys::{self, Id as CacheKeyId},
|
|
||||||
Cache as BlockchainCache,
|
|
||||||
};
|
|
||||||
use sp_blockchain::{HeaderMetadataCache, Result as ClientResult};
|
|
||||||
use sp_database::{Database, Transaction};
|
|
||||||
use sp_runtime::{
|
|
||||||
generic::BlockId,
|
|
||||||
traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero},
|
|
||||||
};
|
|
||||||
|
|
||||||
use self::list_cache::{ListCache, PruningStrategy};
|
|
||||||
|
|
||||||
mod list_cache;
|
|
||||||
mod list_entry;
|
|
||||||
mod list_storage;
|
|
||||||
|
|
||||||
/// Minimal post-finalization age of finalized blocks before they'll pruned.
|
|
||||||
const PRUNE_DEPTH: u32 = 1024;
|
|
||||||
|
|
||||||
/// The type of entry that is inserted to the cache.
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
||||||
pub enum EntryType {
|
|
||||||
/// Non-final entry.
|
|
||||||
NonFinal,
|
|
||||||
/// Final entry.
|
|
||||||
Final,
|
|
||||||
/// Genesis entry (inserted during cache initialization).
|
|
||||||
Genesis,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Block identifier that holds both hash and number.
|
|
||||||
#[derive(Clone, Debug, Encode, Decode, PartialEq)]
|
|
||||||
pub struct ComplexBlockId<Block: BlockT> {
|
|
||||||
/// Hash of the block.
|
|
||||||
pub(crate) hash: Block::Hash,
|
|
||||||
/// Number of the block.
|
|
||||||
pub(crate) number: NumberFor<Block>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Block: BlockT> ComplexBlockId<Block> {
|
|
||||||
/// Create new complex block id.
|
|
||||||
pub fn new(hash: Block::Hash, number: NumberFor<Block>) -> Self {
|
|
||||||
ComplexBlockId { hash, number }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Block: BlockT> ::std::cmp::PartialOrd for ComplexBlockId<Block> {
|
|
||||||
fn partial_cmp(&self, other: &ComplexBlockId<Block>) -> Option<::std::cmp::Ordering> {
|
|
||||||
self.number.partial_cmp(&other.number)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// All cache items must implement this trait.
|
|
||||||
pub trait CacheItemT: Clone + Decode + Encode + PartialEq {}
|
|
||||||
|
|
||||||
impl<T> CacheItemT for T where T: Clone + Decode + Encode + PartialEq {}
|
|
||||||
|
|
||||||
/// Database-backed blockchain data cache.
|
|
||||||
pub struct DbCache<Block: BlockT> {
|
|
||||||
cache_at: HashMap<CacheKeyId, ListCache<Block, Vec<u8>, self::list_storage::DbStorage>>,
|
|
||||||
header_metadata_cache: Arc<HeaderMetadataCache<Block>>,
|
|
||||||
db: Arc<dyn Database<DbHash>>,
|
|
||||||
key_lookup_column: u32,
|
|
||||||
header_column: u32,
|
|
||||||
cache_column: u32,
|
|
||||||
genesis_hash: Block::Hash,
|
|
||||||
best_finalized_block: ComplexBlockId<Block>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Block: BlockT> DbCache<Block> {
|
|
||||||
/// Create new cache.
|
|
||||||
pub fn new(
|
|
||||||
db: Arc<dyn Database<DbHash>>,
|
|
||||||
header_metadata_cache: Arc<HeaderMetadataCache<Block>>,
|
|
||||||
key_lookup_column: u32,
|
|
||||||
header_column: u32,
|
|
||||||
cache_column: u32,
|
|
||||||
genesis_hash: Block::Hash,
|
|
||||||
best_finalized_block: ComplexBlockId<Block>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
cache_at: HashMap::new(),
|
|
||||||
db,
|
|
||||||
header_metadata_cache,
|
|
||||||
key_lookup_column,
|
|
||||||
header_column,
|
|
||||||
cache_column,
|
|
||||||
genesis_hash,
|
|
||||||
best_finalized_block,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set genesis block hash.
|
|
||||||
pub fn set_genesis_hash(&mut self, genesis_hash: Block::Hash) {
|
|
||||||
self.genesis_hash = genesis_hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Begin cache transaction.
|
|
||||||
pub fn transaction<'a>(
|
|
||||||
&'a mut self,
|
|
||||||
tx: &'a mut Transaction<DbHash>,
|
|
||||||
) -> DbCacheTransaction<'a, Block> {
|
|
||||||
DbCacheTransaction {
|
|
||||||
cache: self,
|
|
||||||
tx,
|
|
||||||
cache_at_ops: HashMap::new(),
|
|
||||||
best_finalized_block: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Begin cache transaction with given ops.
|
|
||||||
pub fn transaction_with_ops<'a>(
|
|
||||||
&'a mut self,
|
|
||||||
tx: &'a mut Transaction<DbHash>,
|
|
||||||
ops: DbCacheTransactionOps<Block>,
|
|
||||||
) -> DbCacheTransaction<'a, Block> {
|
|
||||||
DbCacheTransaction {
|
|
||||||
cache: self,
|
|
||||||
tx,
|
|
||||||
cache_at_ops: ops.cache_at_ops,
|
|
||||||
best_finalized_block: ops.best_finalized_block,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Run post-commit cache operations.
|
|
||||||
pub fn commit(&mut self, ops: DbCacheTransactionOps<Block>) -> ClientResult<()> {
|
|
||||||
for (name, ops) in ops.cache_at_ops.into_iter() {
|
|
||||||
self.get_cache(name)?.on_transaction_commit(ops);
|
|
||||||
}
|
|
||||||
if let Some(best_finalized_block) = ops.best_finalized_block {
|
|
||||||
self.best_finalized_block = best_finalized_block;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates `ListCache` with the given name or returns a reference to the existing.
|
|
||||||
pub(crate) fn get_cache(
|
|
||||||
&mut self,
|
|
||||||
name: CacheKeyId,
|
|
||||||
) -> ClientResult<&mut ListCache<Block, Vec<u8>, self::list_storage::DbStorage>> {
|
|
||||||
get_cache_helper(
|
|
||||||
&mut self.cache_at,
|
|
||||||
name,
|
|
||||||
&self.db,
|
|
||||||
self.key_lookup_column,
|
|
||||||
self.header_column,
|
|
||||||
self.cache_column,
|
|
||||||
&self.best_finalized_block,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This helper is needed because otherwise the borrow checker will require to
|
|
||||||
// clone all parameters outside of the closure.
|
|
||||||
fn get_cache_helper<'a, Block: BlockT>(
|
|
||||||
cache_at: &'a mut HashMap<CacheKeyId, ListCache<Block, Vec<u8>, self::list_storage::DbStorage>>,
|
|
||||||
name: CacheKeyId,
|
|
||||||
db: &Arc<dyn Database<DbHash>>,
|
|
||||||
key_lookup: u32,
|
|
||||||
header: u32,
|
|
||||||
cache: u32,
|
|
||||||
best_finalized_block: &ComplexBlockId<Block>,
|
|
||||||
) -> ClientResult<&'a mut ListCache<Block, Vec<u8>, self::list_storage::DbStorage>> {
|
|
||||||
match cache_at.entry(name) {
|
|
||||||
Entry::Occupied(entry) => Ok(entry.into_mut()),
|
|
||||||
Entry::Vacant(entry) => {
|
|
||||||
let cache = ListCache::new(
|
|
||||||
self::list_storage::DbStorage::new(
|
|
||||||
name.to_vec(),
|
|
||||||
db.clone(),
|
|
||||||
self::list_storage::DbColumns { meta: COLUMN_META, key_lookup, header, cache },
|
|
||||||
),
|
|
||||||
cache_pruning_strategy(name),
|
|
||||||
best_finalized_block.clone(),
|
|
||||||
)?;
|
|
||||||
Ok(entry.insert(cache))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Cache operations that are to be committed after database transaction is committed.
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct DbCacheTransactionOps<Block: BlockT> {
|
|
||||||
cache_at_ops: HashMap<CacheKeyId, self::list_cache::CommitOperations<Block, Vec<u8>>>,
|
|
||||||
best_finalized_block: Option<ComplexBlockId<Block>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Block: BlockT> DbCacheTransactionOps<Block> {
|
|
||||||
/// Empty transaction ops.
|
|
||||||
pub fn empty() -> DbCacheTransactionOps<Block> {
|
|
||||||
DbCacheTransactionOps { cache_at_ops: HashMap::new(), best_finalized_block: None }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Database-backed blockchain data cache transaction valid for single block import.
|
|
||||||
pub struct DbCacheTransaction<'a, Block: BlockT> {
|
|
||||||
cache: &'a mut DbCache<Block>,
|
|
||||||
tx: &'a mut Transaction<DbHash>,
|
|
||||||
cache_at_ops: HashMap<CacheKeyId, self::list_cache::CommitOperations<Block, Vec<u8>>>,
|
|
||||||
best_finalized_block: Option<ComplexBlockId<Block>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Block: BlockT> DbCacheTransaction<'a, Block> {
|
|
||||||
/// Convert transaction into post-commit operations set.
|
|
||||||
pub fn into_ops(self) -> DbCacheTransactionOps<Block> {
|
|
||||||
DbCacheTransactionOps {
|
|
||||||
cache_at_ops: self.cache_at_ops,
|
|
||||||
best_finalized_block: self.best_finalized_block,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// When new block is inserted into database.
|
|
||||||
pub fn on_block_insert(
|
|
||||||
mut self,
|
|
||||||
parent: ComplexBlockId<Block>,
|
|
||||||
block: ComplexBlockId<Block>,
|
|
||||||
data_at: HashMap<CacheKeyId, Vec<u8>>,
|
|
||||||
entry_type: EntryType,
|
|
||||||
) -> ClientResult<Self> {
|
|
||||||
// prepare list of caches that are not update
|
|
||||||
// (we might still need to do some cache maintenance in this case)
|
|
||||||
let missed_caches = self
|
|
||||||
.cache
|
|
||||||
.cache_at
|
|
||||||
.keys()
|
|
||||||
.filter(|cache| !data_at.contains_key(*cache))
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let mut insert_op = |name: CacheKeyId,
|
|
||||||
value: Option<Vec<u8>>|
|
|
||||||
-> Result<(), sp_blockchain::Error> {
|
|
||||||
let cache = self.cache.get_cache(name)?;
|
|
||||||
let cache_ops = self.cache_at_ops.entry(name).or_default();
|
|
||||||
cache.on_block_insert(
|
|
||||||
&mut self::list_storage::DbStorageTransaction::new(cache.storage(), &mut self.tx),
|
|
||||||
parent.clone(),
|
|
||||||
block.clone(),
|
|
||||||
value,
|
|
||||||
entry_type,
|
|
||||||
cache_ops,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
|
|
||||||
data_at.into_iter().try_for_each(|(name, data)| insert_op(name, Some(data)))?;
|
|
||||||
missed_caches.into_iter().try_for_each(|name| insert_op(name, None))?;
|
|
||||||
|
|
||||||
match entry_type {
|
|
||||||
EntryType::Final | EntryType::Genesis => self.best_finalized_block = Some(block),
|
|
||||||
EntryType::NonFinal => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// When previously inserted block is finalized.
|
|
||||||
pub fn on_block_finalize(
|
|
||||||
mut self,
|
|
||||||
parent: ComplexBlockId<Block>,
|
|
||||||
block: ComplexBlockId<Block>,
|
|
||||||
) -> ClientResult<Self> {
|
|
||||||
for (name, cache) in self.cache.cache_at.iter() {
|
|
||||||
let cache_ops = self.cache_at_ops.entry(*name).or_default();
|
|
||||||
cache.on_block_finalize(
|
|
||||||
&mut self::list_storage::DbStorageTransaction::new(cache.storage(), &mut self.tx),
|
|
||||||
parent.clone(),
|
|
||||||
block.clone(),
|
|
||||||
cache_ops,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.best_finalized_block = Some(block);
|
|
||||||
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// When block is reverted.
|
|
||||||
pub fn on_block_revert(mut self, reverted_block: &ComplexBlockId<Block>) -> ClientResult<Self> {
|
|
||||||
for (name, cache) in self.cache.cache_at.iter() {
|
|
||||||
let cache_ops = self.cache_at_ops.entry(*name).or_default();
|
|
||||||
cache.on_block_revert(
|
|
||||||
&mut self::list_storage::DbStorageTransaction::new(cache.storage(), &mut self.tx),
|
|
||||||
reverted_block,
|
|
||||||
cache_ops,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Synchronous implementation of database-backed blockchain data cache.
|
|
||||||
pub struct DbCacheSync<Block: BlockT>(pub RwLock<DbCache<Block>>);
|
|
||||||
|
|
||||||
impl<Block: BlockT> BlockchainCache<Block> for DbCacheSync<Block> {
|
|
||||||
fn initialize(&self, key: &CacheKeyId, data: Vec<u8>) -> ClientResult<()> {
|
|
||||||
let mut cache = self.0.write();
|
|
||||||
let genesis_hash = cache.genesis_hash;
|
|
||||||
let cache_contents = vec![(*key, data)].into_iter().collect();
|
|
||||||
let db = cache.db.clone();
|
|
||||||
let mut dbtx = Transaction::new();
|
|
||||||
let tx = cache.transaction(&mut dbtx);
|
|
||||||
let tx = tx.on_block_insert(
|
|
||||||
ComplexBlockId::new(Default::default(), Zero::zero()),
|
|
||||||
ComplexBlockId::new(genesis_hash, Zero::zero()),
|
|
||||||
cache_contents,
|
|
||||||
EntryType::Genesis,
|
|
||||||
)?;
|
|
||||||
let tx_ops = tx.into_ops();
|
|
||||||
db.commit(dbtx)?;
|
|
||||||
cache.commit(tx_ops)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_at(
|
|
||||||
&self,
|
|
||||||
key: &CacheKeyId,
|
|
||||||
at: &BlockId<Block>,
|
|
||||||
) -> ClientResult<
|
|
||||||
Option<((NumberFor<Block>, Block::Hash), Option<(NumberFor<Block>, Block::Hash)>, Vec<u8>)>,
|
|
||||||
> {
|
|
||||||
let mut cache = self.0.write();
|
|
||||||
let header_metadata_cache = cache.header_metadata_cache.clone();
|
|
||||||
let cache = cache.get_cache(*key)?;
|
|
||||||
let storage = cache.storage();
|
|
||||||
let db = storage.db();
|
|
||||||
let columns = storage.columns();
|
|
||||||
let at = match *at {
|
|
||||||
BlockId::Hash(hash) => match header_metadata_cache.header_metadata(hash) {
|
|
||||||
Some(metadata) => ComplexBlockId::new(hash, metadata.number),
|
|
||||||
None => {
|
|
||||||
let header = utils::require_header::<Block>(
|
|
||||||
&**db,
|
|
||||||
columns.key_lookup,
|
|
||||||
columns.header,
|
|
||||||
BlockId::Hash(hash.clone()),
|
|
||||||
)?;
|
|
||||||
ComplexBlockId::new(hash, *header.number())
|
|
||||||
},
|
|
||||||
},
|
|
||||||
BlockId::Number(number) => {
|
|
||||||
let hash = utils::require_header::<Block>(
|
|
||||||
&**db,
|
|
||||||
columns.key_lookup,
|
|
||||||
columns.header,
|
|
||||||
BlockId::Number(number.clone()),
|
|
||||||
)?
|
|
||||||
.hash();
|
|
||||||
ComplexBlockId::new(hash, number)
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
cache.value_at_block(&at).map(|block_and_value| {
|
|
||||||
block_and_value.map(|(begin_block, end_block, value)| {
|
|
||||||
(
|
|
||||||
(begin_block.number, begin_block.hash),
|
|
||||||
end_block.map(|end_block| (end_block.number, end_block.hash)),
|
|
||||||
value,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get pruning strategy for given cache.
|
|
||||||
fn cache_pruning_strategy<N: From<u32>>(cache: CacheKeyId) -> PruningStrategy<N> {
|
|
||||||
// the cache is mostly used to store data from consensus engines
|
|
||||||
// this kind of data is only required for non-finalized blocks
|
|
||||||
// => by default we prune finalized cached entries
|
|
||||||
|
|
||||||
match cache {
|
|
||||||
// we need to keep changes tries configurations forever (or at least until changes tries,
|
|
||||||
// that were built using this configuration, are pruned) to make it possible to refer
|
|
||||||
// to old changes tries
|
|
||||||
well_known_cache_keys::CHANGES_TRIE_CONFIG => PruningStrategy::NeverPrune,
|
|
||||||
_ => PruningStrategy::ByDepth(PRUNE_DEPTH.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -28,14 +28,11 @@
|
|||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
pub mod light;
|
|
||||||
pub mod offchain;
|
pub mod offchain;
|
||||||
|
|
||||||
#[cfg(any(feature = "with-kvdb-rocksdb", test))]
|
#[cfg(any(feature = "with-kvdb-rocksdb", test))]
|
||||||
pub mod bench;
|
pub mod bench;
|
||||||
|
|
||||||
mod cache;
|
|
||||||
mod changes_tries_storage;
|
|
||||||
mod children;
|
mod children;
|
||||||
#[cfg(feature = "with-parity-db")]
|
#[cfg(feature = "with-parity-db")]
|
||||||
mod parity_db;
|
mod parity_db;
|
||||||
@@ -56,7 +53,6 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
changes_tries_storage::{DbChangesTrieStorage, DbChangesTrieStorageTransaction},
|
|
||||||
stats::StateUsageStats,
|
stats::StateUsageStats,
|
||||||
storage_cache::{new_shared_cache, CachingState, SharedCache, SyncingCachingState},
|
storage_cache::{new_shared_cache, CachingState, SharedCache, SyncingCachingState},
|
||||||
utils::{meta_keys, read_db, read_meta, DatabaseType, Meta},
|
utils::{meta_keys, read_db, read_meta, DatabaseType, Meta},
|
||||||
@@ -64,8 +60,7 @@ use crate::{
|
|||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
use hash_db::Prefix;
|
use hash_db::Prefix;
|
||||||
use sc_client_api::{
|
use sc_client_api::{
|
||||||
backend::{NewBlockState, ProvideChtRoots, PrunableStateChangesTrieStorage},
|
backend::NewBlockState,
|
||||||
cht,
|
|
||||||
leaves::{FinalizationDisplaced, LeafSet},
|
leaves::{FinalizationDisplaced, LeafSet},
|
||||||
utils::is_descendent_of,
|
utils::is_descendent_of,
|
||||||
IoInfo, MemoryInfo, MemorySize, UsageInfo,
|
IoInfo, MemoryInfo, MemorySize, UsageInfo,
|
||||||
@@ -79,11 +74,10 @@ use sp_blockchain::{
|
|||||||
use sp_core::{
|
use sp_core::{
|
||||||
offchain::OffchainOverlayedChange,
|
offchain::OffchainOverlayedChange,
|
||||||
storage::{well_known_keys, ChildInfo},
|
storage::{well_known_keys, ChildInfo},
|
||||||
ChangesTrieConfiguration,
|
|
||||||
};
|
};
|
||||||
use sp_database::Transaction;
|
use sp_database::Transaction;
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::{BlockId, DigestItem},
|
generic::BlockId,
|
||||||
traits::{
|
traits::{
|
||||||
Block as BlockT, Hash, HashFor, Header as HeaderT, NumberFor, One, SaturatedConversion,
|
Block as BlockT, Hash, HashFor, Header as HeaderT, NumberFor, One, SaturatedConversion,
|
||||||
Zero,
|
Zero,
|
||||||
@@ -91,9 +85,8 @@ use sp_runtime::{
|
|||||||
Justification, Justifications, Storage,
|
Justification, Justifications, Storage,
|
||||||
};
|
};
|
||||||
use sp_state_machine::{
|
use sp_state_machine::{
|
||||||
backend::Backend as StateBackend, ChangesTrieCacheAction, ChangesTrieTransaction,
|
backend::Backend as StateBackend, ChildStorageCollection, DBValue, IndexOperation,
|
||||||
ChildStorageCollection, DBValue, IndexOperation, OffchainChangesCollection, StateMachineStats,
|
OffchainChangesCollection, StateMachineStats, StorageCollection, UsageInfo as StateUsageInfo,
|
||||||
StorageCollection, UsageInfo as StateUsageInfo,
|
|
||||||
};
|
};
|
||||||
use sp_trie::{prefixed_key, MemoryDB, PrefixedMemoryDB};
|
use sp_trie::{prefixed_key, MemoryDB, PrefixedMemoryDB};
|
||||||
|
|
||||||
@@ -104,7 +97,6 @@ pub use sp_database::Database;
|
|||||||
#[cfg(any(feature = "with-kvdb-rocksdb", test))]
|
#[cfg(any(feature = "with-kvdb-rocksdb", test))]
|
||||||
pub use bench::BenchmarkingState;
|
pub use bench::BenchmarkingState;
|
||||||
|
|
||||||
const MIN_BLOCKS_TO_KEEP_CHANGES_TRIES_FOR: u32 = 32768;
|
|
||||||
const CACHE_HEADERS: usize = 8;
|
const CACHE_HEADERS: usize = 8;
|
||||||
|
|
||||||
/// Default value for storage cache child ratio.
|
/// Default value for storage cache child ratio.
|
||||||
@@ -406,11 +398,9 @@ pub(crate) mod columns {
|
|||||||
pub const HEADER: u32 = 4;
|
pub const HEADER: u32 = 4;
|
||||||
pub const BODY: u32 = 5;
|
pub const BODY: u32 = 5;
|
||||||
pub const JUSTIFICATIONS: u32 = 6;
|
pub const JUSTIFICATIONS: u32 = 6;
|
||||||
pub const CHANGES_TRIE: u32 = 7;
|
|
||||||
pub const AUX: u32 = 8;
|
pub const AUX: u32 = 8;
|
||||||
/// Offchain workers local storage
|
/// Offchain workers local storage
|
||||||
pub const OFFCHAIN: u32 = 9;
|
pub const OFFCHAIN: u32 = 9;
|
||||||
pub const CACHE: u32 = 10;
|
|
||||||
/// Transactions
|
/// Transactions
|
||||||
pub const TRANSACTION: u32 = 11;
|
pub const TRANSACTION: u32 = 11;
|
||||||
}
|
}
|
||||||
@@ -506,13 +496,6 @@ impl<Block: BlockT> BlockchainDb<Block> {
|
|||||||
let mut meta = self.meta.write();
|
let mut meta = self.meta.write();
|
||||||
meta.block_gap = gap;
|
meta.block_gap = gap;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get block changes trie root, if available.
|
|
||||||
fn changes_trie_root(&self, block: BlockId<Block>) -> ClientResult<Option<Block::Hash>> {
|
|
||||||
self.header(block).map(|header| {
|
|
||||||
header.and_then(|header| header.digest().log(DigestItem::as_changes_trie_root).cloned())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Block: BlockT> sc_client_api::blockchain::HeaderBackend<Block> for BlockchainDb<Block> {
|
impl<Block: BlockT> sc_client_api::blockchain::HeaderBackend<Block> for BlockchainDb<Block> {
|
||||||
@@ -646,10 +629,6 @@ impl<Block: BlockT> sc_client_api::blockchain::Backend<Block> for BlockchainDb<B
|
|||||||
Ok(self.meta.read().finalized_hash.clone())
|
Ok(self.meta.read().finalized_hash.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cache(&self) -> Option<Arc<dyn sc_client_api::blockchain::Cache<Block>>> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn leaves(&self) -> ClientResult<Vec<Block::Hash>> {
|
fn leaves(&self) -> ClientResult<Vec<Block::Hash>> {
|
||||||
Ok(self.leaves.read().hashes())
|
Ok(self.leaves.read().hashes())
|
||||||
}
|
}
|
||||||
@@ -702,12 +681,6 @@ impl<Block: BlockT> sc_client_api::blockchain::Backend<Block> for BlockchainDb<B
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Block: BlockT> sc_client_api::blockchain::ProvideCache<Block> for BlockchainDb<Block> {
|
|
||||||
fn cache(&self) -> Option<Arc<dyn sc_client_api::blockchain::Cache<Block>>> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Block: BlockT> HeaderMetadata<Block> for BlockchainDb<Block> {
|
impl<Block: BlockT> HeaderMetadata<Block> for BlockchainDb<Block> {
|
||||||
type Error = sp_blockchain::Error;
|
type Error = sp_blockchain::Error;
|
||||||
|
|
||||||
@@ -745,62 +718,6 @@ impl<Block: BlockT> HeaderMetadata<Block> for BlockchainDb<Block> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Block: BlockT> ProvideChtRoots<Block> for BlockchainDb<Block> {
|
|
||||||
fn header_cht_root(
|
|
||||||
&self,
|
|
||||||
cht_size: NumberFor<Block>,
|
|
||||||
block: NumberFor<Block>,
|
|
||||||
) -> sp_blockchain::Result<Option<Block::Hash>> {
|
|
||||||
let cht_number = match cht::block_to_cht_number(cht_size, block) {
|
|
||||||
Some(number) => number,
|
|
||||||
None => return Ok(None),
|
|
||||||
};
|
|
||||||
|
|
||||||
let cht_start: NumberFor<Block> = cht::start_number(cht::size(), cht_number);
|
|
||||||
|
|
||||||
let mut current_num = cht_start;
|
|
||||||
let cht_range = ::std::iter::from_fn(|| {
|
|
||||||
let old_current_num = current_num;
|
|
||||||
current_num = current_num + One::one();
|
|
||||||
Some(old_current_num)
|
|
||||||
});
|
|
||||||
|
|
||||||
cht::compute_root::<Block::Header, HashFor<Block>, _>(
|
|
||||||
cht::size(),
|
|
||||||
cht_number,
|
|
||||||
cht_range.map(|num| self.hash(num)),
|
|
||||||
)
|
|
||||||
.map(Some)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn changes_trie_cht_root(
|
|
||||||
&self,
|
|
||||||
cht_size: NumberFor<Block>,
|
|
||||||
block: NumberFor<Block>,
|
|
||||||
) -> sp_blockchain::Result<Option<Block::Hash>> {
|
|
||||||
let cht_number = match cht::block_to_cht_number(cht_size, block) {
|
|
||||||
Some(number) => number,
|
|
||||||
None => return Ok(None),
|
|
||||||
};
|
|
||||||
|
|
||||||
let cht_start: NumberFor<Block> = cht::start_number(cht::size(), cht_number);
|
|
||||||
|
|
||||||
let mut current_num = cht_start;
|
|
||||||
let cht_range = ::std::iter::from_fn(|| {
|
|
||||||
let old_current_num = current_num;
|
|
||||||
current_num = current_num + One::one();
|
|
||||||
Some(old_current_num)
|
|
||||||
});
|
|
||||||
|
|
||||||
cht::compute_root::<Block::Header, HashFor<Block>, _>(
|
|
||||||
cht::size(),
|
|
||||||
cht_number,
|
|
||||||
cht_range.map(|num| self.changes_trie_root(BlockId::Number(num))),
|
|
||||||
)
|
|
||||||
.map(Some)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Database transaction
|
/// Database transaction
|
||||||
pub struct BlockImportOperation<Block: BlockT> {
|
pub struct BlockImportOperation<Block: BlockT> {
|
||||||
old_state: SyncingCachingState<RefTrackingState<Block>, Block>,
|
old_state: SyncingCachingState<RefTrackingState<Block>, Block>,
|
||||||
@@ -808,9 +725,6 @@ pub struct BlockImportOperation<Block: BlockT> {
|
|||||||
storage_updates: StorageCollection,
|
storage_updates: StorageCollection,
|
||||||
child_storage_updates: ChildStorageCollection,
|
child_storage_updates: ChildStorageCollection,
|
||||||
offchain_storage_updates: OffchainChangesCollection,
|
offchain_storage_updates: OffchainChangesCollection,
|
||||||
changes_trie_updates: MemoryDB<HashFor<Block>>,
|
|
||||||
changes_trie_build_cache_update: Option<ChangesTrieCacheAction<Block::Hash, NumberFor<Block>>>,
|
|
||||||
changes_trie_config_update: Option<Option<ChangesTrieConfiguration>>,
|
|
||||||
pending_block: Option<PendingBlock<Block>>,
|
pending_block: Option<PendingBlock<Block>>,
|
||||||
aux_ops: Vec<(Vec<u8>, Option<Vec<u8>>)>,
|
aux_ops: Vec<(Vec<u8>, Option<Vec<u8>>)>,
|
||||||
finalized_blocks: Vec<(BlockId<Block>, Option<Justification>)>,
|
finalized_blocks: Vec<(BlockId<Block>, Option<Justification>)>,
|
||||||
@@ -858,25 +772,12 @@ impl<Block: BlockT> BlockImportOperation<Block> {
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut changes_trie_config = None;
|
|
||||||
let (root, transaction) = self.old_state.full_storage_root(
|
let (root, transaction) = self.old_state.full_storage_root(
|
||||||
storage.top.iter().map(|(k, v)| {
|
storage.top.iter().map(|(k, v)| (&k[..], Some(&v[..]))),
|
||||||
if &k[..] == well_known_keys::CHANGES_TRIE_CONFIG {
|
|
||||||
changes_trie_config = Some(Decode::decode(&mut &v[..]));
|
|
||||||
}
|
|
||||||
(&k[..], Some(&v[..]))
|
|
||||||
}),
|
|
||||||
child_delta,
|
child_delta,
|
||||||
);
|
);
|
||||||
|
|
||||||
let changes_trie_config = match changes_trie_config {
|
|
||||||
Some(Ok(c)) => Some(c),
|
|
||||||
Some(Err(_)) => return Err(sp_blockchain::Error::InvalidState.into()),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.db_updates = transaction;
|
self.db_updates = transaction;
|
||||||
self.changes_trie_config_update = Some(changes_trie_config);
|
|
||||||
Ok(root)
|
Ok(root)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -899,11 +800,6 @@ impl<Block: BlockT> sc_client_api::backend::BlockImportOperation<Block>
|
|||||||
leaf_state: NewBlockState,
|
leaf_state: NewBlockState,
|
||||||
) -> ClientResult<()> {
|
) -> ClientResult<()> {
|
||||||
assert!(self.pending_block.is_none(), "Only one block per operation is allowed");
|
assert!(self.pending_block.is_none(), "Only one block per operation is allowed");
|
||||||
if let Some(changes_trie_config_update) =
|
|
||||||
changes_tries_storage::extract_new_configuration(&header)
|
|
||||||
{
|
|
||||||
self.changes_trie_config_update = Some(changes_trie_config_update.clone());
|
|
||||||
}
|
|
||||||
self.pending_block =
|
self.pending_block =
|
||||||
Some(PendingBlock { header, body, indexed_body, justifications, leaf_state });
|
Some(PendingBlock { header, body, indexed_body, justifications, leaf_state });
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -930,15 +826,6 @@ impl<Block: BlockT> sc_client_api::backend::BlockImportOperation<Block>
|
|||||||
Ok(root)
|
Ok(root)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_changes_trie(
|
|
||||||
&mut self,
|
|
||||||
update: ChangesTrieTransaction<HashFor<Block>, NumberFor<Block>>,
|
|
||||||
) -> ClientResult<()> {
|
|
||||||
self.changes_trie_updates = update.0;
|
|
||||||
self.changes_trie_build_cache_update = Some(update.1);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert_aux<I>(&mut self, ops: I) -> ClientResult<()>
|
fn insert_aux<I>(&mut self, ops: I) -> ClientResult<()>
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = (Vec<u8>, Option<Vec<u8>>)>,
|
I: IntoIterator<Item = (Vec<u8>, Option<Vec<u8>>)>,
|
||||||
@@ -1095,7 +982,6 @@ impl<T: Clone> FrozenForDuration<T> {
|
|||||||
pub struct Backend<Block: BlockT> {
|
pub struct Backend<Block: BlockT> {
|
||||||
storage: Arc<StorageDb<Block>>,
|
storage: Arc<StorageDb<Block>>,
|
||||||
offchain_storage: offchain::LocalStorage,
|
offchain_storage: offchain::LocalStorage,
|
||||||
changes_tries_storage: DbChangesTrieStorage<Block>,
|
|
||||||
blockchain: BlockchainDb<Block>,
|
blockchain: BlockchainDb<Block>,
|
||||||
canonicalization_delay: u64,
|
canonicalization_delay: u64,
|
||||||
shared_cache: SharedCache<Block>,
|
shared_cache: SharedCache<Block>,
|
||||||
@@ -1155,7 +1041,6 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
) -> ClientResult<Self> {
|
) -> ClientResult<Self> {
|
||||||
let is_archive_pruning = config.state_pruning.is_archive();
|
let is_archive_pruning = config.state_pruning.is_archive();
|
||||||
let blockchain = BlockchainDb::new(db.clone(), config.transaction_storage.clone())?;
|
let blockchain = BlockchainDb::new(db.clone(), config.transaction_storage.clone())?;
|
||||||
let meta = blockchain.meta.clone();
|
|
||||||
let map_e = |e: sc_state_db::Error<io::Error>| sp_blockchain::Error::from_state_db(e);
|
let map_e = |e: sc_state_db::Error<io::Error>| sp_blockchain::Error::from_state_db(e);
|
||||||
let state_db: StateDb<_, _> = StateDb::new(
|
let state_db: StateDb<_, _> = StateDb::new(
|
||||||
config.state_pruning.clone(),
|
config.state_pruning.clone(),
|
||||||
@@ -1166,22 +1051,10 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
let storage_db =
|
let storage_db =
|
||||||
StorageDb { db: db.clone(), state_db, prefix_keys: !db.supports_ref_counting() };
|
StorageDb { db: db.clone(), state_db, prefix_keys: !db.supports_ref_counting() };
|
||||||
let offchain_storage = offchain::LocalStorage::new(db.clone());
|
let offchain_storage = offchain::LocalStorage::new(db.clone());
|
||||||
let changes_tries_storage = DbChangesTrieStorage::new(
|
|
||||||
db,
|
|
||||||
blockchain.header_metadata_cache.clone(),
|
|
||||||
columns::META,
|
|
||||||
columns::CHANGES_TRIE,
|
|
||||||
columns::KEY_LOOKUP,
|
|
||||||
columns::HEADER,
|
|
||||||
columns::CACHE,
|
|
||||||
meta,
|
|
||||||
if is_archive_pruning { None } else { Some(MIN_BLOCKS_TO_KEEP_CHANGES_TRIES_FOR) },
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let backend = Backend {
|
let backend = Backend {
|
||||||
storage: Arc::new(storage_db),
|
storage: Arc::new(storage_db),
|
||||||
offchain_storage,
|
offchain_storage,
|
||||||
changes_tries_storage,
|
|
||||||
blockchain,
|
blockchain,
|
||||||
canonicalization_delay,
|
canonicalization_delay,
|
||||||
shared_cache: new_shared_cache(
|
shared_cache: new_shared_cache(
|
||||||
@@ -1318,7 +1191,6 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
header: &Block::Header,
|
header: &Block::Header,
|
||||||
last_finalized: Option<Block::Hash>,
|
last_finalized: Option<Block::Hash>,
|
||||||
justification: Option<Justification>,
|
justification: Option<Justification>,
|
||||||
changes_trie_cache_ops: &mut Option<DbChangesTrieStorageTransaction<Block>>,
|
|
||||||
finalization_displaced: &mut Option<FinalizationDisplaced<Block::Hash, NumberFor<Block>>>,
|
finalization_displaced: &mut Option<FinalizationDisplaced<Block::Hash, NumberFor<Block>>>,
|
||||||
) -> ClientResult<MetaUpdate<Block>> {
|
) -> ClientResult<MetaUpdate<Block>> {
|
||||||
// TODO: ensure best chain contains this block.
|
// TODO: ensure best chain contains this block.
|
||||||
@@ -1326,15 +1198,7 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
self.ensure_sequential_finalization(header, last_finalized)?;
|
self.ensure_sequential_finalization(header, last_finalized)?;
|
||||||
let with_state = sc_client_api::Backend::have_state_at(self, &hash, number);
|
let with_state = sc_client_api::Backend::have_state_at(self, &hash, number);
|
||||||
|
|
||||||
self.note_finalized(
|
self.note_finalized(transaction, header, *hash, finalization_displaced, with_state)?;
|
||||||
transaction,
|
|
||||||
false,
|
|
||||||
header,
|
|
||||||
*hash,
|
|
||||||
changes_trie_cache_ops,
|
|
||||||
finalization_displaced,
|
|
||||||
with_state,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
if let Some(justification) = justification {
|
if let Some(justification) = justification {
|
||||||
transaction.set_from_vec(
|
transaction.set_from_vec(
|
||||||
@@ -1400,7 +1264,6 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
(meta.best_number, meta.finalized_hash, meta.finalized_number, meta.block_gap.clone())
|
(meta.best_number, meta.finalized_hash, meta.finalized_number, meta.block_gap.clone())
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut changes_trie_cache_ops = None;
|
|
||||||
for (block, justification) in operation.finalized_blocks {
|
for (block, justification) in operation.finalized_blocks {
|
||||||
let block_hash = self.blockchain.expect_block_hash_from_id(&block)?;
|
let block_hash = self.blockchain.expect_block_hash_from_id(&block)?;
|
||||||
let block_header = self.blockchain.expect_header(BlockId::Hash(block_hash))?;
|
let block_header = self.blockchain.expect_header(BlockId::Hash(block_hash))?;
|
||||||
@@ -1410,7 +1273,6 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
&block_header,
|
&block_header,
|
||||||
Some(last_finalized_hash),
|
Some(last_finalized_hash),
|
||||||
justification,
|
justification,
|
||||||
&mut changes_trie_cache_ops,
|
|
||||||
&mut finalization_displaced_leaves,
|
&mut finalization_displaced_leaves,
|
||||||
)?);
|
)?);
|
||||||
last_finalized_hash = block_hash;
|
last_finalized_hash = block_hash;
|
||||||
@@ -1475,11 +1337,6 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
);
|
);
|
||||||
transaction.set(columns::META, meta_keys::GENESIS_HASH, hash.as_ref());
|
transaction.set(columns::META, meta_keys::GENESIS_HASH, hash.as_ref());
|
||||||
|
|
||||||
// for tests, because config is set from within the reset_storage
|
|
||||||
if operation.changes_trie_config_update.is_none() {
|
|
||||||
operation.changes_trie_config_update = Some(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
if operation.commit_state {
|
if operation.commit_state {
|
||||||
transaction.set_from_vec(columns::META, meta_keys::FINALIZED_STATE, lookup_key);
|
transaction.set_from_vec(columns::META, meta_keys::FINALIZED_STATE, lookup_key);
|
||||||
} else {
|
} else {
|
||||||
@@ -1578,7 +1435,6 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
|
|
||||||
let header = &pending_block.header;
|
let header = &pending_block.header;
|
||||||
let is_best = pending_block.leaf_state.is_best();
|
let is_best = pending_block.leaf_state.is_best();
|
||||||
let changes_trie_updates = operation.changes_trie_updates;
|
|
||||||
debug!(target: "db",
|
debug!(target: "db",
|
||||||
"DB Commit {:?} ({}), best={}, state={}, existing={}",
|
"DB Commit {:?} ({}), best={}, state={}, existing={}",
|
||||||
hash, number, is_best, operation.commit_state, existing_header,
|
hash, number, is_best, operation.commit_state, existing_header,
|
||||||
@@ -1593,10 +1449,8 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
self.ensure_sequential_finalization(header, Some(last_finalized_hash))?;
|
self.ensure_sequential_finalization(header, Some(last_finalized_hash))?;
|
||||||
self.note_finalized(
|
self.note_finalized(
|
||||||
&mut transaction,
|
&mut transaction,
|
||||||
true,
|
|
||||||
header,
|
header,
|
||||||
hash,
|
hash,
|
||||||
&mut changes_trie_cache_ops,
|
|
||||||
&mut finalization_displaced_leaves,
|
&mut finalization_displaced_leaves,
|
||||||
operation.commit_state,
|
operation.commit_state,
|
||||||
)?;
|
)?;
|
||||||
@@ -1606,21 +1460,6 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !existing_header {
|
if !existing_header {
|
||||||
let changes_trie_config_update = operation.changes_trie_config_update;
|
|
||||||
changes_trie_cache_ops = Some(self.changes_tries_storage.commit(
|
|
||||||
&mut transaction,
|
|
||||||
changes_trie_updates,
|
|
||||||
cache::ComplexBlockId::new(
|
|
||||||
*header.parent_hash(),
|
|
||||||
if number.is_zero() { Zero::zero() } else { number - One::one() },
|
|
||||||
),
|
|
||||||
cache::ComplexBlockId::new(hash, number),
|
|
||||||
header,
|
|
||||||
finalized,
|
|
||||||
changes_trie_config_update,
|
|
||||||
changes_trie_cache_ops,
|
|
||||||
)?);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut leaves = self.blockchain.leaves.write();
|
let mut leaves = self.blockchain.leaves.write();
|
||||||
leaves.import(hash, number, parent_hash);
|
leaves.import(hash, number, parent_hash);
|
||||||
@@ -1747,11 +1586,6 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(changes_trie_build_cache_update) = operation.changes_trie_build_cache_update {
|
|
||||||
self.changes_tries_storage.commit_build_cache(changes_trie_build_cache_update);
|
|
||||||
}
|
|
||||||
self.changes_tries_storage.post_commit(changes_trie_cache_ops);
|
|
||||||
|
|
||||||
if let Some((enacted, retracted)) = cache_update {
|
if let Some((enacted, retracted)) = cache_update {
|
||||||
self.shared_cache.write().sync(&enacted, &retracted);
|
self.shared_cache.write().sync(&enacted, &retracted);
|
||||||
}
|
}
|
||||||
@@ -1770,10 +1604,8 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
fn note_finalized(
|
fn note_finalized(
|
||||||
&self,
|
&self,
|
||||||
transaction: &mut Transaction<DbHash>,
|
transaction: &mut Transaction<DbHash>,
|
||||||
is_inserted: bool,
|
|
||||||
f_header: &Block::Header,
|
f_header: &Block::Header,
|
||||||
f_hash: Block::Hash,
|
f_hash: Block::Hash,
|
||||||
changes_trie_cache_ops: &mut Option<DbChangesTrieStorageTransaction<Block>>,
|
|
||||||
displaced: &mut Option<FinalizationDisplaced<Block::Hash, NumberFor<Block>>>,
|
displaced: &mut Option<FinalizationDisplaced<Block::Hash, NumberFor<Block>>>,
|
||||||
with_state: bool,
|
with_state: bool,
|
||||||
) -> ClientResult<()> {
|
) -> ClientResult<()> {
|
||||||
@@ -1798,18 +1630,6 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
apply_state_commit(transaction, commit);
|
apply_state_commit(transaction, commit);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !f_num.is_zero() {
|
|
||||||
let new_changes_trie_cache_ops = self.changes_tries_storage.finalize(
|
|
||||||
transaction,
|
|
||||||
*f_header.parent_hash(),
|
|
||||||
f_hash,
|
|
||||||
f_num,
|
|
||||||
if is_inserted { Some(&f_header) } else { None },
|
|
||||||
changes_trie_cache_ops.take(),
|
|
||||||
)?;
|
|
||||||
*changes_trie_cache_ops = Some(new_changes_trie_cache_ops);
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_displaced = self.blockchain.leaves.write().finalize_height(f_num);
|
let new_displaced = self.blockchain.leaves.write().finalize_height(f_num);
|
||||||
self.prune_blocks(transaction, f_num, &new_displaced)?;
|
self.prune_blocks(transaction, f_num, &new_displaced)?;
|
||||||
match displaced {
|
match displaced {
|
||||||
@@ -2036,9 +1856,6 @@ impl<Block: BlockT> sc_client_api::backend::Backend<Block> for Backend<Block> {
|
|||||||
storage_updates: Default::default(),
|
storage_updates: Default::default(),
|
||||||
child_storage_updates: Default::default(),
|
child_storage_updates: Default::default(),
|
||||||
offchain_storage_updates: Default::default(),
|
offchain_storage_updates: Default::default(),
|
||||||
changes_trie_config_update: None,
|
|
||||||
changes_trie_updates: MemoryDB::default(),
|
|
||||||
changes_trie_build_cache_update: None,
|
|
||||||
aux_ops: Vec::new(),
|
aux_ops: Vec::new(),
|
||||||
finalized_blocks: Vec::new(),
|
finalized_blocks: Vec::new(),
|
||||||
set_head: None,
|
set_head: None,
|
||||||
@@ -2089,19 +1906,16 @@ impl<Block: BlockT> sc_client_api::backend::Backend<Block> for Backend<Block> {
|
|||||||
let header = self.blockchain.expect_header(block)?;
|
let header = self.blockchain.expect_header(block)?;
|
||||||
let mut displaced = None;
|
let mut displaced = None;
|
||||||
|
|
||||||
let mut changes_trie_cache_ops = None;
|
|
||||||
let m = self.finalize_block_with_transaction(
|
let m = self.finalize_block_with_transaction(
|
||||||
&mut transaction,
|
&mut transaction,
|
||||||
&hash,
|
&hash,
|
||||||
&header,
|
&header,
|
||||||
None,
|
None,
|
||||||
justification,
|
justification,
|
||||||
&mut changes_trie_cache_ops,
|
|
||||||
&mut displaced,
|
&mut displaced,
|
||||||
)?;
|
)?;
|
||||||
self.storage.db.commit(transaction)?;
|
self.storage.db.commit(transaction)?;
|
||||||
self.blockchain.update_meta(m);
|
self.blockchain.update_meta(m);
|
||||||
self.changes_tries_storage.post_commit(changes_trie_cache_ops);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2148,10 +1962,6 @@ impl<Block: BlockT> sc_client_api::backend::Backend<Block> for Backend<Block> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn changes_trie_storage(&self) -> Option<&dyn PrunableStateChangesTrieStorage<Block>> {
|
|
||||||
Some(&self.changes_tries_storage)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn offchain_storage(&self) -> Option<Self::OffchainStorage> {
|
fn offchain_storage(&self) -> Option<Self::OffchainStorage> {
|
||||||
Some(self.offchain_storage.clone())
|
Some(self.offchain_storage.clone())
|
||||||
}
|
}
|
||||||
@@ -2208,7 +2018,6 @@ impl<Block: BlockT> sc_client_api::backend::Backend<Block> for Backend<Block> {
|
|||||||
return Ok(c.saturated_into::<NumberFor<Block>>())
|
return Ok(c.saturated_into::<NumberFor<Block>>())
|
||||||
}
|
}
|
||||||
let mut transaction = Transaction::new();
|
let mut transaction = Transaction::new();
|
||||||
let removed_number = best_number;
|
|
||||||
let removed =
|
let removed =
|
||||||
self.blockchain.header(BlockId::Number(best_number))?.ok_or_else(|| {
|
self.blockchain.header(BlockId::Number(best_number))?.ok_or_else(|| {
|
||||||
sp_blockchain::Error::UnknownBlock(format!(
|
sp_blockchain::Error::UnknownBlock(format!(
|
||||||
@@ -2241,10 +2050,6 @@ impl<Block: BlockT> sc_client_api::backend::Backend<Block> for Backend<Block> {
|
|||||||
|
|
||||||
let key =
|
let key =
|
||||||
utils::number_and_hash_to_lookup_key(best_number.clone(), &best_hash)?;
|
utils::number_and_hash_to_lookup_key(best_number.clone(), &best_hash)?;
|
||||||
let changes_trie_cache_ops = self.changes_tries_storage.revert(
|
|
||||||
&mut transaction,
|
|
||||||
&cache::ComplexBlockId::new(removed.hash(), removed_number),
|
|
||||||
)?;
|
|
||||||
if update_finalized {
|
if update_finalized {
|
||||||
transaction.set_from_vec(
|
transaction.set_from_vec(
|
||||||
columns::META,
|
columns::META,
|
||||||
@@ -2283,7 +2088,6 @@ impl<Block: BlockT> sc_client_api::backend::Backend<Block> for Backend<Block> {
|
|||||||
best_hash,
|
best_hash,
|
||||||
);
|
);
|
||||||
self.storage.db.commit(transaction)?;
|
self.storage.db.commit(transaction)?;
|
||||||
self.changes_tries_storage.post_commit(Some(changes_trie_cache_ops));
|
|
||||||
self.blockchain.update_meta(MetaUpdate {
|
self.blockchain.update_meta(MetaUpdate {
|
||||||
hash: best_hash,
|
hash: best_hash,
|
||||||
number: best_number,
|
number: best_number,
|
||||||
@@ -2345,11 +2149,6 @@ impl<Block: BlockT> sc_client_api::backend::Backend<Block> for Backend<Block> {
|
|||||||
apply_state_commit(&mut transaction, commit);
|
apply_state_commit(&mut transaction, commit);
|
||||||
}
|
}
|
||||||
transaction.remove(columns::KEY_LOOKUP, hash.as_ref());
|
transaction.remove(columns::KEY_LOOKUP, hash.as_ref());
|
||||||
let changes_trie_cache_ops = self
|
|
||||||
.changes_tries_storage
|
|
||||||
.revert(&mut transaction, &cache::ComplexBlockId::new(*hash, hdr.number))?;
|
|
||||||
|
|
||||||
self.changes_tries_storage.post_commit(Some(changes_trie_cache_ops));
|
|
||||||
leaves.revert(hash.clone(), hdr.number);
|
leaves.revert(hash.clone(), hdr.number);
|
||||||
leaves.prepare_transaction(&mut transaction, columns::META, meta_keys::LEAF_PREFIX);
|
leaves.prepare_transaction(&mut transaction, columns::META, meta_keys::LEAF_PREFIX);
|
||||||
self.storage.db.commit(transaction)?;
|
self.storage.db.commit(transaction)?;
|
||||||
@@ -2461,32 +2260,16 @@ pub(crate) mod tests {
|
|||||||
use sp_blockchain::{lowest_common_ancestor, tree_route};
|
use sp_blockchain::{lowest_common_ancestor, tree_route};
|
||||||
use sp_core::H256;
|
use sp_core::H256;
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::DigestItem,
|
|
||||||
testing::{Block as RawBlock, ExtrinsicWrapper, Header},
|
testing::{Block as RawBlock, ExtrinsicWrapper, Header},
|
||||||
traits::{BlakeTwo256, Hash},
|
traits::{BlakeTwo256, Hash},
|
||||||
ConsensusEngineId,
|
ConsensusEngineId,
|
||||||
};
|
};
|
||||||
use sp_state_machine::{TrieDBMut, TrieMut};
|
|
||||||
|
|
||||||
const CONS0_ENGINE_ID: ConsensusEngineId = *b"CON0";
|
const CONS0_ENGINE_ID: ConsensusEngineId = *b"CON0";
|
||||||
const CONS1_ENGINE_ID: ConsensusEngineId = *b"CON1";
|
const CONS1_ENGINE_ID: ConsensusEngineId = *b"CON1";
|
||||||
|
|
||||||
pub(crate) type Block = RawBlock<ExtrinsicWrapper<u64>>;
|
pub(crate) type Block = RawBlock<ExtrinsicWrapper<u64>>;
|
||||||
|
|
||||||
pub fn prepare_changes(changes: Vec<(Vec<u8>, Vec<u8>)>) -> (H256, MemoryDB<BlakeTwo256>) {
|
|
||||||
let mut changes_root = H256::default();
|
|
||||||
let mut changes_trie_update = MemoryDB::<BlakeTwo256>::default();
|
|
||||||
{
|
|
||||||
let mut trie =
|
|
||||||
TrieDBMut::<BlakeTwo256>::new(&mut changes_trie_update, &mut changes_root);
|
|
||||||
for (key, value) in changes {
|
|
||||||
trie.insert(&key, &value).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(changes_root, changes_trie_update)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_header(
|
pub fn insert_header(
|
||||||
backend: &Backend<Block>,
|
backend: &Backend<Block>,
|
||||||
number: u64,
|
number: u64,
|
||||||
@@ -2501,20 +2284,14 @@ pub(crate) mod tests {
|
|||||||
backend: &Backend<Block>,
|
backend: &Backend<Block>,
|
||||||
number: u64,
|
number: u64,
|
||||||
parent_hash: H256,
|
parent_hash: H256,
|
||||||
changes: Option<Vec<(Vec<u8>, Vec<u8>)>>,
|
_changes: Option<Vec<(Vec<u8>, Vec<u8>)>>,
|
||||||
extrinsics_root: H256,
|
extrinsics_root: H256,
|
||||||
body: Vec<ExtrinsicWrapper<u64>>,
|
body: Vec<ExtrinsicWrapper<u64>>,
|
||||||
transaction_index: Option<Vec<IndexOperation>>,
|
transaction_index: Option<Vec<IndexOperation>>,
|
||||||
) -> H256 {
|
) -> H256 {
|
||||||
use sp_runtime::testing::Digest;
|
use sp_runtime::testing::Digest;
|
||||||
|
|
||||||
let mut digest = Digest::default();
|
let digest = Digest::default();
|
||||||
let mut changes_trie_update = Default::default();
|
|
||||||
if let Some(changes) = changes {
|
|
||||||
let (root, update) = prepare_changes(changes);
|
|
||||||
digest.push(DigestItem::ChangesTrieRoot(root));
|
|
||||||
changes_trie_update = update;
|
|
||||||
}
|
|
||||||
let header = Header {
|
let header = Header {
|
||||||
number,
|
number,
|
||||||
parent_hash,
|
parent_hash,
|
||||||
@@ -2535,8 +2312,6 @@ pub(crate) mod tests {
|
|||||||
if let Some(index) = transaction_index {
|
if let Some(index) = transaction_index {
|
||||||
op.update_transaction_index(index).unwrap();
|
op.update_transaction_index(index).unwrap();
|
||||||
}
|
}
|
||||||
op.update_changes_trie((changes_trie_update, ChangesTrieCacheAction::Clear))
|
|
||||||
.unwrap();
|
|
||||||
backend.commit_operation(op).unwrap();
|
backend.commit_operation(op).unwrap();
|
||||||
|
|
||||||
header_hash
|
header_hash
|
||||||
@@ -3241,38 +3016,6 @@ pub(crate) mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn header_cht_root_works() {
|
|
||||||
use sc_client_api::ProvideChtRoots;
|
|
||||||
|
|
||||||
let backend = Backend::<Block>::new_test(10, 10);
|
|
||||||
|
|
||||||
// insert 1 + SIZE + SIZE + 1 blocks so that CHT#0 is created
|
|
||||||
let mut prev_hash =
|
|
||||||
insert_header(&backend, 0, Default::default(), None, Default::default());
|
|
||||||
let cht_size: u64 = cht::size();
|
|
||||||
for i in 1..1 + cht_size + cht_size + 1 {
|
|
||||||
prev_hash = insert_header(&backend, i, prev_hash, None, Default::default());
|
|
||||||
}
|
|
||||||
|
|
||||||
let blockchain = backend.blockchain();
|
|
||||||
|
|
||||||
let cht_root_1 = blockchain
|
|
||||||
.header_cht_root(cht_size, cht::start_number(cht_size, 0))
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
let cht_root_2 = blockchain
|
|
||||||
.header_cht_root(cht_size, cht::start_number(cht_size, 0) + cht_size / 2)
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
let cht_root_3 = blockchain
|
|
||||||
.header_cht_root(cht_size, cht::end_number(cht_size, 0))
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(cht_root_1, cht_root_2);
|
|
||||||
assert_eq!(cht_root_2, cht_root_3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn prune_blocks_on_finalize() {
|
fn prune_blocks_on_finalize() {
|
||||||
for storage in &[TransactionStorageMode::BlockBody, TransactionStorageMode::StorageChain] {
|
for storage in &[TransactionStorageMode::BlockBody, TransactionStorageMode::StorageChain] {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -16,7 +16,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
use crate::{
|
use crate::{
|
||||||
columns, light,
|
columns,
|
||||||
utils::{DatabaseType, NUM_COLUMNS},
|
utils::{DatabaseType, NUM_COLUMNS},
|
||||||
};
|
};
|
||||||
/// A `Database` adapter for parity-db.
|
/// A `Database` adapter for parity-db.
|
||||||
@@ -61,10 +61,6 @@ pub fn open<H: Clone + AsRef<[u8]>>(
|
|||||||
state_col.preimage = true;
|
state_col.preimage = true;
|
||||||
state_col.uniform = true;
|
state_col.uniform = true;
|
||||||
},
|
},
|
||||||
DatabaseType::Light => {
|
|
||||||
config.columns[light::columns::HEADER as usize].compression =
|
|
||||||
parity_db::CompressionType::Lz4;
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let db = if create {
|
let db = if create {
|
||||||
|
|||||||
@@ -56,10 +56,6 @@ pub mod meta_keys {
|
|||||||
pub const FINALIZED_STATE: &[u8; 6] = b"fstate";
|
pub const FINALIZED_STATE: &[u8; 6] = b"fstate";
|
||||||
/// Block gap.
|
/// Block gap.
|
||||||
pub const BLOCK_GAP: &[u8; 3] = b"gap";
|
pub const BLOCK_GAP: &[u8; 3] = b"gap";
|
||||||
/// Meta information prefix for list-based caches.
|
|
||||||
pub const CACHE_META_PREFIX: &[u8; 5] = b"cache";
|
|
||||||
/// Meta information for changes tries key.
|
|
||||||
pub const CHANGES_TRIES_META: &[u8; 5] = b"ctrie";
|
|
||||||
/// Genesis block hash.
|
/// Genesis block hash.
|
||||||
pub const GENESIS_HASH: &[u8; 3] = b"gen";
|
pub const GENESIS_HASH: &[u8; 3] = b"gen";
|
||||||
/// Leaves prefix list key.
|
/// Leaves prefix list key.
|
||||||
@@ -95,8 +91,6 @@ pub type NumberIndexKey = [u8; 4];
|
|||||||
pub enum DatabaseType {
|
pub enum DatabaseType {
|
||||||
/// Full node database.
|
/// Full node database.
|
||||||
Full,
|
Full,
|
||||||
/// Light node database.
|
|
||||||
Light,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert block number into short lookup key (LE representation) for
|
/// Convert block number into short lookup key (LE representation) for
|
||||||
@@ -124,19 +118,6 @@ where
|
|||||||
Ok(lookup_key)
|
Ok(lookup_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert block lookup key into block number.
|
|
||||||
/// all block lookup keys start with the block number.
|
|
||||||
pub fn lookup_key_to_number<N>(key: &[u8]) -> sp_blockchain::Result<N>
|
|
||||||
where
|
|
||||||
N: From<u32>,
|
|
||||||
{
|
|
||||||
if key.len() < 4 {
|
|
||||||
return Err(sp_blockchain::Error::Backend("Invalid block key".into()))
|
|
||||||
}
|
|
||||||
Ok((key[0] as u32) << 24 | (key[1] as u32) << 16 | (key[2] as u32) << 8 | (key[3] as u32))
|
|
||||||
.map(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete number to hash mapping in DB transaction.
|
/// Delete number to hash mapping in DB transaction.
|
||||||
pub fn remove_number_to_key_mapping<N: TryInto<u32>>(
|
pub fn remove_number_to_key_mapping<N: TryInto<u32>>(
|
||||||
transaction: &mut Transaction<DbHash>,
|
transaction: &mut Transaction<DbHash>,
|
||||||
@@ -147,18 +128,6 @@ pub fn remove_number_to_key_mapping<N: TryInto<u32>>(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove key mappings.
|
|
||||||
pub fn remove_key_mappings<N: TryInto<u32>, H: AsRef<[u8]>>(
|
|
||||||
transaction: &mut Transaction<DbHash>,
|
|
||||||
key_lookup_col: u32,
|
|
||||||
number: N,
|
|
||||||
hash: H,
|
|
||||||
) -> sp_blockchain::Result<()> {
|
|
||||||
remove_number_to_key_mapping(transaction, key_lookup_col, number)?;
|
|
||||||
transaction.remove(key_lookup_col, hash.as_ref());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Place a number mapping into the database. This maps number to current perceived
|
/// Place a number mapping into the database. This maps number to current perceived
|
||||||
/// block hash at that position.
|
/// block hash at that position.
|
||||||
pub fn insert_number_to_key_mapping<N: TryInto<u32> + Clone, H: AsRef<[u8]>>(
|
pub fn insert_number_to_key_mapping<N: TryInto<u32> + Clone, H: AsRef<[u8]>>(
|
||||||
@@ -357,18 +326,6 @@ fn open_kvdb_rocksdb<Block: BlockT>(
|
|||||||
other_col_budget,
|
other_col_budget,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
DatabaseType::Light => {
|
|
||||||
let col_budget = cache_size / (NUM_COLUMNS as usize);
|
|
||||||
for i in 0..NUM_COLUMNS {
|
|
||||||
memory_budget.insert(i, col_budget);
|
|
||||||
}
|
|
||||||
log::trace!(
|
|
||||||
target: "db",
|
|
||||||
"Open RocksDB light database at {:?}, column cache: {} MiB",
|
|
||||||
path,
|
|
||||||
col_budget,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
db_config.memory_budget = memory_budget;
|
db_config.memory_budget = memory_budget;
|
||||||
|
|
||||||
@@ -424,8 +381,7 @@ fn maybe_migrate_to_type_subdir<Block: BlockT>(
|
|||||||
// See if there's a file identifying a rocksdb or paritydb folder in the parent dir and
|
// See if there's a file identifying a rocksdb or paritydb folder in the parent dir and
|
||||||
// the target path ends in a role specific directory
|
// the target path ends in a role specific directory
|
||||||
if (basedir.join("db_version").exists() || basedir.join("metadata").exists()) &&
|
if (basedir.join("db_version").exists() || basedir.join("metadata").exists()) &&
|
||||||
(p.ends_with(DatabaseType::Full.as_str()) ||
|
(p.ends_with(DatabaseType::Full.as_str()))
|
||||||
p.ends_with(DatabaseType::Light.as_str()))
|
|
||||||
{
|
{
|
||||||
// Try to open the database to check if the current `DatabaseType` matches the type of
|
// Try to open the database to check if the current `DatabaseType` matches the type of
|
||||||
// database stored in the target directory and close the database on success.
|
// database stored in the target directory and close the database on success.
|
||||||
@@ -501,18 +457,6 @@ pub fn read_header<Block: BlockT>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Required header from the database.
|
|
||||||
pub fn require_header<Block: BlockT>(
|
|
||||||
db: &dyn Database<DbHash>,
|
|
||||||
col_index: u32,
|
|
||||||
col: u32,
|
|
||||||
id: BlockId<Block>,
|
|
||||||
) -> sp_blockchain::Result<Block::Header> {
|
|
||||||
read_header(db, col_index, col, id).and_then(|header| {
|
|
||||||
header.ok_or_else(|| sp_blockchain::Error::UnknownBlock(format!("Require header: {}", id)))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read meta from the database.
|
/// Read meta from the database.
|
||||||
pub fn read_meta<Block>(
|
pub fn read_meta<Block>(
|
||||||
db: &dyn Database<DbHash>,
|
db: &dyn Database<DbHash>,
|
||||||
@@ -598,7 +542,6 @@ impl DatabaseType {
|
|||||||
pub fn as_str(&self) -> &'static str {
|
pub fn as_str(&self) -> &'static str {
|
||||||
match *self {
|
match *self {
|
||||||
DatabaseType::Full => "full",
|
DatabaseType::Full => "full",
|
||||||
DatabaseType::Light => "light",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -669,23 +612,12 @@ mod tests {
|
|||||||
assert!(old_db_path.join(db_type.as_str()).join(db_check_file).exists());
|
assert!(old_db_path.join(db_type.as_str()).join(db_check_file).exists());
|
||||||
}
|
}
|
||||||
|
|
||||||
check_dir_for_db_type(
|
|
||||||
DatabaseType::Light,
|
|
||||||
DatabaseSource::RocksDb { path: PathBuf::new(), cache_size: 128 },
|
|
||||||
"db_version",
|
|
||||||
);
|
|
||||||
check_dir_for_db_type(
|
check_dir_for_db_type(
|
||||||
DatabaseType::Full,
|
DatabaseType::Full,
|
||||||
DatabaseSource::RocksDb { path: PathBuf::new(), cache_size: 128 },
|
DatabaseSource::RocksDb { path: PathBuf::new(), cache_size: 128 },
|
||||||
"db_version",
|
"db_version",
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(feature = "with-parity-db")]
|
|
||||||
check_dir_for_db_type(
|
|
||||||
DatabaseType::Light,
|
|
||||||
DatabaseSource::ParityDb { path: PathBuf::new() },
|
|
||||||
"metadata",
|
|
||||||
);
|
|
||||||
#[cfg(feature = "with-parity-db")]
|
#[cfg(feature = "with-parity-db")]
|
||||||
check_dir_for_db_type(
|
check_dir_for_db_type(
|
||||||
DatabaseType::Full,
|
DatabaseType::Full,
|
||||||
@@ -709,16 +641,8 @@ mod tests {
|
|||||||
assert!(!old_db_path.join("light/db_version").exists());
|
assert!(!old_db_path.join("light/db_version").exists());
|
||||||
assert!(!old_db_path.join("full/db_version").exists());
|
assert!(!old_db_path.join("full/db_version").exists());
|
||||||
}
|
}
|
||||||
let source = DatabaseSource::RocksDb {
|
|
||||||
path: old_db_path.join(DatabaseType::Light.as_str()),
|
|
||||||
cache_size: 128,
|
|
||||||
};
|
|
||||||
let settings = db_settings(source);
|
|
||||||
let db_res = open_database::<Block>(&settings, DatabaseType::Light);
|
|
||||||
assert!(db_res.is_err(), "Opening a light database in full role should fail");
|
|
||||||
// assert nothing was changed
|
// assert nothing was changed
|
||||||
assert!(old_db_path.join("db_version").exists());
|
assert!(old_db_path.join("db_version").exists());
|
||||||
assert!(!old_db_path.join("light/db_version").exists());
|
|
||||||
assert!(!old_db_path.join("full/db_version").exists());
|
assert!(!old_db_path.join("full/db_version").exists());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -735,7 +659,6 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn database_type_as_str_works() {
|
fn database_type_as_str_works() {
|
||||||
assert_eq!(DatabaseType::Full.as_str(), "full");
|
assert_eq!(DatabaseType::Full.as_str(), "full");
|
||||||
assert_eq!(DatabaseType::Light.as_str(), "light");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ use tracing_subscriber::layer::SubscriberExt;
|
|||||||
|
|
||||||
use crate::WasmExecutionMethod;
|
use crate::WasmExecutionMethod;
|
||||||
|
|
||||||
pub type TestExternalities = CoreTestExternalities<BlakeTwo256, u64>;
|
pub type TestExternalities = CoreTestExternalities<BlakeTwo256>;
|
||||||
type HostFunctions = sp_io::SubstrateHostFunctions;
|
type HostFunctions = sp_io::SubstrateHostFunctions;
|
||||||
|
|
||||||
/// Simple macro that runs a given method as test with the available wasm execution methods.
|
/// Simple macro that runs a given method as test with the available wasm execution methods.
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
use std::{collections::HashMap, marker::PhantomData, sync::Arc};
|
use std::{collections::HashMap, marker::PhantomData, sync::Arc};
|
||||||
|
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use parity_scale_codec::{Decode, Encode};
|
use parity_scale_codec::Decode;
|
||||||
|
|
||||||
use sc_client_api::{backend::Backend, utils::is_descendent_of};
|
use sc_client_api::{backend::Backend, utils::is_descendent_of};
|
||||||
use sc_consensus::{
|
use sc_consensus::{
|
||||||
@@ -35,7 +35,7 @@ use sp_core::hashing::twox_128;
|
|||||||
use sp_finality_grandpa::{ConsensusLog, GrandpaApi, ScheduledChange, SetId, GRANDPA_ENGINE_ID};
|
use sp_finality_grandpa::{ConsensusLog, GrandpaApi, ScheduledChange, SetId, GRANDPA_ENGINE_ID};
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::{BlockId, OpaqueDigestItemId},
|
generic::{BlockId, OpaqueDigestItemId},
|
||||||
traits::{Block as BlockT, DigestFor, Header as HeaderT, NumberFor, Zero},
|
traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero},
|
||||||
Justification,
|
Justification,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -89,7 +89,6 @@ impl<BE, Block: BlockT, Client, SC> JustificationImport<Block>
|
|||||||
for GrandpaBlockImport<BE, Block, Client, SC>
|
for GrandpaBlockImport<BE, Block, Client, SC>
|
||||||
where
|
where
|
||||||
NumberFor<Block>: finality_grandpa::BlockNumberOps,
|
NumberFor<Block>: finality_grandpa::BlockNumberOps,
|
||||||
DigestFor<Block>: Encode,
|
|
||||||
BE: Backend<Block>,
|
BE: Backend<Block>,
|
||||||
Client: ClientForGrandpa<Block, BE>,
|
Client: ClientForGrandpa<Block, BE>,
|
||||||
SC: SelectChain<Block>,
|
SC: SelectChain<Block>,
|
||||||
@@ -229,7 +228,6 @@ pub fn find_forced_change<B: BlockT>(
|
|||||||
impl<BE, Block: BlockT, Client, SC> GrandpaBlockImport<BE, Block, Client, SC>
|
impl<BE, Block: BlockT, Client, SC> GrandpaBlockImport<BE, Block, Client, SC>
|
||||||
where
|
where
|
||||||
NumberFor<Block>: finality_grandpa::BlockNumberOps,
|
NumberFor<Block>: finality_grandpa::BlockNumberOps,
|
||||||
DigestFor<Block>: Encode,
|
|
||||||
BE: Backend<Block>,
|
BE: Backend<Block>,
|
||||||
Client: ClientForGrandpa<Block, BE>,
|
Client: ClientForGrandpa<Block, BE>,
|
||||||
Client::Api: GrandpaApi<Block>,
|
Client::Api: GrandpaApi<Block>,
|
||||||
@@ -515,7 +513,6 @@ where
|
|||||||
impl<BE, Block: BlockT, Client, SC> BlockImport<Block> for GrandpaBlockImport<BE, Block, Client, SC>
|
impl<BE, Block: BlockT, Client, SC> BlockImport<Block> for GrandpaBlockImport<BE, Block, Client, SC>
|
||||||
where
|
where
|
||||||
NumberFor<Block>: finality_grandpa::BlockNumberOps,
|
NumberFor<Block>: finality_grandpa::BlockNumberOps,
|
||||||
DigestFor<Block>: Encode,
|
|
||||||
BE: Backend<Block>,
|
BE: Backend<Block>,
|
||||||
Client: ClientForGrandpa<Block, BE>,
|
Client: ClientForGrandpa<Block, BE>,
|
||||||
Client::Api: GrandpaApi<Block>,
|
Client::Api: GrandpaApi<Block>,
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
|
|
||||||
use futures::{prelude::*, StreamExt};
|
use futures::{prelude::*, StreamExt};
|
||||||
use log::{debug, error, info};
|
use log::{debug, error, info};
|
||||||
use parity_scale_codec::{Decode, Encode};
|
use parity_scale_codec::Decode;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use prometheus_endpoint::{PrometheusError, Registry};
|
use prometheus_endpoint::{PrometheusError, Registry};
|
||||||
use sc_client_api::{
|
use sc_client_api::{
|
||||||
@@ -77,7 +77,7 @@ use sp_core::crypto::Public;
|
|||||||
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
|
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::BlockId,
|
generic::BlockId,
|
||||||
traits::{Block as BlockT, DigestFor, NumberFor, Zero},
|
traits::{Block as BlockT, NumberFor, Zero},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use finality_grandpa::BlockNumberOps;
|
pub use finality_grandpa::BlockNumberOps;
|
||||||
@@ -718,7 +718,6 @@ where
|
|||||||
SC: SelectChain<Block> + 'static,
|
SC: SelectChain<Block> + 'static,
|
||||||
VR: VotingRule<Block, C> + Clone + 'static,
|
VR: VotingRule<Block, C> + Clone + 'static,
|
||||||
NumberFor<Block>: BlockNumberOps,
|
NumberFor<Block>: BlockNumberOps,
|
||||||
DigestFor<Block>: Encode,
|
|
||||||
C: ClientForGrandpa<Block, BE> + 'static,
|
C: ClientForGrandpa<Block, BE> + 'static,
|
||||||
C::Api: GrandpaApi<Block>,
|
C::Api: GrandpaApi<Block>,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ use sp_finality_grandpa::{
|
|||||||
use sp_keyring::Ed25519Keyring;
|
use sp_keyring::Ed25519Keyring;
|
||||||
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
|
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
|
codec::Encode,
|
||||||
generic::{BlockId, DigestItem},
|
generic::{BlockId, DigestItem},
|
||||||
traits::{Block as BlockT, Header as HeaderT},
|
traits::{Block as BlockT, Header as HeaderT},
|
||||||
Justifications,
|
Justifications,
|
||||||
@@ -139,26 +140,16 @@ impl TestNetFactory for GrandpaTestNet {
|
|||||||
&self,
|
&self,
|
||||||
client: PeersClient,
|
client: PeersClient,
|
||||||
) -> (BlockImportAdapter<Self::BlockImport>, Option<BoxJustificationImport<Block>>, PeerData) {
|
) -> (BlockImportAdapter<Self::BlockImport>, Option<BoxJustificationImport<Block>>, PeerData) {
|
||||||
match client {
|
let (client, backend) = (client.as_client(), client.as_backend());
|
||||||
PeersClient::Full(ref client, ref backend) => {
|
let (import, link) = block_import(
|
||||||
let (import, link) = block_import(
|
client.clone(),
|
||||||
client.clone(),
|
&self.test_config,
|
||||||
&self.test_config,
|
LongestChain::new(backend.clone()),
|
||||||
LongestChain::new(backend.clone()),
|
None,
|
||||||
None,
|
)
|
||||||
)
|
.expect("Could not create block import for fresh peer.");
|
||||||
.expect("Could not create block import for fresh peer.");
|
let justification_import = Box::new(import.clone());
|
||||||
let justification_import = Box::new(import.clone());
|
(BlockImportAdapter::new(import), Some(justification_import), Mutex::new(Some(link)))
|
||||||
(
|
|
||||||
BlockImportAdapter::new(import),
|
|
||||||
Some(justification_import),
|
|
||||||
Mutex::new(Some(link)),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
PeersClient::Light(..) => {
|
|
||||||
panic!("Light client is not used in tests.");
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peer(&mut self, i: usize) -> &mut GrandpaPeer {
|
fn peer(&mut self, i: usize) -> &mut GrandpaPeer {
|
||||||
@@ -466,7 +457,7 @@ fn finalize_3_voters_1_full_observer() {
|
|||||||
|
|
||||||
// all peers should have stored the justification for the best finalized block #20
|
// all peers should have stored the justification for the best finalized block #20
|
||||||
for peer_id in 0..4 {
|
for peer_id in 0..4 {
|
||||||
let client = net.lock().peers[peer_id].client().as_full().unwrap();
|
let client = net.lock().peers[peer_id].client().as_client();
|
||||||
let justification =
|
let justification =
|
||||||
crate::aux_schema::best_justification::<_, Block>(&*client).unwrap().unwrap();
|
crate::aux_schema::best_justification::<_, Block>(&*client).unwrap().unwrap();
|
||||||
|
|
||||||
@@ -539,7 +530,7 @@ fn transition_3_voters_twice_1_full_observer() {
|
|||||||
net.lock().block_until_sync();
|
net.lock().block_until_sync();
|
||||||
|
|
||||||
for (i, peer) in net.lock().peers().iter().enumerate() {
|
for (i, peer) in net.lock().peers().iter().enumerate() {
|
||||||
let full_client = peer.client().as_full().expect("only full clients are used in test");
|
let full_client = peer.client().as_client();
|
||||||
assert_eq!(full_client.chain_info().best_number, 1, "Peer #{} failed to sync", i);
|
assert_eq!(full_client.chain_info().best_number, 1, "Peer #{} failed to sync", i);
|
||||||
|
|
||||||
let set: AuthoritySet<Hash, BlockNumber> =
|
let set: AuthoritySet<Hash, BlockNumber> =
|
||||||
@@ -614,7 +605,7 @@ fn transition_3_voters_twice_1_full_observer() {
|
|||||||
.take_while(|n| future::ready(n.header.number() < &30))
|
.take_while(|n| future::ready(n.header.number() < &30))
|
||||||
.for_each(move |_| future::ready(()))
|
.for_each(move |_| future::ready(()))
|
||||||
.map(move |()| {
|
.map(move |()| {
|
||||||
let full_client = client.as_full().expect("only full clients are used in test");
|
let full_client = client.as_client();
|
||||||
let set: AuthoritySet<Hash, BlockNumber> =
|
let set: AuthoritySet<Hash, BlockNumber> =
|
||||||
crate::aux_schema::load_authorities(&*full_client).unwrap();
|
crate::aux_schema::load_authorities(&*full_client).unwrap();
|
||||||
|
|
||||||
@@ -835,7 +826,7 @@ fn force_change_to_new_set() {
|
|||||||
for (i, peer) in net.lock().peers().iter().enumerate() {
|
for (i, peer) in net.lock().peers().iter().enumerate() {
|
||||||
assert_eq!(peer.client().info().best_number, 26, "Peer #{} failed to sync", i);
|
assert_eq!(peer.client().info().best_number, 26, "Peer #{} failed to sync", i);
|
||||||
|
|
||||||
let full_client = peer.client().as_full().expect("only full clients are used in test");
|
let full_client = peer.client().as_client();
|
||||||
let set: AuthoritySet<Hash, BlockNumber> =
|
let set: AuthoritySet<Hash, BlockNumber> =
|
||||||
crate::aux_schema::load_authorities(&*full_client).unwrap();
|
crate::aux_schema::load_authorities(&*full_client).unwrap();
|
||||||
|
|
||||||
@@ -861,7 +852,7 @@ fn allows_reimporting_change_blocks() {
|
|||||||
let client = net.peer(0).client().clone();
|
let client = net.peer(0).client().clone();
|
||||||
let (mut block_import, ..) = net.make_block_import(client.clone());
|
let (mut block_import, ..) = net.make_block_import(client.clone());
|
||||||
|
|
||||||
let full_client = client.as_full().unwrap();
|
let full_client = client.as_client();
|
||||||
let builder = full_client
|
let builder = full_client
|
||||||
.new_block_at(&BlockId::Number(0), Default::default(), false)
|
.new_block_at(&BlockId::Number(0), Default::default(), false)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -908,7 +899,7 @@ fn test_bad_justification() {
|
|||||||
let client = net.peer(0).client().clone();
|
let client = net.peer(0).client().clone();
|
||||||
let (mut block_import, ..) = net.make_block_import(client.clone());
|
let (mut block_import, ..) = net.make_block_import(client.clone());
|
||||||
|
|
||||||
let full_client = client.as_full().expect("only full clients are used in test");
|
let full_client = client.as_client();
|
||||||
let builder = full_client
|
let builder = full_client
|
||||||
.new_block_at(&BlockId::Number(0), Default::default(), false)
|
.new_block_at(&BlockId::Number(0), Default::default(), false)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -1148,7 +1139,7 @@ fn voter_persists_its_votes() {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
let block_30_hash =
|
let block_30_hash =
|
||||||
net.lock().peer(0).client().as_full().unwrap().hash(30).unwrap().unwrap();
|
net.lock().peer(0).client().as_client().hash(30).unwrap().unwrap();
|
||||||
|
|
||||||
// we restart alice's voter
|
// we restart alice's voter
|
||||||
abort.abort();
|
abort.abort();
|
||||||
@@ -1581,7 +1572,7 @@ fn imports_justification_for_regular_blocks_on_import() {
|
|||||||
let client = net.peer(0).client().clone();
|
let client = net.peer(0).client().clone();
|
||||||
let (mut block_import, ..) = net.make_block_import(client.clone());
|
let (mut block_import, ..) = net.make_block_import(client.clone());
|
||||||
|
|
||||||
let full_client = client.as_full().expect("only full clients are used in test");
|
let full_client = client.as_client();
|
||||||
let builder = full_client
|
let builder = full_client
|
||||||
.new_block_at(&BlockId::Number(0), Default::default(), false)
|
.new_block_at(&BlockId::Number(0), Default::default(), false)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
[package]
|
|
||||||
description = "components for a light client"
|
|
||||||
name = "sc-light"
|
|
||||||
version = "4.0.0-dev"
|
|
||||||
license = "GPL-3.0-or-later WITH Classpath-exception-2.0"
|
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
|
||||||
edition = "2021"
|
|
||||||
homepage = "https://substrate.io"
|
|
||||||
repository = "https://github.com/paritytech/substrate/"
|
|
||||||
documentation = "https://docs.rs/sc-light"
|
|
||||||
readme = "README.md"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
parking_lot = "0.11.1"
|
|
||||||
hash-db = "0.15.2"
|
|
||||||
sp-runtime = { version = "4.0.0-dev", path = "../../primitives/runtime" }
|
|
||||||
sp-externalities = { version = "0.10.0-dev", path = "../../primitives/externalities" }
|
|
||||||
sp-blockchain = { version = "4.0.0-dev", path = "../../primitives/blockchain" }
|
|
||||||
sp-core = { version = "4.0.0-dev", path = "../../primitives/core" }
|
|
||||||
sp-state-machine = { version = "0.10.0-dev", path = "../../primitives/state-machine" }
|
|
||||||
sc-client-api = { version = "4.0.0-dev", path = "../api" }
|
|
||||||
sp-api = { version = "4.0.0-dev", path = "../../primitives/api" }
|
|
||||||
codec = { package = "parity-scale-codec", version = "2.0.0" }
|
|
||||||
sc-executor = { version = "0.10.0-dev", path = "../executor" }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = []
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
Light client components.
|
|
||||||
|
|
||||||
License: GPL-3.0-or-later WITH Classpath-exception-2.0
|
|
||||||
@@ -1,578 +0,0 @@
|
|||||||
// This file is part of Substrate.
|
|
||||||
|
|
||||||
// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Light client backend. Only stores headers and justifications of blocks.
|
|
||||||
//! Everything else is requested from full nodes on demand.
|
|
||||||
|
|
||||||
use parking_lot::RwLock;
|
|
||||||
use std::{
|
|
||||||
collections::{HashMap, HashSet},
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use codec::{Decode, Encode};
|
|
||||||
|
|
||||||
use super::blockchain::Blockchain;
|
|
||||||
use hash_db::Hasher;
|
|
||||||
use sc_client_api::{
|
|
||||||
backend::{
|
|
||||||
AuxStore, Backend as ClientBackend, BlockImportOperation, NewBlockState,
|
|
||||||
PrunableStateChangesTrieStorage, RemoteBackend,
|
|
||||||
},
|
|
||||||
blockchain::{well_known_cache_keys, HeaderBackend as BlockchainHeaderBackend},
|
|
||||||
in_mem::check_genesis_storage,
|
|
||||||
light::Storage as BlockchainStorage,
|
|
||||||
UsageInfo,
|
|
||||||
};
|
|
||||||
use sp_blockchain::{Error as ClientError, Result as ClientResult};
|
|
||||||
use sp_core::{
|
|
||||||
offchain::storage::InMemOffchainStorage,
|
|
||||||
storage::{well_known_keys, ChildInfo},
|
|
||||||
ChangesTrieConfiguration,
|
|
||||||
};
|
|
||||||
use sp_runtime::{
|
|
||||||
generic::BlockId,
|
|
||||||
traits::{Block as BlockT, HashFor, Header, NumberFor, Zero},
|
|
||||||
Justification, Justifications, Storage,
|
|
||||||
};
|
|
||||||
use sp_state_machine::{
|
|
||||||
Backend as StateBackend, ChangesTrieTransaction, ChildStorageCollection, InMemoryBackend,
|
|
||||||
IndexOperation, StorageCollection, TrieBackend,
|
|
||||||
};
|
|
||||||
|
|
||||||
const IN_MEMORY_EXPECT_PROOF: &str =
|
|
||||||
"InMemory state backend has Void error type and always succeeds; qed";
|
|
||||||
|
|
||||||
/// Light client backend.
|
|
||||||
pub struct Backend<S, H: Hasher> {
|
|
||||||
blockchain: Arc<Blockchain<S>>,
|
|
||||||
genesis_state: RwLock<Option<InMemoryBackend<H>>>,
|
|
||||||
import_lock: RwLock<()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Light block (header and justification) import operation.
|
|
||||||
pub struct ImportOperation<Block: BlockT, S> {
|
|
||||||
header: Option<Block::Header>,
|
|
||||||
cache: HashMap<well_known_cache_keys::Id, Vec<u8>>,
|
|
||||||
leaf_state: NewBlockState,
|
|
||||||
aux_ops: Vec<(Vec<u8>, Option<Vec<u8>>)>,
|
|
||||||
finalized_blocks: Vec<BlockId<Block>>,
|
|
||||||
set_head: Option<BlockId<Block>>,
|
|
||||||
storage_update: Option<InMemoryBackend<HashFor<Block>>>,
|
|
||||||
changes_trie_config_update: Option<Option<ChangesTrieConfiguration>>,
|
|
||||||
_phantom: std::marker::PhantomData<S>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Either in-memory genesis state, or locally-unavailable state.
|
|
||||||
pub enum GenesisOrUnavailableState<H: Hasher> {
|
|
||||||
/// Genesis state - storage values are stored in-memory.
|
|
||||||
Genesis(InMemoryBackend<H>),
|
|
||||||
/// We know that state exists, but all calls will fail with error, because it
|
|
||||||
/// isn't locally available.
|
|
||||||
Unavailable,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S, H: Hasher> Backend<S, H> {
|
|
||||||
/// Create new light backend.
|
|
||||||
pub fn new(blockchain: Arc<Blockchain<S>>) -> Self {
|
|
||||||
Self { blockchain, genesis_state: RwLock::new(None), import_lock: Default::default() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get shared blockchain reference.
|
|
||||||
pub fn blockchain(&self) -> &Arc<Blockchain<S>> {
|
|
||||||
&self.blockchain
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: AuxStore, H: Hasher> AuxStore for Backend<S, H> {
|
|
||||||
fn insert_aux<
|
|
||||||
'a,
|
|
||||||
'b: 'a,
|
|
||||||
'c: 'a,
|
|
||||||
I: IntoIterator<Item = &'a (&'c [u8], &'c [u8])>,
|
|
||||||
D: IntoIterator<Item = &'a &'b [u8]>,
|
|
||||||
>(
|
|
||||||
&self,
|
|
||||||
insert: I,
|
|
||||||
delete: D,
|
|
||||||
) -> ClientResult<()> {
|
|
||||||
self.blockchain.storage().insert_aux(insert, delete)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_aux(&self, key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
|
|
||||||
self.blockchain.storage().get_aux(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S, Block> ClientBackend<Block> for Backend<S, HashFor<Block>>
|
|
||||||
where
|
|
||||||
Block: BlockT,
|
|
||||||
S: BlockchainStorage<Block>,
|
|
||||||
Block::Hash: Ord,
|
|
||||||
{
|
|
||||||
type BlockImportOperation = ImportOperation<Block, S>;
|
|
||||||
type Blockchain = Blockchain<S>;
|
|
||||||
type State = GenesisOrUnavailableState<HashFor<Block>>;
|
|
||||||
type OffchainStorage = InMemOffchainStorage;
|
|
||||||
|
|
||||||
fn begin_operation(&self) -> ClientResult<Self::BlockImportOperation> {
|
|
||||||
Ok(ImportOperation {
|
|
||||||
header: None,
|
|
||||||
cache: Default::default(),
|
|
||||||
leaf_state: NewBlockState::Normal,
|
|
||||||
aux_ops: Vec::new(),
|
|
||||||
finalized_blocks: Vec::new(),
|
|
||||||
set_head: None,
|
|
||||||
storage_update: None,
|
|
||||||
changes_trie_config_update: None,
|
|
||||||
_phantom: Default::default(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn begin_state_operation(
|
|
||||||
&self,
|
|
||||||
_operation: &mut Self::BlockImportOperation,
|
|
||||||
_block: BlockId<Block>,
|
|
||||||
) -> ClientResult<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn commit_operation(&self, mut operation: Self::BlockImportOperation) -> ClientResult<()> {
|
|
||||||
if !operation.finalized_blocks.is_empty() {
|
|
||||||
for block in operation.finalized_blocks {
|
|
||||||
self.blockchain.storage().finalize_header(block)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(header) = operation.header {
|
|
||||||
let is_genesis_import = header.number().is_zero();
|
|
||||||
if let Some(new_config) = operation.changes_trie_config_update {
|
|
||||||
operation
|
|
||||||
.cache
|
|
||||||
.insert(well_known_cache_keys::CHANGES_TRIE_CONFIG, new_config.encode());
|
|
||||||
}
|
|
||||||
self.blockchain.storage().import_header(
|
|
||||||
header,
|
|
||||||
operation.cache,
|
|
||||||
operation.leaf_state,
|
|
||||||
operation.aux_ops,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// when importing genesis block => remember its state
|
|
||||||
if is_genesis_import {
|
|
||||||
*self.genesis_state.write() = operation.storage_update.take();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (key, maybe_val) in operation.aux_ops {
|
|
||||||
match maybe_val {
|
|
||||||
Some(val) => self
|
|
||||||
.blockchain
|
|
||||||
.storage()
|
|
||||||
.insert_aux(&[(&key[..], &val[..])], std::iter::empty())?,
|
|
||||||
None =>
|
|
||||||
self.blockchain.storage().insert_aux(std::iter::empty(), &[&key[..]])?,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(set_head) = operation.set_head {
|
|
||||||
self.blockchain.storage().set_head(set_head)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finalize_block(
|
|
||||||
&self,
|
|
||||||
block: BlockId<Block>,
|
|
||||||
_justification: Option<Justification>,
|
|
||||||
) -> ClientResult<()> {
|
|
||||||
self.blockchain.storage().finalize_header(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn append_justification(
|
|
||||||
&self,
|
|
||||||
_block: BlockId<Block>,
|
|
||||||
_justification: Justification,
|
|
||||||
) -> ClientResult<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn blockchain(&self) -> &Blockchain<S> {
|
|
||||||
&self.blockchain
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage_info(&self) -> Option<UsageInfo> {
|
|
||||||
self.blockchain.storage().usage_info()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn changes_trie_storage(&self) -> Option<&dyn PrunableStateChangesTrieStorage<Block>> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn offchain_storage(&self) -> Option<Self::OffchainStorage> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn state_at(&self, block: BlockId<Block>) -> ClientResult<Self::State> {
|
|
||||||
let block_number = self.blockchain.expect_block_number_from_id(&block)?;
|
|
||||||
|
|
||||||
// special case for genesis block
|
|
||||||
if block_number.is_zero() {
|
|
||||||
if let Some(genesis_state) = self.genesis_state.read().clone() {
|
|
||||||
return Ok(GenesisOrUnavailableState::Genesis(genesis_state))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// else return unavailable state. We do not return error here, because error
|
|
||||||
// would mean that we do not know this state at all. But we know that it exists
|
|
||||||
Ok(GenesisOrUnavailableState::Unavailable)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn revert(
|
|
||||||
&self,
|
|
||||||
_n: NumberFor<Block>,
|
|
||||||
_revert_finalized: bool,
|
|
||||||
) -> ClientResult<(NumberFor<Block>, HashSet<Block::Hash>)> {
|
|
||||||
Err(ClientError::NotAvailableOnLightClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_leaf_block(&self, _hash: &Block::Hash) -> ClientResult<()> {
|
|
||||||
Err(ClientError::NotAvailableOnLightClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_import_lock(&self) -> &RwLock<()> {
|
|
||||||
&self.import_lock
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S, Block> RemoteBackend<Block> for Backend<S, HashFor<Block>>
|
|
||||||
where
|
|
||||||
Block: BlockT,
|
|
||||||
S: BlockchainStorage<Block> + 'static,
|
|
||||||
Block::Hash: Ord,
|
|
||||||
{
|
|
||||||
fn is_local_state_available(&self, block: &BlockId<Block>) -> bool {
|
|
||||||
self.genesis_state.read().is_some() &&
|
|
||||||
self.blockchain
|
|
||||||
.expect_block_number_from_id(block)
|
|
||||||
.map(|num| num.is_zero())
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remote_blockchain(&self) -> Arc<dyn super::blockchain::RemoteBlockchain<Block>> {
|
|
||||||
self.blockchain.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S, Block> BlockImportOperation<Block> for ImportOperation<Block, S>
|
|
||||||
where
|
|
||||||
Block: BlockT,
|
|
||||||
S: BlockchainStorage<Block>,
|
|
||||||
Block::Hash: Ord,
|
|
||||||
{
|
|
||||||
type State = GenesisOrUnavailableState<HashFor<Block>>;
|
|
||||||
|
|
||||||
fn state(&self) -> ClientResult<Option<&Self::State>> {
|
|
||||||
// None means 'locally-stateless' backend
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_block_data(
|
|
||||||
&mut self,
|
|
||||||
header: Block::Header,
|
|
||||||
_body: Option<Vec<Block::Extrinsic>>,
|
|
||||||
_indexed_body: Option<Vec<Vec<u8>>>,
|
|
||||||
_justifications: Option<Justifications>,
|
|
||||||
state: NewBlockState,
|
|
||||||
) -> ClientResult<()> {
|
|
||||||
self.leaf_state = state;
|
|
||||||
self.header = Some(header);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_cache(&mut self, cache: HashMap<well_known_cache_keys::Id, Vec<u8>>) {
|
|
||||||
self.cache = cache;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_db_storage(
|
|
||||||
&mut self,
|
|
||||||
_update: <Self::State as StateBackend<HashFor<Block>>>::Transaction,
|
|
||||||
) -> ClientResult<()> {
|
|
||||||
// we're not storing anything locally => ignore changes
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_changes_trie(
|
|
||||||
&mut self,
|
|
||||||
_update: ChangesTrieTransaction<HashFor<Block>, NumberFor<Block>>,
|
|
||||||
) -> ClientResult<()> {
|
|
||||||
// we're not storing anything locally => ignore changes
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_genesis_state(&mut self, input: Storage, commit: bool) -> ClientResult<Block::Hash> {
|
|
||||||
check_genesis_storage(&input)?;
|
|
||||||
|
|
||||||
// changes trie configuration
|
|
||||||
let changes_trie_config = input
|
|
||||||
.top
|
|
||||||
.iter()
|
|
||||||
.find(|(k, _)| &k[..] == well_known_keys::CHANGES_TRIE_CONFIG)
|
|
||||||
.map(|(_, v)| {
|
|
||||||
Decode::decode(&mut &v[..])
|
|
||||||
.expect("changes trie configuration is encoded properly at genesis")
|
|
||||||
});
|
|
||||||
self.changes_trie_config_update = Some(changes_trie_config);
|
|
||||||
|
|
||||||
// this is only called when genesis block is imported => shouldn't be performance bottleneck
|
|
||||||
let mut storage: HashMap<Option<ChildInfo>, _> = HashMap::new();
|
|
||||||
storage.insert(None, input.top);
|
|
||||||
|
|
||||||
// create a list of children keys to re-compute roots for
|
|
||||||
let child_delta = input
|
|
||||||
.children_default
|
|
||||||
.iter()
|
|
||||||
.map(|(_storage_key, storage_child)| (&storage_child.child_info, std::iter::empty()));
|
|
||||||
|
|
||||||
// make sure to persist the child storage
|
|
||||||
for (_child_key, storage_child) in input.children_default.clone() {
|
|
||||||
storage.insert(Some(storage_child.child_info), storage_child.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
let storage_update = InMemoryBackend::from(storage);
|
|
||||||
let (storage_root, _) = storage_update.full_storage_root(std::iter::empty(), child_delta);
|
|
||||||
if commit {
|
|
||||||
self.storage_update = Some(storage_update);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(storage_root)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset_storage(&mut self, _input: Storage) -> ClientResult<Block::Hash> {
|
|
||||||
Err(ClientError::NotAvailableOnLightClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert_aux<I>(&mut self, ops: I) -> ClientResult<()>
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = (Vec<u8>, Option<Vec<u8>>)>,
|
|
||||||
{
|
|
||||||
self.aux_ops.append(&mut ops.into_iter().collect());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_storage(
|
|
||||||
&mut self,
|
|
||||||
_update: StorageCollection,
|
|
||||||
_child_update: ChildStorageCollection,
|
|
||||||
) -> ClientResult<()> {
|
|
||||||
// we're not storing anything locally => ignore changes
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mark_finalized(
|
|
||||||
&mut self,
|
|
||||||
block: BlockId<Block>,
|
|
||||||
_justifications: Option<Justification>,
|
|
||||||
) -> ClientResult<()> {
|
|
||||||
self.finalized_blocks.push(block);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mark_head(&mut self, block: BlockId<Block>) -> ClientResult<()> {
|
|
||||||
self.set_head = Some(block);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_transaction_index(
|
|
||||||
&mut self,
|
|
||||||
_index: Vec<IndexOperation>,
|
|
||||||
) -> sp_blockchain::Result<()> {
|
|
||||||
// noop for the light client
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<H: Hasher> std::fmt::Debug for GenesisOrUnavailableState<H> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match *self {
|
|
||||||
GenesisOrUnavailableState::Genesis(ref state) => state.fmt(f),
|
|
||||||
GenesisOrUnavailableState::Unavailable => write!(f, "Unavailable"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<H: Hasher> StateBackend<H> for GenesisOrUnavailableState<H>
|
|
||||||
where
|
|
||||||
H::Out: Ord + codec::Codec,
|
|
||||||
{
|
|
||||||
type Error = ClientError;
|
|
||||||
type Transaction = <InMemoryBackend<H> as StateBackend<H>>::Transaction;
|
|
||||||
type TrieBackendStorage = <InMemoryBackend<H> as StateBackend<H>>::TrieBackendStorage;
|
|
||||||
|
|
||||||
fn storage(&self, key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
|
|
||||||
match *self {
|
|
||||||
GenesisOrUnavailableState::Genesis(ref state) =>
|
|
||||||
Ok(state.storage(key).expect(IN_MEMORY_EXPECT_PROOF)),
|
|
||||||
GenesisOrUnavailableState::Unavailable => Err(ClientError::NotAvailableOnLightClient),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn child_storage(&self, child_info: &ChildInfo, key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
|
|
||||||
match *self {
|
|
||||||
GenesisOrUnavailableState::Genesis(ref state) =>
|
|
||||||
Ok(state.child_storage(child_info, key).expect(IN_MEMORY_EXPECT_PROOF)),
|
|
||||||
GenesisOrUnavailableState::Unavailable => Err(ClientError::NotAvailableOnLightClient),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_storage_key(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Self::Error> {
|
|
||||||
match *self {
|
|
||||||
GenesisOrUnavailableState::Genesis(ref state) =>
|
|
||||||
Ok(state.next_storage_key(key).expect(IN_MEMORY_EXPECT_PROOF)),
|
|
||||||
GenesisOrUnavailableState::Unavailable => Err(ClientError::NotAvailableOnLightClient),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_child_storage_key(
|
|
||||||
&self,
|
|
||||||
child_info: &ChildInfo,
|
|
||||||
key: &[u8],
|
|
||||||
) -> Result<Option<Vec<u8>>, Self::Error> {
|
|
||||||
match *self {
|
|
||||||
GenesisOrUnavailableState::Genesis(ref state) =>
|
|
||||||
Ok(state.next_child_storage_key(child_info, key).expect(IN_MEMORY_EXPECT_PROOF)),
|
|
||||||
GenesisOrUnavailableState::Unavailable => Err(ClientError::NotAvailableOnLightClient),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn for_keys_with_prefix<A: FnMut(&[u8])>(&self, prefix: &[u8], action: A) {
|
|
||||||
match *self {
|
|
||||||
GenesisOrUnavailableState::Genesis(ref state) =>
|
|
||||||
state.for_keys_with_prefix(prefix, action),
|
|
||||||
GenesisOrUnavailableState::Unavailable => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn for_key_values_with_prefix<A: FnMut(&[u8], &[u8])>(&self, prefix: &[u8], action: A) {
|
|
||||||
match *self {
|
|
||||||
GenesisOrUnavailableState::Genesis(ref state) =>
|
|
||||||
state.for_key_values_with_prefix(prefix, action),
|
|
||||||
GenesisOrUnavailableState::Unavailable => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_to_key_values_while<A: FnMut(Vec<u8>, Vec<u8>) -> bool>(
|
|
||||||
&self,
|
|
||||||
child_info: Option<&ChildInfo>,
|
|
||||||
prefix: Option<&[u8]>,
|
|
||||||
start_at: Option<&[u8]>,
|
|
||||||
action: A,
|
|
||||||
allow_missing: bool,
|
|
||||||
) -> ClientResult<bool> {
|
|
||||||
match *self {
|
|
||||||
GenesisOrUnavailableState::Genesis(ref state) => Ok(state
|
|
||||||
.apply_to_key_values_while(child_info, prefix, start_at, action, allow_missing)
|
|
||||||
.expect(IN_MEMORY_EXPECT_PROOF)),
|
|
||||||
GenesisOrUnavailableState::Unavailable => Err(ClientError::NotAvailableOnLightClient),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_to_keys_while<A: FnMut(&[u8]) -> bool>(
|
|
||||||
&self,
|
|
||||||
child_info: Option<&ChildInfo>,
|
|
||||||
prefix: Option<&[u8]>,
|
|
||||||
action: A,
|
|
||||||
) {
|
|
||||||
match *self {
|
|
||||||
GenesisOrUnavailableState::Genesis(ref state) =>
|
|
||||||
state.apply_to_keys_while(child_info, prefix, action),
|
|
||||||
GenesisOrUnavailableState::Unavailable => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn for_child_keys_with_prefix<A: FnMut(&[u8])>(
|
|
||||||
&self,
|
|
||||||
child_info: &ChildInfo,
|
|
||||||
prefix: &[u8],
|
|
||||||
action: A,
|
|
||||||
) {
|
|
||||||
match *self {
|
|
||||||
GenesisOrUnavailableState::Genesis(ref state) =>
|
|
||||||
state.for_child_keys_with_prefix(child_info, prefix, action),
|
|
||||||
GenesisOrUnavailableState::Unavailable => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn storage_root<'a>(
|
|
||||||
&self,
|
|
||||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
|
||||||
) -> (H::Out, Self::Transaction)
|
|
||||||
where
|
|
||||||
H::Out: Ord,
|
|
||||||
{
|
|
||||||
match *self {
|
|
||||||
GenesisOrUnavailableState::Genesis(ref state) => state.storage_root(delta),
|
|
||||||
GenesisOrUnavailableState::Unavailable => Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn child_storage_root<'a>(
|
|
||||||
&self,
|
|
||||||
child_info: &ChildInfo,
|
|
||||||
delta: impl Iterator<Item = (&'a [u8], Option<&'a [u8]>)>,
|
|
||||||
) -> (H::Out, bool, Self::Transaction)
|
|
||||||
where
|
|
||||||
H::Out: Ord,
|
|
||||||
{
|
|
||||||
match *self {
|
|
||||||
GenesisOrUnavailableState::Genesis(ref state) => {
|
|
||||||
let (root, is_equal, _) = state.child_storage_root(child_info, delta);
|
|
||||||
(root, is_equal, Default::default())
|
|
||||||
},
|
|
||||||
GenesisOrUnavailableState::Unavailable => (H::Out::default(), true, Default::default()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
|
|
||||||
match *self {
|
|
||||||
GenesisOrUnavailableState::Genesis(ref state) => state.pairs(),
|
|
||||||
GenesisOrUnavailableState::Unavailable => Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn keys(&self, prefix: &[u8]) -> Vec<Vec<u8>> {
|
|
||||||
match *self {
|
|
||||||
GenesisOrUnavailableState::Genesis(ref state) => state.keys(prefix),
|
|
||||||
GenesisOrUnavailableState::Unavailable => Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn register_overlay_stats(&self, _stats: &sp_state_machine::StateMachineStats) {}
|
|
||||||
|
|
||||||
fn usage_info(&self) -> sp_state_machine::UsageInfo {
|
|
||||||
sp_state_machine::UsageInfo::empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_trie_backend(&self) -> Option<&TrieBackend<Self::TrieBackendStorage, H>> {
|
|
||||||
match self {
|
|
||||||
GenesisOrUnavailableState::Genesis(ref state) => state.as_trie_backend(),
|
|
||||||
GenesisOrUnavailableState::Unavailable => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,219 +0,0 @@
|
|||||||
// This file is part of Substrate.
|
|
||||||
|
|
||||||
// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Light client blockchain backend. Only stores headers and justifications of recent
|
|
||||||
//! blocks. CHT roots are stored for headers of ancient blocks.
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use sp_runtime::{
|
|
||||||
generic::BlockId,
|
|
||||||
traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero},
|
|
||||||
Justifications,
|
|
||||||
};
|
|
||||||
|
|
||||||
use sc_client_api::light::RemoteHeaderRequest;
|
|
||||||
pub use sc_client_api::{
|
|
||||||
backend::{AuxStore, NewBlockState, ProvideChtRoots},
|
|
||||||
blockchain::{
|
|
||||||
well_known_cache_keys, Backend as BlockchainBackend, BlockStatus, Cache as BlockchainCache,
|
|
||||||
HeaderBackend as BlockchainHeaderBackend, Info as BlockchainInfo, ProvideCache,
|
|
||||||
},
|
|
||||||
cht,
|
|
||||||
light::{LocalOrRemote, RemoteBlockchain, Storage},
|
|
||||||
};
|
|
||||||
use sp_blockchain::{
|
|
||||||
CachedHeaderMetadata, Error as ClientError, HeaderMetadata, Result as ClientResult,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Light client blockchain.
|
|
||||||
pub struct Blockchain<S> {
|
|
||||||
storage: S,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> Blockchain<S> {
|
|
||||||
/// Create new light blockchain backed with given storage.
|
|
||||||
pub fn new(storage: S) -> Self {
|
|
||||||
Self { storage }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get storage reference.
|
|
||||||
pub fn storage(&self) -> &S {
|
|
||||||
&self.storage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S, Block> BlockchainHeaderBackend<Block> for Blockchain<S>
|
|
||||||
where
|
|
||||||
Block: BlockT,
|
|
||||||
S: Storage<Block>,
|
|
||||||
{
|
|
||||||
fn header(&self, id: BlockId<Block>) -> ClientResult<Option<Block::Header>> {
|
|
||||||
match RemoteBlockchain::header(self, id)? {
|
|
||||||
LocalOrRemote::Local(header) => Ok(Some(header)),
|
|
||||||
LocalOrRemote::Remote(_) => Err(ClientError::NotAvailableOnLightClient),
|
|
||||||
LocalOrRemote::Unknown => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn info(&self) -> BlockchainInfo<Block> {
|
|
||||||
self.storage.info()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn status(&self, id: BlockId<Block>) -> ClientResult<BlockStatus> {
|
|
||||||
self.storage.status(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn number(&self, hash: Block::Hash) -> ClientResult<Option<NumberFor<Block>>> {
|
|
||||||
self.storage.number(hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hash(
|
|
||||||
&self,
|
|
||||||
number: <<Block as BlockT>::Header as HeaderT>::Number,
|
|
||||||
) -> ClientResult<Option<Block::Hash>> {
|
|
||||||
self.storage.hash(number)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S, Block> HeaderMetadata<Block> for Blockchain<S>
|
|
||||||
where
|
|
||||||
Block: BlockT,
|
|
||||||
S: Storage<Block>,
|
|
||||||
{
|
|
||||||
type Error = ClientError;
|
|
||||||
|
|
||||||
fn header_metadata(
|
|
||||||
&self,
|
|
||||||
hash: Block::Hash,
|
|
||||||
) -> Result<CachedHeaderMetadata<Block>, Self::Error> {
|
|
||||||
self.storage.header_metadata(hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata<Block>) {
|
|
||||||
self.storage.insert_header_metadata(hash, metadata)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_header_metadata(&self, hash: Block::Hash) {
|
|
||||||
self.storage.remove_header_metadata(hash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S, Block> BlockchainBackend<Block> for Blockchain<S>
|
|
||||||
where
|
|
||||||
Block: BlockT,
|
|
||||||
S: Storage<Block>,
|
|
||||||
{
|
|
||||||
fn body(&self, _id: BlockId<Block>) -> ClientResult<Option<Vec<Block::Extrinsic>>> {
|
|
||||||
Err(ClientError::NotAvailableOnLightClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn justifications(&self, _id: BlockId<Block>) -> ClientResult<Option<Justifications>> {
|
|
||||||
Err(ClientError::NotAvailableOnLightClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn last_finalized(&self) -> ClientResult<Block::Hash> {
|
|
||||||
self.storage.last_finalized()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cache(&self) -> Option<Arc<dyn BlockchainCache<Block>>> {
|
|
||||||
self.storage.cache()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn leaves(&self) -> ClientResult<Vec<Block::Hash>> {
|
|
||||||
Err(ClientError::NotAvailableOnLightClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn children(&self, _parent_hash: Block::Hash) -> ClientResult<Vec<Block::Hash>> {
|
|
||||||
Err(ClientError::NotAvailableOnLightClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn indexed_transaction(&self, _hash: &Block::Hash) -> ClientResult<Option<Vec<u8>>> {
|
|
||||||
Err(ClientError::NotAvailableOnLightClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn block_indexed_body(
|
|
||||||
&self,
|
|
||||||
_id: BlockId<Block>,
|
|
||||||
) -> sp_blockchain::Result<Option<Vec<Vec<u8>>>> {
|
|
||||||
Err(ClientError::NotAvailableOnLightClient)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: Storage<Block>, Block: BlockT> ProvideCache<Block> for Blockchain<S> {
|
|
||||||
fn cache(&self) -> Option<Arc<dyn BlockchainCache<Block>>> {
|
|
||||||
self.storage.cache()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S, Block: BlockT> RemoteBlockchain<Block> for Blockchain<S>
|
|
||||||
where
|
|
||||||
S: Storage<Block>,
|
|
||||||
{
|
|
||||||
fn header(
|
|
||||||
&self,
|
|
||||||
id: BlockId<Block>,
|
|
||||||
) -> ClientResult<LocalOrRemote<Block::Header, RemoteHeaderRequest<Block::Header>>> {
|
|
||||||
// first, try to read header from local storage
|
|
||||||
if let Some(local_header) = self.storage.header(id)? {
|
|
||||||
return Ok(LocalOrRemote::Local(local_header))
|
|
||||||
}
|
|
||||||
|
|
||||||
// we need to know block number to check if it's a part of CHT
|
|
||||||
let number = match id {
|
|
||||||
BlockId::Hash(hash) => match self.storage.number(hash)? {
|
|
||||||
Some(number) => number,
|
|
||||||
None => return Ok(LocalOrRemote::Unknown),
|
|
||||||
},
|
|
||||||
BlockId::Number(number) => number,
|
|
||||||
};
|
|
||||||
|
|
||||||
// if the header is genesis (never pruned), non-canonical, or from future => return
|
|
||||||
if number.is_zero() || self.storage.status(BlockId::Number(number))? == BlockStatus::Unknown
|
|
||||||
{
|
|
||||||
return Ok(LocalOrRemote::Unknown)
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(LocalOrRemote::Remote(RemoteHeaderRequest {
|
|
||||||
cht_root: match self.storage.header_cht_root(cht::size(), number)? {
|
|
||||||
Some(cht_root) => cht_root,
|
|
||||||
None => return Ok(LocalOrRemote::Unknown),
|
|
||||||
},
|
|
||||||
block: number,
|
|
||||||
retry_count: None,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S: Storage<Block>, Block: BlockT> ProvideChtRoots<Block> for Blockchain<S> {
|
|
||||||
fn header_cht_root(
|
|
||||||
&self,
|
|
||||||
cht_size: NumberFor<Block>,
|
|
||||||
block: NumberFor<Block>,
|
|
||||||
) -> sp_blockchain::Result<Option<Block::Hash>> {
|
|
||||||
self.storage().header_cht_root(cht_size, block)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn changes_trie_cht_root(
|
|
||||||
&self,
|
|
||||||
cht_size: NumberFor<Block>,
|
|
||||||
block: NumberFor<Block>,
|
|
||||||
) -> sp_blockchain::Result<Option<Block::Hash>> {
|
|
||||||
self.storage().changes_trie_cht_root(cht_size, block)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,206 +0,0 @@
|
|||||||
// This file is part of Substrate.
|
|
||||||
|
|
||||||
// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Methods that light client could use to execute runtime calls.
|
|
||||||
|
|
||||||
use std::{cell::RefCell, panic::UnwindSafe, result, sync::Arc};
|
|
||||||
|
|
||||||
use codec::{Decode, Encode};
|
|
||||||
use hash_db::Hasher;
|
|
||||||
use sp_core::{
|
|
||||||
convert_hash,
|
|
||||||
traits::{CodeExecutor, SpawnNamed},
|
|
||||||
NativeOrEncoded,
|
|
||||||
};
|
|
||||||
use sp_externalities::Extensions;
|
|
||||||
use sp_runtime::{
|
|
||||||
generic::BlockId,
|
|
||||||
traits::{Block as BlockT, Header as HeaderT},
|
|
||||||
};
|
|
||||||
use sp_state_machine::{
|
|
||||||
create_proof_check_backend, execution_proof_check_on_trie_backend, ExecutionManager,
|
|
||||||
ExecutionStrategy, OverlayedChanges, StorageProof,
|
|
||||||
};
|
|
||||||
|
|
||||||
use sp_api::{ProofRecorder, StorageTransactionCache};
|
|
||||||
|
|
||||||
use sp_blockchain::{Error as ClientError, Result as ClientResult};
|
|
||||||
|
|
||||||
use sc_client_api::{
|
|
||||||
backend::RemoteBackend, call_executor::CallExecutor, light::RemoteCallRequest,
|
|
||||||
};
|
|
||||||
use sc_executor::RuntimeVersion;
|
|
||||||
|
|
||||||
/// Call executor that is able to execute calls only on genesis state.
|
|
||||||
///
|
|
||||||
/// Trying to execute call on non-genesis state leads to error.
|
|
||||||
pub struct GenesisCallExecutor<B, L> {
|
|
||||||
backend: Arc<B>,
|
|
||||||
local: L,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B, L> GenesisCallExecutor<B, L> {
|
|
||||||
/// Create new genesis call executor.
|
|
||||||
pub fn new(backend: Arc<B>, local: L) -> Self {
|
|
||||||
Self { backend, local }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B, L: Clone> Clone for GenesisCallExecutor<B, L> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
GenesisCallExecutor { backend: self.backend.clone(), local: self.local.clone() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Block, B, Local> CallExecutor<Block> for GenesisCallExecutor<B, Local>
|
|
||||||
where
|
|
||||||
Block: BlockT,
|
|
||||||
B: RemoteBackend<Block>,
|
|
||||||
Local: CallExecutor<Block>,
|
|
||||||
{
|
|
||||||
type Error = ClientError;
|
|
||||||
|
|
||||||
type Backend = B;
|
|
||||||
|
|
||||||
fn call(
|
|
||||||
&self,
|
|
||||||
id: &BlockId<Block>,
|
|
||||||
method: &str,
|
|
||||||
call_data: &[u8],
|
|
||||||
strategy: ExecutionStrategy,
|
|
||||||
extensions: Option<Extensions>,
|
|
||||||
) -> ClientResult<Vec<u8>> {
|
|
||||||
if self.backend.is_local_state_available(id) {
|
|
||||||
self.local.call(id, method, call_data, strategy, extensions)
|
|
||||||
} else {
|
|
||||||
Err(ClientError::NotAvailableOnLightClient)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn contextual_call<
|
|
||||||
EM: Fn(
|
|
||||||
Result<NativeOrEncoded<R>, Self::Error>,
|
|
||||||
Result<NativeOrEncoded<R>, Self::Error>,
|
|
||||||
) -> Result<NativeOrEncoded<R>, Self::Error>,
|
|
||||||
R: Encode + Decode + PartialEq,
|
|
||||||
NC: FnOnce() -> result::Result<R, sp_api::ApiError> + UnwindSafe,
|
|
||||||
>(
|
|
||||||
&self,
|
|
||||||
at: &BlockId<Block>,
|
|
||||||
method: &str,
|
|
||||||
call_data: &[u8],
|
|
||||||
changes: &RefCell<OverlayedChanges>,
|
|
||||||
_: Option<&RefCell<StorageTransactionCache<Block, B::State>>>,
|
|
||||||
_manager: ExecutionManager<EM>,
|
|
||||||
native_call: Option<NC>,
|
|
||||||
recorder: &Option<ProofRecorder<Block>>,
|
|
||||||
extensions: Option<Extensions>,
|
|
||||||
) -> ClientResult<NativeOrEncoded<R>>
|
|
||||||
where
|
|
||||||
ExecutionManager<EM>: Clone,
|
|
||||||
{
|
|
||||||
// there's no actual way/need to specify native/wasm execution strategy on light node
|
|
||||||
// => we can safely ignore passed values
|
|
||||||
|
|
||||||
if self.backend.is_local_state_available(at) {
|
|
||||||
CallExecutor::contextual_call::<
|
|
||||||
fn(
|
|
||||||
Result<NativeOrEncoded<R>, Local::Error>,
|
|
||||||
Result<NativeOrEncoded<R>, Local::Error>,
|
|
||||||
) -> Result<NativeOrEncoded<R>, Local::Error>,
|
|
||||||
_,
|
|
||||||
NC,
|
|
||||||
>(
|
|
||||||
&self.local,
|
|
||||||
at,
|
|
||||||
method,
|
|
||||||
call_data,
|
|
||||||
changes,
|
|
||||||
None,
|
|
||||||
ExecutionManager::NativeWhenPossible,
|
|
||||||
native_call,
|
|
||||||
recorder,
|
|
||||||
extensions,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Err(ClientError::NotAvailableOnLightClient)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prove_execution(
|
|
||||||
&self,
|
|
||||||
at: &BlockId<Block>,
|
|
||||||
method: &str,
|
|
||||||
call_data: &[u8],
|
|
||||||
) -> ClientResult<(Vec<u8>, StorageProof)> {
|
|
||||||
if self.backend.is_local_state_available(at) {
|
|
||||||
self.local.prove_execution(at, method, call_data)
|
|
||||||
} else {
|
|
||||||
Err(ClientError::NotAvailableOnLightClient)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn runtime_version(&self, id: &BlockId<Block>) -> ClientResult<RuntimeVersion> {
|
|
||||||
if self.backend.is_local_state_available(id) {
|
|
||||||
self.local.runtime_version(id)
|
|
||||||
} else {
|
|
||||||
Err(ClientError::NotAvailableOnLightClient)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check remote contextual execution proof using given backend.
|
|
||||||
///
|
|
||||||
/// Proof should include the method execution proof.
|
|
||||||
pub fn check_execution_proof<Header, E, H>(
|
|
||||||
executor: &E,
|
|
||||||
spawn_handle: Box<dyn SpawnNamed>,
|
|
||||||
request: &RemoteCallRequest<Header>,
|
|
||||||
remote_proof: StorageProof,
|
|
||||||
) -> ClientResult<Vec<u8>>
|
|
||||||
where
|
|
||||||
Header: HeaderT<Hash = H::Out>,
|
|
||||||
E: CodeExecutor + Clone + 'static,
|
|
||||||
H: Hasher,
|
|
||||||
H::Out: Ord + codec::Codec + 'static,
|
|
||||||
{
|
|
||||||
let local_state_root = request.header.state_root();
|
|
||||||
let root: H::Out = convert_hash(&local_state_root);
|
|
||||||
|
|
||||||
// prepare execution environment
|
|
||||||
let mut changes = OverlayedChanges::default();
|
|
||||||
let trie_backend = create_proof_check_backend(root, remote_proof)?;
|
|
||||||
|
|
||||||
// TODO: Remove when solved: https://github.com/paritytech/substrate/issues/5047
|
|
||||||
let backend_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&trie_backend);
|
|
||||||
let runtime_code = backend_runtime_code
|
|
||||||
.runtime_code()
|
|
||||||
.map_err(|_e| ClientError::RuntimeCodeMissing)?;
|
|
||||||
|
|
||||||
// execute method
|
|
||||||
execution_proof_check_on_trie_backend::<H, Header::Number, _, _>(
|
|
||||||
&trie_backend,
|
|
||||||
&mut changes,
|
|
||||||
executor,
|
|
||||||
spawn_handle,
|
|
||||||
&request.method,
|
|
||||||
&request.call_data,
|
|
||||||
&runtime_code,
|
|
||||||
)
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
// This file is part of Substrate.
|
|
||||||
|
|
||||||
// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Light client components.
|
|
||||||
|
|
||||||
use sp_runtime::traits::{Block as BlockT, HashFor};
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
pub mod backend;
|
|
||||||
pub mod blockchain;
|
|
||||||
pub mod call_executor;
|
|
||||||
|
|
||||||
pub use backend::*;
|
|
||||||
pub use blockchain::*;
|
|
||||||
pub use call_executor::*;
|
|
||||||
|
|
||||||
use sc_client_api::light::Storage as BlockchainStorage;
|
|
||||||
|
|
||||||
/// Create an instance of light client backend.
|
|
||||||
pub fn new_light_backend<B, S>(blockchain: Arc<Blockchain<S>>) -> Arc<Backend<S, HashFor<B>>>
|
|
||||||
where
|
|
||||||
B: BlockT,
|
|
||||||
S: BlockchainStorage<B>,
|
|
||||||
{
|
|
||||||
Arc::new(Backend::new(blockchain))
|
|
||||||
}
|
|
||||||
@@ -20,14 +20,14 @@ use crate::{
|
|||||||
bitswap::Bitswap,
|
bitswap::Bitswap,
|
||||||
config::ProtocolId,
|
config::ProtocolId,
|
||||||
discovery::{DiscoveryBehaviour, DiscoveryConfig, DiscoveryOut},
|
discovery::{DiscoveryBehaviour, DiscoveryConfig, DiscoveryOut},
|
||||||
light_client_requests, peer_info,
|
peer_info,
|
||||||
protocol::{message::Roles, CustomMessageOutcome, NotificationsSink, Protocol},
|
protocol::{message::Roles, CustomMessageOutcome, NotificationsSink, Protocol},
|
||||||
request_responses, DhtEvent, ObservedRole,
|
request_responses, DhtEvent, ObservedRole,
|
||||||
};
|
};
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
use futures::{channel::oneshot, stream::StreamExt};
|
use futures::channel::oneshot;
|
||||||
use libp2p::{
|
use libp2p::{
|
||||||
core::{Multiaddr, PeerId, PublicKey},
|
core::{Multiaddr, PeerId, PublicKey},
|
||||||
identify::IdentifyInfo,
|
identify::IdentifyInfo,
|
||||||
@@ -76,10 +76,6 @@ pub struct Behaviour<B: BlockT> {
|
|||||||
#[behaviour(ignore)]
|
#[behaviour(ignore)]
|
||||||
events: VecDeque<BehaviourOut<B>>,
|
events: VecDeque<BehaviourOut<B>>,
|
||||||
|
|
||||||
/// Light client request handling.
|
|
||||||
#[behaviour(ignore)]
|
|
||||||
light_client_request_sender: light_client_requests::sender::LightClientRequestSender<B>,
|
|
||||||
|
|
||||||
/// Protocol name used to send out block requests via
|
/// Protocol name used to send out block requests via
|
||||||
/// [`request_responses::RequestResponsesBehaviour`].
|
/// [`request_responses::RequestResponsesBehaviour`].
|
||||||
#[behaviour(ignore)]
|
#[behaviour(ignore)]
|
||||||
@@ -198,7 +194,6 @@ impl<B: BlockT> Behaviour<B> {
|
|||||||
substrate: Protocol<B>,
|
substrate: Protocol<B>,
|
||||||
user_agent: String,
|
user_agent: String,
|
||||||
local_public_key: PublicKey,
|
local_public_key: PublicKey,
|
||||||
light_client_request_sender: light_client_requests::sender::LightClientRequestSender<B>,
|
|
||||||
disco_config: DiscoveryConfig,
|
disco_config: DiscoveryConfig,
|
||||||
block_request_protocol_config: request_responses::ProtocolConfig,
|
block_request_protocol_config: request_responses::ProtocolConfig,
|
||||||
state_request_protocol_config: request_responses::ProtocolConfig,
|
state_request_protocol_config: request_responses::ProtocolConfig,
|
||||||
@@ -233,7 +228,6 @@ impl<B: BlockT> Behaviour<B> {
|
|||||||
request_response_protocols.into_iter(),
|
request_response_protocols.into_iter(),
|
||||||
peerset,
|
peerset,
|
||||||
)?,
|
)?,
|
||||||
light_client_request_sender,
|
|
||||||
events: VecDeque::new(),
|
events: VecDeque::new(),
|
||||||
block_request_protocol_name,
|
block_request_protocol_name,
|
||||||
state_request_protocol_name,
|
state_request_protocol_name,
|
||||||
@@ -316,14 +310,6 @@ impl<B: BlockT> Behaviour<B> {
|
|||||||
pub fn put_value(&mut self, key: record::Key, value: Vec<u8>) {
|
pub fn put_value(&mut self, key: record::Key, value: Vec<u8>) {
|
||||||
self.discovery.put_value(key, value);
|
self.discovery.put_value(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Issue a light client request.
|
|
||||||
pub fn light_client_request(
|
|
||||||
&mut self,
|
|
||||||
r: light_client_requests::sender::Request<B>,
|
|
||||||
) -> Result<(), light_client_requests::sender::SendRequestError> {
|
|
||||||
self.light_client_request_sender.request(r)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reported_roles_to_observed_role(roles: Roles) -> ObservedRole {
|
fn reported_roles_to_observed_role(roles: Roles) -> ObservedRole {
|
||||||
@@ -436,17 +422,11 @@ impl<B: BlockT> NetworkBehaviourEventProcess<CustomMessageOutcome<B>> for Behavi
|
|||||||
CustomMessageOutcome::NotificationsReceived { remote, messages } => {
|
CustomMessageOutcome::NotificationsReceived { remote, messages } => {
|
||||||
self.events.push_back(BehaviourOut::NotificationsReceived { remote, messages });
|
self.events.push_back(BehaviourOut::NotificationsReceived { remote, messages });
|
||||||
},
|
},
|
||||||
CustomMessageOutcome::PeerNewBest(peer_id, number) => {
|
CustomMessageOutcome::PeerNewBest(_peer_id, _number) => {},
|
||||||
self.light_client_request_sender.update_best_block(&peer_id, number);
|
CustomMessageOutcome::SyncConnected(peer_id) =>
|
||||||
},
|
self.events.push_back(BehaviourOut::SyncConnected(peer_id)),
|
||||||
CustomMessageOutcome::SyncConnected(peer_id) => {
|
CustomMessageOutcome::SyncDisconnected(peer_id) =>
|
||||||
self.light_client_request_sender.inject_connected(peer_id);
|
self.events.push_back(BehaviourOut::SyncDisconnected(peer_id)),
|
||||||
self.events.push_back(BehaviourOut::SyncConnected(peer_id))
|
|
||||||
},
|
|
||||||
CustomMessageOutcome::SyncDisconnected(peer_id) => {
|
|
||||||
self.light_client_request_sender.inject_disconnected(peer_id);
|
|
||||||
self.events.push_back(BehaviourOut::SyncDisconnected(peer_id))
|
|
||||||
},
|
|
||||||
CustomMessageOutcome::None => {},
|
CustomMessageOutcome::None => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -534,23 +514,9 @@ impl<B: BlockT> NetworkBehaviourEventProcess<DiscoveryOut> for Behaviour<B> {
|
|||||||
impl<B: BlockT> Behaviour<B> {
|
impl<B: BlockT> Behaviour<B> {
|
||||||
fn poll<TEv>(
|
fn poll<TEv>(
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &mut Context,
|
_cx: &mut Context,
|
||||||
_: &mut impl PollParameters,
|
_: &mut impl PollParameters,
|
||||||
) -> Poll<NetworkBehaviourAction<TEv, BehaviourOut<B>>> {
|
) -> Poll<NetworkBehaviourAction<TEv, BehaviourOut<B>>> {
|
||||||
use light_client_requests::sender::OutEvent;
|
|
||||||
while let Poll::Ready(Some(event)) = self.light_client_request_sender.poll_next_unpin(cx) {
|
|
||||||
match event {
|
|
||||||
OutEvent::SendRequest { target, request, pending_response, protocol_name } =>
|
|
||||||
self.request_responses.send_request(
|
|
||||||
&target,
|
|
||||||
&protocol_name,
|
|
||||||
request,
|
|
||||||
pending_response,
|
|
||||||
IfDisconnected::ImmediateError,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(event) = self.events.pop_front() {
|
if let Some(event) = self.events.pop_front() {
|
||||||
return Poll::Ready(NetworkBehaviourAction::GenerateEvent(event))
|
return Poll::Ready(NetworkBehaviourAction::GenerateEvent(event))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
|
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
chain::Client,
|
chain::Client,
|
||||||
on_demand_layer::{AlwaysBadChecker, OnDemand},
|
|
||||||
request_responses::{
|
request_responses::{
|
||||||
IncomingRequest, OutgoingResponse, ProtocolConfig as RequestResponseConfig,
|
IncomingRequest, OutgoingResponse, ProtocolConfig as RequestResponseConfig,
|
||||||
},
|
},
|
||||||
@@ -83,11 +82,6 @@ pub struct Params<B: BlockT, H: ExHashT> {
|
|||||||
/// Client that contains the blockchain.
|
/// Client that contains the blockchain.
|
||||||
pub chain: Arc<dyn Client<B>>,
|
pub chain: Arc<dyn Client<B>>,
|
||||||
|
|
||||||
/// The `OnDemand` object acts as a "receiver" for block data requests from the client.
|
|
||||||
/// If `Some`, the network worker will process these requests and answer them.
|
|
||||||
/// Normally used only for light clients.
|
|
||||||
pub on_demand: Option<Arc<OnDemand<B>>>,
|
|
||||||
|
|
||||||
/// Pool of transactions.
|
/// Pool of transactions.
|
||||||
///
|
///
|
||||||
/// The network worker will fetch transactions from this object in order to propagate them on
|
/// The network worker will fetch transactions from this object in order to propagate them on
|
||||||
|
|||||||
@@ -247,7 +247,6 @@
|
|||||||
mod behaviour;
|
mod behaviour;
|
||||||
mod chain;
|
mod chain;
|
||||||
mod discovery;
|
mod discovery;
|
||||||
mod on_demand_layer;
|
|
||||||
mod peer_info;
|
mod peer_info;
|
||||||
mod protocol;
|
mod protocol;
|
||||||
mod request_responses;
|
mod request_responses;
|
||||||
|
|||||||
@@ -20,8 +20,6 @@
|
|||||||
|
|
||||||
/// For incoming light client requests.
|
/// For incoming light client requests.
|
||||||
pub mod handler;
|
pub mod handler;
|
||||||
/// For outgoing light client requests.
|
|
||||||
pub mod sender;
|
|
||||||
|
|
||||||
use crate::{config::ProtocolId, request_responses::ProtocolConfig};
|
use crate::{config::ProtocolId, request_responses::ProtocolConfig};
|
||||||
|
|
||||||
@@ -47,269 +45,3 @@ pub fn generate_protocol_config(protocol_id: &ProtocolId) -> ProtocolConfig {
|
|||||||
inbound_queue: None,
|
inbound_queue: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::{config::ProtocolId, request_responses::IncomingRequest};
|
|
||||||
|
|
||||||
use assert_matches::assert_matches;
|
|
||||||
use futures::{
|
|
||||||
channel::oneshot,
|
|
||||||
executor::{block_on, LocalPool},
|
|
||||||
prelude::*,
|
|
||||||
task::Spawn,
|
|
||||||
};
|
|
||||||
use libp2p::PeerId;
|
|
||||||
use sc_client_api::{
|
|
||||||
light::{
|
|
||||||
self, ChangesProof, RemoteBodyRequest, RemoteCallRequest, RemoteChangesRequest,
|
|
||||||
RemoteHeaderRequest, RemoteReadRequest,
|
|
||||||
},
|
|
||||||
FetchChecker, RemoteReadChildRequest, StorageProof,
|
|
||||||
};
|
|
||||||
use sp_blockchain::Error as ClientError;
|
|
||||||
use sp_core::storage::ChildInfo;
|
|
||||||
use sp_runtime::{
|
|
||||||
generic::Header,
|
|
||||||
traits::{BlakeTwo256, Block as BlockT, NumberFor},
|
|
||||||
};
|
|
||||||
use std::{collections::HashMap, sync::Arc};
|
|
||||||
|
|
||||||
pub struct DummyFetchChecker<B> {
|
|
||||||
pub ok: bool,
|
|
||||||
pub _mark: std::marker::PhantomData<B>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B: BlockT> FetchChecker<B> for DummyFetchChecker<B> {
|
|
||||||
fn check_header_proof(
|
|
||||||
&self,
|
|
||||||
_request: &RemoteHeaderRequest<B::Header>,
|
|
||||||
header: Option<B::Header>,
|
|
||||||
_remote_proof: StorageProof,
|
|
||||||
) -> Result<B::Header, ClientError> {
|
|
||||||
match self.ok {
|
|
||||||
true if header.is_some() => Ok(header.unwrap()),
|
|
||||||
_ => Err(ClientError::Backend("Test error".into())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_read_proof(
|
|
||||||
&self,
|
|
||||||
request: &RemoteReadRequest<B::Header>,
|
|
||||||
_: StorageProof,
|
|
||||||
) -> Result<HashMap<Vec<u8>, Option<Vec<u8>>>, ClientError> {
|
|
||||||
match self.ok {
|
|
||||||
true => Ok(request.keys.iter().cloned().map(|k| (k, Some(vec![42]))).collect()),
|
|
||||||
false => Err(ClientError::Backend("Test error".into())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_read_child_proof(
|
|
||||||
&self,
|
|
||||||
request: &RemoteReadChildRequest<B::Header>,
|
|
||||||
_: StorageProof,
|
|
||||||
) -> Result<HashMap<Vec<u8>, Option<Vec<u8>>>, ClientError> {
|
|
||||||
match self.ok {
|
|
||||||
true => Ok(request.keys.iter().cloned().map(|k| (k, Some(vec![42]))).collect()),
|
|
||||||
false => Err(ClientError::Backend("Test error".into())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_execution_proof(
|
|
||||||
&self,
|
|
||||||
_: &RemoteCallRequest<B::Header>,
|
|
||||||
_: StorageProof,
|
|
||||||
) -> Result<Vec<u8>, ClientError> {
|
|
||||||
match self.ok {
|
|
||||||
true => Ok(vec![42]),
|
|
||||||
false => Err(ClientError::Backend("Test error".into())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_changes_proof(
|
|
||||||
&self,
|
|
||||||
_: &RemoteChangesRequest<B::Header>,
|
|
||||||
_: ChangesProof<B::Header>,
|
|
||||||
) -> Result<Vec<(NumberFor<B>, u32)>, ClientError> {
|
|
||||||
match self.ok {
|
|
||||||
true => Ok(vec![(100u32.into(), 2)]),
|
|
||||||
false => Err(ClientError::Backend("Test error".into())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_body_proof(
|
|
||||||
&self,
|
|
||||||
_: &RemoteBodyRequest<B::Header>,
|
|
||||||
body: Vec<B::Extrinsic>,
|
|
||||||
) -> Result<Vec<B::Extrinsic>, ClientError> {
|
|
||||||
match self.ok {
|
|
||||||
true => Ok(body),
|
|
||||||
false => Err(ClientError::Backend("Test error".into())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn protocol_id() -> ProtocolId {
|
|
||||||
ProtocolId::from("test")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn peerset() -> (sc_peerset::Peerset, sc_peerset::PeersetHandle) {
|
|
||||||
let cfg = sc_peerset::SetConfig {
|
|
||||||
in_peers: 128,
|
|
||||||
out_peers: 128,
|
|
||||||
bootnodes: Default::default(),
|
|
||||||
reserved_only: false,
|
|
||||||
reserved_nodes: Default::default(),
|
|
||||||
};
|
|
||||||
sc_peerset::Peerset::from_config(sc_peerset::PeersetConfig { sets: vec![cfg] })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dummy_header() -> sp_test_primitives::Header {
|
|
||||||
sp_test_primitives::Header {
|
|
||||||
parent_hash: Default::default(),
|
|
||||||
number: 0,
|
|
||||||
state_root: Default::default(),
|
|
||||||
extrinsics_root: Default::default(),
|
|
||||||
digest: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Block =
|
|
||||||
sp_runtime::generic::Block<Header<u64, BlakeTwo256>, substrate_test_runtime::Extrinsic>;
|
|
||||||
|
|
||||||
fn send_receive(request: sender::Request<Block>, pool: &LocalPool) {
|
|
||||||
let client = Arc::new(substrate_test_runtime_client::new());
|
|
||||||
let (handler, protocol_config) =
|
|
||||||
handler::LightClientRequestHandler::new(&protocol_id(), client);
|
|
||||||
pool.spawner().spawn_obj(handler.run().boxed().into()).unwrap();
|
|
||||||
|
|
||||||
let (_peer_set, peer_set_handle) = peerset();
|
|
||||||
let mut sender = sender::LightClientRequestSender::<Block>::new(
|
|
||||||
&protocol_id(),
|
|
||||||
Arc::new(crate::light_client_requests::tests::DummyFetchChecker {
|
|
||||||
ok: true,
|
|
||||||
_mark: std::marker::PhantomData,
|
|
||||||
}),
|
|
||||||
peer_set_handle,
|
|
||||||
);
|
|
||||||
sender.inject_connected(PeerId::random());
|
|
||||||
|
|
||||||
sender.request(request).unwrap();
|
|
||||||
let sender::OutEvent::SendRequest { pending_response, request, .. } =
|
|
||||||
block_on(sender.next()).unwrap();
|
|
||||||
let (tx, rx) = oneshot::channel();
|
|
||||||
block_on(protocol_config.inbound_queue.unwrap().send(IncomingRequest {
|
|
||||||
peer: PeerId::random(),
|
|
||||||
payload: request,
|
|
||||||
pending_response: tx,
|
|
||||||
}))
|
|
||||||
.unwrap();
|
|
||||||
pool.spawner()
|
|
||||||
.spawn_obj(
|
|
||||||
async move {
|
|
||||||
pending_response.send(Ok(rx.await.unwrap().result.unwrap())).unwrap();
|
|
||||||
}
|
|
||||||
.boxed()
|
|
||||||
.into(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
pool.spawner()
|
|
||||||
.spawn_obj(sender.for_each(|_| future::ready(())).boxed().into())
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn send_receive_call() {
|
|
||||||
let chan = oneshot::channel();
|
|
||||||
let request = light::RemoteCallRequest {
|
|
||||||
block: Default::default(),
|
|
||||||
header: dummy_header(),
|
|
||||||
method: "test".into(),
|
|
||||||
call_data: vec![],
|
|
||||||
retry_count: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut pool = LocalPool::new();
|
|
||||||
send_receive(sender::Request::Call { request, sender: chan.0 }, &pool);
|
|
||||||
assert_eq!(vec![42], pool.run_until(chan.1).unwrap().unwrap());
|
|
||||||
// ^--- from `DummyFetchChecker::check_execution_proof`
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn send_receive_read() {
|
|
||||||
let chan = oneshot::channel();
|
|
||||||
let request = light::RemoteReadRequest {
|
|
||||||
header: dummy_header(),
|
|
||||||
block: Default::default(),
|
|
||||||
keys: vec![b":key".to_vec()],
|
|
||||||
retry_count: None,
|
|
||||||
};
|
|
||||||
let mut pool = LocalPool::new();
|
|
||||||
send_receive(sender::Request::Read { request, sender: chan.0 }, &pool);
|
|
||||||
assert_eq!(
|
|
||||||
Some(vec![42]),
|
|
||||||
pool.run_until(chan.1).unwrap().unwrap().remove(&b":key"[..]).unwrap()
|
|
||||||
);
|
|
||||||
// ^--- from `DummyFetchChecker::check_read_proof`
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn send_receive_read_child() {
|
|
||||||
let chan = oneshot::channel();
|
|
||||||
let child_info = ChildInfo::new_default(&b":child_storage:default:sub"[..]);
|
|
||||||
let request = light::RemoteReadChildRequest {
|
|
||||||
header: dummy_header(),
|
|
||||||
block: Default::default(),
|
|
||||||
storage_key: child_info.prefixed_storage_key(),
|
|
||||||
keys: vec![b":key".to_vec()],
|
|
||||||
retry_count: None,
|
|
||||||
};
|
|
||||||
let mut pool = LocalPool::new();
|
|
||||||
send_receive(sender::Request::ReadChild { request, sender: chan.0 }, &pool);
|
|
||||||
assert_eq!(
|
|
||||||
Some(vec![42]),
|
|
||||||
pool.run_until(chan.1).unwrap().unwrap().remove(&b":key"[..]).unwrap()
|
|
||||||
);
|
|
||||||
// ^--- from `DummyFetchChecker::check_read_child_proof`
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn send_receive_header() {
|
|
||||||
sp_tracing::try_init_simple();
|
|
||||||
let chan = oneshot::channel();
|
|
||||||
let request = light::RemoteHeaderRequest {
|
|
||||||
cht_root: Default::default(),
|
|
||||||
block: 1,
|
|
||||||
retry_count: None,
|
|
||||||
};
|
|
||||||
let mut pool = LocalPool::new();
|
|
||||||
send_receive(sender::Request::Header { request, sender: chan.0 }, &pool);
|
|
||||||
// The remote does not know block 1:
|
|
||||||
assert_matches!(pool.run_until(chan.1).unwrap(), Err(ClientError::RemoteFetchFailed));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn send_receive_changes() {
|
|
||||||
let chan = oneshot::channel();
|
|
||||||
let request = light::RemoteChangesRequest {
|
|
||||||
changes_trie_configs: vec![sp_core::ChangesTrieConfigurationRange {
|
|
||||||
zero: (0, Default::default()),
|
|
||||||
end: None,
|
|
||||||
config: Some(sp_core::ChangesTrieConfiguration::new(4, 2)),
|
|
||||||
}],
|
|
||||||
first_block: (1, Default::default()),
|
|
||||||
last_block: (100, Default::default()),
|
|
||||||
max_block: (100, Default::default()),
|
|
||||||
tries_roots: (1, Default::default(), Vec::new()),
|
|
||||||
key: Vec::new(),
|
|
||||||
storage_key: None,
|
|
||||||
retry_count: None,
|
|
||||||
};
|
|
||||||
let mut pool = LocalPool::new();
|
|
||||||
send_receive(sender::Request::Changes { request, sender: chan.0 }, &pool);
|
|
||||||
assert_eq!(vec![(100, 2)], pool.run_until(chan.1).unwrap().unwrap());
|
|
||||||
// ^--- from `DummyFetchChecker::check_changes_proof`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -32,17 +32,14 @@ use codec::{self, Decode, Encode};
|
|||||||
use futures::{channel::mpsc, prelude::*};
|
use futures::{channel::mpsc, prelude::*};
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
use sc_client_api::{light, StorageProof};
|
use sc_client_api::StorageProof;
|
||||||
use sc_peerset::ReputationChange;
|
use sc_peerset::ReputationChange;
|
||||||
use sp_core::{
|
use sp_core::{
|
||||||
hexdisplay::HexDisplay,
|
hexdisplay::HexDisplay,
|
||||||
storage::{ChildInfo, ChildType, PrefixedStorageKey, StorageKey},
|
storage::{ChildInfo, ChildType, PrefixedStorageKey},
|
||||||
};
|
};
|
||||||
use sp_runtime::{
|
use sp_runtime::{generic::BlockId, traits::Block};
|
||||||
generic::BlockId,
|
use std::sync::Arc;
|
||||||
traits::{Block, Zero},
|
|
||||||
};
|
|
||||||
use std::{collections::BTreeMap, sync::Arc};
|
|
||||||
|
|
||||||
const LOG_TARGET: &str = "light-client-request-handler";
|
const LOG_TARGET: &str = "light-client-request-handler";
|
||||||
|
|
||||||
@@ -137,12 +134,12 @@ impl<B: Block> LightClientRequestHandler<B> {
|
|||||||
self.on_remote_call_request(&peer, r)?,
|
self.on_remote_call_request(&peer, r)?,
|
||||||
Some(schema::v1::light::request::Request::RemoteReadRequest(r)) =>
|
Some(schema::v1::light::request::Request::RemoteReadRequest(r)) =>
|
||||||
self.on_remote_read_request(&peer, r)?,
|
self.on_remote_read_request(&peer, r)?,
|
||||||
Some(schema::v1::light::request::Request::RemoteHeaderRequest(r)) =>
|
Some(schema::v1::light::request::Request::RemoteHeaderRequest(_r)) =>
|
||||||
self.on_remote_header_request(&peer, r)?,
|
return Err(HandleRequestError::BadRequest("Not supported.")),
|
||||||
Some(schema::v1::light::request::Request::RemoteReadChildRequest(r)) =>
|
Some(schema::v1::light::request::Request::RemoteReadChildRequest(r)) =>
|
||||||
self.on_remote_read_child_request(&peer, r)?,
|
self.on_remote_read_child_request(&peer, r)?,
|
||||||
Some(schema::v1::light::request::Request::RemoteChangesRequest(r)) =>
|
Some(schema::v1::light::request::Request::RemoteChangesRequest(_r)) =>
|
||||||
self.on_remote_changes_request(&peer, r)?,
|
return Err(HandleRequestError::BadRequest("Not supported.")),
|
||||||
None =>
|
None =>
|
||||||
return Err(HandleRequestError::BadRequest("Remote request without request data.")),
|
return Err(HandleRequestError::BadRequest("Remote request without request data.")),
|
||||||
};
|
};
|
||||||
@@ -285,106 +282,6 @@ impl<B: Block> LightClientRequestHandler<B> {
|
|||||||
|
|
||||||
Ok(schema::v1::light::Response { response: Some(response) })
|
Ok(schema::v1::light::Response { response: Some(response) })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_remote_header_request(
|
|
||||||
&mut self,
|
|
||||||
peer: &PeerId,
|
|
||||||
request: &schema::v1::light::RemoteHeaderRequest,
|
|
||||||
) -> Result<schema::v1::light::Response, HandleRequestError> {
|
|
||||||
trace!("Remote header proof request from {} ({:?}).", peer, request.block);
|
|
||||||
|
|
||||||
let block = Decode::decode(&mut request.block.as_ref())?;
|
|
||||||
let (header, proof) = match self.client.header_proof(&BlockId::Number(block)) {
|
|
||||||
Ok((header, proof)) => (header.encode(), proof),
|
|
||||||
Err(error) => {
|
|
||||||
trace!(
|
|
||||||
"Remote header proof request from {} ({:?}) failed with: {}.",
|
|
||||||
peer,
|
|
||||||
request.block,
|
|
||||||
error
|
|
||||||
);
|
|
||||||
(Default::default(), StorageProof::empty())
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let response = {
|
|
||||||
let r = schema::v1::light::RemoteHeaderResponse { header, proof: proof.encode() };
|
|
||||||
schema::v1::light::response::Response::RemoteHeaderResponse(r)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(schema::v1::light::Response { response: Some(response) })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_remote_changes_request(
|
|
||||||
&mut self,
|
|
||||||
peer: &PeerId,
|
|
||||||
request: &schema::v1::light::RemoteChangesRequest,
|
|
||||||
) -> Result<schema::v1::light::Response, HandleRequestError> {
|
|
||||||
trace!(
|
|
||||||
"Remote changes proof request from {} for key {} ({:?}..{:?}).",
|
|
||||||
peer,
|
|
||||||
if !request.storage_key.is_empty() {
|
|
||||||
format!(
|
|
||||||
"{} : {}",
|
|
||||||
HexDisplay::from(&request.storage_key),
|
|
||||||
HexDisplay::from(&request.key)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
HexDisplay::from(&request.key).to_string()
|
|
||||||
},
|
|
||||||
request.first,
|
|
||||||
request.last,
|
|
||||||
);
|
|
||||||
|
|
||||||
let first = Decode::decode(&mut request.first.as_ref())?;
|
|
||||||
let last = Decode::decode(&mut request.last.as_ref())?;
|
|
||||||
let min = Decode::decode(&mut request.min.as_ref())?;
|
|
||||||
let max = Decode::decode(&mut request.max.as_ref())?;
|
|
||||||
let key = StorageKey(request.key.clone());
|
|
||||||
let storage_key = if request.storage_key.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(PrefixedStorageKey::new_ref(&request.storage_key))
|
|
||||||
};
|
|
||||||
|
|
||||||
let proof =
|
|
||||||
match self.client.key_changes_proof(first, last, min, max, storage_key, &key) {
|
|
||||||
Ok(proof) => proof,
|
|
||||||
Err(error) => {
|
|
||||||
trace!(
|
|
||||||
"Remote changes proof request from {} for key {} ({:?}..{:?}) failed with: {}.",
|
|
||||||
peer,
|
|
||||||
format!("{} : {}", HexDisplay::from(&request.storage_key), HexDisplay::from(&key.0)),
|
|
||||||
request.first,
|
|
||||||
request.last,
|
|
||||||
error,
|
|
||||||
);
|
|
||||||
|
|
||||||
light::ChangesProof::<B::Header> {
|
|
||||||
max_block: Zero::zero(),
|
|
||||||
proof: Vec::new(),
|
|
||||||
roots: BTreeMap::new(),
|
|
||||||
roots_proof: StorageProof::empty(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let response = {
|
|
||||||
let r = schema::v1::light::RemoteChangesResponse {
|
|
||||||
max: proof.max_block.encode(),
|
|
||||||
proof: proof.proof,
|
|
||||||
roots: proof
|
|
||||||
.roots
|
|
||||||
.into_iter()
|
|
||||||
.map(|(k, v)| schema::v1::light::Pair { fst: k.encode(), snd: v.encode() })
|
|
||||||
.collect(),
|
|
||||||
roots_proof: proof.roots_proof.encode(),
|
|
||||||
};
|
|
||||||
schema::v1::light::response::Response::RemoteChangesResponse(r)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(schema::v1::light::Response { response: Some(response) })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(derive_more::Display, derive_more::From)]
|
#[derive(derive_more::Display, derive_more::From)]
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,241 +0,0 @@
|
|||||||
// This file is part of Substrate.
|
|
||||||
|
|
||||||
// Copyright (C) 2017-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! On-demand requests service.
|
|
||||||
|
|
||||||
use crate::light_client_requests;
|
|
||||||
|
|
||||||
use futures::{channel::oneshot, prelude::*};
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use sc_client_api::{
|
|
||||||
ChangesProof, FetchChecker, Fetcher, RemoteBodyRequest, RemoteCallRequest,
|
|
||||||
RemoteChangesRequest, RemoteHeaderRequest, RemoteReadChildRequest, RemoteReadRequest,
|
|
||||||
StorageProof,
|
|
||||||
};
|
|
||||||
use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender};
|
|
||||||
use sp_blockchain::Error as ClientError;
|
|
||||||
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor};
|
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
pin::Pin,
|
|
||||||
sync::Arc,
|
|
||||||
task::{Context, Poll},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Implements the `Fetcher` trait of the client. Makes it possible for the light client to perform
|
|
||||||
/// network requests for some state.
|
|
||||||
///
|
|
||||||
/// This implementation stores all the requests in a queue. The network, in parallel, is then
|
|
||||||
/// responsible for pulling elements out of that queue and fulfilling them.
|
|
||||||
pub struct OnDemand<B: BlockT> {
|
|
||||||
/// Objects that checks whether what has been retrieved is correct.
|
|
||||||
checker: Arc<dyn FetchChecker<B>>,
|
|
||||||
|
|
||||||
/// Queue of requests. Set to `Some` at initialization, then extracted by the network.
|
|
||||||
///
|
|
||||||
/// Note that a better alternative would be to use a MPMC queue here, and add a `poll` method
|
|
||||||
/// from the `OnDemand`. However there exists no popular implementation of MPMC channels in
|
|
||||||
/// asynchronous Rust at the moment
|
|
||||||
requests_queue:
|
|
||||||
Mutex<Option<TracingUnboundedReceiver<light_client_requests::sender::Request<B>>>>,
|
|
||||||
|
|
||||||
/// Sending side of `requests_queue`.
|
|
||||||
requests_send: TracingUnboundedSender<light_client_requests::sender::Request<B>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
|
||||||
#[error("AlwaysBadChecker")]
|
|
||||||
struct ErrorAlwaysBadChecker;
|
|
||||||
|
|
||||||
impl Into<ClientError> for ErrorAlwaysBadChecker {
|
|
||||||
fn into(self) -> ClientError {
|
|
||||||
ClientError::Application(Box::new(self))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dummy implementation of `FetchChecker` that always assumes that responses are bad.
|
|
||||||
///
|
|
||||||
/// Considering that it is the responsibility of the client to build the fetcher, it can use this
|
|
||||||
/// implementation if it knows that it will never perform any request.
|
|
||||||
#[derive(Default, Clone)]
|
|
||||||
pub struct AlwaysBadChecker;
|
|
||||||
|
|
||||||
impl<Block: BlockT> FetchChecker<Block> for AlwaysBadChecker {
|
|
||||||
fn check_header_proof(
|
|
||||||
&self,
|
|
||||||
_request: &RemoteHeaderRequest<Block::Header>,
|
|
||||||
_remote_header: Option<Block::Header>,
|
|
||||||
_remote_proof: StorageProof,
|
|
||||||
) -> Result<Block::Header, ClientError> {
|
|
||||||
Err(ErrorAlwaysBadChecker.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_read_proof(
|
|
||||||
&self,
|
|
||||||
_request: &RemoteReadRequest<Block::Header>,
|
|
||||||
_remote_proof: StorageProof,
|
|
||||||
) -> Result<HashMap<Vec<u8>, Option<Vec<u8>>>, ClientError> {
|
|
||||||
Err(ErrorAlwaysBadChecker.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_read_child_proof(
|
|
||||||
&self,
|
|
||||||
_request: &RemoteReadChildRequest<Block::Header>,
|
|
||||||
_remote_proof: StorageProof,
|
|
||||||
) -> Result<HashMap<Vec<u8>, Option<Vec<u8>>>, ClientError> {
|
|
||||||
Err(ErrorAlwaysBadChecker.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_execution_proof(
|
|
||||||
&self,
|
|
||||||
_request: &RemoteCallRequest<Block::Header>,
|
|
||||||
_remote_proof: StorageProof,
|
|
||||||
) -> Result<Vec<u8>, ClientError> {
|
|
||||||
Err(ErrorAlwaysBadChecker.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_changes_proof(
|
|
||||||
&self,
|
|
||||||
_request: &RemoteChangesRequest<Block::Header>,
|
|
||||||
_remote_proof: ChangesProof<Block::Header>,
|
|
||||||
) -> Result<Vec<(NumberFor<Block>, u32)>, ClientError> {
|
|
||||||
Err(ErrorAlwaysBadChecker.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_body_proof(
|
|
||||||
&self,
|
|
||||||
_request: &RemoteBodyRequest<Block::Header>,
|
|
||||||
_body: Vec<Block::Extrinsic>,
|
|
||||||
) -> Result<Vec<Block::Extrinsic>, ClientError> {
|
|
||||||
Err(ErrorAlwaysBadChecker.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B: BlockT> OnDemand<B>
|
|
||||||
where
|
|
||||||
B::Header: HeaderT,
|
|
||||||
{
|
|
||||||
/// Creates new on-demand service.
|
|
||||||
pub fn new(checker: Arc<dyn FetchChecker<B>>) -> Self {
|
|
||||||
let (requests_send, requests_queue) = tracing_unbounded("mpsc_ondemand");
|
|
||||||
let requests_queue = Mutex::new(Some(requests_queue));
|
|
||||||
|
|
||||||
Self { checker, requests_queue, requests_send }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get checker reference.
|
|
||||||
pub fn checker(&self) -> &Arc<dyn FetchChecker<B>> {
|
|
||||||
&self.checker
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extracts the queue of requests.
|
|
||||||
///
|
|
||||||
/// Whenever one of the methods of the `Fetcher` trait is called, an element is pushed on this
|
|
||||||
/// channel.
|
|
||||||
///
|
|
||||||
/// If this function returns `None`, that means that the receiver has already been extracted in
|
|
||||||
/// the past, and therefore that something already handles the requests.
|
|
||||||
pub(crate) fn extract_receiver(
|
|
||||||
&self,
|
|
||||||
) -> Option<TracingUnboundedReceiver<light_client_requests::sender::Request<B>>> {
|
|
||||||
self.requests_queue.lock().take()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B> Fetcher<B> for OnDemand<B>
|
|
||||||
where
|
|
||||||
B: BlockT,
|
|
||||||
B::Header: HeaderT,
|
|
||||||
{
|
|
||||||
type RemoteHeaderResult = RemoteResponse<B::Header>;
|
|
||||||
type RemoteReadResult = RemoteResponse<HashMap<Vec<u8>, Option<Vec<u8>>>>;
|
|
||||||
type RemoteCallResult = RemoteResponse<Vec<u8>>;
|
|
||||||
type RemoteChangesResult = RemoteResponse<Vec<(NumberFor<B>, u32)>>;
|
|
||||||
type RemoteBodyResult = RemoteResponse<Vec<B::Extrinsic>>;
|
|
||||||
|
|
||||||
fn remote_header(&self, request: RemoteHeaderRequest<B::Header>) -> Self::RemoteHeaderResult {
|
|
||||||
let (sender, receiver) = oneshot::channel();
|
|
||||||
let _ = self
|
|
||||||
.requests_send
|
|
||||||
.unbounded_send(light_client_requests::sender::Request::Header { request, sender });
|
|
||||||
RemoteResponse { receiver }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remote_read(&self, request: RemoteReadRequest<B::Header>) -> Self::RemoteReadResult {
|
|
||||||
let (sender, receiver) = oneshot::channel();
|
|
||||||
let _ = self
|
|
||||||
.requests_send
|
|
||||||
.unbounded_send(light_client_requests::sender::Request::Read { request, sender });
|
|
||||||
RemoteResponse { receiver }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remote_read_child(
|
|
||||||
&self,
|
|
||||||
request: RemoteReadChildRequest<B::Header>,
|
|
||||||
) -> Self::RemoteReadResult {
|
|
||||||
let (sender, receiver) = oneshot::channel();
|
|
||||||
let _ = self
|
|
||||||
.requests_send
|
|
||||||
.unbounded_send(light_client_requests::sender::Request::ReadChild { request, sender });
|
|
||||||
RemoteResponse { receiver }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remote_call(&self, request: RemoteCallRequest<B::Header>) -> Self::RemoteCallResult {
|
|
||||||
let (sender, receiver) = oneshot::channel();
|
|
||||||
let _ = self
|
|
||||||
.requests_send
|
|
||||||
.unbounded_send(light_client_requests::sender::Request::Call { request, sender });
|
|
||||||
RemoteResponse { receiver }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remote_changes(
|
|
||||||
&self,
|
|
||||||
request: RemoteChangesRequest<B::Header>,
|
|
||||||
) -> Self::RemoteChangesResult {
|
|
||||||
let (sender, receiver) = oneshot::channel();
|
|
||||||
let _ = self
|
|
||||||
.requests_send
|
|
||||||
.unbounded_send(light_client_requests::sender::Request::Changes { request, sender });
|
|
||||||
RemoteResponse { receiver }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remote_body(&self, request: RemoteBodyRequest<B::Header>) -> Self::RemoteBodyResult {
|
|
||||||
let (sender, receiver) = oneshot::channel();
|
|
||||||
let _ = self
|
|
||||||
.requests_send
|
|
||||||
.unbounded_send(light_client_requests::sender::Request::Body { request, sender });
|
|
||||||
RemoteResponse { receiver }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Future for an on-demand remote call response.
|
|
||||||
pub struct RemoteResponse<T> {
|
|
||||||
receiver: oneshot::Receiver<Result<T, ClientError>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Future for RemoteResponse<T> {
|
|
||||||
type Output = Result<T, ClientError>;
|
|
||||||
|
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
|
||||||
match self.receiver.poll_unpin(cx) {
|
|
||||||
Poll::Ready(Ok(res)) => Poll::Ready(res),
|
|
||||||
Poll::Ready(Err(_)) => Poll::Ready(Err(ClientError::RemoteFetchCancelled)),
|
|
||||||
Poll::Pending => Poll::Pending,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -33,11 +33,9 @@ use crate::{
|
|||||||
config::{parse_str_addr, Params, TransportConfig},
|
config::{parse_str_addr, Params, TransportConfig},
|
||||||
discovery::DiscoveryConfig,
|
discovery::DiscoveryConfig,
|
||||||
error::Error,
|
error::Error,
|
||||||
light_client_requests,
|
|
||||||
network_state::{
|
network_state::{
|
||||||
NetworkState, NotConnectedPeer as NetworkStateNotConnectedPeer, Peer as NetworkStatePeer,
|
NetworkState, NotConnectedPeer as NetworkStateNotConnectedPeer, Peer as NetworkStatePeer,
|
||||||
},
|
},
|
||||||
on_demand_layer::AlwaysBadChecker,
|
|
||||||
protocol::{
|
protocol::{
|
||||||
self,
|
self,
|
||||||
event::Event,
|
event::Event,
|
||||||
@@ -238,12 +236,6 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkWorker<B, H> {
|
|||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let checker = params
|
|
||||||
.on_demand
|
|
||||||
.as_ref()
|
|
||||||
.map(|od| od.checker().clone())
|
|
||||||
.unwrap_or_else(|| Arc::new(AlwaysBadChecker));
|
|
||||||
|
|
||||||
let num_connected = Arc::new(AtomicUsize::new(0));
|
let num_connected = Arc::new(AtomicUsize::new(0));
|
||||||
let is_major_syncing = Arc::new(AtomicBool::new(false));
|
let is_major_syncing = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
@@ -255,14 +247,6 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkWorker<B, H> {
|
|||||||
params.network_config.client_version, params.network_config.node_name
|
params.network_config.client_version, params.network_config.node_name
|
||||||
);
|
);
|
||||||
|
|
||||||
let light_client_request_sender = {
|
|
||||||
light_client_requests::sender::LightClientRequestSender::new(
|
|
||||||
¶ms.protocol_id,
|
|
||||||
checker,
|
|
||||||
peerset_handle.clone(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let discovery_config = {
|
let discovery_config = {
|
||||||
let mut config = DiscoveryConfig::new(local_public.clone());
|
let mut config = DiscoveryConfig::new(local_public.clone());
|
||||||
config.with_permanent_addresses(known_addresses);
|
config.with_permanent_addresses(known_addresses);
|
||||||
@@ -347,7 +331,6 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkWorker<B, H> {
|
|||||||
protocol,
|
protocol,
|
||||||
user_agent,
|
user_agent,
|
||||||
local_public,
|
local_public,
|
||||||
light_client_request_sender,
|
|
||||||
discovery_config,
|
discovery_config,
|
||||||
params.block_request_protocol_config,
|
params.block_request_protocol_config,
|
||||||
params.state_request_protocol_config,
|
params.state_request_protocol_config,
|
||||||
@@ -447,7 +430,6 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkWorker<B, H> {
|
|||||||
service,
|
service,
|
||||||
import_queue: params.import_queue,
|
import_queue: params.import_queue,
|
||||||
from_service,
|
from_service,
|
||||||
light_client_rqs: params.on_demand.and_then(|od| od.extract_receiver()),
|
|
||||||
event_streams: out_events::OutChannels::new(params.metrics_registry.as_ref())?,
|
event_streams: out_events::OutChannels::new(params.metrics_registry.as_ref())?,
|
||||||
peers_notifications_sinks,
|
peers_notifications_sinks,
|
||||||
tx_handler_controller,
|
tx_handler_controller,
|
||||||
@@ -1464,8 +1446,6 @@ pub struct NetworkWorker<B: BlockT + 'static, H: ExHashT> {
|
|||||||
import_queue: Box<dyn ImportQueue<B>>,
|
import_queue: Box<dyn ImportQueue<B>>,
|
||||||
/// Messages from the [`NetworkService`] that must be processed.
|
/// Messages from the [`NetworkService`] that must be processed.
|
||||||
from_service: TracingUnboundedReceiver<ServiceToWorkerMsg<B, H>>,
|
from_service: TracingUnboundedReceiver<ServiceToWorkerMsg<B, H>>,
|
||||||
/// Receiver for queries from the light client that must be processed.
|
|
||||||
light_client_rqs: Option<TracingUnboundedReceiver<light_client_requests::sender::Request<B>>>,
|
|
||||||
/// Senders for events that happen on the network.
|
/// Senders for events that happen on the network.
|
||||||
event_streams: out_events::OutChannels,
|
event_streams: out_events::OutChannels,
|
||||||
/// Prometheus network metrics.
|
/// Prometheus network metrics.
|
||||||
@@ -1489,23 +1469,6 @@ impl<B: BlockT + 'static, H: ExHashT> Future for NetworkWorker<B, H> {
|
|||||||
this.import_queue
|
this.import_queue
|
||||||
.poll_actions(cx, &mut NetworkLink { protocol: &mut this.network_service });
|
.poll_actions(cx, &mut NetworkLink { protocol: &mut this.network_service });
|
||||||
|
|
||||||
// Check for new incoming light client requests.
|
|
||||||
if let Some(light_client_rqs) = this.light_client_rqs.as_mut() {
|
|
||||||
while let Poll::Ready(Some(rq)) = light_client_rqs.poll_next_unpin(cx) {
|
|
||||||
let result = this.network_service.behaviour_mut().light_client_request(rq);
|
|
||||||
match result {
|
|
||||||
Ok(()) => {},
|
|
||||||
Err(light_client_requests::sender::SendRequestError::TooManyRequests) => {
|
|
||||||
warn!("Couldn't start light client request: too many pending requests");
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(metrics) = this.metrics.as_ref() {
|
|
||||||
metrics.issued_light_requests.inc();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// At the time of writing of this comment, due to a high volume of messages, the network
|
// At the time of writing of this comment, due to a high volume of messages, the network
|
||||||
// worker sometimes takes a long time to process the loop below. When that happens, the
|
// worker sometimes takes a long time to process the loop below. When that happens, the
|
||||||
// rest of the polling is frozen. In order to avoid negative side-effects caused by this
|
// rest of the polling is frozen. In order to avoid negative side-effects caused by this
|
||||||
|
|||||||
@@ -116,7 +116,6 @@ fn build_test_full_node(
|
|||||||
}),
|
}),
|
||||||
network_config: config,
|
network_config: config,
|
||||||
chain: client.clone(),
|
chain: client.clone(),
|
||||||
on_demand: None,
|
|
||||||
transaction_pool: Arc::new(crate::config::EmptyTransactionPool),
|
transaction_pool: Arc::new(crate::config::EmptyTransactionPool),
|
||||||
protocol_id,
|
protocol_id,
|
||||||
import_queue,
|
import_queue,
|
||||||
|
|||||||
@@ -48,13 +48,13 @@ use sc_consensus::{
|
|||||||
};
|
};
|
||||||
pub use sc_network::config::EmptyTransactionPool;
|
pub use sc_network::config::EmptyTransactionPool;
|
||||||
use sc_network::{
|
use sc_network::{
|
||||||
block_request_handler::{self, BlockRequestHandler},
|
block_request_handler::BlockRequestHandler,
|
||||||
config::{
|
config::{
|
||||||
MultiaddrWithPeerId, NetworkConfiguration, NonDefaultSetConfig, NonReservedPeerMode,
|
MultiaddrWithPeerId, NetworkConfiguration, NonDefaultSetConfig, NonReservedPeerMode,
|
||||||
ProtocolConfig, ProtocolId, Role, SyncMode, TransportConfig,
|
ProtocolConfig, ProtocolId, Role, SyncMode, TransportConfig,
|
||||||
},
|
},
|
||||||
light_client_requests::{self, handler::LightClientRequestHandler},
|
light_client_requests::handler::LightClientRequestHandler,
|
||||||
state_request_handler::{self, StateRequestHandler},
|
state_request_handler::StateRequestHandler,
|
||||||
warp_request_handler, Multiaddr, NetworkService, NetworkWorker,
|
warp_request_handler, Multiaddr, NetworkService, NetworkWorker,
|
||||||
};
|
};
|
||||||
use sc_service::client::Client;
|
use sc_service::client::Client;
|
||||||
@@ -133,25 +133,20 @@ pub type PeersFullClient = Client<
|
|||||||
Block,
|
Block,
|
||||||
substrate_test_runtime_client::runtime::RuntimeApi,
|
substrate_test_runtime_client::runtime::RuntimeApi,
|
||||||
>;
|
>;
|
||||||
pub type PeersLightClient = Client<
|
|
||||||
substrate_test_runtime_client::LightBackend,
|
|
||||||
substrate_test_runtime_client::LightExecutor,
|
|
||||||
Block,
|
|
||||||
substrate_test_runtime_client::runtime::RuntimeApi,
|
|
||||||
>;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum PeersClient {
|
pub struct PeersClient {
|
||||||
Full(Arc<PeersFullClient>, Arc<substrate_test_runtime_client::Backend>),
|
client: Arc<PeersFullClient>,
|
||||||
Light(Arc<PeersLightClient>, Arc<substrate_test_runtime_client::LightBackend>),
|
backend: Arc<substrate_test_runtime_client::Backend>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PeersClient {
|
impl PeersClient {
|
||||||
pub fn as_full(&self) -> Option<Arc<PeersFullClient>> {
|
pub fn as_client(&self) -> Arc<PeersFullClient> {
|
||||||
match *self {
|
self.client.clone()
|
||||||
PeersClient::Full(ref client, _) => Some(client.clone()),
|
}
|
||||||
_ => None,
|
|
||||||
}
|
pub fn as_backend(&self) -> Arc<substrate_test_runtime_client::Backend> {
|
||||||
|
self.backend.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_block_import(&self) -> BlockImportAdapter<Self> {
|
pub fn as_block_import(&self) -> BlockImportAdapter<Self> {
|
||||||
@@ -159,27 +154,18 @@ impl PeersClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_aux(&self, key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
|
pub fn get_aux(&self, key: &[u8]) -> ClientResult<Option<Vec<u8>>> {
|
||||||
match *self {
|
self.client.get_aux(key)
|
||||||
PeersClient::Full(ref client, _) => client.get_aux(key),
|
|
||||||
PeersClient::Light(ref client, _) => client.get_aux(key),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn info(&self) -> BlockchainInfo<Block> {
|
pub fn info(&self) -> BlockchainInfo<Block> {
|
||||||
match *self {
|
self.client.info()
|
||||||
PeersClient::Full(ref client, _) => client.chain_info(),
|
|
||||||
PeersClient::Light(ref client, _) => client.chain_info(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn header(
|
pub fn header(
|
||||||
&self,
|
&self,
|
||||||
block: &BlockId<Block>,
|
block: &BlockId<Block>,
|
||||||
) -> ClientResult<Option<<Block as BlockT>::Header>> {
|
) -> ClientResult<Option<<Block as BlockT>::Header>> {
|
||||||
match *self {
|
self.client.header(block)
|
||||||
PeersClient::Full(ref client, _) => client.header(block),
|
|
||||||
PeersClient::Light(ref client, _) => client.header(block),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_state_at(&self, block: &BlockId<Block>) -> bool {
|
pub fn has_state_at(&self, block: &BlockId<Block>) -> bool {
|
||||||
@@ -187,33 +173,19 @@ impl PeersClient {
|
|||||||
Some(header) => header,
|
Some(header) => header,
|
||||||
None => return false,
|
None => return false,
|
||||||
};
|
};
|
||||||
match self {
|
self.backend.have_state_at(&header.hash(), *header.number())
|
||||||
PeersClient::Full(_client, backend) =>
|
|
||||||
backend.have_state_at(&header.hash(), *header.number()),
|
|
||||||
PeersClient::Light(_client, backend) =>
|
|
||||||
backend.have_state_at(&header.hash(), *header.number()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn justifications(&self, block: &BlockId<Block>) -> ClientResult<Option<Justifications>> {
|
pub fn justifications(&self, block: &BlockId<Block>) -> ClientResult<Option<Justifications>> {
|
||||||
match *self {
|
self.client.justifications(block)
|
||||||
PeersClient::Full(ref client, _) => client.justifications(block),
|
|
||||||
PeersClient::Light(ref client, _) => client.justifications(block),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finality_notification_stream(&self) -> FinalityNotifications<Block> {
|
pub fn finality_notification_stream(&self) -> FinalityNotifications<Block> {
|
||||||
match *self {
|
self.client.finality_notification_stream()
|
||||||
PeersClient::Full(ref client, _) => client.finality_notification_stream(),
|
|
||||||
PeersClient::Light(ref client, _) => client.finality_notification_stream(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn import_notification_stream(&self) -> ImportNotifications<Block> {
|
pub fn import_notification_stream(&self) -> ImportNotifications<Block> {
|
||||||
match *self {
|
self.client.import_notification_stream()
|
||||||
PeersClient::Full(ref client, _) => client.import_notification_stream(),
|
|
||||||
PeersClient::Light(ref client, _) => client.import_notification_stream(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finalize_block(
|
pub fn finalize_block(
|
||||||
@@ -222,12 +194,7 @@ impl PeersClient {
|
|||||||
justification: Option<Justification>,
|
justification: Option<Justification>,
|
||||||
notify: bool,
|
notify: bool,
|
||||||
) -> ClientResult<()> {
|
) -> ClientResult<()> {
|
||||||
match *self {
|
self.client.finalize_block(id, justification, notify)
|
||||||
PeersClient::Full(ref client, ref _backend) =>
|
|
||||||
client.finalize_block(id, justification, notify),
|
|
||||||
PeersClient::Light(ref client, ref _backend) =>
|
|
||||||
client.finalize_block(id, justification, notify),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,10 +207,7 @@ impl BlockImport<Block> for PeersClient {
|
|||||||
&mut self,
|
&mut self,
|
||||||
block: BlockCheckParams<Block>,
|
block: BlockCheckParams<Block>,
|
||||||
) -> Result<ImportResult, Self::Error> {
|
) -> Result<ImportResult, Self::Error> {
|
||||||
match self {
|
self.client.check_block(block).await
|
||||||
PeersClient::Full(client, _) => client.check_block(block).await,
|
|
||||||
PeersClient::Light(client, _) => client.check_block(block).await,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn import_block(
|
async fn import_block(
|
||||||
@@ -251,12 +215,7 @@ impl BlockImport<Block> for PeersClient {
|
|||||||
block: BlockImportParams<Block, ()>,
|
block: BlockImportParams<Block, ()>,
|
||||||
cache: HashMap<well_known_cache_keys::Id, Vec<u8>>,
|
cache: HashMap<well_known_cache_keys::Id, Vec<u8>>,
|
||||||
) -> Result<ImportResult, Self::Error> {
|
) -> Result<ImportResult, Self::Error> {
|
||||||
match self {
|
self.client.import_block(block.clear_storage_changes_and_mutate(), cache).await
|
||||||
PeersClient::Full(client, _) =>
|
|
||||||
client.import_block(block.clear_storage_changes_and_mutate(), cache).await,
|
|
||||||
PeersClient::Light(client, _) =>
|
|
||||||
client.import_block(block.clear_storage_changes_and_mutate(), cache).await,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,8 +329,7 @@ where
|
|||||||
BlockBuilder<Block, PeersFullClient, substrate_test_runtime_client::Backend>,
|
BlockBuilder<Block, PeersFullClient, substrate_test_runtime_client::Backend>,
|
||||||
) -> Block,
|
) -> Block,
|
||||||
{
|
{
|
||||||
let full_client =
|
let full_client = self.client.as_client();
|
||||||
self.client.as_full().expect("blocks could only be generated by full clients");
|
|
||||||
let mut at = full_client.header(&at).unwrap().unwrap().hash();
|
let mut at = full_client.header(&at).unwrap().unwrap().hash();
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
let builder =
|
let builder =
|
||||||
@@ -779,11 +737,11 @@ where
|
|||||||
let (c, longest_chain) = test_client_builder.build_with_longest_chain();
|
let (c, longest_chain) = test_client_builder.build_with_longest_chain();
|
||||||
let client = Arc::new(c);
|
let client = Arc::new(c);
|
||||||
|
|
||||||
let (block_import, justification_import, data) =
|
let (block_import, justification_import, data) = self
|
||||||
self.make_block_import(PeersClient::Full(client.clone(), backend.clone()));
|
.make_block_import(PeersClient { client: client.clone(), backend: backend.clone() });
|
||||||
|
|
||||||
let verifier = self.make_verifier(
|
let verifier = self.make_verifier(
|
||||||
PeersClient::Full(client.clone(), backend.clone()),
|
PeersClient { client: client.clone(), backend: backend.clone() },
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
&data,
|
&data,
|
||||||
);
|
);
|
||||||
@@ -868,7 +826,6 @@ where
|
|||||||
}),
|
}),
|
||||||
network_config,
|
network_config,
|
||||||
chain: client.clone(),
|
chain: client.clone(),
|
||||||
on_demand: None,
|
|
||||||
transaction_pool: Arc::new(EmptyTransactionPool),
|
transaction_pool: Arc::new(EmptyTransactionPool),
|
||||||
protocol_id,
|
protocol_id,
|
||||||
import_queue,
|
import_queue,
|
||||||
@@ -899,7 +856,7 @@ where
|
|||||||
|
|
||||||
peers.push(Peer {
|
peers.push(Peer {
|
||||||
data,
|
data,
|
||||||
client: PeersClient::Full(client.clone(), backend.clone()),
|
client: PeersClient { client: client.clone(), backend: backend.clone() },
|
||||||
select_chain: Some(longest_chain),
|
select_chain: Some(longest_chain),
|
||||||
backend: Some(backend),
|
backend: Some(backend),
|
||||||
imported_blocks_stream,
|
imported_blocks_stream,
|
||||||
@@ -912,94 +869,6 @@ where
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a light peer.
|
|
||||||
fn add_light_peer(&mut self) {
|
|
||||||
let (c, backend) = substrate_test_runtime_client::new_light();
|
|
||||||
let client = Arc::new(c);
|
|
||||||
let (block_import, justification_import, data) =
|
|
||||||
self.make_block_import(PeersClient::Light(client.clone(), backend.clone()));
|
|
||||||
|
|
||||||
let verifier = self.make_verifier(
|
|
||||||
PeersClient::Light(client.clone(), backend.clone()),
|
|
||||||
&Default::default(),
|
|
||||||
&data,
|
|
||||||
);
|
|
||||||
let verifier = VerifierAdapter::new(verifier);
|
|
||||||
|
|
||||||
let import_queue = Box::new(BasicQueue::new(
|
|
||||||
verifier.clone(),
|
|
||||||
Box::new(block_import.clone()),
|
|
||||||
justification_import,
|
|
||||||
&sp_core::testing::TaskExecutor::new(),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
|
|
||||||
let listen_addr = build_multiaddr![Memory(rand::random::<u64>())];
|
|
||||||
|
|
||||||
let mut network_config =
|
|
||||||
NetworkConfiguration::new("test-node", "test-client", Default::default(), None);
|
|
||||||
network_config.transport = TransportConfig::MemoryOnly;
|
|
||||||
network_config.listen_addresses = vec![listen_addr.clone()];
|
|
||||||
network_config.allow_non_globals_in_dht = true;
|
|
||||||
|
|
||||||
let protocol_id = ProtocolId::from("test-protocol-name");
|
|
||||||
|
|
||||||
let block_request_protocol_config =
|
|
||||||
block_request_handler::generate_protocol_config(&protocol_id);
|
|
||||||
let state_request_protocol_config =
|
|
||||||
state_request_handler::generate_protocol_config(&protocol_id);
|
|
||||||
|
|
||||||
let light_client_request_protocol_config =
|
|
||||||
light_client_requests::generate_protocol_config(&protocol_id);
|
|
||||||
|
|
||||||
let network = NetworkWorker::new(sc_network::config::Params {
|
|
||||||
role: Role::Light,
|
|
||||||
executor: None,
|
|
||||||
transactions_handler_executor: Box::new(|task| {
|
|
||||||
async_std::task::spawn(task);
|
|
||||||
}),
|
|
||||||
network_config,
|
|
||||||
chain: client.clone(),
|
|
||||||
on_demand: None,
|
|
||||||
transaction_pool: Arc::new(EmptyTransactionPool),
|
|
||||||
protocol_id,
|
|
||||||
import_queue,
|
|
||||||
block_announce_validator: Box::new(DefaultBlockAnnounceValidator),
|
|
||||||
metrics_registry: None,
|
|
||||||
block_request_protocol_config,
|
|
||||||
state_request_protocol_config,
|
|
||||||
light_client_request_protocol_config,
|
|
||||||
warp_sync: None,
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
self.mut_peers(|peers| {
|
|
||||||
for peer in peers.iter_mut() {
|
|
||||||
peer.network.add_known_address(
|
|
||||||
network.service().local_peer_id().clone(),
|
|
||||||
listen_addr.clone(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let imported_blocks_stream = Box::pin(client.import_notification_stream().fuse());
|
|
||||||
let finality_notification_stream =
|
|
||||||
Box::pin(client.finality_notification_stream().fuse());
|
|
||||||
|
|
||||||
peers.push(Peer {
|
|
||||||
data,
|
|
||||||
verifier,
|
|
||||||
select_chain: None,
|
|
||||||
backend: None,
|
|
||||||
block_import,
|
|
||||||
client: PeersClient::Light(client, backend),
|
|
||||||
imported_blocks_stream,
|
|
||||||
finality_notification_stream,
|
|
||||||
network,
|
|
||||||
listen_addr,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Used to spawn background tasks, e.g. the block request protocol handler.
|
/// Used to spawn background tasks, e.g. the block request protocol handler.
|
||||||
fn spawn_task(&self, f: BoxFuture<'static, ()>) {
|
fn spawn_task(&self, f: BoxFuture<'static, ()>) {
|
||||||
async_std::task::spawn(f);
|
async_std::task::spawn(f);
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ use super::*;
|
|||||||
use futures::{executor::block_on, Future};
|
use futures::{executor::block_on, Future};
|
||||||
use sp_consensus::{block_validation::Validation, BlockOrigin};
|
use sp_consensus::{block_validation::Validation, BlockOrigin};
|
||||||
use sp_runtime::Justifications;
|
use sp_runtime::Justifications;
|
||||||
use std::time::Duration;
|
|
||||||
use substrate_test_runtime::Header;
|
use substrate_test_runtime::Header;
|
||||||
|
|
||||||
fn test_ancestor_search_when_common_is(n: usize) {
|
fn test_ancestor_search_when_common_is(n: usize) {
|
||||||
@@ -391,35 +390,6 @@ fn own_blocks_are_announced() {
|
|||||||
(net.peers()[2].blockchain_canon_equals(peer0));
|
(net.peers()[2].blockchain_canon_equals(peer0));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn blocks_are_not_announced_by_light_nodes() {
|
|
||||||
sp_tracing::try_init_simple();
|
|
||||||
let mut net = TestNet::new(0);
|
|
||||||
|
|
||||||
// full peer0 is connected to light peer
|
|
||||||
// light peer1 is connected to full peer2
|
|
||||||
net.add_full_peer();
|
|
||||||
net.add_light_peer();
|
|
||||||
|
|
||||||
// Sync between 0 and 1.
|
|
||||||
net.peer(0).push_blocks(1, false);
|
|
||||||
assert_eq!(net.peer(0).client.info().best_number, 1);
|
|
||||||
net.block_until_sync();
|
|
||||||
assert_eq!(net.peer(1).client.info().best_number, 1);
|
|
||||||
|
|
||||||
// Add another node and remove node 0.
|
|
||||||
net.add_full_peer();
|
|
||||||
net.peers.remove(0);
|
|
||||||
|
|
||||||
// Poll for a few seconds and make sure 1 and 2 (now 0 and 1) don't sync together.
|
|
||||||
let mut delay = futures_timer::Delay::new(Duration::from_secs(5));
|
|
||||||
block_on(futures::future::poll_fn::<(), _>(|cx| {
|
|
||||||
net.poll(cx);
|
|
||||||
Pin::new(&mut delay).poll(cx)
|
|
||||||
}));
|
|
||||||
assert_eq!(net.peer(1).client.info().best_number, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_sync_small_non_best_forks() {
|
fn can_sync_small_non_best_forks() {
|
||||||
sp_tracing::try_init_simple();
|
sp_tracing::try_init_simple();
|
||||||
@@ -483,72 +453,6 @@ fn can_sync_small_non_best_forks() {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn can_not_sync_from_light_peer() {
|
|
||||||
sp_tracing::try_init_simple();
|
|
||||||
|
|
||||||
// given the network with 1 full nodes (#0) and 1 light node (#1)
|
|
||||||
let mut net = TestNet::new(1);
|
|
||||||
net.add_light_peer();
|
|
||||||
|
|
||||||
// generate some blocks on #0
|
|
||||||
net.peer(0).push_blocks(1, false);
|
|
||||||
|
|
||||||
// and let the light client sync from this node
|
|
||||||
net.block_until_sync();
|
|
||||||
|
|
||||||
// ensure #0 && #1 have the same best block
|
|
||||||
let full0_info = net.peer(0).client.info();
|
|
||||||
let light_info = net.peer(1).client.info();
|
|
||||||
assert_eq!(full0_info.best_number, 1);
|
|
||||||
assert_eq!(light_info.best_number, 1);
|
|
||||||
assert_eq!(light_info.best_hash, full0_info.best_hash);
|
|
||||||
|
|
||||||
// add new full client (#2) && remove #0
|
|
||||||
net.add_full_peer();
|
|
||||||
net.peers.remove(0);
|
|
||||||
|
|
||||||
// ensure that the #2 (now #1) fails to sync block #1 even after 5 seconds
|
|
||||||
let mut test_finished = futures_timer::Delay::new(Duration::from_secs(5));
|
|
||||||
block_on(futures::future::poll_fn::<(), _>(|cx| {
|
|
||||||
net.poll(cx);
|
|
||||||
Pin::new(&mut test_finished).poll(cx)
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn light_peer_imports_header_from_announce() {
|
|
||||||
sp_tracing::try_init_simple();
|
|
||||||
|
|
||||||
fn import_with_announce(net: &mut TestNet, hash: H256) {
|
|
||||||
net.peer(0).announce_block(hash, None);
|
|
||||||
|
|
||||||
block_on(futures::future::poll_fn::<(), _>(|cx| {
|
|
||||||
net.poll(cx);
|
|
||||||
if net.peer(1).client().header(&BlockId::Hash(hash)).unwrap().is_some() {
|
|
||||||
Poll::Ready(())
|
|
||||||
} else {
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// given the network with 1 full nodes (#0) and 1 light node (#1)
|
|
||||||
let mut net = TestNet::new(1);
|
|
||||||
net.add_light_peer();
|
|
||||||
|
|
||||||
// let them connect to each other
|
|
||||||
net.block_until_sync();
|
|
||||||
|
|
||||||
// check that NEW block is imported from announce message
|
|
||||||
let new_hash = net.peer(0).push_blocks(1, false);
|
|
||||||
import_with_announce(&mut net, new_hash);
|
|
||||||
|
|
||||||
// check that KNOWN STALE block is imported from announce message
|
|
||||||
let known_stale_hash = net.peer(0).push_blocks_at(BlockId::Number(0), 1, true);
|
|
||||||
import_with_announce(&mut net, known_stale_hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_sync_explicit_forks() {
|
fn can_sync_explicit_forks() {
|
||||||
sp_tracing::try_init_simple();
|
sp_tracing::try_init_simple();
|
||||||
@@ -1210,16 +1114,14 @@ fn syncs_indexed_blocks() {
|
|||||||
assert!(net
|
assert!(net
|
||||||
.peer(0)
|
.peer(0)
|
||||||
.client()
|
.client()
|
||||||
.as_full()
|
.as_client()
|
||||||
.unwrap()
|
|
||||||
.indexed_transaction(&indexed_key)
|
.indexed_transaction(&indexed_key)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.is_some());
|
.is_some());
|
||||||
assert!(net
|
assert!(net
|
||||||
.peer(1)
|
.peer(1)
|
||||||
.client()
|
.client()
|
||||||
.as_full()
|
.as_client()
|
||||||
.unwrap()
|
|
||||||
.indexed_transaction(&indexed_key)
|
.indexed_transaction(&indexed_key)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.is_none());
|
.is_none());
|
||||||
@@ -1228,8 +1130,7 @@ fn syncs_indexed_blocks() {
|
|||||||
assert!(net
|
assert!(net
|
||||||
.peer(1)
|
.peer(1)
|
||||||
.client()
|
.client()
|
||||||
.as_full()
|
.as_client()
|
||||||
.unwrap()
|
|
||||||
.indexed_transaction(&indexed_key)
|
.indexed_transaction(&indexed_key)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.is_some());
|
.is_some());
|
||||||
|
|||||||
@@ -1,114 +0,0 @@
|
|||||||
// This file is part of Substrate.
|
|
||||||
|
|
||||||
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
|
||||||
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Blockchain API backend for light nodes.
|
|
||||||
|
|
||||||
use futures::{future::ready, FutureExt, TryFutureExt};
|
|
||||||
use jsonrpc_pubsub::manager::SubscriptionManager;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use sc_client_api::light::{Fetcher, RemoteBlockchain, RemoteBodyRequest};
|
|
||||||
use sp_runtime::{
|
|
||||||
generic::{BlockId, SignedBlock},
|
|
||||||
traits::Block as BlockT,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{client_err, error::FutureResult, ChainBackend};
|
|
||||||
use sc_client_api::BlockchainEvents;
|
|
||||||
use sp_blockchain::HeaderBackend;
|
|
||||||
|
|
||||||
/// Blockchain API backend for light nodes. Reads all the data from local
|
|
||||||
/// database, if available, or fetches it from remote node otherwise.
|
|
||||||
pub struct LightChain<Block: BlockT, Client, F> {
|
|
||||||
/// Substrate client.
|
|
||||||
client: Arc<Client>,
|
|
||||||
/// Current subscriptions.
|
|
||||||
subscriptions: SubscriptionManager,
|
|
||||||
/// Remote blockchain reference
|
|
||||||
remote_blockchain: Arc<dyn RemoteBlockchain<Block>>,
|
|
||||||
/// Remote fetcher reference.
|
|
||||||
fetcher: Arc<F>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Block: BlockT, Client, F: Fetcher<Block>> LightChain<Block, Client, F> {
|
|
||||||
/// Create new Chain API RPC handler.
|
|
||||||
pub fn new(
|
|
||||||
client: Arc<Client>,
|
|
||||||
subscriptions: SubscriptionManager,
|
|
||||||
remote_blockchain: Arc<dyn RemoteBlockchain<Block>>,
|
|
||||||
fetcher: Arc<F>,
|
|
||||||
) -> Self {
|
|
||||||
Self { client, subscriptions, remote_blockchain, fetcher }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Block, Client, F> ChainBackend<Client, Block> for LightChain<Block, Client, F>
|
|
||||||
where
|
|
||||||
Block: BlockT + 'static,
|
|
||||||
Block::Header: Unpin,
|
|
||||||
Client: BlockchainEvents<Block> + HeaderBackend<Block> + Send + Sync + 'static,
|
|
||||||
F: Fetcher<Block> + Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
fn client(&self) -> &Arc<Client> {
|
|
||||||
&self.client
|
|
||||||
}
|
|
||||||
|
|
||||||
fn subscriptions(&self) -> &SubscriptionManager {
|
|
||||||
&self.subscriptions
|
|
||||||
}
|
|
||||||
|
|
||||||
fn header(&self, hash: Option<Block::Hash>) -> FutureResult<Option<Block::Header>> {
|
|
||||||
let hash = self.unwrap_or_best(hash);
|
|
||||||
|
|
||||||
let fetcher = self.fetcher.clone();
|
|
||||||
let maybe_header = sc_client_api::light::future_header(
|
|
||||||
&*self.remote_blockchain,
|
|
||||||
&*fetcher,
|
|
||||||
BlockId::Hash(hash),
|
|
||||||
);
|
|
||||||
|
|
||||||
maybe_header.then(move |result| ready(result.map_err(client_err))).boxed()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn block(&self, hash: Option<Block::Hash>) -> FutureResult<Option<SignedBlock<Block>>> {
|
|
||||||
let fetcher = self.fetcher.clone();
|
|
||||||
self.header(hash)
|
|
||||||
.and_then(move |header| async move {
|
|
||||||
match header {
|
|
||||||
Some(header) => {
|
|
||||||
let body = fetcher
|
|
||||||
.remote_body(RemoteBodyRequest {
|
|
||||||
header: header.clone(),
|
|
||||||
retry_count: Default::default(),
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
body.map(|body| {
|
|
||||||
Some(SignedBlock {
|
|
||||||
block: Block::new(header, body),
|
|
||||||
justifications: None,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(client_err)
|
|
||||||
},
|
|
||||||
None => Ok(None),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.boxed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,7 +19,6 @@
|
|||||||
//! Substrate blockchain API.
|
//! Substrate blockchain API.
|
||||||
|
|
||||||
mod chain_full;
|
mod chain_full;
|
||||||
mod chain_light;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
@@ -33,10 +32,7 @@ use rpc::{
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use jsonrpc_pubsub::{manager::SubscriptionManager, typed::Subscriber, SubscriptionId};
|
use jsonrpc_pubsub::{manager::SubscriptionManager, typed::Subscriber, SubscriptionId};
|
||||||
use sc_client_api::{
|
use sc_client_api::BlockchainEvents;
|
||||||
light::{Fetcher, RemoteBlockchain},
|
|
||||||
BlockchainEvents,
|
|
||||||
};
|
|
||||||
use sp_rpc::{list::ListOrValue, number::NumberOrHex};
|
use sp_rpc::{list::ListOrValue, number::NumberOrHex};
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::{BlockId, SignedBlock},
|
generic::{BlockId, SignedBlock},
|
||||||
@@ -204,29 +200,6 @@ where
|
|||||||
Chain { backend: Box::new(self::chain_full::FullChain::new(client, subscriptions)) }
|
Chain { backend: Box::new(self::chain_full::FullChain::new(client, subscriptions)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create new state API that works on light node.
|
|
||||||
pub fn new_light<Block: BlockT, Client, F: Fetcher<Block>>(
|
|
||||||
client: Arc<Client>,
|
|
||||||
subscriptions: SubscriptionManager,
|
|
||||||
remote_blockchain: Arc<dyn RemoteBlockchain<Block>>,
|
|
||||||
fetcher: Arc<F>,
|
|
||||||
) -> Chain<Block, Client>
|
|
||||||
where
|
|
||||||
Block: BlockT + 'static,
|
|
||||||
Block::Header: Unpin,
|
|
||||||
Client: BlockBackend<Block> + HeaderBackend<Block> + BlockchainEvents<Block> + 'static,
|
|
||||||
F: Send + Sync + 'static,
|
|
||||||
{
|
|
||||||
Chain {
|
|
||||||
backend: Box::new(self::chain_light::LightChain::new(
|
|
||||||
client,
|
|
||||||
subscriptions,
|
|
||||||
remote_blockchain,
|
|
||||||
fetcher,
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Chain API with subscriptions support.
|
/// Chain API with subscriptions support.
|
||||||
pub struct Chain<Block: BlockT, Client> {
|
pub struct Chain<Block: BlockT, Client> {
|
||||||
backend: Box<dyn ChainBackend<Client, Block>>,
|
backend: Box<dyn ChainBackend<Client, Block>>,
|
||||||
|
|||||||
@@ -26,11 +26,7 @@ use futures::{
|
|||||||
use jsonrpc_pubsub::{manager::SubscriptionManager, typed::Subscriber, SubscriptionId};
|
use jsonrpc_pubsub::{manager::SubscriptionManager, typed::Subscriber, SubscriptionId};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use rpc::Result as RpcResult;
|
use rpc::Result as RpcResult;
|
||||||
use std::{
|
use std::{collections::HashMap, sync::Arc};
|
||||||
collections::{BTreeMap, HashMap},
|
|
||||||
ops::Range,
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use sc_rpc_api::state::ReadProof;
|
use sc_rpc_api::state::ReadProof;
|
||||||
use sp_blockchain::{
|
use sp_blockchain::{
|
||||||
@@ -43,10 +39,7 @@ use sp_core::{
|
|||||||
},
|
},
|
||||||
Bytes,
|
Bytes,
|
||||||
};
|
};
|
||||||
use sp_runtime::{
|
use sp_runtime::{generic::BlockId, traits::Block as BlockT};
|
||||||
generic::BlockId,
|
|
||||||
traits::{Block as BlockT, CheckedSub, NumberFor, SaturatedConversion},
|
|
||||||
};
|
|
||||||
use sp_version::RuntimeVersion;
|
use sp_version::RuntimeVersion;
|
||||||
|
|
||||||
use sp_api::{CallApiAt, Metadata, ProvideRuntimeApi};
|
use sp_api::{CallApiAt, Metadata, ProvideRuntimeApi};
|
||||||
@@ -66,14 +59,6 @@ use std::marker::PhantomData;
|
|||||||
struct QueryStorageRange<Block: BlockT> {
|
struct QueryStorageRange<Block: BlockT> {
|
||||||
/// Hashes of all the blocks in the range.
|
/// Hashes of all the blocks in the range.
|
||||||
pub hashes: Vec<Block::Hash>,
|
pub hashes: Vec<Block::Hash>,
|
||||||
/// Number of the first block in the range.
|
|
||||||
pub first_number: NumberFor<Block>,
|
|
||||||
/// Blocks subrange ([begin; end) indices within `hashes`) where we should read keys at
|
|
||||||
/// each state to get changes.
|
|
||||||
pub unfiltered_range: Range<usize>,
|
|
||||||
/// Blocks subrange ([begin; end) indices within `hashes`) where we could pre-filter
|
|
||||||
/// blocks-with-changes by using changes tries.
|
|
||||||
pub filtered_range: Option<Range<usize>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// State API backend for full nodes.
|
/// State API backend for full nodes.
|
||||||
@@ -107,10 +92,8 @@ where
|
|||||||
Ok(hash.unwrap_or_else(|| self.client.info().best_hash))
|
Ok(hash.unwrap_or_else(|| self.client.info().best_hash))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Splits the `query_storage` block range into 'filtered' and 'unfiltered' subranges.
|
/// Validates block range.
|
||||||
/// Blocks that contain changes within filtered subrange could be filtered using changes tries.
|
fn query_storage_range(
|
||||||
/// Blocks that contain changes within unfiltered subrange must be filtered manually.
|
|
||||||
fn split_query_storage_range(
|
|
||||||
&self,
|
&self,
|
||||||
from: Block::Hash,
|
from: Block::Hash,
|
||||||
to: Option<Block::Hash>,
|
to: Option<Block::Hash>,
|
||||||
@@ -156,23 +139,7 @@ where
|
|||||||
hashes
|
hashes
|
||||||
};
|
};
|
||||||
|
|
||||||
// check if we can filter blocks-with-changes from some (sub)range using changes tries
|
Ok(QueryStorageRange { hashes })
|
||||||
let changes_trie_range = self
|
|
||||||
.client
|
|
||||||
.max_key_changes_range(from_number, BlockId::Hash(to_meta.hash))
|
|
||||||
.map_err(client_err)?;
|
|
||||||
let filtered_range_begin = changes_trie_range.and_then(|(begin, _)| {
|
|
||||||
// avoids a corner case where begin < from_number (happens when querying genesis)
|
|
||||||
begin.checked_sub(&from_number).map(|x| x.saturated_into::<usize>())
|
|
||||||
});
|
|
||||||
let (unfiltered_range, filtered_range) = split_range(hashes.len(), filtered_range_begin);
|
|
||||||
|
|
||||||
Ok(QueryStorageRange {
|
|
||||||
hashes,
|
|
||||||
first_number: from_number,
|
|
||||||
unfiltered_range,
|
|
||||||
filtered_range,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterates through range.unfiltered_range and check each block for changes of keys' values.
|
/// Iterates through range.unfiltered_range and check each block for changes of keys' values.
|
||||||
@@ -183,8 +150,8 @@ where
|
|||||||
last_values: &mut HashMap<StorageKey, Option<StorageData>>,
|
last_values: &mut HashMap<StorageKey, Option<StorageData>>,
|
||||||
changes: &mut Vec<StorageChangeSet<Block::Hash>>,
|
changes: &mut Vec<StorageChangeSet<Block::Hash>>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
for block in range.unfiltered_range.start..range.unfiltered_range.end {
|
for block_hash in &range.hashes {
|
||||||
let block_hash = range.hashes[block].clone();
|
let block_hash = block_hash.clone();
|
||||||
let mut block_changes =
|
let mut block_changes =
|
||||||
StorageChangeSet { block: block_hash.clone(), changes: Vec::new() };
|
StorageChangeSet { block: block_hash.clone(), changes: Vec::new() };
|
||||||
let id = BlockId::hash(block_hash);
|
let id = BlockId::hash(block_hash);
|
||||||
@@ -207,57 +174,6 @@ where
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterates through all blocks that are changing keys within range.filtered_range and collects
|
|
||||||
/// these changes.
|
|
||||||
fn query_storage_filtered(
|
|
||||||
&self,
|
|
||||||
range: &QueryStorageRange<Block>,
|
|
||||||
keys: &[StorageKey],
|
|
||||||
last_values: &HashMap<StorageKey, Option<StorageData>>,
|
|
||||||
changes: &mut Vec<StorageChangeSet<Block::Hash>>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let (begin, end) = match range.filtered_range {
|
|
||||||
Some(ref filtered_range) => (
|
|
||||||
range.first_number + filtered_range.start.saturated_into(),
|
|
||||||
BlockId::Hash(range.hashes[filtered_range.end - 1].clone()),
|
|
||||||
),
|
|
||||||
None => return Ok(()),
|
|
||||||
};
|
|
||||||
let mut changes_map: BTreeMap<NumberFor<Block>, StorageChangeSet<Block::Hash>> =
|
|
||||||
BTreeMap::new();
|
|
||||||
for key in keys {
|
|
||||||
let mut last_block = None;
|
|
||||||
let mut last_value = last_values.get(key).cloned().unwrap_or_default();
|
|
||||||
let key_changes = self.client.key_changes(begin, end, None, key).map_err(client_err)?;
|
|
||||||
for (block, _) in key_changes.into_iter().rev() {
|
|
||||||
if last_block == Some(block) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
let block_hash =
|
|
||||||
range.hashes[(block - range.first_number).saturated_into::<usize>()].clone();
|
|
||||||
let id = BlockId::Hash(block_hash);
|
|
||||||
let value_at_block = self.client.storage(&id, key).map_err(client_err)?;
|
|
||||||
if last_value == value_at_block {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
changes_map
|
|
||||||
.entry(block)
|
|
||||||
.or_insert_with(|| StorageChangeSet { block: block_hash, changes: Vec::new() })
|
|
||||||
.changes
|
|
||||||
.push((key.clone(), value_at_block.clone()));
|
|
||||||
last_block = Some(block);
|
|
||||||
last_value = value_at_block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(additional_capacity) = changes_map.len().checked_sub(changes.len()) {
|
|
||||||
changes.reserve(additional_capacity);
|
|
||||||
}
|
|
||||||
changes.extend(changes_map.into_iter().map(|(_, cs)| cs));
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<BE, Block, Client> StateBackend<Block, Client> for FullState<BE, Block, Client>
|
impl<BE, Block, Client> StateBackend<Block, Client> for FullState<BE, Block, Client>
|
||||||
@@ -430,11 +346,10 @@ where
|
|||||||
keys: Vec<StorageKey>,
|
keys: Vec<StorageKey>,
|
||||||
) -> FutureResult<Vec<StorageChangeSet<Block::Hash>>> {
|
) -> FutureResult<Vec<StorageChangeSet<Block::Hash>>> {
|
||||||
let call_fn = move || {
|
let call_fn = move || {
|
||||||
let range = self.split_query_storage_range(from, to)?;
|
let range = self.query_storage_range(from, to)?;
|
||||||
let mut changes = Vec::new();
|
let mut changes = Vec::new();
|
||||||
let mut last_values = HashMap::new();
|
let mut last_values = HashMap::new();
|
||||||
self.query_storage_unfiltered(&range, &keys, &mut last_values, &mut changes)?;
|
self.query_storage_unfiltered(&range, &keys, &mut last_values, &mut changes)?;
|
||||||
self.query_storage_filtered(&range, &keys, &last_values, &mut changes)?;
|
|
||||||
Ok(changes)
|
Ok(changes)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -768,30 +683,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Splits passed range into two subranges where:
|
|
||||||
/// - first range has at least one element in it;
|
|
||||||
/// - second range (optionally) starts at given `middle` element.
|
|
||||||
pub(crate) fn split_range(
|
|
||||||
size: usize,
|
|
||||||
middle: Option<usize>,
|
|
||||||
) -> (Range<usize>, Option<Range<usize>>) {
|
|
||||||
// check if we can filter blocks-with-changes from some (sub)range using changes tries
|
|
||||||
let range2_begin = match middle {
|
|
||||||
// some of required changes tries are pruned => use available tries
|
|
||||||
Some(middle) if middle != 0 => Some(middle),
|
|
||||||
// all required changes tries are available, but we still want values at first block
|
|
||||||
// => do 'unfiltered' read for the first block and 'filtered' for the rest
|
|
||||||
Some(_) if size > 1 => Some(1),
|
|
||||||
// range contains single element => do not use changes tries
|
|
||||||
Some(_) => None,
|
|
||||||
// changes tries are not available => do 'unfiltered' read for the whole range
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
let range1 = 0..range2_begin.unwrap_or(size);
|
|
||||||
let range2 = range2_begin.map(|begin| begin..size);
|
|
||||||
(range1, range2)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn invalid_block_range<B: BlockT>(
|
fn invalid_block_range<B: BlockT>(
|
||||||
from: &CachedHeaderMetadata<B>,
|
from: &CachedHeaderMetadata<B>,
|
||||||
to: &CachedHeaderMetadata<B>,
|
to: &CachedHeaderMetadata<B>,
|
||||||
|
|||||||
@@ -17,16 +17,15 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use self::error::Error;
|
use self::error::Error;
|
||||||
use super::{state_full::split_range, *};
|
use super::*;
|
||||||
use crate::testing::TaskExecutor;
|
use crate::testing::TaskExecutor;
|
||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
use futures::{executor, StreamExt};
|
use futures::{executor, StreamExt};
|
||||||
use sc_block_builder::BlockBuilderProvider;
|
use sc_block_builder::BlockBuilderProvider;
|
||||||
use sc_rpc_api::DenyUnsafe;
|
use sc_rpc_api::DenyUnsafe;
|
||||||
use sp_consensus::BlockOrigin;
|
use sp_consensus::BlockOrigin;
|
||||||
use sp_core::{hash::H256, storage::ChildInfo, ChangesTrieConfiguration};
|
use sp_core::{hash::H256, storage::ChildInfo};
|
||||||
use sp_io::hashing::blake2_256;
|
use sp_io::hashing::blake2_256;
|
||||||
use sp_runtime::generic::BlockId;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use substrate_test_runtime_client::{prelude::*, runtime};
|
use substrate_test_runtime_client::{prelude::*, runtime};
|
||||||
|
|
||||||
@@ -336,7 +335,7 @@ fn should_send_initial_storage_changes_and_notifications() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_query_storage() {
|
fn should_query_storage() {
|
||||||
fn run_tests(mut client: Arc<TestClient>, has_changes_trie_config: bool) {
|
fn run_tests(mut client: Arc<TestClient>) {
|
||||||
let (api, _child) = new_full(
|
let (api, _child) = new_full(
|
||||||
client.clone(),
|
client.clone(),
|
||||||
SubscriptionManager::new(Arc::new(TaskExecutor)),
|
SubscriptionManager::new(Arc::new(TaskExecutor)),
|
||||||
@@ -369,13 +368,6 @@ fn should_query_storage() {
|
|||||||
let block2_hash = add_block(1);
|
let block2_hash = add_block(1);
|
||||||
let genesis_hash = client.genesis_hash();
|
let genesis_hash = client.genesis_hash();
|
||||||
|
|
||||||
if has_changes_trie_config {
|
|
||||||
assert_eq!(
|
|
||||||
client.max_key_changes_range(1, BlockId::Hash(block1_hash)).unwrap(),
|
|
||||||
Some((0, BlockId::Hash(block1_hash))),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut expected = vec![
|
let mut expected = vec![
|
||||||
StorageChangeSet {
|
StorageChangeSet {
|
||||||
block: genesis_hash,
|
block: genesis_hash,
|
||||||
@@ -519,24 +511,8 @@ fn should_query_storage() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
run_tests(Arc::new(substrate_test_runtime_client::new()), false);
|
run_tests(Arc::new(substrate_test_runtime_client::new()));
|
||||||
run_tests(
|
run_tests(Arc::new(TestClientBuilder::new().build()));
|
||||||
Arc::new(
|
|
||||||
TestClientBuilder::new()
|
|
||||||
.changes_trie_config(Some(ChangesTrieConfiguration::new(4, 2)))
|
|
||||||
.build(),
|
|
||||||
),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_split_ranges() {
|
|
||||||
assert_eq!(split_range(1, None), (0..1, None));
|
|
||||||
assert_eq!(split_range(100, None), (0..100, None));
|
|
||||||
assert_eq!(split_range(1, Some(0)), (0..1, None));
|
|
||||||
assert_eq!(split_range(100, Some(50)), (0..50, Some(50..100)));
|
|
||||||
assert_eq!(split_range(100, Some(99)), (0..99, Some(99..100)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -30,9 +30,8 @@ use log::info;
|
|||||||
use prometheus_endpoint::Registry;
|
use prometheus_endpoint::Registry;
|
||||||
use sc_chain_spec::get_extension;
|
use sc_chain_spec::get_extension;
|
||||||
use sc_client_api::{
|
use sc_client_api::{
|
||||||
execution_extensions::ExecutionExtensions, light::RemoteBlockchain,
|
execution_extensions::ExecutionExtensions, proof_provider::ProofProvider, BadBlocks,
|
||||||
proof_provider::ProofProvider, BadBlocks, BlockBackend, BlockchainEvents, ExecutorProvider,
|
BlockBackend, BlockchainEvents, ExecutorProvider, ForkBlocks, StorageProvider, UsageProvider,
|
||||||
ForkBlocks, StorageProvider, UsageProvider,
|
|
||||||
};
|
};
|
||||||
use sc_client_db::{Backend, DatabaseSettings};
|
use sc_client_db::{Backend, DatabaseSettings};
|
||||||
use sc_consensus::import_queue::ImportQueue;
|
use sc_consensus::import_queue::ImportQueue;
|
||||||
@@ -40,7 +39,7 @@ use sc_executor::RuntimeVersionOf;
|
|||||||
use sc_keystore::LocalKeystore;
|
use sc_keystore::LocalKeystore;
|
||||||
use sc_network::{
|
use sc_network::{
|
||||||
block_request_handler::{self, BlockRequestHandler},
|
block_request_handler::{self, BlockRequestHandler},
|
||||||
config::{OnDemand, Role, SyncMode},
|
config::{Role, SyncMode},
|
||||||
light_client_requests::{self, handler::LightClientRequestHandler},
|
light_client_requests::{self, handler::LightClientRequestHandler},
|
||||||
state_request_handler::{self, StateRequestHandler},
|
state_request_handler::{self, StateRequestHandler},
|
||||||
warp_request_handler::{self, RequestHandler as WarpSyncRequestHandler, WarpSyncProvider},
|
warp_request_handler::{self, RequestHandler as WarpSyncRequestHandler, WarpSyncProvider},
|
||||||
@@ -381,23 +380,19 @@ where
|
|||||||
pub struct SpawnTasksParams<'a, TBl: BlockT, TCl, TExPool, TRpc, Backend> {
|
pub struct SpawnTasksParams<'a, TBl: BlockT, TCl, TExPool, TRpc, Backend> {
|
||||||
/// The service configuration.
|
/// The service configuration.
|
||||||
pub config: Configuration,
|
pub config: Configuration,
|
||||||
/// A shared client returned by `new_full_parts`/`new_light_parts`.
|
/// A shared client returned by `new_full_parts`.
|
||||||
pub client: Arc<TCl>,
|
pub client: Arc<TCl>,
|
||||||
/// A shared backend returned by `new_full_parts`/`new_light_parts`.
|
/// A shared backend returned by `new_full_parts`.
|
||||||
pub backend: Arc<Backend>,
|
pub backend: Arc<Backend>,
|
||||||
/// A task manager returned by `new_full_parts`/`new_light_parts`.
|
/// A task manager returned by `new_full_parts`.
|
||||||
pub task_manager: &'a mut TaskManager,
|
pub task_manager: &'a mut TaskManager,
|
||||||
/// A shared keystore returned by `new_full_parts`/`new_light_parts`.
|
/// A shared keystore returned by `new_full_parts`.
|
||||||
pub keystore: SyncCryptoStorePtr,
|
pub keystore: SyncCryptoStorePtr,
|
||||||
/// An optional, shared data fetcher for light clients.
|
|
||||||
pub on_demand: Option<Arc<OnDemand<TBl>>>,
|
|
||||||
/// A shared transaction pool.
|
/// A shared transaction pool.
|
||||||
pub transaction_pool: Arc<TExPool>,
|
pub transaction_pool: Arc<TExPool>,
|
||||||
/// A RPC extension builder. Use `NoopRpcExtensionBuilder` if you just want to pass in the
|
/// A RPC extension builder. Use `NoopRpcExtensionBuilder` if you just want to pass in the
|
||||||
/// extensions directly.
|
/// extensions directly.
|
||||||
pub rpc_extensions_builder: Box<dyn RpcExtensionBuilder<Output = TRpc> + Send>,
|
pub rpc_extensions_builder: Box<dyn RpcExtensionBuilder<Output = TRpc> + Send>,
|
||||||
/// An optional, shared remote blockchain instance. Used for light clients.
|
|
||||||
pub remote_blockchain: Option<Arc<dyn RemoteBlockchain<TBl>>>,
|
|
||||||
/// A shared network instance.
|
/// A shared network instance.
|
||||||
pub network: Arc<NetworkService<TBl, <TBl as BlockT>::Hash>>,
|
pub network: Arc<NetworkService<TBl, <TBl as BlockT>::Hash>>,
|
||||||
/// A Sender for RPC requests.
|
/// A Sender for RPC requests.
|
||||||
@@ -475,12 +470,10 @@ where
|
|||||||
mut config,
|
mut config,
|
||||||
task_manager,
|
task_manager,
|
||||||
client,
|
client,
|
||||||
on_demand: _,
|
|
||||||
backend,
|
backend,
|
||||||
keystore,
|
keystore,
|
||||||
transaction_pool,
|
transaction_pool,
|
||||||
rpc_extensions_builder,
|
rpc_extensions_builder,
|
||||||
remote_blockchain: _,
|
|
||||||
network,
|
network,
|
||||||
system_rpc_tx,
|
system_rpc_tx,
|
||||||
telemetry,
|
telemetry,
|
||||||
@@ -725,7 +718,7 @@ where
|
|||||||
pub struct BuildNetworkParams<'a, TBl: BlockT, TExPool, TImpQu, TCl> {
|
pub struct BuildNetworkParams<'a, TBl: BlockT, TExPool, TImpQu, TCl> {
|
||||||
/// The service configuration.
|
/// The service configuration.
|
||||||
pub config: &'a Configuration,
|
pub config: &'a Configuration,
|
||||||
/// A shared client returned by `new_full_parts`/`new_light_parts`.
|
/// A shared client returned by `new_full_parts`.
|
||||||
pub client: Arc<TCl>,
|
pub client: Arc<TCl>,
|
||||||
/// A shared transaction pool.
|
/// A shared transaction pool.
|
||||||
pub transaction_pool: Arc<TExPool>,
|
pub transaction_pool: Arc<TExPool>,
|
||||||
@@ -733,8 +726,6 @@ pub struct BuildNetworkParams<'a, TBl: BlockT, TExPool, TImpQu, TCl> {
|
|||||||
pub spawn_handle: SpawnTaskHandle,
|
pub spawn_handle: SpawnTaskHandle,
|
||||||
/// An import queue.
|
/// An import queue.
|
||||||
pub import_queue: TImpQu,
|
pub import_queue: TImpQu,
|
||||||
/// An optional, shared data fetcher for light clients.
|
|
||||||
pub on_demand: Option<Arc<OnDemand<TBl>>>,
|
|
||||||
/// A block announce validator builder.
|
/// A block announce validator builder.
|
||||||
pub block_announce_validator_builder:
|
pub block_announce_validator_builder:
|
||||||
Option<Box<dyn FnOnce(Arc<TCl>) -> Box<dyn BlockAnnounceValidator<TBl> + Send> + Send>>,
|
Option<Box<dyn FnOnce(Arc<TCl>) -> Box<dyn BlockAnnounceValidator<TBl> + Send> + Send>>,
|
||||||
@@ -773,7 +764,6 @@ where
|
|||||||
transaction_pool,
|
transaction_pool,
|
||||||
spawn_handle,
|
spawn_handle,
|
||||||
import_queue,
|
import_queue,
|
||||||
on_demand,
|
|
||||||
block_announce_validator_builder,
|
block_announce_validator_builder,
|
||||||
warp_sync,
|
warp_sync,
|
||||||
} = params;
|
} = params;
|
||||||
@@ -869,7 +859,6 @@ where
|
|||||||
},
|
},
|
||||||
network_config: config.network.clone(),
|
network_config: config.network.clone(),
|
||||||
chain: client.clone(),
|
chain: client.clone(),
|
||||||
on_demand,
|
|
||||||
transaction_pool: transaction_pool_adapter as _,
|
transaction_pool: transaction_pool_adapter as _,
|
||||||
import_queue: Box::new(import_queue),
|
import_queue: Box::new(import_queue),
|
||||||
protocol_id,
|
protocol_id,
|
||||||
|
|||||||
@@ -26,10 +26,7 @@ use sp_core::{
|
|||||||
NativeOrEncoded, NeverNativeValue,
|
NativeOrEncoded, NeverNativeValue,
|
||||||
};
|
};
|
||||||
use sp_externalities::Extensions;
|
use sp_externalities::Extensions;
|
||||||
use sp_runtime::{
|
use sp_runtime::{generic::BlockId, traits::Block as BlockT};
|
||||||
generic::BlockId,
|
|
||||||
traits::{Block as BlockT, NumberFor},
|
|
||||||
};
|
|
||||||
use sp_state_machine::{
|
use sp_state_machine::{
|
||||||
self, backend::Backend as _, ExecutionManager, ExecutionStrategy, Ext, OverlayedChanges,
|
self, backend::Backend as _, ExecutionManager, ExecutionStrategy, Ext, OverlayedChanges,
|
||||||
StateMachine, StorageProof,
|
StateMachine, StorageProof,
|
||||||
@@ -153,8 +150,6 @@ where
|
|||||||
extensions: Option<Extensions>,
|
extensions: Option<Extensions>,
|
||||||
) -> sp_blockchain::Result<Vec<u8>> {
|
) -> sp_blockchain::Result<Vec<u8>> {
|
||||||
let mut changes = OverlayedChanges::default();
|
let mut changes = OverlayedChanges::default();
|
||||||
let changes_trie =
|
|
||||||
backend::changes_tries_state_at_block(at, self.backend.changes_trie_storage())?;
|
|
||||||
let state = self.backend.state_at(*at)?;
|
let state = self.backend.state_at(*at)?;
|
||||||
let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&state);
|
let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&state);
|
||||||
let runtime_code =
|
let runtime_code =
|
||||||
@@ -168,7 +163,6 @@ where
|
|||||||
|
|
||||||
let return_data = StateMachine::new(
|
let return_data = StateMachine::new(
|
||||||
&state,
|
&state,
|
||||||
changes_trie,
|
|
||||||
&mut changes,
|
&mut changes,
|
||||||
&self.executor,
|
&self.executor,
|
||||||
method,
|
method,
|
||||||
@@ -208,8 +202,6 @@ where
|
|||||||
where
|
where
|
||||||
ExecutionManager<EM>: Clone,
|
ExecutionManager<EM>: Clone,
|
||||||
{
|
{
|
||||||
let changes_trie_state =
|
|
||||||
backend::changes_tries_state_at_block(at, self.backend.changes_trie_storage())?;
|
|
||||||
let mut storage_transaction_cache = storage_transaction_cache.map(|c| c.borrow_mut());
|
let mut storage_transaction_cache = storage_transaction_cache.map(|c| c.borrow_mut());
|
||||||
|
|
||||||
let state = self.backend.state_at(*at)?;
|
let state = self.backend.state_at(*at)?;
|
||||||
@@ -243,7 +235,6 @@ where
|
|||||||
|
|
||||||
let mut state_machine = StateMachine::new(
|
let mut state_machine = StateMachine::new(
|
||||||
&backend,
|
&backend,
|
||||||
changes_trie_state,
|
|
||||||
changes,
|
changes,
|
||||||
&self.executor,
|
&self.executor,
|
||||||
method,
|
method,
|
||||||
@@ -262,7 +253,6 @@ where
|
|||||||
None => {
|
None => {
|
||||||
let mut state_machine = StateMachine::new(
|
let mut state_machine = StateMachine::new(
|
||||||
&state,
|
&state,
|
||||||
changes_trie_state,
|
|
||||||
changes,
|
changes,
|
||||||
&self.executor,
|
&self.executor,
|
||||||
method,
|
method,
|
||||||
@@ -286,11 +276,9 @@ where
|
|||||||
|
|
||||||
fn runtime_version(&self, id: &BlockId<Block>) -> sp_blockchain::Result<RuntimeVersion> {
|
fn runtime_version(&self, id: &BlockId<Block>) -> sp_blockchain::Result<RuntimeVersion> {
|
||||||
let mut overlay = OverlayedChanges::default();
|
let mut overlay = OverlayedChanges::default();
|
||||||
let changes_trie_state =
|
|
||||||
backend::changes_tries_state_at_block(id, self.backend.changes_trie_storage())?;
|
|
||||||
let state = self.backend.state_at(*id)?;
|
let state = self.backend.state_at(*id)?;
|
||||||
let mut cache = StorageTransactionCache::<Block, B::State>::default();
|
let mut cache = StorageTransactionCache::<Block, B::State>::default();
|
||||||
let mut ext = Ext::new(&mut overlay, &mut cache, &state, changes_trie_state, None);
|
let mut ext = Ext::new(&mut overlay, &mut cache, &state, None);
|
||||||
let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&state);
|
let state_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&state);
|
||||||
let runtime_code =
|
let runtime_code =
|
||||||
state_runtime_code.runtime_code().map_err(sp_blockchain::Error::RuntimeCode)?;
|
state_runtime_code.runtime_code().map_err(sp_blockchain::Error::RuntimeCode)?;
|
||||||
@@ -317,7 +305,7 @@ where
|
|||||||
state_runtime_code.runtime_code().map_err(sp_blockchain::Error::RuntimeCode)?;
|
state_runtime_code.runtime_code().map_err(sp_blockchain::Error::RuntimeCode)?;
|
||||||
let runtime_code = self.check_override(runtime_code, at)?;
|
let runtime_code = self.check_override(runtime_code, at)?;
|
||||||
|
|
||||||
sp_state_machine::prove_execution_on_trie_backend::<_, _, NumberFor<Block>, _, _>(
|
sp_state_machine::prove_execution_on_trie_backend(
|
||||||
&trie_backend,
|
&trie_backend,
|
||||||
&mut Default::default(),
|
&mut Default::default(),
|
||||||
&self.executor,
|
&self.executor,
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ use super::{
|
|||||||
genesis,
|
genesis,
|
||||||
};
|
};
|
||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
use hash_db::Prefix;
|
|
||||||
use log::{info, trace, warn};
|
use log::{info, trace, warn};
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use prometheus_endpoint::Registry;
|
use prometheus_endpoint::Registry;
|
||||||
@@ -31,18 +30,15 @@ use rand::Rng;
|
|||||||
use sc_block_builder::{BlockBuilderApi, BlockBuilderProvider, RecordProof};
|
use sc_block_builder::{BlockBuilderApi, BlockBuilderProvider, RecordProof};
|
||||||
use sc_client_api::{
|
use sc_client_api::{
|
||||||
backend::{
|
backend::{
|
||||||
self, apply_aux, changes_tries_state_at_block, BlockImportOperation, ClientImportOperation,
|
self, apply_aux, BlockImportOperation, ClientImportOperation, Finalizer, ImportSummary,
|
||||||
Finalizer, ImportSummary, LockImportRun, NewBlockState, PrunableStateChangesTrieStorage,
|
LockImportRun, NewBlockState, StorageProvider,
|
||||||
StorageProvider,
|
|
||||||
},
|
},
|
||||||
cht,
|
|
||||||
client::{
|
client::{
|
||||||
BadBlocks, BlockBackend, BlockImportNotification, BlockOf, BlockchainEvents, ClientInfo,
|
BadBlocks, BlockBackend, BlockImportNotification, BlockOf, BlockchainEvents, ClientInfo,
|
||||||
FinalityNotification, FinalityNotifications, ForkBlocks, ImportNotifications,
|
FinalityNotification, FinalityNotifications, ForkBlocks, ImportNotifications,
|
||||||
ProvideUncles,
|
ProvideUncles,
|
||||||
},
|
},
|
||||||
execution_extensions::ExecutionExtensions,
|
execution_extensions::ExecutionExtensions,
|
||||||
light::ChangesProof,
|
|
||||||
notifications::{StorageEventStream, StorageNotifications},
|
notifications::{StorageEventStream, StorageNotifications},
|
||||||
CallExecutor, ExecutorProvider, KeyIterator, ProofProvider, UsageProvider,
|
CallExecutor, ExecutorProvider, KeyIterator, ProofProvider, UsageProvider,
|
||||||
};
|
};
|
||||||
@@ -56,39 +52,36 @@ use sp_api::{
|
|||||||
ProvideRuntimeApi,
|
ProvideRuntimeApi,
|
||||||
};
|
};
|
||||||
use sp_blockchain::{
|
use sp_blockchain::{
|
||||||
self as blockchain, well_known_cache_keys::Id as CacheKeyId, Backend as ChainBackend, Cache,
|
self as blockchain, well_known_cache_keys::Id as CacheKeyId, Backend as ChainBackend,
|
||||||
CachedHeaderMetadata, Error, HeaderBackend as ChainHeaderBackend, HeaderMetadata, ProvideCache,
|
CachedHeaderMetadata, Error, HeaderBackend as ChainHeaderBackend, HeaderMetadata,
|
||||||
};
|
};
|
||||||
use sp_consensus::{BlockOrigin, BlockStatus, Error as ConsensusError};
|
use sp_consensus::{BlockOrigin, BlockStatus, Error as ConsensusError};
|
||||||
|
|
||||||
use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedSender};
|
use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedSender};
|
||||||
use sp_core::{
|
use sp_core::{
|
||||||
convert_hash,
|
|
||||||
storage::{
|
storage::{
|
||||||
well_known_keys, ChildInfo, ChildType, PrefixedStorageKey, StorageChild, StorageData,
|
well_known_keys, ChildInfo, ChildType, PrefixedStorageKey, StorageChild, StorageData,
|
||||||
StorageKey,
|
StorageKey,
|
||||||
},
|
},
|
||||||
ChangesTrieConfiguration, NativeOrEncoded,
|
NativeOrEncoded,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "test-helpers")]
|
#[cfg(feature = "test-helpers")]
|
||||||
use sp_keystore::SyncCryptoStorePtr;
|
use sp_keystore::SyncCryptoStorePtr;
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::{BlockId, DigestItem, SignedBlock},
|
generic::{BlockId, SignedBlock},
|
||||||
traits::{
|
traits::{
|
||||||
Block as BlockT, DigestFor, HashFor, Header as HeaderT, NumberFor, One,
|
Block as BlockT, HashFor, Header as HeaderT, NumberFor, One, SaturatedConversion, Zero,
|
||||||
SaturatedConversion, Zero,
|
|
||||||
},
|
},
|
||||||
BuildStorage, Justification, Justifications,
|
BuildStorage, Digest, Justification, Justifications,
|
||||||
};
|
};
|
||||||
use sp_state_machine::{
|
use sp_state_machine::{
|
||||||
key_changes, key_changes_proof, prove_child_read, prove_range_read_with_child_with_size,
|
prove_child_read, prove_range_read_with_child_with_size, prove_read,
|
||||||
prove_read, read_range_proof_check_with_child_on_proving_backend, Backend as StateBackend,
|
read_range_proof_check_with_child_on_proving_backend, Backend as StateBackend, KeyValueStates,
|
||||||
ChangesTrieAnchorBlockId, ChangesTrieConfigurationRange, ChangesTrieRootsStorage,
|
KeyValueStorageLevel, MAX_NESTED_TRIE_DEPTH,
|
||||||
ChangesTrieStorage, DBValue, KeyValueStates, KeyValueStorageLevel, MAX_NESTED_TRIE_DEPTH,
|
|
||||||
};
|
};
|
||||||
use sp_trie::{CompactProof, StorageProof};
|
use sp_trie::{CompactProof, StorageProof};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
panic::UnwindSafe,
|
panic::UnwindSafe,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
@@ -413,250 +406,6 @@ where
|
|||||||
self.executor.runtime_version(id)
|
self.executor.runtime_version(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads given header and generates CHT-based header proof for CHT of given size.
|
|
||||||
pub fn header_proof_with_cht_size(
|
|
||||||
&self,
|
|
||||||
id: &BlockId<Block>,
|
|
||||||
cht_size: NumberFor<Block>,
|
|
||||||
) -> sp_blockchain::Result<(Block::Header, StorageProof)> {
|
|
||||||
let proof_error = || {
|
|
||||||
sp_blockchain::Error::Backend(format!("Failed to generate header proof for {:?}", id))
|
|
||||||
};
|
|
||||||
let header = self.backend.blockchain().expect_header(*id)?;
|
|
||||||
let block_num = *header.number();
|
|
||||||
let cht_num = cht::block_to_cht_number(cht_size, block_num).ok_or_else(proof_error)?;
|
|
||||||
let cht_start = cht::start_number(cht_size, cht_num);
|
|
||||||
let mut current_num = cht_start;
|
|
||||||
let cht_range = ::std::iter::from_fn(|| {
|
|
||||||
let old_current_num = current_num;
|
|
||||||
current_num = current_num + One::one();
|
|
||||||
Some(old_current_num)
|
|
||||||
});
|
|
||||||
let headers = cht_range.map(|num| self.block_hash(num));
|
|
||||||
let proof = cht::build_proof::<Block::Header, HashFor<Block>, _, _>(
|
|
||||||
cht_size,
|
|
||||||
cht_num,
|
|
||||||
std::iter::once(block_num),
|
|
||||||
headers,
|
|
||||||
)?;
|
|
||||||
Ok((header, proof))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Does the same work as `key_changes_proof`, but assumes that CHTs are of passed size.
|
|
||||||
pub fn key_changes_proof_with_cht_size(
|
|
||||||
&self,
|
|
||||||
first: Block::Hash,
|
|
||||||
last: Block::Hash,
|
|
||||||
min: Block::Hash,
|
|
||||||
max: Block::Hash,
|
|
||||||
storage_key: Option<&PrefixedStorageKey>,
|
|
||||||
key: &StorageKey,
|
|
||||||
cht_size: NumberFor<Block>,
|
|
||||||
) -> sp_blockchain::Result<ChangesProof<Block::Header>> {
|
|
||||||
struct AccessedRootsRecorder<'a, Block: BlockT> {
|
|
||||||
storage: &'a dyn ChangesTrieStorage<HashFor<Block>, NumberFor<Block>>,
|
|
||||||
min: NumberFor<Block>,
|
|
||||||
required_roots_proofs: Mutex<BTreeMap<NumberFor<Block>, Block::Hash>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Block: BlockT> ChangesTrieRootsStorage<HashFor<Block>, NumberFor<Block>>
|
|
||||||
for AccessedRootsRecorder<'a, Block>
|
|
||||||
{
|
|
||||||
fn build_anchor(
|
|
||||||
&self,
|
|
||||||
hash: Block::Hash,
|
|
||||||
) -> Result<ChangesTrieAnchorBlockId<Block::Hash, NumberFor<Block>>, String> {
|
|
||||||
self.storage.build_anchor(hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn root(
|
|
||||||
&self,
|
|
||||||
anchor: &ChangesTrieAnchorBlockId<Block::Hash, NumberFor<Block>>,
|
|
||||||
block: NumberFor<Block>,
|
|
||||||
) -> Result<Option<Block::Hash>, String> {
|
|
||||||
let root = self.storage.root(anchor, block)?;
|
|
||||||
if block < self.min {
|
|
||||||
if let Some(ref root) = root {
|
|
||||||
self.required_roots_proofs.lock().insert(block, root.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(root)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Block: BlockT> ChangesTrieStorage<HashFor<Block>, NumberFor<Block>>
|
|
||||||
for AccessedRootsRecorder<'a, Block>
|
|
||||||
{
|
|
||||||
fn as_roots_storage(
|
|
||||||
&self,
|
|
||||||
) -> &dyn sp_state_machine::ChangesTrieRootsStorage<HashFor<Block>, NumberFor<Block>> {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_cached_changed_keys(
|
|
||||||
&self,
|
|
||||||
root: &Block::Hash,
|
|
||||||
functor: &mut dyn FnMut(&HashMap<Option<PrefixedStorageKey>, HashSet<Vec<u8>>>),
|
|
||||||
) -> bool {
|
|
||||||
self.storage.with_cached_changed_keys(root, functor)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get(&self, key: &Block::Hash, prefix: Prefix) -> Result<Option<DBValue>, String> {
|
|
||||||
self.storage.get(key, prefix)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let first_number =
|
|
||||||
self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(first))?;
|
|
||||||
let (storage, configs) = self.require_changes_trie(first_number, last, true)?;
|
|
||||||
let min_number =
|
|
||||||
self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(min))?;
|
|
||||||
|
|
||||||
let recording_storage = AccessedRootsRecorder::<Block> {
|
|
||||||
storage: storage.storage(),
|
|
||||||
min: min_number,
|
|
||||||
required_roots_proofs: Mutex::new(BTreeMap::new()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let max_number = std::cmp::min(
|
|
||||||
self.backend.blockchain().info().best_number,
|
|
||||||
self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(max))?,
|
|
||||||
);
|
|
||||||
|
|
||||||
// fetch key changes proof
|
|
||||||
let mut proof = Vec::new();
|
|
||||||
for (config_zero, config_end, config) in configs {
|
|
||||||
let last_number =
|
|
||||||
self.backend.blockchain().expect_block_number_from_id(&BlockId::Hash(last))?;
|
|
||||||
let config_range = ChangesTrieConfigurationRange {
|
|
||||||
config: &config,
|
|
||||||
zero: config_zero,
|
|
||||||
end: config_end.map(|(config_end_number, _)| config_end_number),
|
|
||||||
};
|
|
||||||
let proof_range = key_changes_proof::<HashFor<Block>, _>(
|
|
||||||
config_range,
|
|
||||||
&recording_storage,
|
|
||||||
first_number,
|
|
||||||
&ChangesTrieAnchorBlockId { hash: convert_hash(&last), number: last_number },
|
|
||||||
max_number,
|
|
||||||
storage_key,
|
|
||||||
&key.0,
|
|
||||||
)
|
|
||||||
.map_err(|err| sp_blockchain::Error::ChangesTrieAccessFailed(err))?;
|
|
||||||
proof.extend(proof_range);
|
|
||||||
}
|
|
||||||
|
|
||||||
// now gather proofs for all changes tries roots that were touched during key_changes_proof
|
|
||||||
// execution AND are unknown (i.e. replaced with CHT) to the requester
|
|
||||||
let roots = recording_storage.required_roots_proofs.into_inner();
|
|
||||||
let roots_proof = self.changes_trie_roots_proof(cht_size, roots.keys().cloned())?;
|
|
||||||
|
|
||||||
Ok(ChangesProof {
|
|
||||||
max_block: max_number,
|
|
||||||
proof,
|
|
||||||
roots: roots.into_iter().map(|(n, h)| (n, convert_hash(&h))).collect(),
|
|
||||||
roots_proof,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate CHT-based proof for roots of changes tries at given blocks.
|
|
||||||
fn changes_trie_roots_proof<I: IntoIterator<Item = NumberFor<Block>>>(
|
|
||||||
&self,
|
|
||||||
cht_size: NumberFor<Block>,
|
|
||||||
blocks: I,
|
|
||||||
) -> sp_blockchain::Result<StorageProof> {
|
|
||||||
// most probably we have touched several changes tries that are parts of the single CHT
|
|
||||||
// => GroupBy changes tries by CHT number and then gather proof for the whole group at once
|
|
||||||
let mut proofs = Vec::new();
|
|
||||||
|
|
||||||
cht::for_each_cht_group::<Block::Header, _, _, _>(
|
|
||||||
cht_size,
|
|
||||||
blocks,
|
|
||||||
|_, cht_num, cht_blocks| {
|
|
||||||
let cht_proof =
|
|
||||||
self.changes_trie_roots_proof_at_cht(cht_size, cht_num, cht_blocks)?;
|
|
||||||
proofs.push(cht_proof);
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(StorageProof::merge(proofs))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates CHT-based proof for roots of changes tries at given blocks
|
|
||||||
/// (that are part of single CHT).
|
|
||||||
fn changes_trie_roots_proof_at_cht(
|
|
||||||
&self,
|
|
||||||
cht_size: NumberFor<Block>,
|
|
||||||
cht_num: NumberFor<Block>,
|
|
||||||
blocks: Vec<NumberFor<Block>>,
|
|
||||||
) -> sp_blockchain::Result<StorageProof> {
|
|
||||||
let cht_start = cht::start_number(cht_size, cht_num);
|
|
||||||
let mut current_num = cht_start;
|
|
||||||
let cht_range = ::std::iter::from_fn(|| {
|
|
||||||
let old_current_num = current_num;
|
|
||||||
current_num = current_num + One::one();
|
|
||||||
Some(old_current_num)
|
|
||||||
});
|
|
||||||
let roots = cht_range.map(|num| {
|
|
||||||
self.header(&BlockId::Number(num)).map(|block| {
|
|
||||||
block
|
|
||||||
.and_then(|block| block.digest().log(DigestItem::as_changes_trie_root).cloned())
|
|
||||||
})
|
|
||||||
});
|
|
||||||
let proof = cht::build_proof::<Block::Header, HashFor<Block>, _, _>(
|
|
||||||
cht_size, cht_num, blocks, roots,
|
|
||||||
)?;
|
|
||||||
Ok(proof)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns changes trie storage and all configurations that have been active
|
|
||||||
/// in the range [first; last].
|
|
||||||
///
|
|
||||||
/// Configurations are returned in descending order (and obviously never overlap).
|
|
||||||
/// If fail_if_disabled is false, returns maximal consequent configurations ranges,
|
|
||||||
/// starting from last and stopping on either first, or when CT have been disabled.
|
|
||||||
/// If fail_if_disabled is true, fails when there's a subrange where CT have been disabled
|
|
||||||
/// inside first..last blocks range.
|
|
||||||
fn require_changes_trie(
|
|
||||||
&self,
|
|
||||||
first: NumberFor<Block>,
|
|
||||||
last: Block::Hash,
|
|
||||||
fail_if_disabled: bool,
|
|
||||||
) -> sp_blockchain::Result<(
|
|
||||||
&dyn PrunableStateChangesTrieStorage<Block>,
|
|
||||||
Vec<(NumberFor<Block>, Option<(NumberFor<Block>, Block::Hash)>, ChangesTrieConfiguration)>,
|
|
||||||
)> {
|
|
||||||
let storage = self
|
|
||||||
.backend
|
|
||||||
.changes_trie_storage()
|
|
||||||
.ok_or_else(|| sp_blockchain::Error::ChangesTriesNotSupported)?;
|
|
||||||
|
|
||||||
let mut configs = Vec::with_capacity(1);
|
|
||||||
let mut current = last;
|
|
||||||
loop {
|
|
||||||
let config_range = storage.configuration_at(&BlockId::Hash(current))?;
|
|
||||||
match config_range.config {
|
|
||||||
Some(config) => configs.push((config_range.zero.0, config_range.end, config)),
|
|
||||||
None if !fail_if_disabled => return Ok((storage, configs)),
|
|
||||||
None => return Err(sp_blockchain::Error::ChangesTriesNotSupported),
|
|
||||||
}
|
|
||||||
|
|
||||||
if config_range.zero.0 < first {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
current = *self
|
|
||||||
.backend
|
|
||||||
.blockchain()
|
|
||||||
.expect_header(BlockId::Hash(config_range.zero.1))?
|
|
||||||
.parent_hash();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((storage, configs))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Apply a checked and validated block to an operation. If a justification is provided
|
/// Apply a checked and validated block to an operation. If a justification is provided
|
||||||
/// then `finalized` *must* be true.
|
/// then `finalized` *must* be true.
|
||||||
fn apply_block(
|
fn apply_block(
|
||||||
@@ -811,7 +560,7 @@ where
|
|||||||
sc_consensus::StorageChanges::Changes(storage_changes) => {
|
sc_consensus::StorageChanges::Changes(storage_changes) => {
|
||||||
self.backend
|
self.backend
|
||||||
.begin_state_operation(&mut operation.op, BlockId::Hash(parent_hash))?;
|
.begin_state_operation(&mut operation.op, BlockId::Hash(parent_hash))?;
|
||||||
let (main_sc, child_sc, offchain_sc, tx, _, changes_trie_tx, tx_index) =
|
let (main_sc, child_sc, offchain_sc, tx, _, tx_index) =
|
||||||
storage_changes.into_inner();
|
storage_changes.into_inner();
|
||||||
|
|
||||||
if self.config.offchain_indexing_api {
|
if self.config.offchain_indexing_api {
|
||||||
@@ -822,9 +571,6 @@ where
|
|||||||
operation.op.update_storage(main_sc.clone(), child_sc.clone())?;
|
operation.op.update_storage(main_sc.clone(), child_sc.clone())?;
|
||||||
operation.op.update_transaction_index(tx_index)?;
|
operation.op.update_transaction_index(tx_index)?;
|
||||||
|
|
||||||
if let Some(changes_trie_transaction) = changes_trie_tx {
|
|
||||||
operation.op.update_changes_trie(changes_trie_transaction)?;
|
|
||||||
}
|
|
||||||
Some((main_sc, child_sc))
|
Some((main_sc, child_sc))
|
||||||
},
|
},
|
||||||
sc_consensus::StorageChanges::Import(changes) => {
|
sc_consensus::StorageChanges::Import(changes) => {
|
||||||
@@ -1003,11 +749,8 @@ where
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
let state = self.backend.state_at(at)?;
|
let state = self.backend.state_at(at)?;
|
||||||
let changes_trie_state =
|
|
||||||
changes_tries_state_at_block(&at, self.backend.changes_trie_storage())?;
|
|
||||||
|
|
||||||
let gen_storage_changes = runtime_api
|
let gen_storage_changes = runtime_api
|
||||||
.into_storage_changes(&state, changes_trie_state.as_ref(), *parent_hash)
|
.into_storage_changes(&state, *parent_hash)
|
||||||
.map_err(sp_blockchain::Error::Storage)?;
|
.map_err(sp_blockchain::Error::Storage)?;
|
||||||
|
|
||||||
if import_block.header.state_root() != &gen_storage_changes.transaction_storage_root
|
if import_block.header.state_root() != &gen_storage_changes.transaction_storage_root
|
||||||
@@ -1356,25 +1099,6 @@ where
|
|||||||
.map(|(r, p)| (r, StorageProof::merge(vec![p, code_proof])))
|
.map(|(r, p)| (r, StorageProof::merge(vec![p, code_proof])))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn header_proof(
|
|
||||||
&self,
|
|
||||||
id: &BlockId<Block>,
|
|
||||||
) -> sp_blockchain::Result<(Block::Header, StorageProof)> {
|
|
||||||
self.header_proof_with_cht_size(id, cht::size())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn key_changes_proof(
|
|
||||||
&self,
|
|
||||||
first: Block::Hash,
|
|
||||||
last: Block::Hash,
|
|
||||||
min: Block::Hash,
|
|
||||||
max: Block::Hash,
|
|
||||||
storage_key: Option<&PrefixedStorageKey>,
|
|
||||||
key: &StorageKey,
|
|
||||||
) -> sp_blockchain::Result<ChangesProof<Block::Header>> {
|
|
||||||
self.key_changes_proof_with_cht_size(first, last, min, max, storage_key, key, cht::size())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_proof_collection(
|
fn read_proof_collection(
|
||||||
&self,
|
&self,
|
||||||
id: &BlockId<Block>,
|
id: &BlockId<Block>,
|
||||||
@@ -1540,7 +1264,7 @@ where
|
|||||||
fn new_block_at<R: Into<RecordProof>>(
|
fn new_block_at<R: Into<RecordProof>>(
|
||||||
&self,
|
&self,
|
||||||
parent: &BlockId<Block>,
|
parent: &BlockId<Block>,
|
||||||
inherent_digests: DigestFor<Block>,
|
inherent_digests: Digest,
|
||||||
record_proof: R,
|
record_proof: R,
|
||||||
) -> sp_blockchain::Result<sc_block_builder::BlockBuilder<Block, Self, B>> {
|
) -> sp_blockchain::Result<sc_block_builder::BlockBuilder<Block, Self, B>> {
|
||||||
sc_block_builder::BlockBuilder::new(
|
sc_block_builder::BlockBuilder::new(
|
||||||
@@ -1555,7 +1279,7 @@ where
|
|||||||
|
|
||||||
fn new_block(
|
fn new_block(
|
||||||
&self,
|
&self,
|
||||||
inherent_digests: DigestFor<Block>,
|
inherent_digests: Digest,
|
||||||
) -> sp_blockchain::Result<sc_block_builder::BlockBuilder<Block, Self, B>> {
|
) -> sp_blockchain::Result<sc_block_builder::BlockBuilder<Block, Self, B>> {
|
||||||
let info = self.chain_info();
|
let info = self.chain_info();
|
||||||
sc_block_builder::BlockBuilder::new(
|
sc_block_builder::BlockBuilder::new(
|
||||||
@@ -1703,89 +1427,6 @@ where
|
|||||||
.child_storage_hash(child_info, &key.0)
|
.child_storage_hash(child_info, &key.0)
|
||||||
.map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))?)
|
.map_err(|e| sp_blockchain::Error::from_state(Box::new(e)))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn max_key_changes_range(
|
|
||||||
&self,
|
|
||||||
first: NumberFor<Block>,
|
|
||||||
last: BlockId<Block>,
|
|
||||||
) -> sp_blockchain::Result<Option<(NumberFor<Block>, BlockId<Block>)>> {
|
|
||||||
let last_number = self.backend.blockchain().expect_block_number_from_id(&last)?;
|
|
||||||
let last_hash = self.backend.blockchain().expect_block_hash_from_id(&last)?;
|
|
||||||
if first > last_number {
|
|
||||||
return Err(sp_blockchain::Error::ChangesTrieAccessFailed(
|
|
||||||
"Invalid changes trie range".into(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
let (storage, configs) = match self.require_changes_trie(first, last_hash, false).ok() {
|
|
||||||
Some((storage, configs)) => (storage, configs),
|
|
||||||
None => return Ok(None),
|
|
||||||
};
|
|
||||||
|
|
||||||
let first_available_changes_trie = configs.last().map(|config| config.0);
|
|
||||||
match first_available_changes_trie {
|
|
||||||
Some(first_available_changes_trie) => {
|
|
||||||
let oldest_unpruned = storage.oldest_pruned_digest_range_end();
|
|
||||||
let first = std::cmp::max(first_available_changes_trie, oldest_unpruned);
|
|
||||||
Ok(Some((first, last)))
|
|
||||||
},
|
|
||||||
None => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn key_changes(
|
|
||||||
&self,
|
|
||||||
first: NumberFor<Block>,
|
|
||||||
last: BlockId<Block>,
|
|
||||||
storage_key: Option<&PrefixedStorageKey>,
|
|
||||||
key: &StorageKey,
|
|
||||||
) -> sp_blockchain::Result<Vec<(NumberFor<Block>, u32)>> {
|
|
||||||
let last_number = self.backend.blockchain().expect_block_number_from_id(&last)?;
|
|
||||||
let last_hash = self.backend.blockchain().expect_block_hash_from_id(&last)?;
|
|
||||||
let (storage, configs) = self.require_changes_trie(first, last_hash, true)?;
|
|
||||||
|
|
||||||
let mut result = Vec::new();
|
|
||||||
let best_number = self.backend.blockchain().info().best_number;
|
|
||||||
for (config_zero, config_end, config) in configs {
|
|
||||||
let range_first = ::std::cmp::max(first, config_zero + One::one());
|
|
||||||
let range_anchor = match config_end {
|
|
||||||
Some((config_end_number, config_end_hash)) =>
|
|
||||||
if last_number > config_end_number {
|
|
||||||
ChangesTrieAnchorBlockId {
|
|
||||||
hash: config_end_hash,
|
|
||||||
number: config_end_number,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ChangesTrieAnchorBlockId {
|
|
||||||
hash: convert_hash(&last_hash),
|
|
||||||
number: last_number,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None =>
|
|
||||||
ChangesTrieAnchorBlockId { hash: convert_hash(&last_hash), number: last_number },
|
|
||||||
};
|
|
||||||
|
|
||||||
let config_range = ChangesTrieConfigurationRange {
|
|
||||||
config: &config,
|
|
||||||
zero: config_zero.clone(),
|
|
||||||
end: config_end.map(|(config_end_number, _)| config_end_number),
|
|
||||||
};
|
|
||||||
let result_range: Vec<(NumberFor<Block>, u32)> = key_changes::<HashFor<Block>, _>(
|
|
||||||
config_range,
|
|
||||||
storage.storage(),
|
|
||||||
range_first,
|
|
||||||
&range_anchor,
|
|
||||||
best_number,
|
|
||||||
storage_key,
|
|
||||||
&key.0,
|
|
||||||
)
|
|
||||||
.and_then(|r| r.map(|r| r.map(|(block, tx)| (block, tx))).collect::<Result<_, _>>())
|
|
||||||
.map_err(|err| sp_blockchain::Error::ChangesTrieAccessFailed(err))?;
|
|
||||||
result.extend(result_range);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B, E, Block, RA> HeaderMetadata<Block> for Client<B, E, Block, RA>
|
impl<B, E, Block, RA> HeaderMetadata<Block> for Client<B, E, Block, RA>
|
||||||
@@ -1913,16 +1554,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B, E, Block, RA> ProvideCache<Block> for Client<B, E, Block, RA>
|
|
||||||
where
|
|
||||||
B: backend::Backend<Block>,
|
|
||||||
Block: BlockT,
|
|
||||||
{
|
|
||||||
fn cache(&self) -> Option<Arc<dyn Cache<Block>>> {
|
|
||||||
self.backend.blockchain().cache()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B, E, Block, RA> ProvideRuntimeApi<Block> for Client<B, E, Block, RA>
|
impl<B, E, Block, RA> ProvideRuntimeApi<Block> for Client<B, E, Block, RA>
|
||||||
where
|
where
|
||||||
B: backend::Backend<Block>,
|
B: backend::Backend<Block>,
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ use sc_client_api::{blockchain::HeaderBackend, BlockchainEvents};
|
|||||||
pub use sc_consensus::ImportQueue;
|
pub use sc_consensus::ImportQueue;
|
||||||
pub use sc_executor::NativeExecutionDispatch;
|
pub use sc_executor::NativeExecutionDispatch;
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub use sc_network::config::{OnDemand, TransactionImport, TransactionImportFuture};
|
pub use sc_network::config::{TransactionImport, TransactionImportFuture};
|
||||||
pub use sc_rpc::Metadata as RpcMetadata;
|
pub use sc_rpc::Metadata as RpcMetadata;
|
||||||
pub use sc_tracing::TracingReceiver;
|
pub use sc_tracing::TracingReceiver;
|
||||||
pub use sc_transaction_pool::Options as TransactionPoolOptions;
|
pub use sc_transaction_pool::Options as TransactionPoolOptions;
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ tokio = { version = "1.10.0", features = ["time"] }
|
|||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
fdlimit = "0.2.1"
|
fdlimit = "0.2.1"
|
||||||
parking_lot = "0.11.1"
|
parking_lot = "0.11.1"
|
||||||
sc-light = { version = "4.0.0-dev", path = "../../light" }
|
|
||||||
sp-blockchain = { version = "4.0.0-dev", path = "../../../primitives/blockchain" }
|
sp-blockchain = { version = "4.0.0-dev", path = "../../../primitives/blockchain" }
|
||||||
sp-api = { version = "4.0.0-dev", path = "../../../primitives/api" }
|
sp-api = { version = "4.0.0-dev", path = "../../../primitives/api" }
|
||||||
sp-state-machine = { version = "0.10.0-dev", path = "../../../primitives/state-machine" }
|
sp-state-machine = { version = "0.10.0-dev", path = "../../../primitives/state-machine" }
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
type TestBackend = sc_client_api::in_mem::Backend<substrate_test_runtime::Block>;
|
type TestBackend = sc_client_api::in_mem::Backend<substrate_test_runtime::Block>;
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_leaves_with_complex_block_tree() {
|
fn test_leaves_with_complex_block_tree() {
|
||||||
let backend = Arc::new(TestBackend::new());
|
let backend = Arc::new(TestBackend::new());
|
||||||
|
|||||||
@@ -27,29 +27,25 @@ use sc_client_db::{
|
|||||||
use sc_consensus::{
|
use sc_consensus::{
|
||||||
BlockCheckParams, BlockImport, BlockImportParams, ForkChoiceStrategy, ImportResult,
|
BlockCheckParams, BlockImport, BlockImportParams, ForkChoiceStrategy, ImportResult,
|
||||||
};
|
};
|
||||||
use sc_service::client::{self, new_in_mem, Client, LocalCallExecutor};
|
use sc_service::client::{new_in_mem, Client, LocalCallExecutor};
|
||||||
use sp_api::ProvideRuntimeApi;
|
use sp_api::ProvideRuntimeApi;
|
||||||
use sp_consensus::{BlockOrigin, BlockStatus, Error as ConsensusError, SelectChain};
|
use sp_consensus::{BlockOrigin, BlockStatus, Error as ConsensusError, SelectChain};
|
||||||
use sp_core::{blake2_256, testing::TaskExecutor, ChangesTrieConfiguration, H256};
|
use sp_core::{testing::TaskExecutor, H256};
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::BlockId,
|
generic::BlockId,
|
||||||
traits::{BlakeTwo256, Block as BlockT, Header as HeaderT},
|
traits::{BlakeTwo256, Block as BlockT, Header as HeaderT},
|
||||||
ConsensusEngineId, DigestItem, Justifications,
|
ConsensusEngineId, Justifications,
|
||||||
};
|
};
|
||||||
use sp_state_machine::{
|
use sp_state_machine::{
|
||||||
backend::Backend as _, ExecutionStrategy, InMemoryBackend, OverlayedChanges, StateMachine,
|
backend::Backend as _, ExecutionStrategy, InMemoryBackend, OverlayedChanges, StateMachine,
|
||||||
};
|
};
|
||||||
use sp_storage::{ChildInfo, StorageKey};
|
use sp_storage::{ChildInfo, StorageKey};
|
||||||
use sp_trie::{trie_types::Layout, TrieConfiguration};
|
use sp_trie::{trie_types::Layout, TrieConfiguration};
|
||||||
use std::{
|
use std::{collections::HashSet, sync::Arc};
|
||||||
collections::{HashMap, HashSet},
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
use substrate_test_runtime::TestAPI;
|
use substrate_test_runtime::TestAPI;
|
||||||
use substrate_test_runtime_client::{
|
use substrate_test_runtime_client::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
runtime::{
|
runtime::{
|
||||||
self,
|
|
||||||
genesismap::{insert_genesis_block, GenesisConfig},
|
genesismap::{insert_genesis_block, GenesisConfig},
|
||||||
Block, BlockNumber, Digest, Hash, Header, RuntimeApi, Transfer,
|
Block, BlockNumber, Digest, Hash, Header, RuntimeApi, Transfer,
|
||||||
},
|
},
|
||||||
@@ -57,6 +53,8 @@ use substrate_test_runtime_client::{
|
|||||||
Sr25519Keyring, TestClientBuilder, TestClientBuilderExt,
|
Sr25519Keyring, TestClientBuilder, TestClientBuilderExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod db;
|
||||||
|
|
||||||
const TEST_ENGINE_ID: ConsensusEngineId = *b"TEST";
|
const TEST_ENGINE_ID: ConsensusEngineId = *b"TEST";
|
||||||
|
|
||||||
pub struct ExecutorDispatch;
|
pub struct ExecutorDispatch;
|
||||||
@@ -77,86 +75,6 @@ fn executor() -> sc_executor::NativeElseWasmExecutor<ExecutorDispatch> {
|
|||||||
sc_executor::NativeElseWasmExecutor::new(sc_executor::WasmExecutionMethod::Interpreted, None, 8)
|
sc_executor::NativeElseWasmExecutor::new(sc_executor::WasmExecutionMethod::Interpreted, None, 8)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prepare_client_with_key_changes() -> (
|
|
||||||
client::Client<
|
|
||||||
substrate_test_runtime_client::Backend,
|
|
||||||
substrate_test_runtime_client::ExecutorDispatch,
|
|
||||||
Block,
|
|
||||||
RuntimeApi,
|
|
||||||
>,
|
|
||||||
Vec<H256>,
|
|
||||||
Vec<(u64, u64, Vec<u8>, Vec<(u64, u32)>)>,
|
|
||||||
) {
|
|
||||||
// prepare block structure
|
|
||||||
let blocks_transfers = vec![
|
|
||||||
vec![
|
|
||||||
(AccountKeyring::Alice, AccountKeyring::Dave),
|
|
||||||
(AccountKeyring::Bob, AccountKeyring::Dave),
|
|
||||||
],
|
|
||||||
vec![(AccountKeyring::Charlie, AccountKeyring::Eve)],
|
|
||||||
vec![],
|
|
||||||
vec![(AccountKeyring::Alice, AccountKeyring::Dave)],
|
|
||||||
];
|
|
||||||
|
|
||||||
// prepare client ang import blocks
|
|
||||||
let mut local_roots = Vec::new();
|
|
||||||
let config = Some(ChangesTrieConfiguration::new(4, 2));
|
|
||||||
let mut remote_client = TestClientBuilder::new().changes_trie_config(config).build();
|
|
||||||
let mut nonces: HashMap<_, u64> = Default::default();
|
|
||||||
for (i, block_transfers) in blocks_transfers.into_iter().enumerate() {
|
|
||||||
let mut builder = remote_client.new_block(Default::default()).unwrap();
|
|
||||||
for (from, to) in block_transfers {
|
|
||||||
builder
|
|
||||||
.push_transfer(Transfer {
|
|
||||||
from: from.into(),
|
|
||||||
to: to.into(),
|
|
||||||
amount: 1,
|
|
||||||
nonce: *nonces.entry(from).and_modify(|n| *n = *n + 1).or_default(),
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
let block = builder.build().unwrap().block;
|
|
||||||
block_on(remote_client.import(BlockOrigin::Own, block)).unwrap();
|
|
||||||
|
|
||||||
let header = remote_client.header(&BlockId::Number(i as u64 + 1)).unwrap().unwrap();
|
|
||||||
let trie_root = header
|
|
||||||
.digest()
|
|
||||||
.log(DigestItem::as_changes_trie_root)
|
|
||||||
.map(|root| H256::from_slice(root.as_ref()))
|
|
||||||
.unwrap();
|
|
||||||
local_roots.push(trie_root);
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepare test cases
|
|
||||||
let alice = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Alice.into())).to_vec();
|
|
||||||
let bob = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Bob.into())).to_vec();
|
|
||||||
let charlie =
|
|
||||||
blake2_256(&runtime::system::balance_of_key(AccountKeyring::Charlie.into())).to_vec();
|
|
||||||
let dave = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Dave.into())).to_vec();
|
|
||||||
let eve = blake2_256(&runtime::system::balance_of_key(AccountKeyring::Eve.into())).to_vec();
|
|
||||||
let ferdie =
|
|
||||||
blake2_256(&runtime::system::balance_of_key(AccountKeyring::Ferdie.into())).to_vec();
|
|
||||||
let test_cases = vec![
|
|
||||||
(1, 4, alice.clone(), vec![(4, 0), (1, 0)]),
|
|
||||||
(1, 3, alice.clone(), vec![(1, 0)]),
|
|
||||||
(2, 4, alice.clone(), vec![(4, 0)]),
|
|
||||||
(2, 3, alice.clone(), vec![]),
|
|
||||||
(1, 4, bob.clone(), vec![(1, 1)]),
|
|
||||||
(1, 1, bob.clone(), vec![(1, 1)]),
|
|
||||||
(2, 4, bob.clone(), vec![]),
|
|
||||||
(1, 4, charlie.clone(), vec![(2, 0)]),
|
|
||||||
(1, 4, dave.clone(), vec![(4, 0), (1, 1), (1, 0)]),
|
|
||||||
(1, 1, dave.clone(), vec![(1, 1), (1, 0)]),
|
|
||||||
(3, 4, dave.clone(), vec![(4, 0)]),
|
|
||||||
(1, 4, eve.clone(), vec![(2, 0)]),
|
|
||||||
(1, 1, eve.clone(), vec![]),
|
|
||||||
(3, 4, eve.clone(), vec![]),
|
|
||||||
(1, 4, ferdie.clone(), vec![]),
|
|
||||||
];
|
|
||||||
|
|
||||||
(remote_client, local_roots, test_cases)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn construct_block(
|
fn construct_block(
|
||||||
backend: &InMemoryBackend<BlakeTwo256>,
|
backend: &InMemoryBackend<BlakeTwo256>,
|
||||||
number: BlockNumber,
|
number: BlockNumber,
|
||||||
@@ -184,7 +102,6 @@ fn construct_block(
|
|||||||
|
|
||||||
StateMachine::new(
|
StateMachine::new(
|
||||||
backend,
|
backend,
|
||||||
sp_state_machine::disabled_changes_trie_state::<_, u64>(),
|
|
||||||
&mut overlay,
|
&mut overlay,
|
||||||
&executor(),
|
&executor(),
|
||||||
"Core_initialize_block",
|
"Core_initialize_block",
|
||||||
@@ -199,7 +116,6 @@ fn construct_block(
|
|||||||
for tx in transactions.iter() {
|
for tx in transactions.iter() {
|
||||||
StateMachine::new(
|
StateMachine::new(
|
||||||
backend,
|
backend,
|
||||||
sp_state_machine::disabled_changes_trie_state::<_, u64>(),
|
|
||||||
&mut overlay,
|
&mut overlay,
|
||||||
&executor(),
|
&executor(),
|
||||||
"BlockBuilder_apply_extrinsic",
|
"BlockBuilder_apply_extrinsic",
|
||||||
@@ -214,7 +130,6 @@ fn construct_block(
|
|||||||
|
|
||||||
let ret_data = StateMachine::new(
|
let ret_data = StateMachine::new(
|
||||||
backend,
|
backend,
|
||||||
sp_state_machine::disabled_changes_trie_state::<_, u64>(),
|
|
||||||
&mut overlay,
|
&mut overlay,
|
||||||
&executor(),
|
&executor(),
|
||||||
"BlockBuilder_finalize_block",
|
"BlockBuilder_finalize_block",
|
||||||
@@ -248,7 +163,6 @@ fn block1(genesis_hash: Hash, backend: &InMemoryBackend<BlakeTwo256>) -> (Vec<u8
|
|||||||
#[test]
|
#[test]
|
||||||
fn construct_genesis_should_work_with_native() {
|
fn construct_genesis_should_work_with_native() {
|
||||||
let mut storage = GenesisConfig::new(
|
let mut storage = GenesisConfig::new(
|
||||||
None,
|
|
||||||
vec![Sr25519Keyring::One.public().into(), Sr25519Keyring::Two.public().into()],
|
vec![Sr25519Keyring::One.public().into(), Sr25519Keyring::Two.public().into()],
|
||||||
vec![AccountKeyring::One.into(), AccountKeyring::Two.into()],
|
vec![AccountKeyring::One.into(), AccountKeyring::Two.into()],
|
||||||
1000,
|
1000,
|
||||||
@@ -267,7 +181,6 @@ fn construct_genesis_should_work_with_native() {
|
|||||||
|
|
||||||
let _ = StateMachine::new(
|
let _ = StateMachine::new(
|
||||||
&backend,
|
&backend,
|
||||||
sp_state_machine::disabled_changes_trie_state::<_, u64>(),
|
|
||||||
&mut overlay,
|
&mut overlay,
|
||||||
&executor(),
|
&executor(),
|
||||||
"Core_execute_block",
|
"Core_execute_block",
|
||||||
@@ -283,7 +196,6 @@ fn construct_genesis_should_work_with_native() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn construct_genesis_should_work_with_wasm() {
|
fn construct_genesis_should_work_with_wasm() {
|
||||||
let mut storage = GenesisConfig::new(
|
let mut storage = GenesisConfig::new(
|
||||||
None,
|
|
||||||
vec![Sr25519Keyring::One.public().into(), Sr25519Keyring::Two.public().into()],
|
vec![Sr25519Keyring::One.public().into(), Sr25519Keyring::Two.public().into()],
|
||||||
vec![AccountKeyring::One.into(), AccountKeyring::Two.into()],
|
vec![AccountKeyring::One.into(), AccountKeyring::Two.into()],
|
||||||
1000,
|
1000,
|
||||||
@@ -302,7 +214,6 @@ fn construct_genesis_should_work_with_wasm() {
|
|||||||
|
|
||||||
let _ = StateMachine::new(
|
let _ = StateMachine::new(
|
||||||
&backend,
|
&backend,
|
||||||
sp_state_machine::disabled_changes_trie_state::<_, u64>(),
|
|
||||||
&mut overlay,
|
&mut overlay,
|
||||||
&executor(),
|
&executor(),
|
||||||
"Core_execute_block",
|
"Core_execute_block",
|
||||||
@@ -318,7 +229,6 @@ fn construct_genesis_should_work_with_wasm() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn construct_genesis_with_bad_transaction_should_panic() {
|
fn construct_genesis_with_bad_transaction_should_panic() {
|
||||||
let mut storage = GenesisConfig::new(
|
let mut storage = GenesisConfig::new(
|
||||||
None,
|
|
||||||
vec![Sr25519Keyring::One.public().into(), Sr25519Keyring::Two.public().into()],
|
vec![Sr25519Keyring::One.public().into(), Sr25519Keyring::Two.public().into()],
|
||||||
vec![AccountKeyring::One.into(), AccountKeyring::Two.into()],
|
vec![AccountKeyring::One.into(), AccountKeyring::Two.into()],
|
||||||
68,
|
68,
|
||||||
@@ -337,7 +247,6 @@ fn construct_genesis_with_bad_transaction_should_panic() {
|
|||||||
|
|
||||||
let r = StateMachine::new(
|
let r = StateMachine::new(
|
||||||
&backend,
|
&backend,
|
||||||
sp_state_machine::disabled_changes_trie_state::<_, u64>(),
|
|
||||||
&mut overlay,
|
&mut overlay,
|
||||||
&executor(),
|
&executor(),
|
||||||
"Core_execute_block",
|
"Core_execute_block",
|
||||||
@@ -906,23 +815,6 @@ fn best_containing_on_longest_chain_with_max_depth_higher_than_best() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn key_changes_works() {
|
|
||||||
let (client, _, test_cases) = prepare_client_with_key_changes();
|
|
||||||
|
|
||||||
for (index, (begin, end, key, expected_result)) in test_cases.into_iter().enumerate() {
|
|
||||||
let end = client.block_hash(end).unwrap().unwrap();
|
|
||||||
let actual_result =
|
|
||||||
client.key_changes(begin, BlockId::Hash(end), None, &StorageKey(key)).unwrap();
|
|
||||||
if actual_result != expected_result {
|
|
||||||
panic!(
|
|
||||||
"Failed test {}: actual = {:?}, expected = {:?}",
|
|
||||||
index, actual_result, expected_result,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn import_with_justification() {
|
fn import_with_justification() {
|
||||||
let mut client = substrate_test_runtime_client::new();
|
let mut client = substrate_test_runtime_client::new();
|
||||||
@@ -1229,12 +1121,8 @@ fn doesnt_import_blocks_that_revert_finality() {
|
|||||||
ClientExt::finalize_block(&client, BlockId::Hash(a2.hash()), None).unwrap();
|
ClientExt::finalize_block(&client, BlockId::Hash(a2.hash()), None).unwrap();
|
||||||
|
|
||||||
let import_err = block_on(client.import(BlockOrigin::Own, b3)).err().unwrap();
|
let import_err = block_on(client.import(BlockOrigin::Own, b3)).err().unwrap();
|
||||||
let expected_err = ConsensusError::ClientImport(
|
let expected_err =
|
||||||
sp_blockchain::Error::RuntimeApiError(sp_api::ApiError::Application(Box::new(
|
ConsensusError::ClientImport(sp_blockchain::Error::NotInFinalizedChain.to_string());
|
||||||
sp_blockchain::Error::NotInFinalizedChain,
|
|
||||||
)))
|
|
||||||
.to_string(),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(import_err.to_string(), expected_err.to_string());
|
assert_eq!(import_err.to_string(), expected_err.to_string());
|
||||||
|
|
||||||
@@ -1536,152 +1424,6 @@ fn returns_status_for_pruned_blocks() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn imports_blocks_with_changes_tries_config_change() {
|
|
||||||
// create client with initial 4^2 configuration
|
|
||||||
let mut client = TestClientBuilder::with_default_backend()
|
|
||||||
.changes_trie_config(Some(ChangesTrieConfiguration {
|
|
||||||
digest_interval: 4,
|
|
||||||
digest_levels: 2,
|
|
||||||
}))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// ===================================================================
|
|
||||||
// blocks 1,2,3,4,5,6,7,8,9,10 are empty
|
|
||||||
// block 11 changes the key
|
|
||||||
// block 12 is the L1 digest that covers this change
|
|
||||||
// blocks 13,14,15,16,17,18,19,20,21,22 are empty
|
|
||||||
// block 23 changes the configuration to 5^1 AND is skewed digest
|
|
||||||
// ===================================================================
|
|
||||||
// blocks 24,25 are changing the key
|
|
||||||
// block 26 is empty
|
|
||||||
// block 27 changes the key
|
|
||||||
// block 28 is the L1 digest (NOT SKEWED!!!) that covers changes AND changes configuration to
|
|
||||||
// `3^1`
|
|
||||||
// ===================================================================
|
|
||||||
// block 29 is empty
|
|
||||||
// block 30 changes the key
|
|
||||||
// block 31 is L1 digest that covers this change
|
|
||||||
// ===================================================================
|
|
||||||
(1..11).for_each(|number| {
|
|
||||||
let block = client
|
|
||||||
.new_block_at(&BlockId::Number(number - 1), Default::default(), false)
|
|
||||||
.unwrap()
|
|
||||||
.build()
|
|
||||||
.unwrap()
|
|
||||||
.block;
|
|
||||||
block_on(client.import(BlockOrigin::Own, block)).unwrap();
|
|
||||||
});
|
|
||||||
(11..12).for_each(|number| {
|
|
||||||
let mut block = client
|
|
||||||
.new_block_at(&BlockId::Number(number - 1), Default::default(), false)
|
|
||||||
.unwrap();
|
|
||||||
block
|
|
||||||
.push_storage_change(vec![42], Some(number.to_le_bytes().to_vec()))
|
|
||||||
.unwrap();
|
|
||||||
let block = block.build().unwrap().block;
|
|
||||||
block_on(client.import(BlockOrigin::Own, block)).unwrap();
|
|
||||||
});
|
|
||||||
(12..23).for_each(|number| {
|
|
||||||
let block = client
|
|
||||||
.new_block_at(&BlockId::Number(number - 1), Default::default(), false)
|
|
||||||
.unwrap()
|
|
||||||
.build()
|
|
||||||
.unwrap()
|
|
||||||
.block;
|
|
||||||
block_on(client.import(BlockOrigin::Own, block)).unwrap();
|
|
||||||
});
|
|
||||||
(23..24).for_each(|number| {
|
|
||||||
let mut block = client
|
|
||||||
.new_block_at(&BlockId::Number(number - 1), Default::default(), false)
|
|
||||||
.unwrap();
|
|
||||||
block
|
|
||||||
.push_changes_trie_configuration_update(Some(ChangesTrieConfiguration {
|
|
||||||
digest_interval: 5,
|
|
||||||
digest_levels: 1,
|
|
||||||
}))
|
|
||||||
.unwrap();
|
|
||||||
let block = block.build().unwrap().block;
|
|
||||||
block_on(client.import(BlockOrigin::Own, block)).unwrap();
|
|
||||||
});
|
|
||||||
(24..26).for_each(|number| {
|
|
||||||
let mut block = client
|
|
||||||
.new_block_at(&BlockId::Number(number - 1), Default::default(), false)
|
|
||||||
.unwrap();
|
|
||||||
block
|
|
||||||
.push_storage_change(vec![42], Some(number.to_le_bytes().to_vec()))
|
|
||||||
.unwrap();
|
|
||||||
let block = block.build().unwrap().block;
|
|
||||||
block_on(client.import(BlockOrigin::Own, block)).unwrap();
|
|
||||||
});
|
|
||||||
(26..27).for_each(|number| {
|
|
||||||
let block = client
|
|
||||||
.new_block_at(&BlockId::Number(number - 1), Default::default(), false)
|
|
||||||
.unwrap()
|
|
||||||
.build()
|
|
||||||
.unwrap()
|
|
||||||
.block;
|
|
||||||
block_on(client.import(BlockOrigin::Own, block)).unwrap();
|
|
||||||
});
|
|
||||||
(27..28).for_each(|number| {
|
|
||||||
let mut block = client
|
|
||||||
.new_block_at(&BlockId::Number(number - 1), Default::default(), false)
|
|
||||||
.unwrap();
|
|
||||||
block
|
|
||||||
.push_storage_change(vec![42], Some(number.to_le_bytes().to_vec()))
|
|
||||||
.unwrap();
|
|
||||||
let block = block.build().unwrap().block;
|
|
||||||
block_on(client.import(BlockOrigin::Own, block)).unwrap();
|
|
||||||
});
|
|
||||||
(28..29).for_each(|number| {
|
|
||||||
let mut block = client
|
|
||||||
.new_block_at(&BlockId::Number(number - 1), Default::default(), false)
|
|
||||||
.unwrap();
|
|
||||||
block
|
|
||||||
.push_changes_trie_configuration_update(Some(ChangesTrieConfiguration {
|
|
||||||
digest_interval: 3,
|
|
||||||
digest_levels: 1,
|
|
||||||
}))
|
|
||||||
.unwrap();
|
|
||||||
let block = block.build().unwrap().block;
|
|
||||||
block_on(client.import(BlockOrigin::Own, block)).unwrap();
|
|
||||||
});
|
|
||||||
(29..30).for_each(|number| {
|
|
||||||
let block = client
|
|
||||||
.new_block_at(&BlockId::Number(number - 1), Default::default(), false)
|
|
||||||
.unwrap()
|
|
||||||
.build()
|
|
||||||
.unwrap()
|
|
||||||
.block;
|
|
||||||
block_on(client.import(BlockOrigin::Own, block)).unwrap();
|
|
||||||
});
|
|
||||||
(30..31).for_each(|number| {
|
|
||||||
let mut block = client
|
|
||||||
.new_block_at(&BlockId::Number(number - 1), Default::default(), false)
|
|
||||||
.unwrap();
|
|
||||||
block
|
|
||||||
.push_storage_change(vec![42], Some(number.to_le_bytes().to_vec()))
|
|
||||||
.unwrap();
|
|
||||||
let block = block.build().unwrap().block;
|
|
||||||
block_on(client.import(BlockOrigin::Own, block)).unwrap();
|
|
||||||
});
|
|
||||||
(31..32).for_each(|number| {
|
|
||||||
let block = client
|
|
||||||
.new_block_at(&BlockId::Number(number - 1), Default::default(), false)
|
|
||||||
.unwrap()
|
|
||||||
.build()
|
|
||||||
.unwrap()
|
|
||||||
.block;
|
|
||||||
block_on(client.import(BlockOrigin::Own, block)).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
// now check that configuration cache works
|
|
||||||
assert_eq!(
|
|
||||||
client.key_changes(1, BlockId::Number(31), None, &StorageKey(vec![42])).unwrap(),
|
|
||||||
vec![(30, 0), (27, 0), (25, 0), (24, 0), (11, 0)]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn storage_keys_iter_prefix_and_start_key_works() {
|
fn storage_keys_iter_prefix_and_start_key_works() {
|
||||||
let child_info = ChildInfo::new_default(b"child");
|
let child_info = ChildInfo::new_default(b"child");
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
//! Chain api required for the transaction pool.
|
//! Chain api required for the transaction pool.
|
||||||
|
|
||||||
use codec::{Decode, Encode};
|
use codec::Encode;
|
||||||
use futures::{
|
use futures::{
|
||||||
channel::{mpsc, oneshot},
|
channel::{mpsc, oneshot},
|
||||||
future::{ready, Future, FutureExt, Ready},
|
future::{ready, Future, FutureExt, Ready},
|
||||||
@@ -28,16 +28,12 @@ use futures::{
|
|||||||
use std::{marker::PhantomData, pin::Pin, sync::Arc};
|
use std::{marker::PhantomData, pin::Pin, sync::Arc};
|
||||||
|
|
||||||
use prometheus_endpoint::Registry as PrometheusRegistry;
|
use prometheus_endpoint::Registry as PrometheusRegistry;
|
||||||
use sc_client_api::{
|
use sc_client_api::{blockchain::HeaderBackend, BlockBackend};
|
||||||
blockchain::HeaderBackend,
|
|
||||||
light::{Fetcher, RemoteBodyRequest, RemoteCallRequest},
|
|
||||||
BlockBackend,
|
|
||||||
};
|
|
||||||
use sp_api::{ApiExt, ProvideRuntimeApi};
|
use sp_api::{ApiExt, ProvideRuntimeApi};
|
||||||
use sp_core::traits::SpawnEssentialNamed;
|
use sp_core::traits::SpawnEssentialNamed;
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::BlockId,
|
generic::BlockId,
|
||||||
traits::{self, Block as BlockT, BlockIdTo, Hash as HashT, Header as HeaderT},
|
traits::{self, Block as BlockT, BlockIdTo},
|
||||||
transaction_validity::{TransactionSource, TransactionValidity},
|
transaction_validity::{TransactionSource, TransactionValidity},
|
||||||
};
|
};
|
||||||
use sp_transaction_pool::runtime_api::TaggedTransactionQueue;
|
use sp_transaction_pool::runtime_api::TaggedTransactionQueue;
|
||||||
@@ -290,127 +286,3 @@ where
|
|||||||
validate_transaction_blocking(&*self.client, at, source, uxt)
|
validate_transaction_blocking(&*self.client, at, source, uxt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The transaction pool logic for light client.
|
|
||||||
pub struct LightChainApi<Client, F, Block> {
|
|
||||||
client: Arc<Client>,
|
|
||||||
fetcher: Arc<F>,
|
|
||||||
_phantom: PhantomData<Block>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Client, F, Block> LightChainApi<Client, F, Block> {
|
|
||||||
/// Create new transaction pool logic.
|
|
||||||
pub fn new(client: Arc<Client>, fetcher: Arc<F>) -> Self {
|
|
||||||
LightChainApi { client, fetcher, _phantom: Default::default() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Client, F, Block> graph::ChainApi for LightChainApi<Client, F, Block>
|
|
||||||
where
|
|
||||||
Block: BlockT,
|
|
||||||
Client: HeaderBackend<Block> + 'static,
|
|
||||||
F: Fetcher<Block> + 'static,
|
|
||||||
{
|
|
||||||
type Block = Block;
|
|
||||||
type Error = error::Error;
|
|
||||||
type ValidationFuture =
|
|
||||||
Box<dyn Future<Output = error::Result<TransactionValidity>> + Send + Unpin>;
|
|
||||||
type BodyFuture = Pin<
|
|
||||||
Box<
|
|
||||||
dyn Future<Output = error::Result<Option<Vec<<Self::Block as BlockT>::Extrinsic>>>>
|
|
||||||
+ Send,
|
|
||||||
>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
fn validate_transaction(
|
|
||||||
&self,
|
|
||||||
at: &BlockId<Self::Block>,
|
|
||||||
source: TransactionSource,
|
|
||||||
uxt: graph::ExtrinsicFor<Self>,
|
|
||||||
) -> Self::ValidationFuture {
|
|
||||||
let header_hash = self.client.expect_block_hash_from_id(at);
|
|
||||||
let header_and_hash = header_hash.and_then(|header_hash| {
|
|
||||||
self.client
|
|
||||||
.expect_header(BlockId::Hash(header_hash))
|
|
||||||
.map(|header| (header_hash, header))
|
|
||||||
});
|
|
||||||
let (block, header) = match header_and_hash {
|
|
||||||
Ok((header_hash, header)) => (header_hash, header),
|
|
||||||
Err(err) => return Box::new(ready(Err(err.into()))),
|
|
||||||
};
|
|
||||||
let remote_validation_request = self.fetcher.remote_call(RemoteCallRequest {
|
|
||||||
block,
|
|
||||||
header,
|
|
||||||
method: "TaggedTransactionQueue_validate_transaction".into(),
|
|
||||||
call_data: (source, uxt, block).encode(),
|
|
||||||
retry_count: None,
|
|
||||||
});
|
|
||||||
let remote_validation_request = remote_validation_request.then(move |result| {
|
|
||||||
let result: error::Result<TransactionValidity> =
|
|
||||||
result.map_err(Into::into).and_then(|result| {
|
|
||||||
Decode::decode(&mut &result[..]).map_err(|e| {
|
|
||||||
Error::RuntimeApi(format!("Error decoding tx validation result: {:?}", e))
|
|
||||||
})
|
|
||||||
});
|
|
||||||
ready(result)
|
|
||||||
});
|
|
||||||
|
|
||||||
Box::new(remote_validation_request)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn block_id_to_number(
|
|
||||||
&self,
|
|
||||||
at: &BlockId<Self::Block>,
|
|
||||||
) -> error::Result<Option<graph::NumberFor<Self>>> {
|
|
||||||
Ok(self.client.block_number_from_id(at)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn block_id_to_hash(
|
|
||||||
&self,
|
|
||||||
at: &BlockId<Self::Block>,
|
|
||||||
) -> error::Result<Option<graph::BlockHash<Self>>> {
|
|
||||||
Ok(self.client.block_hash_from_id(at)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hash_and_length(
|
|
||||||
&self,
|
|
||||||
ex: &graph::ExtrinsicFor<Self>,
|
|
||||||
) -> (graph::ExtrinsicHash<Self>, usize) {
|
|
||||||
ex.using_encoded(|x| (<<Block::Header as HeaderT>::Hashing as HashT>::hash(x), x.len()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn block_body(&self, id: &BlockId<Self::Block>) -> Self::BodyFuture {
|
|
||||||
let header = self
|
|
||||||
.client
|
|
||||||
.header(*id)
|
|
||||||
.and_then(|h| h.ok_or_else(|| sp_blockchain::Error::UnknownBlock(format!("{}", id))));
|
|
||||||
let header = match header {
|
|
||||||
Ok(header) => header,
|
|
||||||
Err(err) => {
|
|
||||||
log::warn!(target: "txpool", "Failed to query header: {:?}", err);
|
|
||||||
return Box::pin(ready(Ok(None)))
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let fetcher = self.fetcher.clone();
|
|
||||||
async move {
|
|
||||||
let transactions = fetcher
|
|
||||||
.remote_body(RemoteBodyRequest { header, retry_count: None })
|
|
||||||
.await
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
log::warn!(target: "txpool", "Failed to fetch block body: {:?}", e);
|
|
||||||
Vec::new()
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(Some(transactions))
|
|
||||||
}
|
|
||||||
.boxed()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn block_header(
|
|
||||||
&self,
|
|
||||||
at: &BlockId<Self::Block>,
|
|
||||||
) -> Result<Option<<Self::Block as BlockT>::Header>, Self::Error> {
|
|
||||||
self.client.header(*at).map_err(Into::into)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ pub mod test_helpers {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use crate::api::{FullChainApi, LightChainApi};
|
pub use crate::api::FullChainApi;
|
||||||
use futures::{
|
use futures::{
|
||||||
channel::oneshot,
|
channel::oneshot,
|
||||||
future::{self, ready},
|
future::{self, ready},
|
||||||
@@ -79,9 +79,6 @@ type PolledIterator<PoolApi> = Pin<Box<dyn Future<Output = ReadyIteratorFor<Pool
|
|||||||
|
|
||||||
/// A transaction pool for a full node.
|
/// A transaction pool for a full node.
|
||||||
pub type FullPool<Block, Client> = BasicPool<FullChainApi<Client, Block>, Block>;
|
pub type FullPool<Block, Client> = BasicPool<FullChainApi<Client, Block>, Block>;
|
||||||
/// A transaction pool for a light node.
|
|
||||||
pub type LightPool<Block, Client, Fetcher> =
|
|
||||||
BasicPool<LightChainApi<Client, Fetcher, Block>, Block>;
|
|
||||||
|
|
||||||
/// Basic implementation of transaction pool that can be customized by providing PoolApi.
|
/// Basic implementation of transaction pool that can be customized by providing PoolApi.
|
||||||
pub struct BasicPool<PoolApi, Block>
|
pub struct BasicPool<PoolApi, Block>
|
||||||
@@ -364,33 +361,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Block, Client, Fetcher> LightPool<Block, Client, Fetcher>
|
|
||||||
where
|
|
||||||
Block: BlockT,
|
|
||||||
Client: sp_blockchain::HeaderBackend<Block> + sc_client_api::UsageProvider<Block> + 'static,
|
|
||||||
Fetcher: sc_client_api::Fetcher<Block> + 'static,
|
|
||||||
{
|
|
||||||
/// Create new basic transaction pool for a light node with the provided api.
|
|
||||||
pub fn new_light(
|
|
||||||
options: graph::Options,
|
|
||||||
prometheus: Option<&PrometheusRegistry>,
|
|
||||||
spawner: impl SpawnEssentialNamed,
|
|
||||||
client: Arc<Client>,
|
|
||||||
fetcher: Arc<Fetcher>,
|
|
||||||
) -> Self {
|
|
||||||
let pool_api = Arc::new(LightChainApi::new(client.clone(), fetcher));
|
|
||||||
Self::with_revalidation_type(
|
|
||||||
options,
|
|
||||||
false.into(),
|
|
||||||
pool_api,
|
|
||||||
prometheus,
|
|
||||||
RevalidationType::Light,
|
|
||||||
spawner,
|
|
||||||
client.usage_info().chain.best_number,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Block, Client> FullPool<Block, Client>
|
impl<Block, Client> FullPool<Block, Client>
|
||||||
where
|
where
|
||||||
Block: BlockT,
|
Block: BlockT,
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ impl<T: Config> Pallet<T> {
|
|||||||
fn change_authorities(new: WeakBoundedVec<T::AuthorityId, T::MaxAuthorities>) {
|
fn change_authorities(new: WeakBoundedVec<T::AuthorityId, T::MaxAuthorities>) {
|
||||||
<Authorities<T>>::put(&new);
|
<Authorities<T>>::put(&new);
|
||||||
|
|
||||||
let log: DigestItem<T::Hash> = DigestItem::Consensus(
|
let log = DigestItem::Consensus(
|
||||||
AURA_ENGINE_ID,
|
AURA_ENGINE_ID,
|
||||||
ConsensusLog::AuthoritiesChange(new.into_inner()).encode(),
|
ConsensusLog::AuthoritiesChange(new.into_inner()).encode(),
|
||||||
);
|
);
|
||||||
@@ -222,7 +222,7 @@ impl<T: Config> OneSessionHandler<T::AccountId> for Pallet<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_disabled(i: u32) {
|
fn on_disabled(i: u32) {
|
||||||
let log: DigestItem<T::Hash> = DigestItem::Consensus(
|
let log = DigestItem::Consensus(
|
||||||
AURA_ENGINE_ID,
|
AURA_ENGINE_ID,
|
||||||
ConsensusLog::<T::AuthorityId>::OnDisabled(i as AuthorityIndex).encode(),
|
ConsensusLog::<T::AuthorityId>::OnDisabled(i as AuthorityIndex).encode(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -633,7 +633,7 @@ impl<T: Config> Pallet<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn deposit_consensus<U: Encode>(new: U) {
|
fn deposit_consensus<U: Encode>(new: U) {
|
||||||
let log: DigestItem<T::Hash> = DigestItem::Consensus(BABE_ENGINE_ID, new.encode());
|
let log = DigestItem::Consensus(BABE_ENGINE_ID, new.encode());
|
||||||
<frame_system::Pallet<T>>::deposit_log(log.into())
|
<frame_system::Pallet<T>>::deposit_log(log.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ fn init_block(block: u64) {
|
|||||||
BeefyMmr::on_initialize(block);
|
BeefyMmr::on_initialize(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn beefy_log(log: ConsensusLog<BeefyId>) -> DigestItem<H256> {
|
pub fn beefy_log(log: ConsensusLog<BeefyId>) -> DigestItem {
|
||||||
DigestItem::Consensus(BEEFY_ENGINE_ID, log.encode())
|
DigestItem::Consensus(BEEFY_ENGINE_ID, log.encode())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ impl<T: Config> Pallet<T> {
|
|||||||
let next_id = Self::validator_set_id() + 1u64;
|
let next_id = Self::validator_set_id() + 1u64;
|
||||||
<ValidatorSetId<T>>::put(next_id);
|
<ValidatorSetId<T>>::put(next_id);
|
||||||
|
|
||||||
let log: DigestItem<T::Hash> = DigestItem::Consensus(
|
let log = DigestItem::Consensus(
|
||||||
BEEFY_ENGINE_ID,
|
BEEFY_ENGINE_ID,
|
||||||
ConsensusLog::AuthoritiesChange(ValidatorSet { validators: new, id: next_id })
|
ConsensusLog::AuthoritiesChange(ValidatorSet { validators: new, id: next_id })
|
||||||
.encode(),
|
.encode(),
|
||||||
@@ -163,7 +163,7 @@ impl<T: Config> OneSessionHandler<T::AccountId> for Pallet<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_disabled(i: u32) {
|
fn on_disabled(i: u32) {
|
||||||
let log: DigestItem<T::Hash> = DigestItem::Consensus(
|
let log = DigestItem::Consensus(
|
||||||
BEEFY_ENGINE_ID,
|
BEEFY_ENGINE_ID,
|
||||||
ConsensusLog::<T::BeefyId>::OnDisabled(i as AuthorityIndex).encode(),
|
ConsensusLog::<T::BeefyId>::OnDisabled(i as AuthorityIndex).encode(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ use std::vec;
|
|||||||
use beefy_primitives::ValidatorSet;
|
use beefy_primitives::ValidatorSet;
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
|
|
||||||
use sp_core::H256;
|
|
||||||
use sp_runtime::DigestItem;
|
use sp_runtime::DigestItem;
|
||||||
|
|
||||||
use frame_support::traits::OnInitialize;
|
use frame_support::traits::OnInitialize;
|
||||||
@@ -32,7 +31,7 @@ fn init_block(block: u64) {
|
|||||||
Session::on_initialize(block);
|
Session::on_initialize(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn beefy_log(log: ConsensusLog<BeefyId>) -> DigestItem<H256> {
|
pub fn beefy_log(log: ConsensusLog<BeefyId>) -> DigestItem {
|
||||||
DigestItem::Consensus(BEEFY_ENGINE_ID, log.encode())
|
DigestItem::Consensus(BEEFY_ENGINE_ID, log.encode())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,6 @@ use frame_support::{
|
|||||||
},
|
},
|
||||||
weights::{DispatchClass, DispatchInfo, GetDispatchInfo},
|
weights::{DispatchClass, DispatchInfo, GetDispatchInfo},
|
||||||
};
|
};
|
||||||
use frame_system::DigestOf;
|
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::Digest,
|
generic::Digest,
|
||||||
traits::{
|
traits::{
|
||||||
@@ -281,8 +280,8 @@ where
|
|||||||
Self::initialize_block_impl(header.number(), header.parent_hash(), &digests);
|
Self::initialize_block_impl(header.number(), header.parent_hash(), &digests);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_pre_digest(header: &System::Header) -> DigestOf<System> {
|
fn extract_pre_digest(header: &System::Header) -> Digest {
|
||||||
let mut digest = <DigestOf<System>>::default();
|
let mut digest = <Digest>::default();
|
||||||
header.digest().logs().iter().for_each(|d| {
|
header.digest().logs().iter().for_each(|d| {
|
||||||
if d.as_pre_runtime().is_some() {
|
if d.as_pre_runtime().is_some() {
|
||||||
digest.push(d.clone())
|
digest.push(d.clone())
|
||||||
@@ -294,7 +293,7 @@ where
|
|||||||
fn initialize_block_impl(
|
fn initialize_block_impl(
|
||||||
block_number: &System::BlockNumber,
|
block_number: &System::BlockNumber,
|
||||||
parent_hash: &System::Hash,
|
parent_hash: &System::Hash,
|
||||||
digest: &Digest<System::Hash>,
|
digest: &Digest,
|
||||||
) {
|
) {
|
||||||
let mut weight = 0;
|
let mut weight = 0;
|
||||||
if Self::runtime_upgraded() {
|
if Self::runtime_upgraded() {
|
||||||
|
|||||||
@@ -508,7 +508,7 @@ impl<T: Config> Pallet<T> {
|
|||||||
|
|
||||||
/// Deposit one of this module's logs.
|
/// Deposit one of this module's logs.
|
||||||
fn deposit_log(log: ConsensusLog<T::BlockNumber>) {
|
fn deposit_log(log: ConsensusLog<T::BlockNumber>) {
|
||||||
let log: DigestItem<T::Hash> = DigestItem::Consensus(GRANDPA_ENGINE_ID, log.encode());
|
let log = DigestItem::Consensus(GRANDPA_ENGINE_ID, log.encode());
|
||||||
<frame_system::Pallet<T>>::deposit_log(log.into());
|
<frame_system::Pallet<T>>::deposit_log(log.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ impl Config for Test {
|
|||||||
type MaxAuthorities = MaxAuthorities;
|
type MaxAuthorities = MaxAuthorities;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn grandpa_log(log: ConsensusLog<u64>) -> DigestItem<H256> {
|
pub fn grandpa_log(log: ConsensusLog<u64>) -> DigestItem {
|
||||||
DigestItem::Consensus(GRANDPA_ENGINE_ID, log.encode())
|
DigestItem::Consensus(GRANDPA_ENGINE_ID, log.encode())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1223,7 +1223,7 @@ mod private {
|
|||||||
pub trait Sealed {}
|
pub trait Sealed {}
|
||||||
|
|
||||||
impl<T: Encode> Sealed for Vec<T> {}
|
impl<T: Encode> Sealed for Vec<T> {}
|
||||||
impl<Hash: Encode> Sealed for Digest<Hash> {}
|
impl Sealed for Digest {}
|
||||||
impl<T, S> Sealed for BoundedVec<T, S> {}
|
impl<T, S> Sealed for BoundedVec<T, S> {}
|
||||||
impl<T, S> Sealed for WeakBoundedVec<T, S> {}
|
impl<T, S> Sealed for WeakBoundedVec<T, S> {}
|
||||||
impl<K, V, S> Sealed for bounded_btree_map::BoundedBTreeMap<K, V, S> {}
|
impl<K, V, S> Sealed for bounded_btree_map::BoundedBTreeMap<K, V, S> {}
|
||||||
@@ -1263,7 +1263,7 @@ impl<T: Encode> StorageDecodeLength for Vec<T> {}
|
|||||||
/// We abuse the fact that SCALE does not put any marker into the encoding, i.e. we only encode the
|
/// We abuse the fact that SCALE does not put any marker into the encoding, i.e. we only encode the
|
||||||
/// internal vec and we can append to this vec. We have a test that ensures that if the `Digest`
|
/// internal vec and we can append to this vec. We have a test that ensures that if the `Digest`
|
||||||
/// format ever changes, we need to remove this here.
|
/// format ever changes, we need to remove this here.
|
||||||
impl<Hash: Encode> StorageAppend<DigestItem<Hash>> for Digest<Hash> {}
|
impl StorageAppend<DigestItem> for Digest {}
|
||||||
|
|
||||||
/// Marker trait that is implemented for types that support the `storage::append` api with a limit
|
/// Marker trait that is implemented for types that support the `storage::append` api with a limit
|
||||||
/// on the number of element.
|
/// on the number of element.
|
||||||
@@ -1484,8 +1484,8 @@ mod test {
|
|||||||
fn digest_storage_append_works_as_expected() {
|
fn digest_storage_append_works_as_expected() {
|
||||||
TestExternalities::default().execute_with(|| {
|
TestExternalities::default().execute_with(|| {
|
||||||
struct Storage;
|
struct Storage;
|
||||||
impl generator::StorageValue<Digest<u32>> for Storage {
|
impl generator::StorageValue<Digest> for Storage {
|
||||||
type Query = Digest<u32>;
|
type Query = Digest;
|
||||||
|
|
||||||
fn module_prefix() -> &'static [u8] {
|
fn module_prefix() -> &'static [u8] {
|
||||||
b"MyModule"
|
b"MyModule"
|
||||||
@@ -1495,23 +1495,20 @@ mod test {
|
|||||||
b"Storage"
|
b"Storage"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_optional_value_to_query(v: Option<Digest<u32>>) -> Self::Query {
|
fn from_optional_value_to_query(v: Option<Digest>) -> Self::Query {
|
||||||
v.unwrap()
|
v.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_query_to_optional_value(v: Self::Query) -> Option<Digest<u32>> {
|
fn from_query_to_optional_value(v: Self::Query) -> Option<Digest> {
|
||||||
Some(v)
|
Some(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Storage::append(DigestItem::ChangesTrieRoot(1));
|
|
||||||
Storage::append(DigestItem::Other(Vec::new()));
|
Storage::append(DigestItem::Other(Vec::new()));
|
||||||
|
|
||||||
let value = unhashed::get_raw(&Storage::storage_value_final_key()).unwrap();
|
let value = unhashed::get_raw(&Storage::storage_value_final_key()).unwrap();
|
||||||
|
|
||||||
let expected = Digest {
|
let expected = Digest { logs: vec![DigestItem::Other(Vec::new())] };
|
||||||
logs: vec![DigestItem::ChangesTrieRoot(1), DigestItem::Other(Vec::new())],
|
|
||||||
};
|
|
||||||
assert_eq!(Digest::decode(&mut &value[..]).unwrap(), expected);
|
assert_eq!(Digest::decode(&mut &value[..]).unwrap(), expected);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,8 @@
|
|||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
use frame_benchmarking::{benchmarks, whitelisted_caller};
|
use frame_benchmarking::{benchmarks, whitelisted_caller};
|
||||||
use frame_support::{storage, traits::Get, weights::DispatchClass};
|
use frame_support::{storage, traits::Get, weights::DispatchClass};
|
||||||
use frame_system::{Call, DigestItemOf, Pallet as System, RawOrigin};
|
use frame_system::{Call, Pallet as System, RawOrigin};
|
||||||
use sp_core::{storage::well_known_keys, ChangesTrieConfiguration};
|
use sp_core::storage::well_known_keys;
|
||||||
use sp_runtime::traits::Hash;
|
use sp_runtime::traits::Hash;
|
||||||
use sp_std::{prelude::*, vec};
|
use sp_std::{prelude::*, vec};
|
||||||
|
|
||||||
@@ -62,23 +62,6 @@ benchmarks! {
|
|||||||
assert_eq!(current_code.len(), 4_000_000 as usize);
|
assert_eq!(current_code.len(), 4_000_000 as usize);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_changes_trie_config {
|
|
||||||
let d = 1000;
|
|
||||||
|
|
||||||
let digest_item = DigestItemOf::<T>::Other(vec![]);
|
|
||||||
|
|
||||||
for i in 0 .. d {
|
|
||||||
System::<T>::deposit_log(digest_item.clone());
|
|
||||||
}
|
|
||||||
let changes_trie_config = ChangesTrieConfiguration {
|
|
||||||
digest_interval: d,
|
|
||||||
digest_levels: d,
|
|
||||||
};
|
|
||||||
}: _(RawOrigin::Root, Some(changes_trie_config))
|
|
||||||
verify {
|
|
||||||
assert_eq!(System::<T>::digest().logs.len(), (d + 1) as usize)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[skip_meta]
|
#[skip_meta]
|
||||||
set_storage {
|
set_storage {
|
||||||
let i in 1 .. 1000;
|
let i in 1 .. 1000;
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ use frame_support::{
|
|||||||
Parameter,
|
Parameter,
|
||||||
};
|
};
|
||||||
use scale_info::TypeInfo;
|
use scale_info::TypeInfo;
|
||||||
use sp_core::{storage::well_known_keys, ChangesTrieConfiguration};
|
use sp_core::storage::well_known_keys;
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use frame_support::traits::GenesisBuild;
|
use frame_support::traits::GenesisBuild;
|
||||||
@@ -405,37 +405,6 @@ pub mod pallet {
|
|||||||
Ok(().into())
|
Ok(().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the new changes trie configuration.
|
|
||||||
///
|
|
||||||
/// # <weight>
|
|
||||||
/// - `O(1)`
|
|
||||||
/// - 1 storage write or delete (codec `O(1)`).
|
|
||||||
/// - 1 call to `deposit_log`: Uses `append` API, so O(1)
|
|
||||||
/// - Base Weight: 7.218 µs
|
|
||||||
/// - DB Weight:
|
|
||||||
/// - Writes: Changes Trie, System Digest
|
|
||||||
/// # </weight>
|
|
||||||
#[pallet::weight((T::SystemWeightInfo::set_changes_trie_config(), DispatchClass::Operational))]
|
|
||||||
pub fn set_changes_trie_config(
|
|
||||||
origin: OriginFor<T>,
|
|
||||||
changes_trie_config: Option<ChangesTrieConfiguration>,
|
|
||||||
) -> DispatchResultWithPostInfo {
|
|
||||||
ensure_root(origin)?;
|
|
||||||
match changes_trie_config.clone() {
|
|
||||||
Some(changes_trie_config) => storage::unhashed::put_raw(
|
|
||||||
well_known_keys::CHANGES_TRIE_CONFIG,
|
|
||||||
&changes_trie_config.encode(),
|
|
||||||
),
|
|
||||||
None => storage::unhashed::kill(well_known_keys::CHANGES_TRIE_CONFIG),
|
|
||||||
}
|
|
||||||
|
|
||||||
let log = generic::DigestItem::ChangesTrieSignal(
|
|
||||||
generic::ChangesTrieSignal::NewConfiguration(changes_trie_config),
|
|
||||||
);
|
|
||||||
Self::deposit_log(log.into());
|
|
||||||
Ok(().into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set some items of storage.
|
/// Set some items of storage.
|
||||||
///
|
///
|
||||||
/// # <weight>
|
/// # <weight>
|
||||||
@@ -617,7 +586,7 @@ pub mod pallet {
|
|||||||
/// Digest of the current block, also part of the block header.
|
/// Digest of the current block, also part of the block header.
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
#[pallet::getter(fn digest)]
|
#[pallet::getter(fn digest)]
|
||||||
pub(super) type Digest<T: Config> = StorageValue<_, DigestOf<T>, ValueQuery>;
|
pub(super) type Digest<T: Config> = StorageValue<_, generic::Digest, ValueQuery>;
|
||||||
|
|
||||||
/// Events deposited for the current block.
|
/// Events deposited for the current block.
|
||||||
///
|
///
|
||||||
@@ -666,7 +635,6 @@ pub mod pallet {
|
|||||||
|
|
||||||
#[pallet::genesis_config]
|
#[pallet::genesis_config]
|
||||||
pub struct GenesisConfig {
|
pub struct GenesisConfig {
|
||||||
pub changes_trie_config: Option<ChangesTrieConfiguration>,
|
|
||||||
#[serde(with = "sp_core::bytes")]
|
#[serde(with = "sp_core::bytes")]
|
||||||
pub code: Vec<u8>,
|
pub code: Vec<u8>,
|
||||||
}
|
}
|
||||||
@@ -674,7 +642,7 @@ pub mod pallet {
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl Default for GenesisConfig {
|
impl Default for GenesisConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self { changes_trie_config: Default::default(), code: Default::default() }
|
Self { code: Default::default() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -689,12 +657,6 @@ pub mod pallet {
|
|||||||
|
|
||||||
sp_io::storage::set(well_known_keys::CODE, &self.code);
|
sp_io::storage::set(well_known_keys::CODE, &self.code);
|
||||||
sp_io::storage::set(well_known_keys::EXTRINSIC_INDEX, &0u32.encode());
|
sp_io::storage::set(well_known_keys::EXTRINSIC_INDEX, &0u32.encode());
|
||||||
if let Some(ref changes_trie_config) = self.changes_trie_config {
|
|
||||||
sp_io::storage::set(
|
|
||||||
well_known_keys::CHANGES_TRIE_CONFIG,
|
|
||||||
&changes_trie_config.encode(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -759,9 +721,6 @@ impl GenesisConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type DigestOf<T> = generic::Digest<<T as Config>::Hash>;
|
|
||||||
pub type DigestItemOf<T> = generic::DigestItem<<T as Config>::Hash>;
|
|
||||||
|
|
||||||
pub type Key = Vec<u8>;
|
pub type Key = Vec<u8>;
|
||||||
pub type KeyValue = (Vec<u8>, Vec<u8>);
|
pub type KeyValue = (Vec<u8>, Vec<u8>);
|
||||||
|
|
||||||
@@ -1369,7 +1328,7 @@ impl<T: Config> Pallet<T> {
|
|||||||
pub fn initialize(
|
pub fn initialize(
|
||||||
number: &T::BlockNumber,
|
number: &T::BlockNumber,
|
||||||
parent_hash: &T::Hash,
|
parent_hash: &T::Hash,
|
||||||
digest: &DigestOf<T>,
|
digest: &generic::Digest,
|
||||||
kind: InitKind,
|
kind: InitKind,
|
||||||
) {
|
) {
|
||||||
// populate environment
|
// populate environment
|
||||||
@@ -1409,7 +1368,7 @@ impl<T: Config> Pallet<T> {
|
|||||||
// stay to be inspected by the client and will be cleared by `Self::initialize`.
|
// stay to be inspected by the client and will be cleared by `Self::initialize`.
|
||||||
let number = <Number<T>>::get();
|
let number = <Number<T>>::get();
|
||||||
let parent_hash = <ParentHash<T>>::get();
|
let parent_hash = <ParentHash<T>>::get();
|
||||||
let mut digest = <Digest<T>>::get();
|
let digest = <Digest<T>>::get();
|
||||||
|
|
||||||
let extrinsics = (0..ExtrinsicCount::<T>::take().unwrap_or_default())
|
let extrinsics = (0..ExtrinsicCount::<T>::take().unwrap_or_default())
|
||||||
.map(ExtrinsicData::<T>::take)
|
.map(ExtrinsicData::<T>::take)
|
||||||
@@ -1427,17 +1386,6 @@ impl<T: Config> Pallet<T> {
|
|||||||
|
|
||||||
let storage_root = T::Hash::decode(&mut &sp_io::storage::root()[..])
|
let storage_root = T::Hash::decode(&mut &sp_io::storage::root()[..])
|
||||||
.expect("Node is configured to use the same hash; qed");
|
.expect("Node is configured to use the same hash; qed");
|
||||||
let storage_changes_root = sp_io::storage::changes_root(&parent_hash.encode());
|
|
||||||
|
|
||||||
// we can't compute changes trie root earlier && put it to the Digest
|
|
||||||
// because it will include all currently existing temporaries.
|
|
||||||
if let Some(storage_changes_root) = storage_changes_root {
|
|
||||||
let item = generic::DigestItem::ChangesTrieRoot(
|
|
||||||
T::Hash::decode(&mut &storage_changes_root[..])
|
|
||||||
.expect("Node is configured to use the same hash; qed"),
|
|
||||||
);
|
|
||||||
digest.push(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
<T::Header as traits::Header>::new(
|
<T::Header as traits::Header>::new(
|
||||||
number,
|
number,
|
||||||
@@ -1454,7 +1402,7 @@ impl<T: Config> Pallet<T> {
|
|||||||
/// - `O(1)`
|
/// - `O(1)`
|
||||||
/// - 1 storage write (codec `O(1)`)
|
/// - 1 storage write (codec `O(1)`)
|
||||||
/// # </weight>
|
/// # </weight>
|
||||||
pub fn deposit_log(item: DigestItemOf<T>) {
|
pub fn deposit_log(item: generic::DigestItem) {
|
||||||
<Digest<T>>::append(item);
|
<Digest<T>>::append(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -288,10 +288,6 @@ fn generate_runtime_api_base_structures() -> Result<TokenStream> {
|
|||||||
fn into_storage_changes(
|
fn into_storage_changes(
|
||||||
&self,
|
&self,
|
||||||
backend: &Self::StateBackend,
|
backend: &Self::StateBackend,
|
||||||
changes_trie_state: Option<&#crate_::ChangesTrieState<
|
|
||||||
#crate_::HashFor<Block>,
|
|
||||||
#crate_::NumberFor<Block>,
|
|
||||||
>>,
|
|
||||||
parent_hash: Block::Hash,
|
parent_hash: Block::Hash,
|
||||||
) -> std::result::Result<
|
) -> std::result::Result<
|
||||||
#crate_::StorageChanges<C::StateBackend, Block>,
|
#crate_::StorageChanges<C::StateBackend, Block>,
|
||||||
@@ -299,7 +295,6 @@ fn generate_runtime_api_base_structures() -> Result<TokenStream> {
|
|||||||
> where Self: Sized {
|
> where Self: Sized {
|
||||||
self.changes.replace(Default::default()).into_storage_changes(
|
self.changes.replace(Default::default()).into_storage_changes(
|
||||||
backend,
|
backend,
|
||||||
changes_trie_state,
|
|
||||||
parent_hash,
|
parent_hash,
|
||||||
self.storage_transaction_cache.replace(Default::default()),
|
self.storage_transaction_cache.replace(Default::default()),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -116,10 +116,6 @@ fn implement_common_api_traits(block_type: TypePath, self_ty: Type) -> Result<To
|
|||||||
fn into_storage_changes(
|
fn into_storage_changes(
|
||||||
&self,
|
&self,
|
||||||
_: &Self::StateBackend,
|
_: &Self::StateBackend,
|
||||||
_: Option<&#crate_::ChangesTrieState<
|
|
||||||
#crate_::HashFor<#block_type>,
|
|
||||||
#crate_::NumberFor<#block_type>,
|
|
||||||
>>,
|
|
||||||
_: <#block_type as #crate_::BlockT>::Hash,
|
_: <#block_type as #crate_::BlockT>::Hash,
|
||||||
) -> std::result::Result<
|
) -> std::result::Result<
|
||||||
#crate_::StorageChanges<Self::StateBackend, #block_type>,
|
#crate_::StorageChanges<Self::StateBackend, #block_type>,
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ pub use sp_runtime::{
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub use sp_state_machine::{
|
pub use sp_state_machine::{
|
||||||
Backend as StateBackend, ChangesTrieState, InMemoryBackend, OverlayedChanges, StorageProof,
|
Backend as StateBackend, InMemoryBackend, OverlayedChanges, StorageProof,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use sp_std::result;
|
use sp_std::result;
|
||||||
@@ -394,14 +394,12 @@ pub type ProofRecorder<B> = sp_state_machine::ProofRecorder<<B as BlockT>::Hash>
|
|||||||
pub type StorageTransactionCache<Block, Backend> = sp_state_machine::StorageTransactionCache<
|
pub type StorageTransactionCache<Block, Backend> = sp_state_machine::StorageTransactionCache<
|
||||||
<Backend as StateBackend<HashFor<Block>>>::Transaction,
|
<Backend as StateBackend<HashFor<Block>>>::Transaction,
|
||||||
HashFor<Block>,
|
HashFor<Block>,
|
||||||
NumberFor<Block>,
|
|
||||||
>;
|
>;
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub type StorageChanges<SBackend, Block> = sp_state_machine::StorageChanges<
|
pub type StorageChanges<SBackend, Block> = sp_state_machine::StorageChanges<
|
||||||
<SBackend as StateBackend<HashFor<Block>>>::Transaction,
|
<SBackend as StateBackend<HashFor<Block>>>::Transaction,
|
||||||
HashFor<Block>,
|
HashFor<Block>,
|
||||||
NumberFor<Block>,
|
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/// Extract the state backend type for a type that implements `ProvideRuntimeApi`.
|
/// Extract the state backend type for a type that implements `ProvideRuntimeApi`.
|
||||||
@@ -514,7 +512,6 @@ pub trait ApiExt<Block: BlockT> {
|
|||||||
fn into_storage_changes(
|
fn into_storage_changes(
|
||||||
&self,
|
&self,
|
||||||
backend: &Self::StateBackend,
|
backend: &Self::StateBackend,
|
||||||
changes_trie_state: Option<&ChangesTrieState<HashFor<Block>, NumberFor<Block>>>,
|
|
||||||
parent_hash: Block::Hash,
|
parent_hash: Block::Hash,
|
||||||
) -> Result<StorageChanges<Self::StateBackend, Block>, String>
|
) -> Result<StorageChanges<Self::StateBackend, Block>, String>
|
||||||
where
|
where
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ fn record_proof_works() {
|
|||||||
None,
|
None,
|
||||||
8,
|
8,
|
||||||
);
|
);
|
||||||
execution_proof_check_on_trie_backend::<_, u64, _, _>(
|
execution_proof_check_on_trie_backend(
|
||||||
&backend,
|
&backend,
|
||||||
&mut overlay,
|
&mut overlay,
|
||||||
&executor,
|
&executor,
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
|
|
||||||
//! Substrate blockchain trait
|
//! Substrate blockchain trait
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
@@ -96,8 +94,6 @@ pub trait Backend<Block: BlockT>:
|
|||||||
fn justifications(&self, id: BlockId<Block>) -> Result<Option<Justifications>>;
|
fn justifications(&self, id: BlockId<Block>) -> Result<Option<Justifications>>;
|
||||||
/// Get last finalized block hash.
|
/// Get last finalized block hash.
|
||||||
fn last_finalized(&self) -> Result<Block::Hash>;
|
fn last_finalized(&self) -> Result<Block::Hash>;
|
||||||
/// Returns data cache reference, if it is enabled on this backend.
|
|
||||||
fn cache(&self) -> Option<Arc<dyn Cache<Block>>>;
|
|
||||||
|
|
||||||
/// Returns hashes of all blocks that are leaves of the block tree.
|
/// Returns hashes of all blocks that are leaves of the block tree.
|
||||||
/// in other words, that have no children, are chain heads.
|
/// in other words, that have no children, are chain heads.
|
||||||
@@ -237,33 +233,6 @@ pub trait Backend<Block: BlockT>:
|
|||||||
fn block_indexed_body(&self, id: BlockId<Block>) -> Result<Option<Vec<Vec<u8>>>>;
|
fn block_indexed_body(&self, id: BlockId<Block>) -> Result<Option<Vec<Vec<u8>>>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provides access to the optional cache.
|
|
||||||
pub trait ProvideCache<Block: BlockT> {
|
|
||||||
/// Returns data cache reference, if it is enabled on this backend.
|
|
||||||
fn cache(&self) -> Option<Arc<dyn Cache<Block>>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Blockchain optional data cache.
|
|
||||||
pub trait Cache<Block: BlockT>: Send + Sync {
|
|
||||||
/// Initialize genesis value for the given cache.
|
|
||||||
///
|
|
||||||
/// The operation should be performed once before anything else is inserted in the cache.
|
|
||||||
/// Otherwise cache may end up in inconsistent state.
|
|
||||||
fn initialize(&self, key: &well_known_cache_keys::Id, value_at_genesis: Vec<u8>) -> Result<()>;
|
|
||||||
/// Returns cached value by the given key.
|
|
||||||
///
|
|
||||||
/// Returned tuple is the range where value has been active and the value itself.
|
|
||||||
/// Fails if read from cache storage fails or if the value for block is discarded
|
|
||||||
/// (i.e. if block is earlier that best finalized, but it is not in canonical chain).
|
|
||||||
fn get_at(
|
|
||||||
&self,
|
|
||||||
key: &well_known_cache_keys::Id,
|
|
||||||
block: &BlockId<Block>,
|
|
||||||
) -> Result<
|
|
||||||
Option<((NumberFor<Block>, Block::Hash), Option<(NumberFor<Block>, Block::Hash)>, Vec<u8>)>,
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Blockchain info
|
/// Blockchain info
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub struct Info<Block: BlockT> {
|
pub struct Info<Block: BlockT> {
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ use crate::AURA_ENGINE_ID;
|
|||||||
use codec::{Codec, Encode};
|
use codec::{Codec, Encode};
|
||||||
use sp_consensus_slots::Slot;
|
use sp_consensus_slots::Slot;
|
||||||
use sp_runtime::generic::DigestItem;
|
use sp_runtime::generic::DigestItem;
|
||||||
use sp_std::fmt::Debug;
|
|
||||||
|
|
||||||
/// A digest item which is usable with aura consensus.
|
/// A digest item which is usable with aura consensus.
|
||||||
pub trait CompatibleDigestItem<Signature>: Sized {
|
pub trait CompatibleDigestItem<Signature>: Sized {
|
||||||
@@ -42,10 +41,9 @@ pub trait CompatibleDigestItem<Signature>: Sized {
|
|||||||
fn as_aura_pre_digest(&self) -> Option<Slot>;
|
fn as_aura_pre_digest(&self) -> Option<Slot>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Signature, Hash> CompatibleDigestItem<Signature> for DigestItem<Hash>
|
impl<Signature> CompatibleDigestItem<Signature> for DigestItem
|
||||||
where
|
where
|
||||||
Signature: Codec,
|
Signature: Codec,
|
||||||
Hash: Debug + Send + Sync + Eq + Clone + Codec + 'static,
|
|
||||||
{
|
{
|
||||||
fn aura_seal(signature: Signature) -> Self {
|
fn aura_seal(signature: Signature) -> Self {
|
||||||
DigestItem::Seal(AURA_ENGINE_ID, signature.encode())
|
DigestItem::Seal(AURA_ENGINE_ID, signature.encode())
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ use super::{
|
|||||||
AllowedSlots, AuthorityId, AuthorityIndex, AuthoritySignature, BabeAuthorityWeight,
|
AllowedSlots, AuthorityId, AuthorityIndex, AuthoritySignature, BabeAuthorityWeight,
|
||||||
BabeEpochConfiguration, Slot, BABE_ENGINE_ID,
|
BabeEpochConfiguration, Slot, BABE_ENGINE_ID,
|
||||||
};
|
};
|
||||||
use codec::{Codec, Decode, Encode, MaxEncodedLen};
|
use codec::{Decode, Encode, MaxEncodedLen};
|
||||||
use sp_runtime::{DigestItem, RuntimeDebug};
|
use sp_runtime::{DigestItem, RuntimeDebug};
|
||||||
use sp_std::vec::Vec;
|
use sp_std::vec::Vec;
|
||||||
|
|
||||||
@@ -177,10 +177,7 @@ pub trait CompatibleDigestItem: Sized {
|
|||||||
fn as_next_config_descriptor(&self) -> Option<NextConfigDescriptor>;
|
fn as_next_config_descriptor(&self) -> Option<NextConfigDescriptor>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Hash> CompatibleDigestItem for DigestItem<Hash>
|
impl CompatibleDigestItem for DigestItem {
|
||||||
where
|
|
||||||
Hash: Send + Sync + Eq + Clone + Codec + 'static,
|
|
||||||
{
|
|
||||||
fn babe_pre_digest(digest: PreDigest) -> Self {
|
fn babe_pre_digest(digest: PreDigest) -> Self {
|
||||||
DigestItem::PreRuntime(BABE_ENGINE_ID, digest.encode())
|
DigestItem::PreRuntime(BABE_ENGINE_ID, digest.encode())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ use std::{sync::Arc, time::Duration};
|
|||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::BlockId,
|
generic::BlockId,
|
||||||
traits::{Block as BlockT, DigestFor, HashFor, NumberFor},
|
traits::{Block as BlockT, HashFor},
|
||||||
|
Digest,
|
||||||
};
|
};
|
||||||
use sp_state_machine::StorageProof;
|
use sp_state_machine::StorageProof;
|
||||||
|
|
||||||
@@ -111,8 +112,7 @@ pub struct Proposal<Block: BlockT, Transaction, Proof> {
|
|||||||
/// Proof that was recorded while building the block.
|
/// Proof that was recorded while building the block.
|
||||||
pub proof: Proof,
|
pub proof: Proof,
|
||||||
/// The storage changes while building this block.
|
/// The storage changes while building this block.
|
||||||
pub storage_changes:
|
pub storage_changes: sp_state_machine::StorageChanges<Transaction, HashFor<Block>>,
|
||||||
sp_state_machine::StorageChanges<Transaction, HashFor<Block>, NumberFor<Block>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error that is returned when [`ProofRecording`] requested to record a proof,
|
/// Error that is returned when [`ProofRecording`] requested to record a proof,
|
||||||
@@ -224,7 +224,7 @@ pub trait Proposer<B: BlockT> {
|
|||||||
fn propose(
|
fn propose(
|
||||||
self,
|
self,
|
||||||
inherent_data: InherentData,
|
inherent_data: InherentData,
|
||||||
inherent_digests: DigestFor<B>,
|
inherent_digests: Digest,
|
||||||
max_duration: Duration,
|
max_duration: Duration,
|
||||||
block_size_limit: Option<usize>,
|
block_size_limit: Option<usize>,
|
||||||
) -> Self::Proposal;
|
) -> Self::Proposal;
|
||||||
|
|||||||
@@ -1,321 +0,0 @@
|
|||||||
// This file is part of Substrate.
|
|
||||||
|
|
||||||
// Copyright (C) 2018-2021 Parity Technologies (UK) Ltd.
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
//! Substrate changes trie configuration.
|
|
||||||
|
|
||||||
use codec::{Decode, Encode};
|
|
||||||
use num_traits::Zero;
|
|
||||||
#[cfg(any(feature = "std", test))]
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
/// Substrate changes trie configuration.
|
|
||||||
#[cfg_attr(
|
|
||||||
any(feature = "std", test),
|
|
||||||
derive(Serialize, Deserialize, parity_util_mem::MallocSizeOf)
|
|
||||||
)]
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Encode, Decode, scale_info::TypeInfo)]
|
|
||||||
pub struct ChangesTrieConfiguration {
|
|
||||||
/// Interval (in blocks) at which level1-digests are created. Digests are not
|
|
||||||
/// created when this is less or equal to 1.
|
|
||||||
pub digest_interval: u32,
|
|
||||||
/// Maximal number of digest levels in hierarchy. 0 means that digests are not
|
|
||||||
/// created at all (even level1 digests). 1 means only level1-digests are created.
|
|
||||||
/// 2 means that every digest_interval^2 there will be a level2-digest, and so on.
|
|
||||||
/// Please ensure that maximum digest interval (i.e. digest_interval^digest_levels)
|
|
||||||
/// is within `u32` limits. Otherwise you'll never see digests covering such intervals
|
|
||||||
/// && maximal digests interval will be truncated to the last interval that fits
|
|
||||||
/// `u32` limits.
|
|
||||||
pub digest_levels: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Substrate changes trie configuration range.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub struct ChangesTrieConfigurationRange<Number, Hash> {
|
|
||||||
/// Zero block of configuration.
|
|
||||||
pub zero: (Number, Hash),
|
|
||||||
/// Last block of configuration (if configuration has been deactivated at some point).
|
|
||||||
pub end: Option<(Number, Hash)>,
|
|
||||||
/// The configuration itself. None if changes tries were disabled in this range.
|
|
||||||
pub config: Option<ChangesTrieConfiguration>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ChangesTrieConfiguration {
|
|
||||||
/// Create new configuration given digest interval and levels.
|
|
||||||
pub fn new(digest_interval: u32, digest_levels: u32) -> Self {
|
|
||||||
Self { digest_interval, digest_levels }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Is digest build enabled?
|
|
||||||
pub fn is_digest_build_enabled(&self) -> bool {
|
|
||||||
self.digest_interval > 1 && self.digest_levels > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Do we need to build digest at given block?
|
|
||||||
pub fn is_digest_build_required_at_block<Number>(&self, zero: Number, block: Number) -> bool
|
|
||||||
where
|
|
||||||
Number: From<u32>
|
|
||||||
+ PartialEq
|
|
||||||
+ ::sp_std::ops::Rem<Output = Number>
|
|
||||||
+ ::sp_std::ops::Sub<Output = Number>
|
|
||||||
+ ::sp_std::cmp::PartialOrd
|
|
||||||
+ Zero,
|
|
||||||
{
|
|
||||||
block > zero &&
|
|
||||||
self.is_digest_build_enabled() &&
|
|
||||||
((block - zero) % self.digest_interval.into()).is_zero()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns max digest interval. One if digests are not created at all.
|
|
||||||
pub fn max_digest_interval(&self) -> u32 {
|
|
||||||
if !self.is_digest_build_enabled() {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// we'll get >1 loop iteration only when bad configuration parameters are selected
|
|
||||||
let mut current_level = self.digest_levels;
|
|
||||||
loop {
|
|
||||||
if let Some(max_digest_interval) = self.digest_interval.checked_pow(current_level) {
|
|
||||||
return max_digest_interval
|
|
||||||
}
|
|
||||||
|
|
||||||
current_level -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns max level digest block number that has been created at block <= passed block number.
|
|
||||||
///
|
|
||||||
/// Returns None if digests are not created at all.
|
|
||||||
pub fn prev_max_level_digest_block<Number>(&self, zero: Number, block: Number) -> Option<Number>
|
|
||||||
where
|
|
||||||
Number: Clone
|
|
||||||
+ From<u32>
|
|
||||||
+ PartialOrd
|
|
||||||
+ PartialEq
|
|
||||||
+ ::sp_std::ops::Add<Output = Number>
|
|
||||||
+ ::sp_std::ops::Sub<Output = Number>
|
|
||||||
+ ::sp_std::ops::Div<Output = Number>
|
|
||||||
+ ::sp_std::ops::Mul<Output = Number>
|
|
||||||
+ Zero,
|
|
||||||
{
|
|
||||||
if block <= zero {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
|
|
||||||
let (next_begin, next_end) =
|
|
||||||
self.next_max_level_digest_range(zero.clone(), block.clone())?;
|
|
||||||
|
|
||||||
// if 'next' digest includes our block, then it is a also a previous digest
|
|
||||||
if next_end == block {
|
|
||||||
return Some(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if previous digest ends at zero block, then there are no previous digest
|
|
||||||
let prev_end = next_begin - 1.into();
|
|
||||||
if prev_end == zero {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(prev_end)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns max level digest blocks range (inclusive) which includes passed block.
|
|
||||||
///
|
|
||||||
/// Returns None if digests are not created at all.
|
|
||||||
/// It will return the first max-level digest if block is <= zero.
|
|
||||||
pub fn next_max_level_digest_range<Number>(
|
|
||||||
&self,
|
|
||||||
zero: Number,
|
|
||||||
mut block: Number,
|
|
||||||
) -> Option<(Number, Number)>
|
|
||||||
where
|
|
||||||
Number: Clone
|
|
||||||
+ From<u32>
|
|
||||||
+ PartialOrd
|
|
||||||
+ PartialEq
|
|
||||||
+ ::sp_std::ops::Add<Output = Number>
|
|
||||||
+ ::sp_std::ops::Sub<Output = Number>
|
|
||||||
+ ::sp_std::ops::Div<Output = Number>
|
|
||||||
+ ::sp_std::ops::Mul<Output = Number>,
|
|
||||||
{
|
|
||||||
if !self.is_digest_build_enabled() {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
|
|
||||||
if block <= zero {
|
|
||||||
block = zero.clone() + 1.into();
|
|
||||||
}
|
|
||||||
|
|
||||||
let max_digest_interval: Number = self.max_digest_interval().into();
|
|
||||||
let max_digests_since_zero = (block.clone() - zero.clone()) / max_digest_interval.clone();
|
|
||||||
if max_digests_since_zero == 0.into() {
|
|
||||||
return Some((zero.clone() + 1.into(), zero + max_digest_interval))
|
|
||||||
}
|
|
||||||
let last_max_digest_block = zero + max_digests_since_zero * max_digest_interval.clone();
|
|
||||||
Some(if block == last_max_digest_block {
|
|
||||||
(block.clone() - max_digest_interval + 1.into(), block)
|
|
||||||
} else {
|
|
||||||
(last_max_digest_block.clone() + 1.into(), last_max_digest_block + max_digest_interval)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns Some if digest must be built at given block number.
|
|
||||||
/// The tuple is:
|
|
||||||
/// (
|
|
||||||
/// digest level
|
|
||||||
/// digest interval (in blocks)
|
|
||||||
/// step between blocks we're interested in when digest is built
|
|
||||||
/// )
|
|
||||||
pub fn digest_level_at_block<Number>(
|
|
||||||
&self,
|
|
||||||
zero: Number,
|
|
||||||
block: Number,
|
|
||||||
) -> Option<(u32, u32, u32)>
|
|
||||||
where
|
|
||||||
Number: Clone
|
|
||||||
+ From<u32>
|
|
||||||
+ PartialEq
|
|
||||||
+ ::sp_std::ops::Rem<Output = Number>
|
|
||||||
+ ::sp_std::ops::Sub<Output = Number>
|
|
||||||
+ ::sp_std::cmp::PartialOrd
|
|
||||||
+ Zero,
|
|
||||||
{
|
|
||||||
if !self.is_digest_build_required_at_block(zero.clone(), block.clone()) {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
|
|
||||||
let relative_block = block - zero;
|
|
||||||
let mut digest_interval = self.digest_interval;
|
|
||||||
let mut current_level = 1u32;
|
|
||||||
let mut digest_step = 1u32;
|
|
||||||
while current_level < self.digest_levels {
|
|
||||||
let new_digest_interval = match digest_interval.checked_mul(self.digest_interval) {
|
|
||||||
Some(new_digest_interval)
|
|
||||||
if (relative_block.clone() % new_digest_interval.into()).is_zero() =>
|
|
||||||
new_digest_interval,
|
|
||||||
_ => break,
|
|
||||||
};
|
|
||||||
|
|
||||||
digest_step = digest_interval;
|
|
||||||
digest_interval = new_digest_interval;
|
|
||||||
current_level += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some((current_level, digest_interval, digest_step))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::ChangesTrieConfiguration;
|
|
||||||
|
|
||||||
fn config(interval: u32, levels: u32) -> ChangesTrieConfiguration {
|
|
||||||
ChangesTrieConfiguration { digest_interval: interval, digest_levels: levels }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn is_digest_build_enabled_works() {
|
|
||||||
assert!(!config(0, 100).is_digest_build_enabled());
|
|
||||||
assert!(!config(1, 100).is_digest_build_enabled());
|
|
||||||
assert!(config(2, 100).is_digest_build_enabled());
|
|
||||||
assert!(!config(100, 0).is_digest_build_enabled());
|
|
||||||
assert!(config(100, 1).is_digest_build_enabled());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn is_digest_build_required_at_block_works() {
|
|
||||||
fn test_with_zero(zero: u64) {
|
|
||||||
assert!(!config(8, 4).is_digest_build_required_at_block(zero, zero));
|
|
||||||
assert!(!config(8, 4).is_digest_build_required_at_block(zero, zero + 1u64));
|
|
||||||
assert!(!config(8, 4).is_digest_build_required_at_block(zero, zero + 2u64));
|
|
||||||
assert!(!config(8, 4).is_digest_build_required_at_block(zero, zero + 4u64));
|
|
||||||
assert!(config(8, 4).is_digest_build_required_at_block(zero, zero + 8u64));
|
|
||||||
assert!(!config(8, 4).is_digest_build_required_at_block(zero, zero + 9u64));
|
|
||||||
assert!(config(8, 4).is_digest_build_required_at_block(zero, zero + 64u64));
|
|
||||||
assert!(config(8, 4).is_digest_build_required_at_block(zero, zero + 64u64));
|
|
||||||
assert!(config(8, 4).is_digest_build_required_at_block(zero, zero + 512u64));
|
|
||||||
assert!(config(8, 4).is_digest_build_required_at_block(zero, zero + 4096u64));
|
|
||||||
assert!(!config(8, 4).is_digest_build_required_at_block(zero, zero + 4103u64));
|
|
||||||
assert!(config(8, 4).is_digest_build_required_at_block(zero, zero + 4104u64));
|
|
||||||
assert!(!config(8, 4).is_digest_build_required_at_block(zero, zero + 4108u64));
|
|
||||||
}
|
|
||||||
|
|
||||||
test_with_zero(0);
|
|
||||||
test_with_zero(8);
|
|
||||||
test_with_zero(17);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn digest_level_at_block_works() {
|
|
||||||
fn test_with_zero(zero: u64) {
|
|
||||||
assert_eq!(config(8, 4).digest_level_at_block(zero, zero), None);
|
|
||||||
assert_eq!(config(8, 4).digest_level_at_block(zero, zero + 7u64), None);
|
|
||||||
assert_eq!(config(8, 4).digest_level_at_block(zero, zero + 63u64), None);
|
|
||||||
assert_eq!(config(8, 4).digest_level_at_block(zero, zero + 8u64), Some((1, 8, 1)));
|
|
||||||
assert_eq!(config(8, 4).digest_level_at_block(zero, zero + 64u64), Some((2, 64, 8)));
|
|
||||||
assert_eq!(config(8, 4).digest_level_at_block(zero, zero + 512u64), Some((3, 512, 64)));
|
|
||||||
assert_eq!(
|
|
||||||
config(8, 4).digest_level_at_block(zero, zero + 4096u64),
|
|
||||||
Some((4, 4096, 512))
|
|
||||||
);
|
|
||||||
assert_eq!(config(8, 4).digest_level_at_block(zero, zero + 4112u64), Some((1, 8, 1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
test_with_zero(0);
|
|
||||||
test_with_zero(8);
|
|
||||||
test_with_zero(17);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn max_digest_interval_works() {
|
|
||||||
assert_eq!(config(0, 0).max_digest_interval(), 1);
|
|
||||||
assert_eq!(config(2, 2).max_digest_interval(), 4);
|
|
||||||
assert_eq!(config(8, 4).max_digest_interval(), 4096);
|
|
||||||
assert_eq!(config(::std::u32::MAX, 1024).max_digest_interval(), ::std::u32::MAX);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn next_max_level_digest_range_works() {
|
|
||||||
assert_eq!(config(0, 0).next_max_level_digest_range(0u64, 16), None);
|
|
||||||
assert_eq!(config(1, 1).next_max_level_digest_range(0u64, 16), None);
|
|
||||||
assert_eq!(config(2, 1).next_max_level_digest_range(0u64, 16), Some((15, 16)));
|
|
||||||
assert_eq!(config(4, 1).next_max_level_digest_range(0u64, 16), Some((13, 16)));
|
|
||||||
assert_eq!(config(32, 1).next_max_level_digest_range(0u64, 16), Some((1, 32)));
|
|
||||||
assert_eq!(config(2, 3).next_max_level_digest_range(0u64, 10), Some((9, 16)));
|
|
||||||
assert_eq!(config(2, 3).next_max_level_digest_range(0u64, 8), Some((1, 8)));
|
|
||||||
assert_eq!(config(2, 1).next_max_level_digest_range(1u64, 1), Some((2, 3)));
|
|
||||||
assert_eq!(config(2, 2).next_max_level_digest_range(7u64, 9), Some((8, 11)));
|
|
||||||
|
|
||||||
assert_eq!(config(2, 2).next_max_level_digest_range(7u64, 5), Some((8, 11)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn prev_max_level_digest_block_works() {
|
|
||||||
assert_eq!(config(0, 0).prev_max_level_digest_block(0u64, 16), None);
|
|
||||||
assert_eq!(config(1, 1).prev_max_level_digest_block(0u64, 16), None);
|
|
||||||
assert_eq!(config(2, 1).prev_max_level_digest_block(0u64, 16), Some(16));
|
|
||||||
assert_eq!(config(4, 1).prev_max_level_digest_block(0u64, 16), Some(16));
|
|
||||||
assert_eq!(config(4, 2).prev_max_level_digest_block(0u64, 16), Some(16));
|
|
||||||
assert_eq!(config(4, 2).prev_max_level_digest_block(0u64, 17), Some(16));
|
|
||||||
assert_eq!(config(4, 2).prev_max_level_digest_block(0u64, 33), Some(32));
|
|
||||||
assert_eq!(config(32, 1).prev_max_level_digest_block(0u64, 16), None);
|
|
||||||
assert_eq!(config(2, 3).prev_max_level_digest_block(0u64, 10), Some(8));
|
|
||||||
assert_eq!(config(2, 3).prev_max_level_digest_block(0u64, 8), Some(8));
|
|
||||||
assert_eq!(config(2, 2).prev_max_level_digest_block(7u64, 8), None);
|
|
||||||
|
|
||||||
assert_eq!(config(2, 2).prev_max_level_digest_block(7u64, 5), None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -58,7 +58,6 @@ pub mod hexdisplay;
|
|||||||
|
|
||||||
pub mod u32_trait;
|
pub mod u32_trait;
|
||||||
|
|
||||||
mod changes_trie;
|
|
||||||
pub mod ecdsa;
|
pub mod ecdsa;
|
||||||
pub mod ed25519;
|
pub mod ed25519;
|
||||||
pub mod hash;
|
pub mod hash;
|
||||||
@@ -76,7 +75,6 @@ pub use self::{
|
|||||||
hash::{convert_hash, H160, H256, H512},
|
hash::{convert_hash, H160, H256, H512},
|
||||||
uint::{U256, U512},
|
uint::{U256, U512},
|
||||||
};
|
};
|
||||||
pub use changes_trie::{ChangesTrieConfiguration, ChangesTrieConfigurationRange};
|
|
||||||
#[cfg(feature = "full_crypto")]
|
#[cfg(feature = "full_crypto")]
|
||||||
pub use crypto::{DeriveJunction, Pair, Public};
|
pub use crypto::{DeriveJunction, Pair, Public};
|
||||||
|
|
||||||
|
|||||||
@@ -173,13 +173,6 @@ pub trait Externalities: ExtensionStore {
|
|||||||
/// operation.
|
/// operation.
|
||||||
fn storage_append(&mut self, key: Vec<u8>, value: Vec<u8>);
|
fn storage_append(&mut self, key: Vec<u8>, value: Vec<u8>);
|
||||||
|
|
||||||
/// Get the changes trie root of the current storage overlay at a block with given `parent`.
|
|
||||||
///
|
|
||||||
/// `parent` expects a SCALE encoded hash.
|
|
||||||
///
|
|
||||||
/// The returned hash is defined by the `Block` and is SCALE encoded.
|
|
||||||
fn storage_changes_root(&mut self, parent: &[u8]) -> Result<Option<Vec<u8>>, ()>;
|
|
||||||
|
|
||||||
/// Start a new nested transaction.
|
/// Start a new nested transaction.
|
||||||
///
|
///
|
||||||
/// This allows to either commit or roll back all changes made after this call to the
|
/// This allows to either commit or roll back all changes made after this call to the
|
||||||
|
|||||||
@@ -195,16 +195,9 @@ pub trait Storage {
|
|||||||
self.storage_root()
|
self.storage_root()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// "Commit" all existing operations and get the resulting storage change root.
|
/// Always returns `None`. This function exists for compatibility reasons.
|
||||||
/// `parent_hash` is a SCALE encoded hash.
|
fn changes_root(&mut self, _parent_hash: &[u8]) -> Option<Vec<u8>> {
|
||||||
///
|
None
|
||||||
/// The hashing algorithm is defined by the `Block`.
|
|
||||||
///
|
|
||||||
/// Returns `Some(Vec<u8>)` which holds the SCALE encoded hash or `None` when
|
|
||||||
/// changes trie is disabled.
|
|
||||||
fn changes_root(&mut self, parent_hash: &[u8]) -> Option<Vec<u8>> {
|
|
||||||
self.storage_changes_root(parent_hash)
|
|
||||||
.expect("Invalid `parent_hash` given to `changes_root`.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the next key in storage after the given one in lexicographic order.
|
/// Get the next key in storage after the given one in lexicographic order.
|
||||||
@@ -1497,7 +1490,7 @@ pub fn oom(_: core::alloc::Layout) -> ! {
|
|||||||
|
|
||||||
/// Type alias for Externalities implementation used in tests.
|
/// Type alias for Externalities implementation used in tests.
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub type TestExternalities = sp_state_machine::TestExternalities<sp_core::Blake2Hasher, u64>;
|
pub type TestExternalities = sp_state_machine::TestExternalities<sp_core::Blake2Hasher>;
|
||||||
|
|
||||||
/// The host functions Substrate provides for the Wasm runtime environment.
|
/// The host functions Substrate provides for the Wasm runtime environment.
|
||||||
///
|
///
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user