mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 16:21:02 +00:00
Warp sync part I (#9227)
* Started warp sync * BABE & GRANDPA recovery * Warp sync protocol * Sync warp proofs first * Added basic documentation * Apply suggestions from code review Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> * Style changes * Apply suggestions from code review Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> * fmt * Apply suggestions from code review Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> * Fixed chage trie pruning wrt missing blocks * Restore parent finalization * fmt * fmt * Revert pwasm-utils bump * Change error type & check API version * Apply suggestions from code review Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Build fix * Fixed target block check * Formatting Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
Generated
+19
-45
@@ -1714,7 +1714,7 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
"parity-scale-codec",
|
"parity-scale-codec",
|
||||||
"parking_lot 0.11.1",
|
"parking_lot 0.11.1",
|
||||||
"rand 0.8.3",
|
"rand 0.8.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1724,7 +1724,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c"
|
checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"rand 0.8.3",
|
"rand 0.8.4",
|
||||||
"rustc-hex",
|
"rustc-hex",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
]
|
]
|
||||||
@@ -3384,7 +3384,7 @@ dependencies = [
|
|||||||
"libp2p-core",
|
"libp2p-core",
|
||||||
"libp2p-swarm",
|
"libp2p-swarm",
|
||||||
"log",
|
"log",
|
||||||
"rand 0.8.3",
|
"rand 0.8.4",
|
||||||
"smallvec 1.6.1",
|
"smallvec 1.6.1",
|
||||||
"socket2 0.4.0",
|
"socket2 0.4.0",
|
||||||
"void",
|
"void",
|
||||||
@@ -4056,7 +4056,7 @@ dependencies = [
|
|||||||
"num-complex",
|
"num-complex",
|
||||||
"num-rational 0.4.0",
|
"num-rational 0.4.0",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"rand 0.8.3",
|
"rand 0.8.4",
|
||||||
"rand_distr",
|
"rand_distr",
|
||||||
"simba",
|
"simba",
|
||||||
"typenum",
|
"typenum",
|
||||||
@@ -4216,7 +4216,6 @@ dependencies = [
|
|||||||
"sc-consensus-slots",
|
"sc-consensus-slots",
|
||||||
"sc-consensus-uncles",
|
"sc-consensus-uncles",
|
||||||
"sc-finality-grandpa",
|
"sc-finality-grandpa",
|
||||||
"sc-finality-grandpa-warp-sync",
|
|
||||||
"sc-keystore",
|
"sc-keystore",
|
||||||
"sc-network",
|
"sc-network",
|
||||||
"sc-offchain",
|
"sc-offchain",
|
||||||
@@ -4903,7 +4902,7 @@ dependencies = [
|
|||||||
"paste 1.0.4",
|
"paste 1.0.4",
|
||||||
"pretty_assertions 0.7.2",
|
"pretty_assertions 0.7.2",
|
||||||
"pwasm-utils",
|
"pwasm-utils",
|
||||||
"rand 0.8.3",
|
"rand 0.8.4",
|
||||||
"rand_pcg 0.3.0",
|
"rand_pcg 0.3.0",
|
||||||
"serde",
|
"serde",
|
||||||
"smallvec 1.6.1",
|
"smallvec 1.6.1",
|
||||||
@@ -5753,7 +5752,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"memmap2",
|
"memmap2",
|
||||||
"parking_lot 0.11.1",
|
"parking_lot 0.11.1",
|
||||||
"rand 0.8.3",
|
"rand 0.8.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6482,7 +6481,7 @@ checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"env_logger 0.8.3",
|
"env_logger 0.8.3",
|
||||||
"log",
|
"log",
|
||||||
"rand 0.8.3",
|
"rand 0.8.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6550,9 +6549,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.3"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
|
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha 0.3.0",
|
"rand_chacha 0.3.0",
|
||||||
@@ -6620,7 +6619,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "051b398806e42b9cd04ad9ec8f81e355d0a382c543ac6672c62f5a5b452ef142"
|
checksum = "051b398806e42b9cd04ad9ec8f81e355d0a382c543ac6672c62f5a5b452ef142"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"rand 0.8.3",
|
"rand 0.8.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -7638,7 +7637,7 @@ dependencies = [
|
|||||||
"parity-scale-codec",
|
"parity-scale-codec",
|
||||||
"parking_lot 0.11.1",
|
"parking_lot 0.11.1",
|
||||||
"pin-project 1.0.5",
|
"pin-project 1.0.5",
|
||||||
"rand 0.7.3",
|
"rand 0.8.4",
|
||||||
"sc-block-builder",
|
"sc-block-builder",
|
||||||
"sc-client-api",
|
"sc-client-api",
|
||||||
"sc-consensus",
|
"sc-consensus",
|
||||||
@@ -7700,33 +7699,6 @@ dependencies = [
|
|||||||
"substrate-test-runtime-client",
|
"substrate-test-runtime-client",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sc-finality-grandpa-warp-sync"
|
|
||||||
version = "0.10.0-dev"
|
|
||||||
dependencies = [
|
|
||||||
"derive_more",
|
|
||||||
"finality-grandpa",
|
|
||||||
"futures 0.3.15",
|
|
||||||
"log",
|
|
||||||
"num-traits",
|
|
||||||
"parity-scale-codec",
|
|
||||||
"parking_lot 0.11.1",
|
|
||||||
"prost",
|
|
||||||
"rand 0.8.3",
|
|
||||||
"sc-block-builder",
|
|
||||||
"sc-client-api",
|
|
||||||
"sc-consensus",
|
|
||||||
"sc-finality-grandpa",
|
|
||||||
"sc-network",
|
|
||||||
"sc-service",
|
|
||||||
"sp-blockchain",
|
|
||||||
"sp-consensus",
|
|
||||||
"sp-finality-grandpa",
|
|
||||||
"sp-keyring",
|
|
||||||
"sp-runtime",
|
|
||||||
"substrate-test-runtime-client",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sc-informant"
|
name = "sc-informant"
|
||||||
version = "0.10.0-dev"
|
version = "0.10.0-dev"
|
||||||
@@ -7827,6 +7799,7 @@ dependencies = [
|
|||||||
"sp-blockchain",
|
"sp-blockchain",
|
||||||
"sp-consensus",
|
"sp-consensus",
|
||||||
"sp-core",
|
"sp-core",
|
||||||
|
"sp-finality-grandpa",
|
||||||
"sp-keyring",
|
"sp-keyring",
|
||||||
"sp-runtime",
|
"sp-runtime",
|
||||||
"sp-test-primitives",
|
"sp-test-primitives",
|
||||||
@@ -8710,7 +8683,7 @@ dependencies = [
|
|||||||
"futures 0.3.15",
|
"futures 0.3.15",
|
||||||
"httparse",
|
"httparse",
|
||||||
"log",
|
"log",
|
||||||
"rand 0.8.3",
|
"rand 0.8.4",
|
||||||
"sha-1 0.9.4",
|
"sha-1 0.9.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -9550,7 +9523,7 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
"nalgebra",
|
"nalgebra",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"rand 0.8.3",
|
"rand 0.8.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -9966,7 +9939,7 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"libc",
|
"libc",
|
||||||
"rand 0.8.3",
|
"rand 0.8.4",
|
||||||
"redox_syscall 0.2.5",
|
"redox_syscall 0.2.5",
|
||||||
"remove_dir_all",
|
"remove_dir_all",
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
@@ -10012,6 +9985,7 @@ dependencies = [
|
|||||||
"sp-consensus-babe",
|
"sp-consensus-babe",
|
||||||
"sp-core",
|
"sp-core",
|
||||||
"sp-externalities",
|
"sp-externalities",
|
||||||
|
"sp-finality-grandpa",
|
||||||
"sp-inherents",
|
"sp-inherents",
|
||||||
"sp-keyring",
|
"sp-keyring",
|
||||||
"sp-keystore",
|
"sp-keystore",
|
||||||
@@ -10638,7 +10612,7 @@ dependencies = [
|
|||||||
"ipnet",
|
"ipnet",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"rand 0.8.3",
|
"rand 0.8.4",
|
||||||
"smallvec 1.6.1",
|
"smallvec 1.6.1",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
@@ -11287,7 +11261,7 @@ dependencies = [
|
|||||||
"mach",
|
"mach",
|
||||||
"memoffset 0.6.1",
|
"memoffset 0.6.1",
|
||||||
"more-asserts",
|
"more-asserts",
|
||||||
"rand 0.8.3",
|
"rand 0.8.4",
|
||||||
"region",
|
"region",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"wasmtime-environ",
|
"wasmtime-environ",
|
||||||
@@ -11455,7 +11429,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"nohash-hasher",
|
"nohash-hasher",
|
||||||
"parking_lot 0.11.1",
|
"parking_lot 0.11.1",
|
||||||
"rand 0.8.3",
|
"rand 0.8.4",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ members = [
|
|||||||
"client/executor/wasmi",
|
"client/executor/wasmi",
|
||||||
"client/executor/wasmtime",
|
"client/executor/wasmtime",
|
||||||
"client/finality-grandpa",
|
"client/finality-grandpa",
|
||||||
"client/finality-grandpa-warp-sync",
|
|
||||||
"client/informant",
|
"client/informant",
|
||||||
"client/keystore",
|
"client/keystore",
|
||||||
"client/light",
|
"client/light",
|
||||||
|
|||||||
@@ -162,6 +162,10 @@ pub fn new_full(mut config: Configuration) -> Result<TaskManager, ServiceError>
|
|||||||
}
|
}
|
||||||
|
|
||||||
config.network.extra_sets.push(sc_finality_grandpa::grandpa_peers_set_config());
|
config.network.extra_sets.push(sc_finality_grandpa::grandpa_peers_set_config());
|
||||||
|
let warp_sync = Arc::new(sc_finality_grandpa::warp_proof::NetworkProvider::new(
|
||||||
|
backend.clone(),
|
||||||
|
grandpa_link.shared_authority_set().clone(),
|
||||||
|
));
|
||||||
|
|
||||||
let (network, system_rpc_tx, network_starter) =
|
let (network, system_rpc_tx, network_starter) =
|
||||||
sc_service::build_network(sc_service::BuildNetworkParams {
|
sc_service::build_network(sc_service::BuildNetworkParams {
|
||||||
@@ -172,6 +176,7 @@ pub fn new_full(mut config: Configuration) -> Result<TaskManager, ServiceError>
|
|||||||
import_queue,
|
import_queue,
|
||||||
on_demand: None,
|
on_demand: None,
|
||||||
block_announce_validator_builder: None,
|
block_announce_validator_builder: None,
|
||||||
|
warp_sync: Some(warp_sync),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if config.offchain_worker.enabled {
|
if config.offchain_worker.enabled {
|
||||||
@@ -380,6 +385,11 @@ pub fn new_light(mut config: Configuration) -> Result<TaskManager, ServiceError>
|
|||||||
telemetry: telemetry.as_ref().map(|x| x.handle()),
|
telemetry: telemetry.as_ref().map(|x| x.handle()),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
let warp_sync = Arc::new(sc_finality_grandpa::warp_proof::NetworkProvider::new(
|
||||||
|
backend.clone(),
|
||||||
|
grandpa_link.shared_authority_set().clone(),
|
||||||
|
));
|
||||||
|
|
||||||
let (network, system_rpc_tx, network_starter) =
|
let (network, system_rpc_tx, network_starter) =
|
||||||
sc_service::build_network(sc_service::BuildNetworkParams {
|
sc_service::build_network(sc_service::BuildNetworkParams {
|
||||||
config: &config,
|
config: &config,
|
||||||
@@ -389,6 +399,7 @@ pub fn new_light(mut config: Configuration) -> Result<TaskManager, ServiceError>
|
|||||||
import_queue,
|
import_queue,
|
||||||
on_demand: Some(on_demand.clone()),
|
on_demand: Some(on_demand.clone()),
|
||||||
block_announce_validator_builder: None,
|
block_announce_validator_builder: None,
|
||||||
|
warp_sync: Some(warp_sync),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if config.offchain_worker.enabled {
|
if config.offchain_worker.enabled {
|
||||||
|
|||||||
@@ -402,6 +402,10 @@ impl_runtime_apis! {
|
|||||||
Grandpa::grandpa_authorities()
|
Grandpa::grandpa_authorities()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn current_set_id() -> fg_primitives::SetId {
|
||||||
|
Grandpa::current_set_id()
|
||||||
|
}
|
||||||
|
|
||||||
fn submit_report_equivocation_unsigned_extrinsic(
|
fn submit_report_equivocation_unsigned_extrinsic(
|
||||||
_equivocation_proof: fg_primitives::EquivocationProof<
|
_equivocation_proof: fg_primitives::EquivocationProof<
|
||||||
<Block as BlockT>::Hash,
|
<Block as BlockT>::Hash,
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ sc-service = { version = "0.10.0-dev", default-features = false, path = "../../.
|
|||||||
sc-tracing = { version = "4.0.0-dev", path = "../../../client/tracing" }
|
sc-tracing = { version = "4.0.0-dev", path = "../../../client/tracing" }
|
||||||
sc-telemetry = { version = "4.0.0-dev", path = "../../../client/telemetry" }
|
sc-telemetry = { version = "4.0.0-dev", path = "../../../client/telemetry" }
|
||||||
sc-authority-discovery = { version = "0.10.0-dev", path = "../../../client/authority-discovery" }
|
sc-authority-discovery = { version = "0.10.0-dev", path = "../../../client/authority-discovery" }
|
||||||
sc-finality-grandpa-warp-sync = { version = "0.10.0-dev", path = "../../../client/finality-grandpa-warp-sync", optional = true }
|
|
||||||
|
|
||||||
# frame dependencies
|
# frame dependencies
|
||||||
pallet-indices = { version = "4.0.0-dev", path = "../../../frame/indices" }
|
pallet-indices = { version = "4.0.0-dev", path = "../../../frame/indices" }
|
||||||
@@ -161,7 +160,6 @@ cli = [
|
|||||||
"frame-benchmarking-cli",
|
"frame-benchmarking-cli",
|
||||||
"substrate-frame-cli",
|
"substrate-frame-cli",
|
||||||
"sc-service/db",
|
"sc-service/db",
|
||||||
"sc-finality-grandpa-warp-sync",
|
|
||||||
"structopt",
|
"structopt",
|
||||||
"substrate-build-script-utils",
|
"substrate-build-script-utils",
|
||||||
"try-runtime-cli",
|
"try-runtime-cli",
|
||||||
|
|||||||
@@ -228,16 +228,10 @@ pub fn new_full_base(
|
|||||||
let auth_disc_publish_non_global_ips = config.network.allow_non_globals_in_dht;
|
let auth_disc_publish_non_global_ips = config.network.allow_non_globals_in_dht;
|
||||||
|
|
||||||
config.network.extra_sets.push(grandpa::grandpa_peers_set_config());
|
config.network.extra_sets.push(grandpa::grandpa_peers_set_config());
|
||||||
|
let warp_sync = Arc::new(grandpa::warp_proof::NetworkProvider::new(
|
||||||
#[cfg(feature = "cli")]
|
|
||||||
config.network.request_response_protocols.push(
|
|
||||||
sc_finality_grandpa_warp_sync::request_response_config_for_chain(
|
|
||||||
&config,
|
|
||||||
task_manager.spawn_handle(),
|
|
||||||
backend.clone(),
|
backend.clone(),
|
||||||
import_setup.1.shared_authority_set().clone(),
|
import_setup.1.shared_authority_set().clone(),
|
||||||
),
|
));
|
||||||
);
|
|
||||||
|
|
||||||
let (network, system_rpc_tx, network_starter) =
|
let (network, system_rpc_tx, network_starter) =
|
||||||
sc_service::build_network(sc_service::BuildNetworkParams {
|
sc_service::build_network(sc_service::BuildNetworkParams {
|
||||||
@@ -248,6 +242,7 @@ pub fn new_full_base(
|
|||||||
import_queue,
|
import_queue,
|
||||||
on_demand: None,
|
on_demand: None,
|
||||||
block_announce_validator_builder: None,
|
block_announce_validator_builder: None,
|
||||||
|
warp_sync: Some(warp_sync),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if config.offchain_worker.enabled {
|
if config.offchain_worker.enabled {
|
||||||
@@ -512,6 +507,11 @@ pub fn new_light_base(
|
|||||||
telemetry.as_ref().map(|x| x.handle()),
|
telemetry.as_ref().map(|x| x.handle()),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let warp_sync = Arc::new(grandpa::warp_proof::NetworkProvider::new(
|
||||||
|
backend.clone(),
|
||||||
|
grandpa_link.shared_authority_set().clone(),
|
||||||
|
));
|
||||||
|
|
||||||
let (network, system_rpc_tx, network_starter) =
|
let (network, system_rpc_tx, network_starter) =
|
||||||
sc_service::build_network(sc_service::BuildNetworkParams {
|
sc_service::build_network(sc_service::BuildNetworkParams {
|
||||||
config: &config,
|
config: &config,
|
||||||
@@ -521,6 +521,7 @@ pub fn new_light_base(
|
|||||||
import_queue,
|
import_queue,
|
||||||
on_demand: Some(on_demand.clone()),
|
on_demand: Some(on_demand.clone()),
|
||||||
block_announce_validator_builder: None,
|
block_announce_validator_builder: None,
|
||||||
|
warp_sync: Some(warp_sync),
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let enable_grandpa = !config.disable_grandpa;
|
let enable_grandpa = !config.disable_grandpa;
|
||||||
|
|||||||
@@ -1336,6 +1336,10 @@ impl_runtime_apis! {
|
|||||||
Grandpa::grandpa_authorities()
|
Grandpa::grandpa_authorities()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn current_set_id() -> fg_primitives::SetId {
|
||||||
|
Grandpa::current_set_id()
|
||||||
|
}
|
||||||
|
|
||||||
fn submit_report_equivocation_unsigned_extrinsic(
|
fn submit_report_equivocation_unsigned_extrinsic(
|
||||||
equivocation_proof: fg_primitives::EquivocationProof<
|
equivocation_proof: fg_primitives::EquivocationProof<
|
||||||
<Block as BlockT>::Hash,
|
<Block as BlockT>::Hash,
|
||||||
|
|||||||
@@ -242,6 +242,8 @@ arg_enum! {
|
|||||||
Fast,
|
Fast,
|
||||||
// Download blocks without executing them. Download latest state without proofs.
|
// Download blocks without executing them. Download latest state without proofs.
|
||||||
FastUnsafe,
|
FastUnsafe,
|
||||||
|
// Prove finality and download the latest state.
|
||||||
|
Warp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,6 +255,7 @@ impl Into<sc_network::config::SyncMode> for SyncMode {
|
|||||||
sc_network::config::SyncMode::Fast { skip_proofs: false, storage_chain_mode: false },
|
sc_network::config::SyncMode::Fast { skip_proofs: false, storage_chain_mode: false },
|
||||||
SyncMode::FastUnsafe =>
|
SyncMode::FastUnsafe =>
|
||||||
sc_network::config::SyncMode::Fast { skip_proofs: true, storage_chain_mode: false },
|
sc_network::config::SyncMode::Fast { skip_proofs: true, storage_chain_mode: false },
|
||||||
|
SyncMode::Warp => sc_network::config::SyncMode::Warp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ use sp_blockchain::{
|
|||||||
well_known_cache_keys::{self, Id as CacheKeyId},
|
well_known_cache_keys::{self, Id as CacheKeyId},
|
||||||
HeaderBackend, ProvideCache,
|
HeaderBackend, ProvideCache,
|
||||||
};
|
};
|
||||||
use sp_consensus::{BlockOrigin, CanAuthorWith, Error as ConsensusError};
|
use sp_consensus::{CanAuthorWith, Error as ConsensusError};
|
||||||
use sp_consensus_aura::{
|
use sp_consensus_aura::{
|
||||||
digests::CompatibleDigestItem, inherents::AuraInherentData, AuraApi, ConsensusLog,
|
digests::CompatibleDigestItem, inherents::AuraInherentData, AuraApi, ConsensusLog,
|
||||||
AURA_ENGINE_ID,
|
AURA_ENGINE_ID,
|
||||||
@@ -46,7 +46,6 @@ 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, DigestItemFor, Header},
|
||||||
Justifications,
|
|
||||||
};
|
};
|
||||||
use std::{fmt::Debug, hash::Hash, marker::PhantomData, sync::Arc};
|
use std::{fmt::Debug, hash::Hash, marker::PhantomData, sync::Arc};
|
||||||
|
|
||||||
@@ -206,13 +205,10 @@ where
|
|||||||
{
|
{
|
||||||
async fn verify(
|
async fn verify(
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
mut block: BlockImportParams<B, ()>,
|
||||||
header: B::Header,
|
|
||||||
justifications: Option<Justifications>,
|
|
||||||
mut body: Option<Vec<B::Extrinsic>>,
|
|
||||||
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
||||||
let hash = header.hash();
|
let hash = block.header.hash();
|
||||||
let parent_hash = *header.parent_hash();
|
let parent_hash = *block.header.parent_hash();
|
||||||
let authorities = authorities(self.client.as_ref(), &BlockId::Hash(parent_hash))
|
let authorities = authorities(self.client.as_ref(), &BlockId::Hash(parent_hash))
|
||||||
.map_err(|e| format!("Could not fetch authorities at {:?}: {:?}", parent_hash, e))?;
|
.map_err(|e| format!("Could not fetch authorities at {:?}: {:?}", parent_hash, e))?;
|
||||||
|
|
||||||
@@ -234,7 +230,7 @@ where
|
|||||||
let checked_header = check_header::<C, B, P>(
|
let checked_header = check_header::<C, B, P>(
|
||||||
&self.client,
|
&self.client,
|
||||||
slot_now + 1,
|
slot_now + 1,
|
||||||
header,
|
block.header,
|
||||||
hash,
|
hash,
|
||||||
&authorities[..],
|
&authorities[..],
|
||||||
self.check_for_equivocation,
|
self.check_for_equivocation,
|
||||||
@@ -245,8 +241,8 @@ where
|
|||||||
// if the body is passed through, we need to use the runtime
|
// if the body is passed through, we need to use the runtime
|
||||||
// to check that the internally-set timestamp in the inherents
|
// to check that the internally-set timestamp in the inherents
|
||||||
// actually matches the slot set in the seal.
|
// actually matches the slot set in the seal.
|
||||||
if let Some(inner_body) = body.take() {
|
if let Some(inner_body) = block.body.take() {
|
||||||
let block = B::new(pre_header.clone(), inner_body);
|
let new_block = B::new(pre_header.clone(), inner_body);
|
||||||
|
|
||||||
inherent_data.aura_replace_inherent_data(slot);
|
inherent_data.aura_replace_inherent_data(slot);
|
||||||
|
|
||||||
@@ -261,7 +257,7 @@ where
|
|||||||
.map_err(|e| format!("{:?}", e))?
|
.map_err(|e| format!("{:?}", e))?
|
||||||
{
|
{
|
||||||
self.check_inherents(
|
self.check_inherents(
|
||||||
block.clone(),
|
new_block.clone(),
|
||||||
BlockId::Hash(parent_hash),
|
BlockId::Hash(parent_hash),
|
||||||
inherent_data,
|
inherent_data,
|
||||||
create_inherent_data_providers,
|
create_inherent_data_providers,
|
||||||
@@ -270,8 +266,8 @@ where
|
|||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (_, inner_body) = block.deconstruct();
|
let (_, inner_body) = new_block.deconstruct();
|
||||||
body = Some(inner_body);
|
block.body = Some(inner_body);
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!(target: "aura", "Checked {:?}; importing.", pre_header);
|
trace!(target: "aura", "Checked {:?}; importing.", pre_header);
|
||||||
@@ -298,14 +294,12 @@ where
|
|||||||
_ => None,
|
_ => None,
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut import_block = BlockImportParams::new(origin, pre_header);
|
block.header = pre_header;
|
||||||
import_block.post_digests.push(seal);
|
block.post_digests.push(seal);
|
||||||
import_block.body = body;
|
block.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
||||||
import_block.justifications = justifications;
|
block.post_hash = Some(hash);
|
||||||
import_block.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
|
||||||
import_block.post_hash = Some(hash);
|
|
||||||
|
|
||||||
Ok((import_block, maybe_keys))
|
Ok((block, maybe_keys))
|
||||||
},
|
},
|
||||||
CheckedHeader::Deferred(a, b) => {
|
CheckedHeader::Deferred(a, b) => {
|
||||||
debug!(target: "aura", "Checking {:?} failed; {:?}, {:?}.", hash, a, b);
|
debug!(target: "aura", "Checking {:?} failed; {:?}, {:?}.", hash, a, b);
|
||||||
|
|||||||
@@ -118,7 +118,6 @@ 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, DigestItemFor, Header, Zero},
|
||||||
Justifications,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use sc_consensus_slots::SlotProportion;
|
pub use sc_consensus_slots::SlotProportion;
|
||||||
@@ -187,6 +186,19 @@ impl EpochT for Epoch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<sp_consensus_babe::Epoch> for Epoch {
|
||||||
|
fn from(epoch: sp_consensus_babe::Epoch) -> Self {
|
||||||
|
Epoch {
|
||||||
|
epoch_index: epoch.epoch_index,
|
||||||
|
start_slot: epoch.start_slot,
|
||||||
|
duration: epoch.duration,
|
||||||
|
authorities: epoch.authorities,
|
||||||
|
randomness: epoch.randomness,
|
||||||
|
config: epoch.config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Epoch {
|
impl Epoch {
|
||||||
/// Create the genesis epoch (epoch #0). This is defined to start at the slot of
|
/// Create the genesis epoch (epoch #0). This is defined to start at the slot of
|
||||||
/// the first block, so that has to be provided.
|
/// the first block, so that has to be provided.
|
||||||
@@ -1128,24 +1140,29 @@ where
|
|||||||
{
|
{
|
||||||
async fn verify(
|
async fn verify(
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
mut block: BlockImportParams<Block, ()>,
|
||||||
header: Block::Header,
|
|
||||||
justifications: Option<Justifications>,
|
|
||||||
mut body: Option<Vec<Block::Extrinsic>>,
|
|
||||||
) -> BlockVerificationResult<Block> {
|
) -> BlockVerificationResult<Block> {
|
||||||
trace!(
|
trace!(
|
||||||
target: "babe",
|
target: "babe",
|
||||||
"Verifying origin: {:?} header: {:?} justification(s): {:?} body: {:?}",
|
"Verifying origin: {:?} header: {:?} justification(s): {:?} body: {:?}",
|
||||||
origin,
|
block.origin,
|
||||||
header,
|
block.header,
|
||||||
justifications,
|
block.justifications,
|
||||||
body,
|
block.body,
|
||||||
);
|
);
|
||||||
|
|
||||||
let hash = header.hash();
|
let hash = block.header.hash();
|
||||||
let parent_hash = *header.parent_hash();
|
let parent_hash = *block.header.parent_hash();
|
||||||
|
|
||||||
debug!(target: "babe", "We have {:?} logs in this header", header.digest().logs().len());
|
if block.with_state() {
|
||||||
|
// When importing whole state we don't calculate epoch descriptor, but rather
|
||||||
|
// read it from the state after import. We also skip all verifications
|
||||||
|
// because there's no parent state and we trust the sync module to verify
|
||||||
|
// that the state is correct and finalized.
|
||||||
|
return Ok((block, Default::default()))
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!(target: "babe", "We have {:?} logs in this header", block.header.digest().logs().len());
|
||||||
|
|
||||||
let create_inherent_data_providers = self
|
let create_inherent_data_providers = self
|
||||||
.create_inherent_data_providers
|
.create_inherent_data_providers
|
||||||
@@ -1160,7 +1177,7 @@ where
|
|||||||
.header_metadata(parent_hash)
|
.header_metadata(parent_hash)
|
||||||
.map_err(Error::<Block>::FetchParentHeader)?;
|
.map_err(Error::<Block>::FetchParentHeader)?;
|
||||||
|
|
||||||
let pre_digest = find_pre_digest::<Block>(&header)?;
|
let pre_digest = find_pre_digest::<Block>(&block.header)?;
|
||||||
let (check_header, epoch_descriptor) = {
|
let (check_header, epoch_descriptor) = {
|
||||||
let epoch_changes = self.epoch_changes.shared_data();
|
let epoch_changes = self.epoch_changes.shared_data();
|
||||||
let epoch_descriptor = epoch_changes
|
let epoch_descriptor = epoch_changes
|
||||||
@@ -1179,7 +1196,7 @@ where
|
|||||||
// We add one to the current slot to allow for some small drift.
|
// We add one to the current slot to allow for some small drift.
|
||||||
// FIXME #1019 in the future, alter this queue to allow deferring of headers
|
// FIXME #1019 in the future, alter this queue to allow deferring of headers
|
||||||
let v_params = verification::VerificationParams {
|
let v_params = verification::VerificationParams {
|
||||||
header: header.clone(),
|
header: block.header.clone(),
|
||||||
pre_digest: Some(pre_digest),
|
pre_digest: Some(pre_digest),
|
||||||
slot_now: slot_now + 1,
|
slot_now: slot_now + 1,
|
||||||
epoch: viable_epoch.as_ref(),
|
epoch: viable_epoch.as_ref(),
|
||||||
@@ -1203,9 +1220,9 @@ where
|
|||||||
.check_and_report_equivocation(
|
.check_and_report_equivocation(
|
||||||
slot_now,
|
slot_now,
|
||||||
slot,
|
slot,
|
||||||
&header,
|
&block.header,
|
||||||
&verified_info.author,
|
&verified_info.author,
|
||||||
&origin,
|
&block.origin,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
@@ -1215,23 +1232,23 @@ where
|
|||||||
// if the body is passed through, we need to use the runtime
|
// if the body is passed through, we need to use the runtime
|
||||||
// to check that the internally-set timestamp in the inherents
|
// to check that the internally-set timestamp in the inherents
|
||||||
// actually matches the slot set in the seal.
|
// actually matches the slot set in the seal.
|
||||||
if let Some(inner_body) = body.take() {
|
if let Some(inner_body) = block.body {
|
||||||
let mut inherent_data = create_inherent_data_providers
|
let mut inherent_data = create_inherent_data_providers
|
||||||
.create_inherent_data()
|
.create_inherent_data()
|
||||||
.map_err(Error::<Block>::CreateInherents)?;
|
.map_err(Error::<Block>::CreateInherents)?;
|
||||||
inherent_data.babe_replace_inherent_data(slot);
|
inherent_data.babe_replace_inherent_data(slot);
|
||||||
let block = Block::new(pre_header.clone(), inner_body);
|
let new_block = Block::new(pre_header.clone(), inner_body);
|
||||||
|
|
||||||
self.check_inherents(
|
self.check_inherents(
|
||||||
block.clone(),
|
new_block.clone(),
|
||||||
BlockId::Hash(parent_hash),
|
BlockId::Hash(parent_hash),
|
||||||
inherent_data,
|
inherent_data,
|
||||||
create_inherent_data_providers,
|
create_inherent_data_providers,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let (_, inner_body) = block.deconstruct();
|
let (_, inner_body) = new_block.deconstruct();
|
||||||
body = Some(inner_body);
|
block.body = Some(inner_body);
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!(target: "babe", "Checked {:?}; importing.", pre_header);
|
trace!(target: "babe", "Checked {:?}; importing.", pre_header);
|
||||||
@@ -1242,17 +1259,15 @@ where
|
|||||||
"pre_header" => ?pre_header,
|
"pre_header" => ?pre_header,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut import_block = BlockImportParams::new(origin, pre_header);
|
block.header = pre_header;
|
||||||
import_block.post_digests.push(verified_info.seal);
|
block.post_digests.push(verified_info.seal);
|
||||||
import_block.body = body;
|
block.intermediates.insert(
|
||||||
import_block.justifications = justifications;
|
|
||||||
import_block.intermediates.insert(
|
|
||||||
Cow::from(INTERMEDIATE_KEY),
|
Cow::from(INTERMEDIATE_KEY),
|
||||||
Box::new(BabeIntermediate::<Block> { epoch_descriptor }) as Box<_>,
|
Box::new(BabeIntermediate::<Block> { epoch_descriptor }) as Box<_>,
|
||||||
);
|
);
|
||||||
import_block.post_hash = Some(hash);
|
block.post_hash = Some(hash);
|
||||||
|
|
||||||
Ok((import_block, Default::default()))
|
Ok((block, Default::default()))
|
||||||
},
|
},
|
||||||
CheckedHeader::Deferred(a, b) => {
|
CheckedHeader::Deferred(a, b) => {
|
||||||
debug!(target: "babe", "Checking {:?} failed; {:?}, {:?}.", hash, a, b);
|
debug!(target: "babe", "Checking {:?} failed; {:?}, {:?}.", hash, a, b);
|
||||||
@@ -1305,6 +1320,72 @@ impl<Block: BlockT, Client, I> BabeBlockImport<Block, Client, I> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<Block, Client, Inner> BabeBlockImport<Block, Client, Inner>
|
||||||
|
where
|
||||||
|
Block: BlockT,
|
||||||
|
Inner: BlockImport<Block, Transaction = sp_api::TransactionFor<Client, Block>> + Send + Sync,
|
||||||
|
Inner::Error: Into<ConsensusError>,
|
||||||
|
Client: HeaderBackend<Block>
|
||||||
|
+ HeaderMetadata<Block, Error = sp_blockchain::Error>
|
||||||
|
+ AuxStore
|
||||||
|
+ ProvideRuntimeApi<Block>
|
||||||
|
+ ProvideCache<Block>
|
||||||
|
+ Send
|
||||||
|
+ Sync,
|
||||||
|
Client::Api: BabeApi<Block> + ApiExt<Block>,
|
||||||
|
{
|
||||||
|
/// Import whole state after warp sync.
|
||||||
|
// This function makes multiple transactions to the DB. If one of them fails we may
|
||||||
|
// end up in an inconsistent state and have to resync.
|
||||||
|
async fn import_state(
|
||||||
|
&mut self,
|
||||||
|
mut block: BlockImportParams<Block, sp_api::TransactionFor<Client, Block>>,
|
||||||
|
new_cache: HashMap<CacheKeyId, Vec<u8>>,
|
||||||
|
) -> Result<ImportResult, ConsensusError> {
|
||||||
|
let hash = block.post_hash();
|
||||||
|
let parent_hash = *block.header.parent_hash();
|
||||||
|
let number = *block.header.number();
|
||||||
|
|
||||||
|
block.fork_choice = Some(ForkChoiceStrategy::Custom(true));
|
||||||
|
// Reset block weight.
|
||||||
|
aux_schema::write_block_weight(hash, 0, |values| {
|
||||||
|
block
|
||||||
|
.auxiliary
|
||||||
|
.extend(values.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec()))))
|
||||||
|
});
|
||||||
|
|
||||||
|
// First make the client import the state.
|
||||||
|
let import_result = self.inner.import_block(block, new_cache).await;
|
||||||
|
let aux = match import_result {
|
||||||
|
Ok(ImportResult::Imported(aux)) => aux,
|
||||||
|
Ok(r) =>
|
||||||
|
return Err(ConsensusError::ClientImport(format!(
|
||||||
|
"Unexpected import result: {:?}",
|
||||||
|
r
|
||||||
|
))),
|
||||||
|
Err(r) => return Err(r.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Read epoch info from the imported state.
|
||||||
|
let block_id = BlockId::hash(hash);
|
||||||
|
let current_epoch = self.client.runtime_api().current_epoch(&block_id).map_err(|e| {
|
||||||
|
ConsensusError::ClientImport(babe_err::<Block>(Error::RuntimeApi(e)).into())
|
||||||
|
})?;
|
||||||
|
let next_epoch = self.client.runtime_api().next_epoch(&block_id).map_err(|e| {
|
||||||
|
ConsensusError::ClientImport(babe_err::<Block>(Error::RuntimeApi(e)).into())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut epoch_changes = self.epoch_changes.shared_data_locked();
|
||||||
|
epoch_changes.reset(parent_hash, hash, number, current_epoch.into(), next_epoch.into());
|
||||||
|
aux_schema::write_epoch_changes::<Block, _, _>(&*epoch_changes, |insert| {
|
||||||
|
self.client.insert_aux(insert, [])
|
||||||
|
})
|
||||||
|
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?;
|
||||||
|
|
||||||
|
Ok(ImportResult::Imported(aux))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl<Block, Client, Inner> BlockImport<Block> for BabeBlockImport<Block, Client, Inner>
|
impl<Block, Client, Inner> BlockImport<Block> for BabeBlockImport<Block, Client, Inner>
|
||||||
where
|
where
|
||||||
@@ -1336,7 +1417,7 @@ where
|
|||||||
match self.client.status(BlockId::Hash(hash)) {
|
match self.client.status(BlockId::Hash(hash)) {
|
||||||
Ok(sp_blockchain::BlockStatus::InChain) => {
|
Ok(sp_blockchain::BlockStatus::InChain) => {
|
||||||
// When re-importing existing block strip away intermediates.
|
// When re-importing existing block strip away intermediates.
|
||||||
let _ = block.take_intermediate::<BabeIntermediate<Block>>(INTERMEDIATE_KEY)?;
|
let _ = block.take_intermediate::<BabeIntermediate<Block>>(INTERMEDIATE_KEY);
|
||||||
block.fork_choice = Some(ForkChoiceStrategy::Custom(false));
|
block.fork_choice = Some(ForkChoiceStrategy::Custom(false));
|
||||||
return self.inner.import_block(block, new_cache).await.map_err(Into::into)
|
return self.inner.import_block(block, new_cache).await.map_err(Into::into)
|
||||||
},
|
},
|
||||||
@@ -1344,6 +1425,10 @@ where
|
|||||||
Err(e) => return Err(ConsensusError::ClientImport(e.to_string())),
|
Err(e) => return Err(ConsensusError::ClientImport(e.to_string())),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if block.with_state() {
|
||||||
|
return self.import_state(block, new_cache).await
|
||||||
|
}
|
||||||
|
|
||||||
let pre_digest = find_pre_digest::<Block>(&block.header).expect(
|
let pre_digest = find_pre_digest::<Block>(&block.header).expect(
|
||||||
"valid babe headers must contain a predigest; header has been already verified; qed",
|
"valid babe headers must contain a predigest; header has been already verified; qed",
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -228,7 +228,6 @@ pub struct BabeTestNet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TestHeader = <TestBlock as BlockT>::Header;
|
type TestHeader = <TestBlock as BlockT>::Header;
|
||||||
type TestExtrinsic = <TestBlock as BlockT>::Extrinsic;
|
|
||||||
|
|
||||||
type TestSelectChain =
|
type TestSelectChain =
|
||||||
substrate_test_runtime_client::LongestChain<substrate_test_runtime_client::Backend, TestBlock>;
|
substrate_test_runtime_client::LongestChain<substrate_test_runtime_client::Backend, TestBlock>;
|
||||||
@@ -257,14 +256,11 @@ impl Verifier<TestBlock> for TestVerifier {
|
|||||||
/// presented to the User in the logs.
|
/// presented to the User in the logs.
|
||||||
async fn verify(
|
async fn verify(
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
mut block: BlockImportParams<TestBlock, ()>,
|
||||||
mut header: TestHeader,
|
|
||||||
justifications: Option<Justifications>,
|
|
||||||
body: Option<Vec<TestExtrinsic>>,
|
|
||||||
) -> Result<(BlockImportParams<TestBlock, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
) -> Result<(BlockImportParams<TestBlock, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
||||||
// apply post-sealing mutations (i.e. stripping seal, if desired).
|
// apply post-sealing mutations (i.e. stripping seal, if desired).
|
||||||
(self.mutator)(&mut header, Stage::PostSeal);
|
(self.mutator)(&mut block.header, Stage::PostSeal);
|
||||||
self.inner.verify(origin, header, justifications, body).await
|
self.inner.verify(block).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ pub struct BlockCheckParams<Block: BlockT> {
|
|||||||
pub parent_hash: Block::Hash,
|
pub parent_hash: Block::Hash,
|
||||||
/// Allow importing the block skipping state verification if parent state is missing.
|
/// Allow importing the block skipping state verification if parent state is missing.
|
||||||
pub allow_missing_state: bool,
|
pub allow_missing_state: bool,
|
||||||
|
/// Allow importing the block if parent block is missing.
|
||||||
|
pub allow_missing_parent: bool,
|
||||||
/// Re-validate existing block.
|
/// Re-validate existing block.
|
||||||
pub import_existing: bool,
|
pub import_existing: bool,
|
||||||
}
|
}
|
||||||
@@ -306,6 +308,11 @@ impl<Block: BlockT, Transaction> BlockImportParams<Block, Transaction> {
|
|||||||
.downcast_mut::<T>()
|
.downcast_mut::<T>()
|
||||||
.ok_or(Error::InvalidIntermediate)
|
.ok_or(Error::InvalidIntermediate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if this block contains state import action
|
||||||
|
pub fn with_state(&self) -> bool {
|
||||||
|
matches!(self.state_action, StateAction::ApplyChanges(StorageChanges::Import(_)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Block import trait.
|
/// Block import trait.
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
//! instantiated. The `BasicQueue` and `BasicVerifier` traits allow serial
|
//! instantiated. The `BasicQueue` and `BasicVerifier` traits allow serial
|
||||||
//! queues to be instantiated simply.
|
//! queues to be instantiated simply.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, iter::FromIterator};
|
||||||
|
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
@@ -97,10 +97,7 @@ pub trait Verifier<B: BlockT>: Send + Sync {
|
|||||||
/// presented to the User in the logs.
|
/// presented to the User in the logs.
|
||||||
async fn verify(
|
async fn verify(
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
block: BlockImportParams<B, ()>,
|
||||||
header: B::Header,
|
|
||||||
justifications: Option<Justifications>,
|
|
||||||
body: Option<Vec<B::Extrinsic>>,
|
|
||||||
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String>;
|
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +219,7 @@ pub(crate) async fn import_single_block_metered<
|
|||||||
trace!(target: "sync", "Header {} has {:?} logs", block.hash, header.digest().logs().len());
|
trace!(target: "sync", "Header {} has {:?} logs", block.hash, header.digest().logs().len());
|
||||||
|
|
||||||
let number = header.number().clone();
|
let number = header.number().clone();
|
||||||
let hash = header.hash();
|
let hash = block.hash;
|
||||||
let parent_hash = header.parent_hash().clone();
|
let parent_hash = header.parent_hash().clone();
|
||||||
|
|
||||||
let import_handler = |import| match import {
|
let import_handler = |import| match import {
|
||||||
@@ -260,6 +257,7 @@ pub(crate) async fn import_single_block_metered<
|
|||||||
parent_hash,
|
parent_hash,
|
||||||
allow_missing_state: block.allow_missing_state,
|
allow_missing_state: block.allow_missing_state,
|
||||||
import_existing: block.import_existing,
|
import_existing: block.import_existing,
|
||||||
|
allow_missing_parent: block.state.is_some(),
|
||||||
})
|
})
|
||||||
.await,
|
.await,
|
||||||
)? {
|
)? {
|
||||||
@@ -268,10 +266,24 @@ pub(crate) async fn import_single_block_metered<
|
|||||||
}
|
}
|
||||||
|
|
||||||
let started = wasm_timer::Instant::now();
|
let started = wasm_timer::Instant::now();
|
||||||
let (mut import_block, maybe_keys) = verifier
|
|
||||||
.verify(block_origin, header, justifications, block.body)
|
let mut import_block = BlockImportParams::new(block_origin, header);
|
||||||
.await
|
import_block.body = block.body;
|
||||||
.map_err(|msg| {
|
import_block.justifications = justifications;
|
||||||
|
import_block.post_hash = Some(hash);
|
||||||
|
import_block.import_existing = block.import_existing;
|
||||||
|
import_block.indexed_body = block.indexed_body;
|
||||||
|
|
||||||
|
if let Some(state) = block.state {
|
||||||
|
let changes = crate::block_import::StorageChanges::Import(state);
|
||||||
|
import_block.state_action = StateAction::ApplyChanges(changes);
|
||||||
|
} else if block.skip_execution {
|
||||||
|
import_block.state_action = StateAction::Skip;
|
||||||
|
} else if block.allow_missing_state {
|
||||||
|
import_block.state_action = StateAction::ExecuteIfPossible;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (import_block, maybe_keys) = verifier.verify(import_block).await.map_err(|msg| {
|
||||||
if let Some(ref peer) = peer {
|
if let Some(ref peer) = peer {
|
||||||
trace!(target: "sync", "Verifying {}({}) from {} failed: {}", number, hash, peer, msg);
|
trace!(target: "sync", "Verifying {}({}) from {} failed: {}", number, hash, peer, msg);
|
||||||
} else {
|
} else {
|
||||||
@@ -287,22 +299,8 @@ pub(crate) async fn import_single_block_metered<
|
|||||||
metrics.report_verification(true, started.elapsed());
|
metrics.report_verification(true, started.elapsed());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut cache = HashMap::new();
|
let cache = HashMap::from_iter(maybe_keys.unwrap_or_default());
|
||||||
if let Some(keys) = maybe_keys {
|
let import_block = import_block.clear_storage_changes_and_mutate();
|
||||||
cache.extend(keys.into_iter());
|
|
||||||
}
|
|
||||||
import_block.import_existing = block.import_existing;
|
|
||||||
import_block.indexed_body = block.indexed_body;
|
|
||||||
let mut import_block = import_block.clear_storage_changes_and_mutate();
|
|
||||||
if let Some(state) = block.state {
|
|
||||||
let changes = crate::block_import::StorageChanges::Import(state);
|
|
||||||
import_block.state_action = StateAction::ApplyChanges(changes);
|
|
||||||
} else if block.skip_execution {
|
|
||||||
import_block.state_action = StateAction::Skip;
|
|
||||||
} else if block.allow_missing_state {
|
|
||||||
import_block.state_action = StateAction::ExecuteIfPossible;
|
|
||||||
}
|
|
||||||
|
|
||||||
let imported = import_handle.import_block(import_block, cache).await;
|
let imported = import_handle.import_block(import_block, cache).await;
|
||||||
if let Some(metrics) = metrics.as_ref() {
|
if let Some(metrics) = metrics.as_ref() {
|
||||||
metrics.report_verification_and_import(started.elapsed());
|
metrics.report_verification_and_import(started.elapsed());
|
||||||
|
|||||||
@@ -455,12 +455,9 @@ mod tests {
|
|||||||
impl Verifier<Block> for () {
|
impl Verifier<Block> for () {
|
||||||
async fn verify(
|
async fn verify(
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
block: BlockImportParams<Block, ()>,
|
||||||
header: Header,
|
|
||||||
_justifications: Option<Justifications>,
|
|
||||||
_body: Option<Vec<Extrinsic>>,
|
|
||||||
) -> Result<(BlockImportParams<Block, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
) -> Result<(BlockImportParams<Block, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
||||||
Ok((BlockImportParams::new(origin, header), None))
|
Ok((BlockImportParams::new(block.origin, block.header), None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ use sp_runtime::traits::{Block as BlockT, NumberFor, One, Zero};
|
|||||||
use std::{
|
use std::{
|
||||||
borrow::{Borrow, BorrowMut},
|
borrow::{Borrow, BorrowMut},
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
ops::Add,
|
ops::{Add, Sub},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A builder for `is_descendent_of` functions.
|
/// A builder for `is_descendent_of` functions.
|
||||||
@@ -228,7 +228,7 @@ impl<Hash, Number, E: Epoch> ViableEpochDescriptor<Hash, Number, E> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Persisted epoch stored in EpochChanges.
|
/// Persisted epoch stored in EpochChanges.
|
||||||
#[derive(Clone, Encode, Decode, Debug)]
|
#[derive(Clone, Encode, Decode)]
|
||||||
pub enum PersistedEpoch<E: Epoch> {
|
pub enum PersistedEpoch<E: Epoch> {
|
||||||
/// Genesis persisted epoch data. epoch_0, epoch_1.
|
/// Genesis persisted epoch data. epoch_0, epoch_1.
|
||||||
Genesis(E, E),
|
Genesis(E, E),
|
||||||
@@ -322,7 +322,7 @@ where
|
|||||||
impl<Hash, Number, E: Epoch> EpochChanges<Hash, Number, E>
|
impl<Hash, Number, E: Epoch> EpochChanges<Hash, Number, E>
|
||||||
where
|
where
|
||||||
Hash: PartialEq + Ord + AsRef<[u8]> + AsMut<[u8]> + Copy,
|
Hash: PartialEq + Ord + AsRef<[u8]> + AsMut<[u8]> + Copy,
|
||||||
Number: Ord + One + Zero + Add<Output = Number> + Copy,
|
Number: Ord + One + Zero + Add<Output = Number> + Sub<Output = Number> + Copy,
|
||||||
{
|
{
|
||||||
/// Create a new epoch change.
|
/// Create a new epoch change.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
@@ -614,6 +614,25 @@ where
|
|||||||
pub fn tree(&self) -> &ForkTree<Hash, Number, PersistedEpochHeader<E>> {
|
pub fn tree(&self) -> &ForkTree<Hash, Number, PersistedEpochHeader<E>> {
|
||||||
&self.inner
|
&self.inner
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reset to a specified pair of epochs, as if they were announced at blocks `parent_hash` and `hash`.
|
||||||
|
pub fn reset(&mut self, parent_hash: Hash, hash: Hash, number: Number, current: E, next: E) {
|
||||||
|
self.inner = ForkTree::new();
|
||||||
|
self.epochs.clear();
|
||||||
|
let persisted = PersistedEpoch::Regular(current);
|
||||||
|
let header = PersistedEpochHeader::from(&persisted);
|
||||||
|
let _res = self.inner.import(parent_hash, number - One::one(), header, &|_, _| {
|
||||||
|
Ok(false) as Result<bool, fork_tree::Error<ClientError>>
|
||||||
|
});
|
||||||
|
self.epochs.insert((parent_hash, number - One::one()), persisted);
|
||||||
|
|
||||||
|
let persisted = PersistedEpoch::Regular(next);
|
||||||
|
let header = PersistedEpochHeader::from(&persisted);
|
||||||
|
let _res = self.inner.import(hash, number, header, &|_, _| {
|
||||||
|
Ok(true) as Result<bool, fork_tree::Error<ClientError>>
|
||||||
|
});
|
||||||
|
self.epochs.insert((hash, number), persisted);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type alias to produce the epoch-changes tree from a block type.
|
/// Type alias to produce the epoch-changes tree from a block type.
|
||||||
@@ -694,6 +713,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn genesis_epoch_is_created_but_not_imported() {
|
fn genesis_epoch_is_created_but_not_imported() {
|
||||||
|
//
|
||||||
// A - B
|
// A - B
|
||||||
// \
|
// \
|
||||||
// — C
|
// — C
|
||||||
@@ -735,6 +755,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn epoch_changes_between_blocks() {
|
fn epoch_changes_between_blocks() {
|
||||||
|
//
|
||||||
// A - B
|
// A - B
|
||||||
// \
|
// \
|
||||||
// — C
|
// — C
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ use std::{
|
|||||||
use sc_consensus::{BlockImportParams, ForkChoiceStrategy, Verifier};
|
use sc_consensus::{BlockImportParams, ForkChoiceStrategy, Verifier};
|
||||||
use sp_api::{ProvideRuntimeApi, TransactionFor};
|
use sp_api::{ProvideRuntimeApi, TransactionFor};
|
||||||
use sp_blockchain::{HeaderBackend, HeaderMetadata};
|
use sp_blockchain::{HeaderBackend, HeaderMetadata};
|
||||||
use sp_consensus::{BlockOrigin, CacheKeyId};
|
use sp_consensus::CacheKeyId;
|
||||||
use sp_consensus_babe::{
|
use sp_consensus_babe::{
|
||||||
digests::{NextEpochDescriptor, PreDigest, SecondaryPlainPreDigest},
|
digests::{NextEpochDescriptor, PreDigest, SecondaryPlainPreDigest},
|
||||||
inherents::BabeInherentData,
|
inherents::BabeInherentData,
|
||||||
@@ -50,7 +50,6 @@ 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, DigestFor, DigestItemFor, Header, Zero},
|
||||||
Justifications,
|
|
||||||
};
|
};
|
||||||
use sp_timestamp::{InherentType, TimestampInherentData, INHERENT_IDENTIFIER};
|
use sp_timestamp::{InherentType, TimestampInherentData, INHERENT_IDENTIFIER};
|
||||||
|
|
||||||
@@ -98,20 +97,14 @@ where
|
|||||||
{
|
{
|
||||||
async fn verify(
|
async fn verify(
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
mut import_params: BlockImportParams<B, ()>,
|
||||||
header: B::Header,
|
|
||||||
justifications: Option<Justifications>,
|
|
||||||
body: Option<Vec<B::Extrinsic>>,
|
|
||||||
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
||||||
let mut import_params = BlockImportParams::new(origin, header.clone());
|
|
||||||
import_params.justifications = justifications;
|
|
||||||
import_params.body = body;
|
|
||||||
import_params.finalized = false;
|
import_params.finalized = false;
|
||||||
import_params.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
import_params.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
||||||
|
|
||||||
let pre_digest = find_pre_digest::<B>(&header)?;
|
let pre_digest = find_pre_digest::<B>(&import_params.header)?;
|
||||||
|
|
||||||
let parent_hash = header.parent_hash();
|
let parent_hash = import_params.header.parent_hash();
|
||||||
let parent = self
|
let parent = self
|
||||||
.client
|
.client
|
||||||
.header(BlockId::Hash(*parent_hash))
|
.header(BlockId::Hash(*parent_hash))
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ use sc_consensus::{
|
|||||||
import_queue::{BasicQueue, BoxBlockImport, Verifier},
|
import_queue::{BasicQueue, BoxBlockImport, Verifier},
|
||||||
};
|
};
|
||||||
use sp_blockchain::HeaderBackend;
|
use sp_blockchain::HeaderBackend;
|
||||||
use sp_consensus::{BlockOrigin, CacheKeyId, Environment, Proposer, SelectChain};
|
use sp_consensus::{CacheKeyId, Environment, Proposer, SelectChain};
|
||||||
use sp_inherents::CreateInherentDataProviders;
|
use sp_inherents::CreateInherentDataProviders;
|
||||||
use sp_runtime::{traits::Block as BlockT, ConsensusEngineId, Justifications};
|
use sp_runtime::{traits::Block as BlockT, ConsensusEngineId};
|
||||||
use std::{marker::PhantomData, sync::Arc};
|
use std::{marker::PhantomData, sync::Arc};
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
@@ -59,18 +59,11 @@ struct ManualSealVerifier;
|
|||||||
impl<B: BlockT> Verifier<B> for ManualSealVerifier {
|
impl<B: BlockT> Verifier<B> for ManualSealVerifier {
|
||||||
async fn verify(
|
async fn verify(
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
mut block: BlockImportParams<B, ()>,
|
||||||
header: B::Header,
|
|
||||||
justifications: Option<Justifications>,
|
|
||||||
body: Option<Vec<B::Extrinsic>>,
|
|
||||||
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
||||||
let mut import_params = BlockImportParams::new(origin, header);
|
block.finalized = false;
|
||||||
import_params.justifications = justifications;
|
block.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
||||||
import_params.body = body;
|
Ok((block, None))
|
||||||
import_params.finalized = false;
|
|
||||||
import_params.fork_choice = Some(ForkChoiceStrategy::LongestChain);
|
|
||||||
|
|
||||||
Ok((import_params, None))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,15 +58,14 @@ 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, ProvideCache};
|
||||||
use sp_consensus::{
|
use sp_consensus::{
|
||||||
BlockOrigin, CanAuthorWith, Environment, Error as ConsensusError, Proposer, SelectChain,
|
CanAuthorWith, Environment, Error as ConsensusError, Proposer, SelectChain, SyncOracle,
|
||||||
SyncOracle,
|
|
||||||
};
|
};
|
||||||
use sp_consensus_pow::{Seal, TotalDifficulty, POW_ENGINE_ID};
|
use sp_consensus_pow::{Seal, TotalDifficulty, POW_ENGINE_ID};
|
||||||
use sp_inherents::{CreateInherentDataProviders, InherentDataProvider};
|
use sp_inherents::{CreateInherentDataProviders, InherentDataProvider};
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
generic::{BlockId, Digest, DigestItem},
|
generic::{BlockId, Digest, DigestItem},
|
||||||
traits::{Block as BlockT, Header as HeaderT},
|
traits::{Block as BlockT, Header as HeaderT},
|
||||||
Justifications, RuntimeString,
|
RuntimeString,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow, cmp::Ordering, collections::HashMap, marker::PhantomData, sync::Arc,
|
borrow::Cow, cmp::Ordering, collections::HashMap, marker::PhantomData, sync::Arc,
|
||||||
@@ -461,26 +460,20 @@ where
|
|||||||
{
|
{
|
||||||
async fn verify(
|
async fn verify(
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
mut block: BlockImportParams<B, ()>,
|
||||||
header: B::Header,
|
|
||||||
justifications: Option<Justifications>,
|
|
||||||
body: Option<Vec<B::Extrinsic>>,
|
|
||||||
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
||||||
let hash = header.hash();
|
let hash = block.header.hash();
|
||||||
let (checked_header, seal) = self.check_header(header)?;
|
let (checked_header, seal) = self.check_header(block.header)?;
|
||||||
|
|
||||||
let intermediate = PowIntermediate::<Algorithm::Difficulty> { difficulty: None };
|
let intermediate = PowIntermediate::<Algorithm::Difficulty> { difficulty: None };
|
||||||
|
block.header = checked_header;
|
||||||
let mut import_block = BlockImportParams::new(origin, checked_header);
|
block.post_digests.push(seal);
|
||||||
import_block.post_digests.push(seal);
|
block
|
||||||
import_block.body = body;
|
|
||||||
import_block.justifications = justifications;
|
|
||||||
import_block
|
|
||||||
.intermediates
|
.intermediates
|
||||||
.insert(Cow::from(INTERMEDIATE_KEY), Box::new(intermediate) as Box<_>);
|
.insert(Cow::from(INTERMEDIATE_KEY), Box::new(intermediate) as Box<_>);
|
||||||
import_block.post_hash = Some(hash);
|
block.post_hash = Some(hash);
|
||||||
|
|
||||||
Ok((import_block, None))
|
Ok((block, None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
@@ -302,6 +302,7 @@ impl<Block: BlockT, T: CacheItemT, S: Storage<Block, T>> ListCache<Block, T, S>
|
|||||||
let prev_operation = operations.operations.last();
|
let prev_operation = operations.operations.last();
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
entry_type != EntryType::Final ||
|
entry_type != EntryType::Final ||
|
||||||
|
self.unfinalized.is_empty() ||
|
||||||
self.best_finalized_block.hash == parent.hash ||
|
self.best_finalized_block.hash == parent.hash ||
|
||||||
match prev_operation {
|
match prev_operation {
|
||||||
Some(&CommitOperation::BlockFinalized(ref best_finalized_block, _, _)) =>
|
Some(&CommitOperation::BlockFinalized(ref best_finalized_block, _, _)) =>
|
||||||
|
|||||||
@@ -358,18 +358,23 @@ impl<Block: BlockT> DbChangesTrieStorage<Block> {
|
|||||||
let next_config = match cache_tx {
|
let next_config = match cache_tx {
|
||||||
Some(cache_tx) if config_for_new_block && cache_tx.new_config.is_some() => {
|
Some(cache_tx) if config_for_new_block && cache_tx.new_config.is_some() => {
|
||||||
let config = cache_tx.new_config.clone().expect("guarded by is_some(); qed");
|
let config = cache_tx.new_config.clone().expect("guarded by is_some(); qed");
|
||||||
ChangesTrieConfigurationRange {
|
Ok(ChangesTrieConfigurationRange {
|
||||||
zero: (block_num, block_hash),
|
zero: (block_num, block_hash),
|
||||||
end: None,
|
end: None,
|
||||||
config,
|
config,
|
||||||
}
|
})
|
||||||
},
|
},
|
||||||
_ if config_for_new_block => self.configuration_at(&BlockId::Hash(
|
_ if config_for_new_block => self.configuration_at(&BlockId::Hash(
|
||||||
*new_header
|
*new_header
|
||||||
.expect("config_for_new_block is only true when new_header is passed; qed")
|
.expect("config_for_new_block is only true when new_header is passed; qed")
|
||||||
.parent_hash(),
|
.parent_hash(),
|
||||||
))?,
|
)),
|
||||||
_ => self.configuration_at(&BlockId::Hash(next_digest_range_start_hash))?,
|
_ => self.configuration_at(&BlockId::Hash(next_digest_range_start_hash)),
|
||||||
|
};
|
||||||
|
let next_config = match next_config {
|
||||||
|
Ok(next_config) => next_config,
|
||||||
|
Err(ClientError::UnknownBlock(_)) => break, // No block means nothing to prune.
|
||||||
|
Err(e) => return Err(e),
|
||||||
};
|
};
|
||||||
if let Some(config) = next_config.config {
|
if let Some(config) = next_config.config {
|
||||||
let mut oldest_digest_range = config
|
let mut oldest_digest_range = config
|
||||||
|
|||||||
@@ -692,7 +692,10 @@ impl<Block: BlockT> HeaderMetadata<Block> for BlockchainDb<Block> {
|
|||||||
header_metadata
|
header_metadata
|
||||||
})
|
})
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
ClientError::UnknownBlock(format!("header not found in db: {}", hash))
|
ClientError::UnknownBlock(format!(
|
||||||
|
"Header was not found in the database: {:?}",
|
||||||
|
hash
|
||||||
|
))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
Ok,
|
Ok,
|
||||||
@@ -1210,8 +1213,11 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
return Err(sp_blockchain::Error::SetHeadTooOld.into())
|
return Err(sp_blockchain::Error::SetHeadTooOld.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
// cannot find tree route with empty DB.
|
let parent_exists =
|
||||||
if meta.best_hash != Default::default() {
|
self.blockchain.status(BlockId::Hash(route_to))? == sp_blockchain::BlockStatus::InChain;
|
||||||
|
|
||||||
|
// Cannot find tree route with empty DB or when imported a detached block.
|
||||||
|
if meta.best_hash != Default::default() && parent_exists {
|
||||||
let tree_route = sp_blockchain::tree_route(&self.blockchain, meta.best_hash, route_to)?;
|
let tree_route = sp_blockchain::tree_route(&self.blockchain, meta.best_hash, route_to)?;
|
||||||
|
|
||||||
// uncanonicalize: check safety violations and ensure the numbers no longer
|
// uncanonicalize: check safety violations and ensure the numbers no longer
|
||||||
@@ -1261,8 +1267,10 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
) -> ClientResult<()> {
|
) -> ClientResult<()> {
|
||||||
let last_finalized =
|
let last_finalized =
|
||||||
last_finalized.unwrap_or_else(|| self.blockchain.meta.read().finalized_hash);
|
last_finalized.unwrap_or_else(|| self.blockchain.meta.read().finalized_hash);
|
||||||
if *header.parent_hash() != last_finalized {
|
if last_finalized != self.blockchain.meta.read().genesis_hash &&
|
||||||
return Err(::sp_blockchain::Error::NonSequentialFinalization(format!(
|
*header.parent_hash() != last_finalized
|
||||||
|
{
|
||||||
|
return Err(sp_blockchain::Error::NonSequentialFinalization(format!(
|
||||||
"Last finalized {:?} not parent of {:?}",
|
"Last finalized {:?} not parent of {:?}",
|
||||||
last_finalized,
|
last_finalized,
|
||||||
header.hash()
|
header.hash()
|
||||||
@@ -1588,7 +1596,7 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
columns::META,
|
columns::META,
|
||||||
meta_keys::LEAF_PREFIX,
|
meta_keys::LEAF_PREFIX,
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
let mut children = children::read_children(
|
let mut children = children::read_children(
|
||||||
&*self.storage.db,
|
&*self.storage.db,
|
||||||
@@ -1598,7 +1606,6 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
)?;
|
)?;
|
||||||
if !children.contains(&hash) {
|
if !children.contains(&hash) {
|
||||||
children.push(hash);
|
children.push(hash);
|
||||||
}
|
|
||||||
children::write_children(
|
children::write_children(
|
||||||
&mut transaction,
|
&mut transaction,
|
||||||
columns::META,
|
columns::META,
|
||||||
@@ -1607,6 +1614,7 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
children,
|
children,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
meta_updates.push(MetaUpdate {
|
meta_updates.push(MetaUpdate {
|
||||||
hash,
|
hash,
|
||||||
@@ -1615,7 +1623,6 @@ impl<Block: BlockT> Backend<Block> {
|
|||||||
is_finalized: finalized,
|
is_finalized: finalized,
|
||||||
with_state: operation.commit_state,
|
with_state: operation.commit_state,
|
||||||
});
|
});
|
||||||
|
|
||||||
Some((pending_block.header, number, hash, enacted, retracted, is_best, cache))
|
Some((pending_block.header, number, hash, enacted, retracted, is_best, cache))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ futures = "0.3.9"
|
|||||||
futures-timer = "3.0.1"
|
futures-timer = "3.0.1"
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
parking_lot = "0.11.1"
|
parking_lot = "0.11.1"
|
||||||
rand = "0.7.2"
|
rand = "0.8.4"
|
||||||
parity-scale-codec = { version = "2.0.0", features = ["derive"] }
|
parity-scale-codec = { version = "2.0.0", features = ["derive"] }
|
||||||
sp-application-crypto = { version = "4.0.0-dev", path = "../../primitives/application-crypto" }
|
sp-application-crypto = { version = "4.0.0-dev", path = "../../primitives/application-crypto" }
|
||||||
sp-arithmetic = { version = "4.0.0-dev", path = "../../primitives/arithmetic" }
|
sp-arithmetic = { version = "4.0.0-dev", path = "../../primitives/arithmetic" }
|
||||||
|
|||||||
@@ -1087,7 +1087,7 @@ where
|
|||||||
|
|
||||||
// random between `[0, 2 * gossip_duration]` seconds.
|
// random between `[0, 2 * gossip_duration]` seconds.
|
||||||
let delay: u64 =
|
let delay: u64 =
|
||||||
thread_rng().gen_range(0, 2 * self.config.gossip_duration.as_millis() as u64);
|
thread_rng().gen_range(0..2 * self.config.gossip_duration.as_millis() as u64);
|
||||||
Box::pin(Delay::new(Duration::from_millis(delay)).map(Ok))
|
Box::pin(Delay::new(Duration::from_millis(delay)).map(Ok))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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::Encode;
|
use parity_scale_codec::{Decode, Encode};
|
||||||
|
|
||||||
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::{
|
||||||
@@ -27,10 +27,11 @@ use sc_consensus::{
|
|||||||
BlockCheckParams, BlockImport, BlockImportParams, ImportResult, JustificationImport,
|
BlockCheckParams, BlockImport, BlockImportParams, ImportResult, JustificationImport,
|
||||||
};
|
};
|
||||||
use sc_telemetry::TelemetryHandle;
|
use sc_telemetry::TelemetryHandle;
|
||||||
use sp_api::TransactionFor;
|
use sp_api::{Core, RuntimeApiInfo, TransactionFor};
|
||||||
use sp_blockchain::{well_known_cache_keys, BlockStatus};
|
use sp_blockchain::{well_known_cache_keys, BlockStatus};
|
||||||
use sp_consensus::{BlockOrigin, Error as ConsensusError, SelectChain};
|
use sp_consensus::{BlockOrigin, Error as ConsensusError, SelectChain};
|
||||||
use sp_finality_grandpa::{ConsensusLog, ScheduledChange, SetId, GRANDPA_ENGINE_ID};
|
use sp_core::hashing::twox_128;
|
||||||
|
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, DigestFor, Header as HeaderT, NumberFor, Zero},
|
||||||
@@ -43,7 +44,7 @@ use crate::{
|
|||||||
environment::finalize_block,
|
environment::finalize_block,
|
||||||
justification::GrandpaJustification,
|
justification::GrandpaJustification,
|
||||||
notification::GrandpaJustificationSender,
|
notification::GrandpaJustificationSender,
|
||||||
ClientForGrandpa, CommandOrError, Error, NewAuthoritySet, VoterCommand,
|
AuthoritySetChanges, ClientForGrandpa, CommandOrError, Error, NewAuthoritySet, VoterCommand,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A block-import handler for GRANDPA.
|
/// A block-import handler for GRANDPA.
|
||||||
@@ -230,6 +231,10 @@ where
|
|||||||
DigestFor<Block>: Encode,
|
DigestFor<Block>: Encode,
|
||||||
BE: Backend<Block>,
|
BE: Backend<Block>,
|
||||||
Client: ClientForGrandpa<Block, BE>,
|
Client: ClientForGrandpa<Block, BE>,
|
||||||
|
Client::Api: GrandpaApi<Block>,
|
||||||
|
for<'a> &'a Client:
|
||||||
|
BlockImport<Block, Error = ConsensusError, Transaction = TransactionFor<Client, Block>>,
|
||||||
|
TransactionFor<Client, Block>: 'static,
|
||||||
{
|
{
|
||||||
// check for a new authority set change.
|
// check for a new authority set change.
|
||||||
fn check_new_change(
|
fn check_new_change(
|
||||||
@@ -418,6 +423,91 @@ where
|
|||||||
|
|
||||||
Ok(PendingSetChanges { just_in_case, applied_changes, do_pause })
|
Ok(PendingSetChanges { just_in_case, applied_changes, do_pause })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read current set id form a given state.
|
||||||
|
fn current_set_id(&self, id: &BlockId<Block>) -> Result<SetId, ConsensusError> {
|
||||||
|
let runtime_version = self.inner.runtime_api().version(id).map_err(|e| {
|
||||||
|
ConsensusError::ClientImport(format!(
|
||||||
|
"Unable to retrieve current runtime version. {}",
|
||||||
|
e
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
if runtime_version
|
||||||
|
.api_version(&<dyn GrandpaApi<Block>>::ID)
|
||||||
|
.map_or(false, |v| v < 3)
|
||||||
|
{
|
||||||
|
// The new API is not supported in this runtime. Try reading directly from storage.
|
||||||
|
// This code may be removed once warp sync to an old runtime is no longer needed.
|
||||||
|
for prefix in ["GrandpaFinality", "Grandpa"] {
|
||||||
|
let k = [twox_128(prefix.as_bytes()), twox_128(b"CurrentSetId")].concat();
|
||||||
|
if let Ok(Some(id)) =
|
||||||
|
self.inner.storage(&id, &sc_client_api::StorageKey(k.to_vec()))
|
||||||
|
{
|
||||||
|
if let Ok(id) = SetId::decode(&mut id.0.as_ref()) {
|
||||||
|
return Ok(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(ConsensusError::ClientImport("Unable to retrieve current set id.".into()))
|
||||||
|
} else {
|
||||||
|
self.inner
|
||||||
|
.runtime_api()
|
||||||
|
.current_set_id(&id)
|
||||||
|
.map_err(|e| ConsensusError::ClientImport(e.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Import whole new state and reset authority set.
|
||||||
|
async fn import_state(
|
||||||
|
&mut self,
|
||||||
|
mut block: BlockImportParams<Block, TransactionFor<Client, Block>>,
|
||||||
|
new_cache: HashMap<well_known_cache_keys::Id, Vec<u8>>,
|
||||||
|
) -> Result<ImportResult, ConsensusError> {
|
||||||
|
let hash = block.post_hash();
|
||||||
|
let number = *block.header.number();
|
||||||
|
// Force imported state finality.
|
||||||
|
block.finalized = true;
|
||||||
|
let import_result = (&*self.inner).import_block(block, new_cache).await;
|
||||||
|
match import_result {
|
||||||
|
Ok(ImportResult::Imported(aux)) => {
|
||||||
|
// We've just imported a new state. We trust the sync module has verified
|
||||||
|
// finality proofs and that the state is correct and final.
|
||||||
|
// So we can read the authority list and set id from the state.
|
||||||
|
self.authority_set_hard_forks.clear();
|
||||||
|
let block_id = BlockId::hash(hash);
|
||||||
|
let authorities = self
|
||||||
|
.inner
|
||||||
|
.runtime_api()
|
||||||
|
.grandpa_authorities(&block_id)
|
||||||
|
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?;
|
||||||
|
let set_id = self.current_set_id(&block_id)?;
|
||||||
|
let authority_set = AuthoritySet::new(
|
||||||
|
authorities.clone(),
|
||||||
|
set_id,
|
||||||
|
fork_tree::ForkTree::new(),
|
||||||
|
Vec::new(),
|
||||||
|
AuthoritySetChanges::empty(),
|
||||||
|
)
|
||||||
|
.ok_or_else(|| ConsensusError::ClientImport("Invalid authority list".into()))?;
|
||||||
|
*self.authority_set.inner_locked() = authority_set.clone();
|
||||||
|
|
||||||
|
crate::aux_schema::update_authority_set::<Block, _, _>(
|
||||||
|
&authority_set,
|
||||||
|
None,
|
||||||
|
|insert| self.inner.insert_aux(insert, []),
|
||||||
|
)
|
||||||
|
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?;
|
||||||
|
let new_set =
|
||||||
|
NewAuthoritySet { canon_number: number, canon_hash: hash, set_id, authorities };
|
||||||
|
let _ = self
|
||||||
|
.send_voter_commands
|
||||||
|
.unbounded_send(VoterCommand::ChangeAuthorities(new_set));
|
||||||
|
Ok(ImportResult::Imported(aux))
|
||||||
|
},
|
||||||
|
Ok(r) => Ok(r),
|
||||||
|
Err(e) => Err(ConsensusError::ClientImport(e.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
@@ -427,6 +517,7 @@ where
|
|||||||
DigestFor<Block>: Encode,
|
DigestFor<Block>: Encode,
|
||||||
BE: Backend<Block>,
|
BE: Backend<Block>,
|
||||||
Client: ClientForGrandpa<Block, BE>,
|
Client: ClientForGrandpa<Block, BE>,
|
||||||
|
Client::Api: GrandpaApi<Block>,
|
||||||
for<'a> &'a Client:
|
for<'a> &'a Client:
|
||||||
BlockImport<Block, Error = ConsensusError, Transaction = TransactionFor<Client, Block>>,
|
BlockImport<Block, Error = ConsensusError, Transaction = TransactionFor<Client, Block>>,
|
||||||
TransactionFor<Client, Block>: 'static,
|
TransactionFor<Client, Block>: 'static,
|
||||||
@@ -455,6 +546,10 @@ where
|
|||||||
Err(e) => return Err(ConsensusError::ClientImport(e.to_string())),
|
Err(e) => return Err(ConsensusError::ClientImport(e.to_string())),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if block.with_state() {
|
||||||
|
return self.import_state(block, new_cache).await
|
||||||
|
}
|
||||||
|
|
||||||
// on initial sync we will restrict logging under info to avoid spam.
|
// on initial sync we will restrict logging under info to avoid spam.
|
||||||
let initial_sync = block.origin == BlockOrigin::NetworkInitialSync;
|
let initial_sync = block.origin == BlockOrigin::NetworkInitialSync;
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ use prometheus_endpoint::{PrometheusError, Registry};
|
|||||||
use sc_client_api::{
|
use sc_client_api::{
|
||||||
backend::{AuxStore, Backend},
|
backend::{AuxStore, Backend},
|
||||||
BlockchainEvents, CallExecutor, ExecutionStrategy, ExecutorProvider, Finalizer, LockImportRun,
|
BlockchainEvents, CallExecutor, ExecutionStrategy, ExecutorProvider, Finalizer, LockImportRun,
|
||||||
TransactionFor,
|
StorageProvider, TransactionFor,
|
||||||
};
|
};
|
||||||
use sc_consensus::BlockImport;
|
use sc_consensus::BlockImport;
|
||||||
use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_INFO};
|
use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_INFO};
|
||||||
@@ -119,6 +119,7 @@ mod notification;
|
|||||||
mod observer;
|
mod observer;
|
||||||
mod until_imported;
|
mod until_imported;
|
||||||
mod voting_rule;
|
mod voting_rule;
|
||||||
|
pub mod warp_proof;
|
||||||
|
|
||||||
pub use authorities::{AuthoritySet, AuthoritySetChanges, SharedAuthoritySet};
|
pub use authorities::{AuthoritySet, AuthoritySetChanges, SharedAuthoritySet};
|
||||||
pub use aux_schema::best_justification;
|
pub use aux_schema::best_justification;
|
||||||
@@ -335,6 +336,7 @@ pub trait ClientForGrandpa<Block, BE>:
|
|||||||
+ ProvideRuntimeApi<Block>
|
+ ProvideRuntimeApi<Block>
|
||||||
+ ExecutorProvider<Block>
|
+ ExecutorProvider<Block>
|
||||||
+ BlockImport<Block, Transaction = TransactionFor<BE, Block>, Error = sp_consensus::Error>
|
+ BlockImport<Block, Transaction = TransactionFor<BE, Block>, Error = sp_consensus::Error>
|
||||||
|
+ StorageProvider<Block, BE>
|
||||||
where
|
where
|
||||||
BE: Backend<Block>,
|
BE: Backend<Block>,
|
||||||
Block: BlockT,
|
Block: BlockT,
|
||||||
@@ -353,7 +355,8 @@ where
|
|||||||
+ BlockchainEvents<Block>
|
+ BlockchainEvents<Block>
|
||||||
+ ProvideRuntimeApi<Block>
|
+ ProvideRuntimeApi<Block>
|
||||||
+ ExecutorProvider<Block>
|
+ ExecutorProvider<Block>
|
||||||
+ BlockImport<Block, Transaction = TransactionFor<BE, Block>, Error = sp_consensus::Error>,
|
+ BlockImport<Block, Transaction = TransactionFor<BE, Block>, Error = sp_consensus::Error>
|
||||||
|
+ StorageProvider<Block, BE>,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -177,12 +177,11 @@ where
|
|||||||
{
|
{
|
||||||
let LinkHalf {
|
let LinkHalf {
|
||||||
client,
|
client,
|
||||||
select_chain: _,
|
|
||||||
persistent_data,
|
persistent_data,
|
||||||
voter_commands_rx,
|
voter_commands_rx,
|
||||||
justification_sender,
|
justification_sender,
|
||||||
justification_stream: _,
|
|
||||||
telemetry,
|
telemetry,
|
||||||
|
..
|
||||||
} = link;
|
} = link;
|
||||||
|
|
||||||
let network = NetworkBridge::new(
|
let network = NetworkBridge::new(
|
||||||
|
|||||||
@@ -203,6 +203,10 @@ sp_api::mock_impl_runtime_apis! {
|
|||||||
self.inner.genesis_authorities.clone()
|
self.inner.genesis_authorities.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn current_set_id(&self) -> SetId {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
fn submit_report_equivocation_unsigned_extrinsic(
|
fn submit_report_equivocation_unsigned_extrinsic(
|
||||||
_equivocation_proof: EquivocationProof<Hash, BlockNumber>,
|
_equivocation_proof: EquivocationProof<Hash, BlockNumber>,
|
||||||
_key_owner_proof: OpaqueKeyOwnershipProof,
|
_key_owner_proof: OpaqueKeyOwnershipProof,
|
||||||
|
|||||||
+123
-31
@@ -14,12 +14,16 @@
|
|||||||
// 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use codec::{Decode, Encode};
|
//! Utilities for generating and verifying GRANDPA warp sync proofs.
|
||||||
|
|
||||||
use sc_client_api::Backend as ClientBackend;
|
use sp_runtime::codec::{self, Decode, Encode};
|
||||||
use sc_finality_grandpa::{
|
|
||||||
find_scheduled_change, AuthoritySetChanges, BlockNumberOps, GrandpaJustification,
|
use crate::{
|
||||||
|
best_justification, find_scheduled_change, AuthoritySetChanges, BlockNumberOps,
|
||||||
|
GrandpaJustification, SharedAuthoritySet,
|
||||||
};
|
};
|
||||||
|
use sc_client_api::Backend as ClientBackend;
|
||||||
|
use sc_network::warp_request_handler::{EncodedProof, VerificationResult, WarpSyncProvider};
|
||||||
use sp_blockchain::{Backend as BlockchainBackend, HeaderBackend};
|
use sp_blockchain::{Backend as BlockchainBackend, HeaderBackend};
|
||||||
use sp_finality_grandpa::{AuthorityList, SetId, GRANDPA_ENGINE_ID};
|
use sp_finality_grandpa::{AuthorityList, SetId, GRANDPA_ENGINE_ID};
|
||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
@@ -27,13 +31,34 @@ use sp_runtime::{
|
|||||||
traits::{Block as BlockT, Header as HeaderT, NumberFor, One},
|
traits::{Block as BlockT, Header as HeaderT, NumberFor, One},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::HandleRequestError;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
/// Warp proof processing error.
|
||||||
|
#[derive(Debug, derive_more::Display, derive_more::From)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Decoding error.
|
||||||
|
#[display(fmt = "Failed to decode block hash: {}.", _0)]
|
||||||
|
DecodeScale(codec::Error),
|
||||||
|
/// Client backend error.
|
||||||
|
Client(sp_blockchain::Error),
|
||||||
|
/// Invalid request data.
|
||||||
|
#[from(ignore)]
|
||||||
|
InvalidRequest(String),
|
||||||
|
/// Invalid warp proof.
|
||||||
|
#[from(ignore)]
|
||||||
|
InvalidProof(String),
|
||||||
|
/// Missing header or authority set change data.
|
||||||
|
#[display(fmt = "Missing required data to be able to answer request.")]
|
||||||
|
MissingData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {}
|
||||||
|
|
||||||
/// The maximum size in bytes of the `WarpSyncProof`.
|
/// The maximum size in bytes of the `WarpSyncProof`.
|
||||||
pub(super) const MAX_WARP_SYNC_PROOF_SIZE: usize = 16 * 1024 * 1024;
|
pub(super) const MAX_WARP_SYNC_PROOF_SIZE: usize = 8 * 1024 * 1024;
|
||||||
|
|
||||||
/// A proof of an authority set change.
|
/// A proof of an authority set change.
|
||||||
#[derive(Decode, Encode)]
|
#[derive(Decode, Encode, Debug)]
|
||||||
pub struct WarpSyncFragment<Block: BlockT> {
|
pub struct WarpSyncFragment<Block: BlockT> {
|
||||||
/// The last block that the given authority set finalized. This block should contain a digest
|
/// The last block that the given authority set finalized. This block should contain a digest
|
||||||
/// signaling an authority set change from which we can fetch the next authority set.
|
/// signaling an authority set change from which we can fetch the next authority set.
|
||||||
@@ -54,11 +79,11 @@ impl<Block: BlockT> WarpSyncProof<Block> {
|
|||||||
/// Generates a warp sync proof starting at the given block. It will generate authority set
|
/// Generates a warp sync proof starting at the given block. It will generate authority set
|
||||||
/// change proofs for all changes that happened from `begin` until the current authority set
|
/// change proofs for all changes that happened from `begin` until the current authority set
|
||||||
/// (capped by MAX_WARP_SYNC_PROOF_SIZE).
|
/// (capped by MAX_WARP_SYNC_PROOF_SIZE).
|
||||||
pub fn generate<Backend>(
|
fn generate<Backend>(
|
||||||
backend: &Backend,
|
backend: &Backend,
|
||||||
begin: Block::Hash,
|
begin: Block::Hash,
|
||||||
set_changes: &AuthoritySetChanges<NumberFor<Block>>,
|
set_changes: &AuthoritySetChanges<NumberFor<Block>>,
|
||||||
) -> Result<WarpSyncProof<Block>, HandleRequestError>
|
) -> Result<WarpSyncProof<Block>, Error>
|
||||||
where
|
where
|
||||||
Backend: ClientBackend<Block>,
|
Backend: ClientBackend<Block>,
|
||||||
{
|
{
|
||||||
@@ -67,12 +92,10 @@ impl<Block: BlockT> WarpSyncProof<Block> {
|
|||||||
|
|
||||||
let begin_number = blockchain
|
let begin_number = blockchain
|
||||||
.block_number_from_id(&BlockId::Hash(begin))?
|
.block_number_from_id(&BlockId::Hash(begin))?
|
||||||
.ok_or_else(|| HandleRequestError::InvalidRequest("Missing start block".to_string()))?;
|
.ok_or_else(|| Error::InvalidRequest("Missing start block".to_string()))?;
|
||||||
|
|
||||||
if begin_number > blockchain.info().finalized_number {
|
if begin_number > blockchain.info().finalized_number {
|
||||||
return Err(HandleRequestError::InvalidRequest(
|
return Err(Error::InvalidRequest("Start block is not finalized".to_string()))
|
||||||
"Start block is not finalized".to_string(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let canon_hash = blockchain.hash(begin_number)?.expect(
|
let canon_hash = blockchain.hash(begin_number)?.expect(
|
||||||
@@ -82,7 +105,7 @@ impl<Block: BlockT> WarpSyncProof<Block> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if canon_hash != begin {
|
if canon_hash != begin {
|
||||||
return Err(HandleRequestError::InvalidRequest(
|
return Err(Error::InvalidRequest(
|
||||||
"Start block is not in the finalized chain".to_string(),
|
"Start block is not in the finalized chain".to_string(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@@ -91,8 +114,7 @@ impl<Block: BlockT> WarpSyncProof<Block> {
|
|||||||
let mut proofs_encoded_len = 0;
|
let mut proofs_encoded_len = 0;
|
||||||
let mut proof_limit_reached = false;
|
let mut proof_limit_reached = false;
|
||||||
|
|
||||||
let set_changes =
|
let set_changes = set_changes.iter_from(begin_number).ok_or(Error::MissingData)?;
|
||||||
set_changes.iter_from(begin_number).ok_or(HandleRequestError::MissingData)?;
|
|
||||||
|
|
||||||
for (_, last_block) in set_changes {
|
for (_, last_block) in set_changes {
|
||||||
let header = blockchain.header(BlockId::Number(*last_block))?.expect(
|
let header = blockchain.header(BlockId::Number(*last_block))?.expect(
|
||||||
@@ -137,8 +159,7 @@ impl<Block: BlockT> WarpSyncProof<Block> {
|
|||||||
let is_finished = if proof_limit_reached {
|
let is_finished = if proof_limit_reached {
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
let latest_justification =
|
let latest_justification = best_justification(backend)?.filter(|justification| {
|
||||||
sc_finality_grandpa::best_justification(backend)?.filter(|justification| {
|
|
||||||
// the existing best justification must be for a block higher than the
|
// the existing best justification must be for a block higher than the
|
||||||
// last authority set change. if we didn't prove any authority set
|
// last authority set change. if we didn't prove any authority set
|
||||||
// change then we fallback to make sure it's higher or equal to the
|
// change then we fallback to make sure it's higher or equal to the
|
||||||
@@ -167,12 +188,13 @@ impl<Block: BlockT> WarpSyncProof<Block> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Verifies the warp sync proof starting at the given set id and with the given authorities.
|
/// Verifies the warp sync proof starting at the given set id and with the given authorities.
|
||||||
|
/// Verification stops when either the proof is exhausted or finality for the target header can be proven.
|
||||||
/// If the proof is valid the new set id and authorities is returned.
|
/// If the proof is valid the new set id and authorities is returned.
|
||||||
pub fn verify(
|
fn verify(
|
||||||
&self,
|
&self,
|
||||||
set_id: SetId,
|
set_id: SetId,
|
||||||
authorities: AuthorityList,
|
authorities: AuthorityList,
|
||||||
) -> Result<(SetId, AuthorityList), HandleRequestError>
|
) -> Result<(SetId, AuthorityList), Error>
|
||||||
where
|
where
|
||||||
NumberFor<Block>: BlockNumberOps,
|
NumberFor<Block>: BlockNumberOps,
|
||||||
{
|
{
|
||||||
@@ -183,37 +205,107 @@ impl<Block: BlockT> WarpSyncProof<Block> {
|
|||||||
proof
|
proof
|
||||||
.justification
|
.justification
|
||||||
.verify(current_set_id, ¤t_authorities)
|
.verify(current_set_id, ¤t_authorities)
|
||||||
.map_err(|err| HandleRequestError::InvalidProof(err.to_string()))?;
|
.map_err(|err| Error::InvalidProof(err.to_string()))?;
|
||||||
|
|
||||||
if proof.justification.target().1 != proof.header.hash() {
|
if proof.justification.target().1 != proof.header.hash() {
|
||||||
return Err(HandleRequestError::InvalidProof(
|
return Err(Error::InvalidProof(
|
||||||
"mismatch between header and justification".to_owned(),
|
"Mismatch between header and justification".to_owned(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(scheduled_change) = find_scheduled_change::<Block>(&proof.header) {
|
if let Some(scheduled_change) = find_scheduled_change::<Block>(&proof.header) {
|
||||||
current_authorities = scheduled_change.next_authorities;
|
current_authorities = scheduled_change.next_authorities;
|
||||||
current_set_id += 1;
|
current_set_id += 1;
|
||||||
} else if fragment_num != self.proofs.len() - 1 {
|
} else if fragment_num != self.proofs.len() - 1 || !self.is_finished {
|
||||||
// Only the last fragment of the proof is allowed to be missing the authority
|
// Only the last fragment of the last proof message is allowed to be missing
|
||||||
// set change.
|
// the authority set change.
|
||||||
return Err(HandleRequestError::InvalidProof(
|
return Err(Error::InvalidProof(
|
||||||
"Header is missing authority set change digest".to_string(),
|
"Header is missing authority set change digest".to_string(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((current_set_id, current_authorities))
|
Ok((current_set_id, current_authorities))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Implements network API for warp sync.
|
||||||
|
pub struct NetworkProvider<Block: BlockT, Backend: ClientBackend<Block>>
|
||||||
|
where
|
||||||
|
NumberFor<Block>: BlockNumberOps,
|
||||||
|
{
|
||||||
|
backend: Arc<Backend>,
|
||||||
|
authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Block: BlockT, Backend: ClientBackend<Block>> NetworkProvider<Block, Backend>
|
||||||
|
where
|
||||||
|
NumberFor<Block>: BlockNumberOps,
|
||||||
|
{
|
||||||
|
/// Create a new istance for a given backend and authority set.
|
||||||
|
pub fn new(
|
||||||
|
backend: Arc<Backend>,
|
||||||
|
authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
|
||||||
|
) -> Self {
|
||||||
|
NetworkProvider { backend, authority_set }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Block: BlockT, Backend: ClientBackend<Block>> WarpSyncProvider<Block>
|
||||||
|
for NetworkProvider<Block, Backend>
|
||||||
|
where
|
||||||
|
NumberFor<Block>: BlockNumberOps,
|
||||||
|
{
|
||||||
|
fn generate(
|
||||||
|
&self,
|
||||||
|
start: Block::Hash,
|
||||||
|
) -> Result<EncodedProof, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let proof = WarpSyncProof::<Block>::generate(
|
||||||
|
&*self.backend,
|
||||||
|
start,
|
||||||
|
&self.authority_set.authority_set_changes(),
|
||||||
|
)
|
||||||
|
.map_err(Box::new)?;
|
||||||
|
Ok(EncodedProof(proof.encode()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify(
|
||||||
|
&self,
|
||||||
|
proof: &EncodedProof,
|
||||||
|
set_id: SetId,
|
||||||
|
authorities: AuthorityList,
|
||||||
|
) -> Result<VerificationResult<Block>, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
let EncodedProof(proof) = proof;
|
||||||
|
let proof = WarpSyncProof::<Block>::decode(&mut proof.as_slice())
|
||||||
|
.map_err(|e| format!("Proof decoding error: {:?}", e))?;
|
||||||
|
let last_header = proof
|
||||||
|
.proofs
|
||||||
|
.last()
|
||||||
|
.map(|p| p.header.clone())
|
||||||
|
.ok_or_else(|| "Empty proof".to_string())?;
|
||||||
|
let (next_set_id, next_authorities) =
|
||||||
|
proof.verify(set_id, authorities).map_err(Box::new)?;
|
||||||
|
if proof.is_finished {
|
||||||
|
Ok(VerificationResult::<Block>::Complete(next_set_id, next_authorities, last_header))
|
||||||
|
} else {
|
||||||
|
Ok(VerificationResult::<Block>::Partial(
|
||||||
|
next_set_id,
|
||||||
|
next_authorities,
|
||||||
|
last_header.hash(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn current_authorities(&self) -> AuthorityList {
|
||||||
|
self.authority_set.inner().current_authorities.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::WarpSyncProof;
|
use super::{codec::Encode, WarpSyncProof};
|
||||||
use codec::Encode;
|
use crate::{AuthoritySetChanges, GrandpaJustification};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use sc_block_builder::BlockBuilderProvider;
|
use sc_block_builder::BlockBuilderProvider;
|
||||||
use sc_finality_grandpa::{AuthoritySetChanges, GrandpaJustification};
|
|
||||||
use sp_blockchain::HeaderBackend;
|
use sp_blockchain::HeaderBackend;
|
||||||
use sp_consensus::BlockOrigin;
|
use sp_consensus::BlockOrigin;
|
||||||
use sp_finality_grandpa::GRANDPA_ENGINE_ID;
|
use sp_finality_grandpa::GRANDPA_ENGINE_ID;
|
||||||
@@ -40,6 +40,7 @@ use wasm_timer::Instant;
|
|||||||
///
|
///
|
||||||
/// Call `InformantDisplay::new` to initialize the state, then regularly call `display` with the
|
/// Call `InformantDisplay::new` to initialize the state, then regularly call `display` with the
|
||||||
/// information to display.
|
/// information to display.
|
||||||
|
///
|
||||||
pub struct InformantDisplay<B: BlockT> {
|
pub struct InformantDisplay<B: BlockT> {
|
||||||
/// Head of chain block number from the last time `display` has been called.
|
/// Head of chain block number from the last time `display` has been called.
|
||||||
/// `None` if `display` has never been called.
|
/// `None` if `display` has never been called.
|
||||||
@@ -91,9 +92,22 @@ impl<B: BlockT> InformantDisplay<B> {
|
|||||||
(diff_bytes_inbound, diff_bytes_outbound)
|
(diff_bytes_inbound, diff_bytes_outbound)
|
||||||
};
|
};
|
||||||
|
|
||||||
let (level, status, target) =
|
let (level, status, target) = match (
|
||||||
match (net_status.sync_state, net_status.best_seen_block, net_status.state_sync) {
|
net_status.sync_state,
|
||||||
(_, _, Some(state)) => (
|
net_status.best_seen_block,
|
||||||
|
net_status.state_sync,
|
||||||
|
net_status.warp_sync,
|
||||||
|
) {
|
||||||
|
(_, _, _, Some(warp)) => (
|
||||||
|
"⏩",
|
||||||
|
"Warping".into(),
|
||||||
|
format!(
|
||||||
|
", {}, ({:.2}) Mib",
|
||||||
|
warp.phase,
|
||||||
|
(warp.total_bytes as f32) / (1024f32 * 1024f32)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(_, _, Some(state), _) => (
|
||||||
"⚙️ ",
|
"⚙️ ",
|
||||||
"Downloading state".into(),
|
"Downloading state".into(),
|
||||||
format!(
|
format!(
|
||||||
@@ -102,10 +116,10 @@ impl<B: BlockT> InformantDisplay<B> {
|
|||||||
(state.size as f32) / (1024f32 * 1024f32)
|
(state.size as f32) / (1024f32 * 1024f32)
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(SyncState::Idle, _, _) => ("💤", "Idle".into(), "".into()),
|
(SyncState::Idle, _, _, _) => ("💤", "Idle".into(), "".into()),
|
||||||
(SyncState::Downloading, None, _) =>
|
(SyncState::Downloading, None, _, _) =>
|
||||||
("⚙️ ", format!("Preparing{}", speed), "".into()),
|
("⚙️ ", format!("Preparing{}", speed), "".into()),
|
||||||
(SyncState::Downloading, Some(n), None) =>
|
(SyncState::Downloading, Some(n), None, _) =>
|
||||||
("⚙️ ", format!("Syncing{}", speed), format!(", target=#{}", n)),
|
("⚙️ ", format!("Syncing{}", speed), format!(", target=#{}", n)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
use ansi_term::Colour;
|
use ansi_term::Colour;
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use futures_timer::Delay;
|
use futures_timer::Delay;
|
||||||
use log::{info, trace, warn};
|
use log::{debug, info, trace};
|
||||||
use parity_util_mem::MallocSizeOf;
|
use parity_util_mem::MallocSizeOf;
|
||||||
use sc_client_api::{BlockchainEvents, UsageProvider};
|
use sc_client_api::{BlockchainEvents, UsageProvider};
|
||||||
use sc_network::NetworkService;
|
use sc_network::NetworkService;
|
||||||
@@ -143,7 +143,7 @@ where
|
|||||||
ancestor.hash,
|
ancestor.hash,
|
||||||
),
|
),
|
||||||
Ok(_) => {},
|
Ok(_) => {},
|
||||||
Err(e) => warn!("Error computing tree route: {}", e),
|
Err(e) => debug!("Error computing tree route: {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ sc-consensus = { version = "0.10.0-dev", path = "../consensus/common" }
|
|||||||
sp-core = { version = "4.0.0-dev", path = "../../primitives/core" }
|
sp-core = { version = "4.0.0-dev", path = "../../primitives/core" }
|
||||||
sp-runtime = { version = "4.0.0-dev", path = "../../primitives/runtime" }
|
sp-runtime = { version = "4.0.0-dev", path = "../../primitives/runtime" }
|
||||||
sp-utils = { version = "4.0.0-dev", path = "../../primitives/utils" }
|
sp-utils = { version = "4.0.0-dev", path = "../../primitives/utils" }
|
||||||
|
sp-finality-grandpa = { version = "4.0.0-dev", path = "../../primitives/finality-grandpa" }
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
unsigned-varint = { version = "0.6.0", features = ["futures", "asynchronous_codec"] }
|
unsigned-varint = { version = "0.6.0", features = ["futures", "asynchronous_codec"] }
|
||||||
void = "1.0.2"
|
void = "1.0.2"
|
||||||
|
|||||||
@@ -203,6 +203,69 @@ integer representing the role of the node:
|
|||||||
|
|
||||||
In the future, though, these restrictions will be removed.
|
In the future, though, these restrictions will be removed.
|
||||||
|
|
||||||
|
# Sync
|
||||||
|
|
||||||
|
The crate implements a number of syncing algorithms. The main purpose of the syncing algorithm is
|
||||||
|
get the chain to the latest state and keep it synced with the rest of the network by downloading and
|
||||||
|
importing new data as soon as it becomes available. Once the node starts it catches up with the network
|
||||||
|
with one of the initial sync methods listed below, and once it is completed uses a keep-up sync to
|
||||||
|
download new blocks.
|
||||||
|
|
||||||
|
## Full and light sync
|
||||||
|
|
||||||
|
This is the default syncing method for the initial and keep-up sync. The algorithm starts with the
|
||||||
|
current best block and downloads block data progressively from multiple peers if available. Once
|
||||||
|
there's a sequence of blocks ready to be imported they are fed to the import queue. Full nodes download
|
||||||
|
and execute full blocks, while light nodes only download and import headers. This continues until each peers
|
||||||
|
has no more new blocks to give.
|
||||||
|
|
||||||
|
For each peer the sync maintains the number of our common best block with that peer. This number is updates
|
||||||
|
whenever peer announce new blocks or our best block advances. This allows to keep track of peers that have new
|
||||||
|
block data and request new information as soon as it is announced. In keep-up mode, we also track peers that
|
||||||
|
announce blocks on all branches and not just the best branch. The sync algorithm tries to be greedy and download
|
||||||
|
All data that's announced.
|
||||||
|
|
||||||
|
## Fast sync
|
||||||
|
|
||||||
|
In this mode the initial downloads and verifies full header history. This allows to validate
|
||||||
|
authority set transitions and arrive at a recent header. After header chain is verified and imported
|
||||||
|
the node starts downloading a state snapshot using the state request protocol. Each `StateRequest`
|
||||||
|
contains a starting storage key, which is empty for the first request.
|
||||||
|
`StateResponse` contains a storage proof for a sequence of keys and values in the storage
|
||||||
|
starting (but not including) from the key that is in the request. After iterating the proof trie against
|
||||||
|
the storage root that is in the target header, the node issues The next `StateRequest` with set starting
|
||||||
|
key set to the last key from the previous response. This continues until trie iteration reaches the end.
|
||||||
|
The state is then imported into the database and the keep-up sync starts in normal full/light sync mode.
|
||||||
|
|
||||||
|
## Warp sync
|
||||||
|
|
||||||
|
This is similar to fast sync, but instead of downloading and verifying full header chain, the algorithm
|
||||||
|
only downloads finalized authority set changes.
|
||||||
|
|
||||||
|
### GRANDPA warp sync.
|
||||||
|
|
||||||
|
GRANDPA keeps justifications for each finalized authority set change. Each change is signed by the
|
||||||
|
authorities from the previous set. By downloading and verifying these signed hand-offs starting from genesis,
|
||||||
|
we arrive at a recent header faster than downloading full header chain. Each `WarpSyncRequest` contains a block
|
||||||
|
hash to a to start collecting proofs from. `WarpSyncResponse` contains a sequence of block headers and
|
||||||
|
justifications. The proof downloader checks the justifications and continues requesting proofs from the last
|
||||||
|
header hash, until it arrives at some recent header.
|
||||||
|
|
||||||
|
Once the finality chain is proved for a header, the state matching the header is downloaded much like during
|
||||||
|
the fast sync. The state is verified to match the header storage root. After the state is imported into the
|
||||||
|
database it is queried for the information that allows GRANDPA and BABE to continue operating from that state.
|
||||||
|
This includes BABE epoch information and GRANDPA authority set id.
|
||||||
|
|
||||||
|
### Background block download.
|
||||||
|
|
||||||
|
After the latest state has been imported the node is fully operational, but is still missing historic block
|
||||||
|
data. I.e. it is unable to serve bock bodies and headers other than the most recent one. To make sure all
|
||||||
|
nodes have block history available, a background sync process is started that downloads all the missing blocks.
|
||||||
|
It is run in parallel with the keep-up sync and does not interfere with downloading of the recent blocks.
|
||||||
|
During this download we also import GRANPA justifications for blocks with authority set changes, so that
|
||||||
|
The warp-synced node has all the data to serve for other nodes nodes that might want to sync from it with
|
||||||
|
any method.
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
Using the `sc-network` crate is done through the [`NetworkWorker`] struct. Create this
|
Using the `sc-network` crate is done through the [`NetworkWorker`] struct. Create this
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
use codec::Encode;
|
||||||
use futures::{channel::oneshot, stream::StreamExt};
|
use futures::{channel::oneshot, stream::StreamExt};
|
||||||
use libp2p::{
|
use libp2p::{
|
||||||
core::{Multiaddr, PeerId, PublicKey},
|
core::{Multiaddr, PeerId, PublicKey},
|
||||||
@@ -87,6 +88,11 @@ pub struct Behaviour<B: BlockT> {
|
|||||||
/// [`request_responses::RequestResponsesBehaviour`].
|
/// [`request_responses::RequestResponsesBehaviour`].
|
||||||
#[behaviour(ignore)]
|
#[behaviour(ignore)]
|
||||||
state_request_protocol_name: String,
|
state_request_protocol_name: String,
|
||||||
|
|
||||||
|
/// Protocol name used to send out warp sync requests via
|
||||||
|
/// [`request_responses::RequestResponsesBehaviour`].
|
||||||
|
#[behaviour(ignore)]
|
||||||
|
warp_sync_protocol_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Event generated by `Behaviour`.
|
/// Event generated by `Behaviour`.
|
||||||
@@ -195,6 +201,7 @@ impl<B: BlockT> Behaviour<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,
|
||||||
|
warp_sync_protocol_config: Option<request_responses::ProtocolConfig>,
|
||||||
bitswap: Option<Bitswap<B>>,
|
bitswap: Option<Bitswap<B>>,
|
||||||
light_client_request_protocol_config: request_responses::ProtocolConfig,
|
light_client_request_protocol_config: request_responses::ProtocolConfig,
|
||||||
// All remaining request protocol configs.
|
// All remaining request protocol configs.
|
||||||
@@ -203,9 +210,16 @@ impl<B: BlockT> Behaviour<B> {
|
|||||||
// Extract protocol name and add to `request_response_protocols`.
|
// Extract protocol name and add to `request_response_protocols`.
|
||||||
let block_request_protocol_name = block_request_protocol_config.name.to_string();
|
let block_request_protocol_name = block_request_protocol_config.name.to_string();
|
||||||
let state_request_protocol_name = state_request_protocol_config.name.to_string();
|
let state_request_protocol_name = state_request_protocol_config.name.to_string();
|
||||||
|
let warp_sync_protocol_name = match warp_sync_protocol_config {
|
||||||
|
Some(config) => {
|
||||||
|
let name = config.name.to_string();
|
||||||
|
request_response_protocols.push(config);
|
||||||
|
Some(name)
|
||||||
|
},
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
request_response_protocols.push(block_request_protocol_config);
|
request_response_protocols.push(block_request_protocol_config);
|
||||||
request_response_protocols.push(state_request_protocol_config);
|
request_response_protocols.push(state_request_protocol_config);
|
||||||
|
|
||||||
request_response_protocols.push(light_client_request_protocol_config);
|
request_response_protocols.push(light_client_request_protocol_config);
|
||||||
|
|
||||||
Ok(Behaviour {
|
Ok(Behaviour {
|
||||||
@@ -220,6 +234,7 @@ impl<B: BlockT> Behaviour<B> {
|
|||||||
events: VecDeque::new(),
|
events: VecDeque::new(),
|
||||||
block_request_protocol_name,
|
block_request_protocol_name,
|
||||||
state_request_protocol_name,
|
state_request_protocol_name,
|
||||||
|
warp_sync_protocol_name,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,6 +383,24 @@ impl<B: BlockT> NetworkBehaviourEventProcess<CustomMessageOutcome<B>> for Behavi
|
|||||||
IfDisconnected::ImmediateError,
|
IfDisconnected::ImmediateError,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
CustomMessageOutcome::WarpSyncRequest { target, request, pending_response } =>
|
||||||
|
match &self.warp_sync_protocol_name {
|
||||||
|
Some(name) => self.request_responses.send_request(
|
||||||
|
&target,
|
||||||
|
name,
|
||||||
|
request.encode(),
|
||||||
|
pending_response,
|
||||||
|
IfDisconnected::ImmediateError,
|
||||||
|
),
|
||||||
|
None => {
|
||||||
|
log::warn!(
|
||||||
|
target: "sync",
|
||||||
|
"Trying to send warp sync request when no protocol is configured {:?}",
|
||||||
|
request,
|
||||||
|
);
|
||||||
|
return
|
||||||
|
},
|
||||||
|
},
|
||||||
CustomMessageOutcome::NotificationStreamOpened {
|
CustomMessageOutcome::NotificationStreamOpened {
|
||||||
remote,
|
remote,
|
||||||
protocol,
|
protocol,
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ pub use crate::{
|
|||||||
request_responses::{
|
request_responses::{
|
||||||
IncomingRequest, OutgoingResponse, ProtocolConfig as RequestResponseConfig,
|
IncomingRequest, OutgoingResponse, ProtocolConfig as RequestResponseConfig,
|
||||||
},
|
},
|
||||||
|
warp_request_handler::WarpSyncProvider,
|
||||||
};
|
};
|
||||||
pub use libp2p::{build_multiaddr, core::PublicKey, identity, wasm_ext::ExtTransport};
|
pub use libp2p::{build_multiaddr, core::PublicKey, identity, wasm_ext::ExtTransport};
|
||||||
|
|
||||||
@@ -137,6 +138,9 @@ pub struct Params<B: BlockT, H: ExHashT> {
|
|||||||
/// [`crate::state_request_handler::StateRequestHandler::new`] allowing
|
/// [`crate::state_request_handler::StateRequestHandler::new`] allowing
|
||||||
/// both outgoing and incoming requests.
|
/// both outgoing and incoming requests.
|
||||||
pub state_request_protocol_config: RequestResponseConfig,
|
pub state_request_protocol_config: RequestResponseConfig,
|
||||||
|
|
||||||
|
/// Optional warp sync protocol support. Include protocol config and sync provider.
|
||||||
|
pub warp_sync: Option<(Arc<dyn WarpSyncProvider<B>>, RequestResponseConfig)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Role of the local node.
|
/// Role of the local node.
|
||||||
@@ -268,6 +272,7 @@ impl fmt::Debug for ProtocolId {
|
|||||||
/// assert_eq!(peer_id, "QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV".parse::<PeerId>().unwrap());
|
/// assert_eq!(peer_id, "QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV".parse::<PeerId>().unwrap());
|
||||||
/// assert_eq!(addr, "/ip4/198.51.100.19/tcp/30333".parse::<Multiaddr>().unwrap());
|
/// assert_eq!(addr, "/ip4/198.51.100.19/tcp/30333".parse::<Multiaddr>().unwrap());
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
pub fn parse_str_addr(addr_str: &str) -> Result<(PeerId, Multiaddr), ParseErr> {
|
pub fn parse_str_addr(addr_str: &str) -> Result<(PeerId, Multiaddr), ParseErr> {
|
||||||
let addr: Multiaddr = addr_str.parse()?;
|
let addr: Multiaddr = addr_str.parse()?;
|
||||||
parse_addr(addr)
|
parse_addr(addr)
|
||||||
@@ -391,6 +396,8 @@ pub enum SyncMode {
|
|||||||
/// Download indexed transactions for recent blocks.
|
/// Download indexed transactions for recent blocks.
|
||||||
storage_chain_mode: bool,
|
storage_chain_mode: bool,
|
||||||
},
|
},
|
||||||
|
/// Warp sync - verify authority set transitions and the latest state.
|
||||||
|
Warp,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SyncMode {
|
impl Default for SyncMode {
|
||||||
|
|||||||
@@ -53,10 +53,7 @@ fn build_test_full_node(network_config: config::NetworkConfiguration)
|
|||||||
impl<B: BlockT> sc_consensus::Verifier<B> for PassThroughVerifier {
|
impl<B: BlockT> sc_consensus::Verifier<B> for PassThroughVerifier {
|
||||||
async fn verify(
|
async fn verify(
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: sp_consensus::BlockOrigin,
|
mut block: sp_consensus::BlockImportParams<B, ()>,
|
||||||
header: B::Header,
|
|
||||||
justifications: Option<sp_runtime::Justifications>,
|
|
||||||
body: Option<Vec<B::Extrinsic>>,
|
|
||||||
) -> Result<
|
) -> Result<
|
||||||
(
|
(
|
||||||
sc_consensus::BlockImportParams<B, ()>,
|
sc_consensus::BlockImportParams<B, ()>,
|
||||||
@@ -64,7 +61,7 @@ fn build_test_full_node(network_config: config::NetworkConfiguration)
|
|||||||
),
|
),
|
||||||
String,
|
String,
|
||||||
> {
|
> {
|
||||||
let maybe_keys = header
|
let maybe_keys = block.header
|
||||||
.digest()
|
.digest()
|
||||||
.log(|l| {
|
.log(|l| {
|
||||||
l.try_as_raw(sp_runtime::generic::OpaqueDigestItemId::Consensus(b"aura"))
|
l.try_as_raw(sp_runtime::generic::OpaqueDigestItemId::Consensus(b"aura"))
|
||||||
@@ -79,12 +76,9 @@ fn build_test_full_node(network_config: config::NetworkConfiguration)
|
|||||||
)]
|
)]
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut import = sc_consensus::BlockImportParams::new(origin, header);
|
block.finalized = self.0;
|
||||||
import.body = body;
|
block.fork_choice = Some(sc_consensus::ForkChoiceStrategy::LongestChain);
|
||||||
import.finalized = self.0;
|
Ok((block, maybe_keys))
|
||||||
import.justifications = justifications;
|
|
||||||
import.fork_choice = Some(sc_consensus::ForkChoiceStrategy::LongestChain);
|
|
||||||
Ok((import, maybe_keys))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,6 +138,7 @@ fn build_test_full_node(network_config: config::NetworkConfiguration)
|
|||||||
block_request_protocol_config,
|
block_request_protocol_config,
|
||||||
state_request_protocol_config,
|
state_request_protocol_config,
|
||||||
light_client_request_protocol_config,
|
light_client_request_protocol_config,
|
||||||
|
warp_sync: None,
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -243,6 +243,7 @@
|
|||||||
//! - Calling `trigger_repropagate` when a transaction is added to the pool.
|
//! - Calling `trigger_repropagate` when a transaction is added to the pool.
|
||||||
//!
|
//!
|
||||||
//! More precise usage details are still being worked on and will likely change in the future.
|
//! More precise usage details are still being worked on and will likely change in the future.
|
||||||
|
//!
|
||||||
|
|
||||||
mod behaviour;
|
mod behaviour;
|
||||||
mod chain;
|
mod chain;
|
||||||
@@ -264,12 +265,13 @@ pub mod light_client_requests;
|
|||||||
pub mod network_state;
|
pub mod network_state;
|
||||||
pub mod state_request_handler;
|
pub mod state_request_handler;
|
||||||
pub mod transactions;
|
pub mod transactions;
|
||||||
|
pub mod warp_request_handler;
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use libp2p::{multiaddr, Multiaddr, PeerId};
|
pub use libp2p::{multiaddr, Multiaddr, PeerId};
|
||||||
pub use protocol::{
|
pub use protocol::{
|
||||||
event::{DhtEvent, Event, ObservedRole},
|
event::{DhtEvent, Event, ObservedRole},
|
||||||
sync::{StateDownloadProgress, SyncState},
|
sync::{StateDownloadProgress, SyncState, WarpSyncPhase, WarpSyncProgress},
|
||||||
PeerInfo,
|
PeerInfo,
|
||||||
};
|
};
|
||||||
pub use service::{
|
pub use service::{
|
||||||
@@ -326,4 +328,6 @@ pub struct NetworkStatus<B: BlockT> {
|
|||||||
pub total_bytes_outbound: u64,
|
pub total_bytes_outbound: u64,
|
||||||
/// State sync in progress.
|
/// State sync in progress.
|
||||||
pub state_sync: Option<protocol::sync::StateDownloadProgress>,
|
pub state_sync: Option<protocol::sync::StateDownloadProgress>,
|
||||||
|
/// Warp sync in progress.
|
||||||
|
pub warp_sync: Option<protocol::sync::WarpSyncProgress>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,12 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
chain::Client,
|
chain::Client,
|
||||||
config::{self, ProtocolId},
|
config::{self, ProtocolId, WarpSyncProvider},
|
||||||
error,
|
error,
|
||||||
request_responses::RequestFailure,
|
request_responses::RequestFailure,
|
||||||
schema::v1::StateResponse,
|
schema::v1::StateResponse,
|
||||||
utils::{interval, LruHashSet},
|
utils::{interval, LruHashSet},
|
||||||
|
warp_request_handler::EncodedProof,
|
||||||
};
|
};
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
@@ -196,6 +197,7 @@ pub struct Protocol<B: BlockT> {
|
|||||||
enum PeerRequest<B: BlockT> {
|
enum PeerRequest<B: BlockT> {
|
||||||
Block(message::BlockRequest<B>),
|
Block(message::BlockRequest<B>),
|
||||||
State,
|
State,
|
||||||
|
WarpProof,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Peer information
|
/// Peer information
|
||||||
@@ -239,6 +241,7 @@ impl ProtocolConfig {
|
|||||||
config::SyncMode::Full => sync::SyncMode::Full,
|
config::SyncMode::Full => sync::SyncMode::Full,
|
||||||
config::SyncMode::Fast { skip_proofs, storage_chain_mode } =>
|
config::SyncMode::Fast { skip_proofs, storage_chain_mode } =>
|
||||||
sync::SyncMode::LightState { skip_proofs, storage_chain_mode },
|
sync::SyncMode::LightState { skip_proofs, storage_chain_mode },
|
||||||
|
config::SyncMode::Warp => sync::SyncMode::Warp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -293,6 +296,7 @@ impl<B: BlockT> Protocol<B> {
|
|||||||
notifications_protocols_handshakes: Vec<Vec<u8>>,
|
notifications_protocols_handshakes: Vec<Vec<u8>>,
|
||||||
block_announce_validator: Box<dyn BlockAnnounceValidator<B> + Send>,
|
block_announce_validator: Box<dyn BlockAnnounceValidator<B> + Send>,
|
||||||
metrics_registry: Option<&Registry>,
|
metrics_registry: Option<&Registry>,
|
||||||
|
warp_sync_provider: Option<Arc<dyn WarpSyncProvider<B>>>,
|
||||||
) -> error::Result<(Protocol<B>, sc_peerset::PeersetHandle, Vec<(PeerId, Multiaddr)>)> {
|
) -> error::Result<(Protocol<B>, sc_peerset::PeersetHandle, Vec<(PeerId, Multiaddr)>)> {
|
||||||
let info = chain.info();
|
let info = chain.info();
|
||||||
let sync = ChainSync::new(
|
let sync = ChainSync::new(
|
||||||
@@ -300,6 +304,7 @@ impl<B: BlockT> Protocol<B> {
|
|||||||
chain.clone(),
|
chain.clone(),
|
||||||
block_announce_validator,
|
block_announce_validator,
|
||||||
config.max_parallel_downloads,
|
config.max_parallel_downloads,
|
||||||
|
warp_sync_provider,
|
||||||
)
|
)
|
||||||
.map_err(Box::new)?;
|
.map_err(Box::new)?;
|
||||||
|
|
||||||
@@ -724,6 +729,26 @@ impl<B: BlockT> Protocol<B> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Must be called in response to a [`CustomMessageOutcome::WarpSyncRequest`] being emitted.
|
||||||
|
/// Must contain the same `PeerId` and request that have been emitted.
|
||||||
|
pub fn on_warp_sync_response(
|
||||||
|
&mut self,
|
||||||
|
peer_id: PeerId,
|
||||||
|
response: crate::warp_request_handler::EncodedProof,
|
||||||
|
) -> CustomMessageOutcome<B> {
|
||||||
|
match self.sync.on_warp_sync_data(&peer_id, response) {
|
||||||
|
Ok(sync::OnWarpSyncData::WarpProofRequest(peer, req)) =>
|
||||||
|
prepare_warp_sync_request::<B>(&mut self.peers, peer, req),
|
||||||
|
Ok(sync::OnWarpSyncData::StateRequest(peer, req)) =>
|
||||||
|
prepare_state_request::<B>(&mut self.peers, peer, req),
|
||||||
|
Err(sync::BadPeer(id, repu)) => {
|
||||||
|
self.behaviour.disconnect_peer(&id, HARDCODED_PEERSETS_SYNC);
|
||||||
|
self.peerset_handle.report_peer(id, repu);
|
||||||
|
CustomMessageOutcome::None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Perform time based maintenance.
|
/// Perform time based maintenance.
|
||||||
///
|
///
|
||||||
/// > **Note**: This method normally doesn't have to be called except for testing purposes.
|
/// > **Note**: This method normally doesn't have to be called except for testing purposes.
|
||||||
@@ -1248,6 +1273,19 @@ fn prepare_state_request<B: BlockT>(
|
|||||||
CustomMessageOutcome::StateRequest { target: who, request, pending_response: tx }
|
CustomMessageOutcome::StateRequest { target: who, request, pending_response: tx }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn prepare_warp_sync_request<B: BlockT>(
|
||||||
|
peers: &mut HashMap<PeerId, Peer<B>>,
|
||||||
|
who: PeerId,
|
||||||
|
request: crate::warp_request_handler::Request<B>,
|
||||||
|
) -> CustomMessageOutcome<B> {
|
||||||
|
let (tx, rx) = oneshot::channel();
|
||||||
|
|
||||||
|
if let Some(ref mut peer) = peers.get_mut(&who) {
|
||||||
|
peer.request = Some((PeerRequest::WarpProof, rx));
|
||||||
|
}
|
||||||
|
CustomMessageOutcome::WarpSyncRequest { target: who, request, pending_response: tx }
|
||||||
|
}
|
||||||
|
|
||||||
/// Outcome of an incoming custom message.
|
/// Outcome of an incoming custom message.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@@ -1291,6 +1329,12 @@ pub enum CustomMessageOutcome<B: BlockT> {
|
|||||||
request: crate::schema::v1::StateRequest,
|
request: crate::schema::v1::StateRequest,
|
||||||
pending_response: oneshot::Sender<Result<Vec<u8>, RequestFailure>>,
|
pending_response: oneshot::Sender<Result<Vec<u8>, RequestFailure>>,
|
||||||
},
|
},
|
||||||
|
/// A new warp sync request must be emitted.
|
||||||
|
WarpSyncRequest {
|
||||||
|
target: PeerId,
|
||||||
|
request: crate::warp_request_handler::Request<B>,
|
||||||
|
pending_response: oneshot::Sender<Result<Vec<u8>, RequestFailure>>,
|
||||||
|
},
|
||||||
/// Peer has a reported a new head of chain.
|
/// Peer has a reported a new head of chain.
|
||||||
PeerNewBest(PeerId, NumberFor<B>),
|
PeerNewBest(PeerId, NumberFor<B>),
|
||||||
/// Now connected to a new peer for syncing purposes.
|
/// Now connected to a new peer for syncing purposes.
|
||||||
@@ -1364,6 +1408,7 @@ impl<B: BlockT> NetworkBehaviour for Protocol<B> {
|
|||||||
// Check for finished outgoing requests.
|
// Check for finished outgoing requests.
|
||||||
let mut finished_block_requests = Vec::new();
|
let mut finished_block_requests = Vec::new();
|
||||||
let mut finished_state_requests = Vec::new();
|
let mut finished_state_requests = Vec::new();
|
||||||
|
let mut finished_warp_sync_requests = Vec::new();
|
||||||
for (id, peer) in self.peers.iter_mut() {
|
for (id, peer) in self.peers.iter_mut() {
|
||||||
if let Peer { request: Some((_, pending_response)), .. } = peer {
|
if let Peer { request: Some((_, pending_response)), .. } = peer {
|
||||||
match pending_response.poll_unpin(cx) {
|
match pending_response.poll_unpin(cx) {
|
||||||
@@ -1412,6 +1457,9 @@ impl<B: BlockT> NetworkBehaviour for Protocol<B> {
|
|||||||
|
|
||||||
finished_state_requests.push((id.clone(), protobuf_response));
|
finished_state_requests.push((id.clone(), protobuf_response));
|
||||||
},
|
},
|
||||||
|
PeerRequest::WarpProof => {
|
||||||
|
finished_warp_sync_requests.push((id.clone(), resp));
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Poll::Ready(Ok(Err(e))) => {
|
Poll::Ready(Ok(Err(e))) => {
|
||||||
@@ -1474,6 +1522,10 @@ impl<B: BlockT> NetworkBehaviour for Protocol<B> {
|
|||||||
let ev = self.on_state_response(id, protobuf_response);
|
let ev = self.on_state_response(id, protobuf_response);
|
||||||
self.pending_messages.push_back(ev);
|
self.pending_messages.push_back(ev);
|
||||||
}
|
}
|
||||||
|
for (id, response) in finished_warp_sync_requests {
|
||||||
|
let ev = self.on_warp_sync_response(id, EncodedProof(response));
|
||||||
|
self.pending_messages.push_back(ev);
|
||||||
|
}
|
||||||
|
|
||||||
while let Poll::Ready(Some(())) = self.tick_timeout.poll_next_unpin(cx) {
|
while let Poll::Ready(Some(())) = self.tick_timeout.poll_next_unpin(cx) {
|
||||||
self.tick();
|
self.tick();
|
||||||
@@ -1491,6 +1543,10 @@ impl<B: BlockT> NetworkBehaviour for Protocol<B> {
|
|||||||
let event = prepare_block_request(&mut self.peers, id, request);
|
let event = prepare_block_request(&mut self.peers, id, request);
|
||||||
self.pending_messages.push_back(event);
|
self.pending_messages.push_back(event);
|
||||||
}
|
}
|
||||||
|
if let Some((id, request)) = self.sync.warp_sync_request() {
|
||||||
|
let event = prepare_warp_sync_request(&mut self.peers, id, request);
|
||||||
|
self.pending_messages.push_back(event);
|
||||||
|
}
|
||||||
|
|
||||||
// Check if there is any block announcement validation finished.
|
// Check if there is any block announcement validation finished.
|
||||||
while let Poll::Ready(result) = self.sync.poll_block_announce_validation(cx) {
|
while let Poll::Ready(result) = self.sync.poll_block_announce_validation(cx) {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
//! The `ChainSync` struct maintains the state of the block requests. Whenever something happens on
|
//! The `ChainSync` struct maintains the state of the block requests. Whenever something happens on
|
||||||
//! the network, or whenever a block has been successfully verified, call the appropriate method in
|
//! the network, or whenever a block has been successfully verified, call the appropriate method in
|
||||||
//! order to update it.
|
//! order to update it.
|
||||||
|
//!
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
protocol::message::{self, BlockAnnounce, BlockAttributes, BlockRequest, BlockResponse},
|
protocol::message::{self, BlockAnnounce, BlockAttributes, BlockRequest, BlockResponse},
|
||||||
@@ -62,10 +63,12 @@ use std::{
|
|||||||
pin::Pin,
|
pin::Pin,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
use warp::{WarpProofRequest, WarpSync, WarpSyncProvider};
|
||||||
|
|
||||||
mod blocks;
|
mod blocks;
|
||||||
mod extra_requests;
|
mod extra_requests;
|
||||||
mod state;
|
mod state;
|
||||||
|
mod warp;
|
||||||
|
|
||||||
/// Maximum blocks to request in a single packet.
|
/// Maximum blocks to request in a single packet.
|
||||||
const MAX_BLOCKS_TO_REQUEST: usize = 128;
|
const MAX_BLOCKS_TO_REQUEST: usize = 128;
|
||||||
@@ -101,6 +104,9 @@ const STATE_SYNC_FINALITY_THRESHOLD: u32 = 8;
|
|||||||
/// so far behind.
|
/// so far behind.
|
||||||
const MAJOR_SYNC_BLOCKS: u8 = 5;
|
const MAJOR_SYNC_BLOCKS: u8 = 5;
|
||||||
|
|
||||||
|
/// Number of peers that need to be connected before warp sync is started.
|
||||||
|
const MIN_PEERS_TO_START_WARP_SYNC: usize = 3;
|
||||||
|
|
||||||
mod rep {
|
mod rep {
|
||||||
use sc_peerset::ReputationChange as Rep;
|
use sc_peerset::ReputationChange as Rep;
|
||||||
/// Reputation change when a peer sent us a message that led to a
|
/// Reputation change when a peer sent us a message that led to a
|
||||||
@@ -217,6 +223,10 @@ pub struct ChainSync<B: BlockT> {
|
|||||||
block_announce_validation_per_peer_stats: HashMap<PeerId, usize>,
|
block_announce_validation_per_peer_stats: HashMap<PeerId, usize>,
|
||||||
/// State sync in progress, if any.
|
/// State sync in progress, if any.
|
||||||
state_sync: Option<StateSync<B>>,
|
state_sync: Option<StateSync<B>>,
|
||||||
|
/// Warp sync in progress, if any.
|
||||||
|
warp_sync: Option<WarpSync<B>>,
|
||||||
|
/// Warp sync provider.
|
||||||
|
warp_sync_provider: Option<Arc<dyn WarpSyncProvider<B>>>,
|
||||||
/// Enable importing existing blocks. This is used used after the state download to
|
/// Enable importing existing blocks. This is used used after the state download to
|
||||||
/// catch up to the latest state while re-importing blocks.
|
/// catch up to the latest state while re-importing blocks.
|
||||||
import_existing: bool,
|
import_existing: bool,
|
||||||
@@ -290,6 +300,8 @@ pub enum PeerSyncState<B: BlockT> {
|
|||||||
DownloadingJustification(B::Hash),
|
DownloadingJustification(B::Hash),
|
||||||
/// Downloading state.
|
/// Downloading state.
|
||||||
DownloadingState,
|
DownloadingState,
|
||||||
|
/// Downloading warp proof.
|
||||||
|
DownloadingWarpProof,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: BlockT> PeerSyncState<B> {
|
impl<B: BlockT> PeerSyncState<B> {
|
||||||
@@ -316,6 +328,39 @@ pub struct StateDownloadProgress {
|
|||||||
pub size: u64,
|
pub size: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reported warp sync phase.
|
||||||
|
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||||
|
pub enum WarpSyncPhase {
|
||||||
|
/// Waiting for peers to connect.
|
||||||
|
AwaitingPeers,
|
||||||
|
/// Downloading and verifying grandpa warp proofs.
|
||||||
|
DownloadingWarpProofs,
|
||||||
|
/// Downloading state data.
|
||||||
|
DownloadingState,
|
||||||
|
/// Importing state.
|
||||||
|
ImportingState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for WarpSyncPhase {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
WarpSyncPhase::AwaitingPeers => write!(f, "Waiting for peers"),
|
||||||
|
WarpSyncPhase::DownloadingWarpProofs => write!(f, "Downloading finality proofs"),
|
||||||
|
WarpSyncPhase::DownloadingState => write!(f, "Downloading state"),
|
||||||
|
WarpSyncPhase::ImportingState => write!(f, "Importing state"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reported warp sync progress.
|
||||||
|
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||||
|
pub struct WarpSyncProgress {
|
||||||
|
/// Estimated download percentage.
|
||||||
|
pub phase: WarpSyncPhase,
|
||||||
|
/// Total bytes downloaded so far.
|
||||||
|
pub total_bytes: u64,
|
||||||
|
}
|
||||||
|
|
||||||
/// Syncing status and statistics.
|
/// Syncing status and statistics.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Status<B: BlockT> {
|
pub struct Status<B: BlockT> {
|
||||||
@@ -329,6 +374,8 @@ pub struct Status<B: BlockT> {
|
|||||||
pub queued_blocks: u32,
|
pub queued_blocks: u32,
|
||||||
/// State sync status in progress, if any.
|
/// State sync status in progress, if any.
|
||||||
pub state_sync: Option<StateDownloadProgress>,
|
pub state_sync: Option<StateDownloadProgress>,
|
||||||
|
/// Warp sync in progress, if any.
|
||||||
|
pub warp_sync: Option<WarpSyncProgress>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A peer did not behave as expected and should be reported.
|
/// A peer did not behave as expected and should be reported.
|
||||||
@@ -373,6 +420,15 @@ pub enum OnStateData<B: BlockT> {
|
|||||||
Request(PeerId, StateRequest),
|
Request(PeerId, StateRequest),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Result of [`ChainSync::on_warp_sync_data`].
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum OnWarpSyncData<B: BlockT> {
|
||||||
|
/// Warp proof request is issued.
|
||||||
|
WarpProofRequest(PeerId, warp::WarpProofRequest<B>),
|
||||||
|
/// A new state request needs to be made to the given peer.
|
||||||
|
StateRequest(PeerId, StateRequest),
|
||||||
|
}
|
||||||
|
|
||||||
/// Result of [`ChainSync::poll_block_announce_validation`].
|
/// Result of [`ChainSync::poll_block_announce_validation`].
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum PollBlockAnnounceValidation<H> {
|
pub enum PollBlockAnnounceValidation<H> {
|
||||||
@@ -460,6 +516,8 @@ pub enum SyncMode {
|
|||||||
Full,
|
Full,
|
||||||
// Sync headers and the last finalied state
|
// Sync headers and the last finalied state
|
||||||
LightState { storage_chain_mode: bool, skip_proofs: bool },
|
LightState { storage_chain_mode: bool, skip_proofs: bool },
|
||||||
|
// Warp sync mode.
|
||||||
|
Warp,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Result of [`ChainSync::has_slot_for_block_announce_validation`].
|
/// Result of [`ChainSync::has_slot_for_block_announce_validation`].
|
||||||
@@ -479,6 +537,7 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
client: Arc<dyn crate::chain::Client<B>>,
|
client: Arc<dyn crate::chain::Client<B>>,
|
||||||
block_announce_validator: Box<dyn BlockAnnounceValidator<B> + Send>,
|
block_announce_validator: Box<dyn BlockAnnounceValidator<B> + Send>,
|
||||||
max_parallel_downloads: u32,
|
max_parallel_downloads: u32,
|
||||||
|
warp_sync_provider: Option<Arc<dyn WarpSyncProvider<B>>>,
|
||||||
) -> Result<Self, ClientError> {
|
) -> Result<Self, ClientError> {
|
||||||
let mut sync = ChainSync {
|
let mut sync = ChainSync {
|
||||||
client,
|
client,
|
||||||
@@ -497,6 +556,8 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
block_announce_validation: Default::default(),
|
block_announce_validation: Default::default(),
|
||||||
block_announce_validation_per_peer_stats: Default::default(),
|
block_announce_validation_per_peer_stats: Default::default(),
|
||||||
state_sync: None,
|
state_sync: None,
|
||||||
|
warp_sync: None,
|
||||||
|
warp_sync_provider,
|
||||||
import_existing: false,
|
import_existing: false,
|
||||||
};
|
};
|
||||||
sync.reset_sync_start_point()?;
|
sync.reset_sync_start_point()?;
|
||||||
@@ -508,7 +569,7 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
SyncMode::Full =>
|
SyncMode::Full =>
|
||||||
BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION | BlockAttributes::BODY,
|
BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION | BlockAttributes::BODY,
|
||||||
SyncMode::Light => BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION,
|
SyncMode::Light => BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION,
|
||||||
SyncMode::LightState { storage_chain_mode: false, .. } =>
|
SyncMode::LightState { storage_chain_mode: false, .. } | SyncMode::Warp =>
|
||||||
BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION | BlockAttributes::BODY,
|
BlockAttributes::HEADER | BlockAttributes::JUSTIFICATION | BlockAttributes::BODY,
|
||||||
SyncMode::LightState { storage_chain_mode: true, .. } =>
|
SyncMode::LightState { storage_chain_mode: true, .. } =>
|
||||||
BlockAttributes::HEADER |
|
BlockAttributes::HEADER |
|
||||||
@@ -522,6 +583,7 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
SyncMode::Full => false,
|
SyncMode::Full => false,
|
||||||
SyncMode::Light => true,
|
SyncMode::Light => true,
|
||||||
SyncMode::LightState { .. } => true,
|
SyncMode::LightState { .. } => true,
|
||||||
|
SyncMode::Warp => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -550,12 +612,20 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
SyncState::Idle
|
SyncState::Idle
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let warp_sync_progress = match (&self.warp_sync, &self.mode) {
|
||||||
|
(None, SyncMode::Warp) =>
|
||||||
|
Some(WarpSyncProgress { phase: WarpSyncPhase::AwaitingPeers, total_bytes: 0 }),
|
||||||
|
(Some(sync), _) => Some(sync.progress()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
Status {
|
Status {
|
||||||
state: sync_state,
|
state: sync_state,
|
||||||
best_seen_block: best_seen,
|
best_seen_block: best_seen,
|
||||||
num_peers: self.peers.len() as u32,
|
num_peers: self.peers.len() as u32,
|
||||||
queued_blocks: self.queue_blocks.len() as u32,
|
queued_blocks: self.queue_blocks.len() as u32,
|
||||||
state_sync: self.state_sync.as_ref().map(|s| s.progress()),
|
state_sync: self.state_sync.as_ref().map(|s| s.progress()),
|
||||||
|
warp_sync: warp_sync_progress,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -620,6 +690,17 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
return Ok(None)
|
return Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let SyncMode::Warp = &self.mode {
|
||||||
|
if self.peers.len() >= MIN_PEERS_TO_START_WARP_SYNC && self.warp_sync.is_none()
|
||||||
|
{
|
||||||
|
log::debug!(target: "sync", "Starting warp state sync.");
|
||||||
|
if let Some(provider) = &self.warp_sync_provider {
|
||||||
|
self.warp_sync =
|
||||||
|
Some(WarpSync::new(self.client.clone(), provider.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If we are at genesis, just start downloading.
|
// If we are at genesis, just start downloading.
|
||||||
let (state, req) = if self.best_queued_number.is_zero() {
|
let (state, req) = if self.best_queued_number.is_zero() {
|
||||||
debug!(
|
debug!(
|
||||||
@@ -792,7 +873,8 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
|
|
||||||
/// Get an iterator over all block requests of all peers.
|
/// Get an iterator over all block requests of all peers.
|
||||||
pub fn block_requests(&mut self) -> impl Iterator<Item = (&PeerId, BlockRequest<B>)> + '_ {
|
pub fn block_requests(&mut self) -> impl Iterator<Item = (&PeerId, BlockRequest<B>)> + '_ {
|
||||||
if self.pending_requests.is_empty() || self.state_sync.is_some() {
|
if self.pending_requests.is_empty() || self.state_sync.is_some() || self.warp_sync.is_some()
|
||||||
|
{
|
||||||
return Either::Left(std::iter::empty())
|
return Either::Left(std::iter::empty())
|
||||||
}
|
}
|
||||||
if self.queue_blocks.len() > MAX_IMPORTING_BLOCKS {
|
if self.queue_blocks.len() > MAX_IMPORTING_BLOCKS {
|
||||||
@@ -876,16 +958,16 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
Either::Right(iter)
|
Either::Right(iter)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a state request, if any
|
/// Get a state request, if any.
|
||||||
pub fn state_request(&mut self) -> Option<(PeerId, StateRequest)> {
|
pub fn state_request(&mut self) -> Option<(PeerId, StateRequest)> {
|
||||||
if let Some(sync) = &self.state_sync {
|
|
||||||
if sync.is_complete() {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
if self.peers.iter().any(|(_, peer)| peer.state == PeerSyncState::DownloadingState) {
|
if self.peers.iter().any(|(_, peer)| peer.state == PeerSyncState::DownloadingState) {
|
||||||
// Only one pending state request is allowed.
|
// Only one pending state request is allowed.
|
||||||
return None
|
return None
|
||||||
}
|
}
|
||||||
|
if let Some(sync) = &self.state_sync {
|
||||||
|
if sync.is_complete() {
|
||||||
|
return None
|
||||||
|
}
|
||||||
for (id, peer) in self.peers.iter_mut() {
|
for (id, peer) in self.peers.iter_mut() {
|
||||||
if peer.state.is_available() && peer.common_number >= sync.target_block_num() {
|
if peer.state.is_available() && peer.common_number >= sync.target_block_num() {
|
||||||
trace!(target: "sync", "New StateRequest for {}", id);
|
trace!(target: "sync", "New StateRequest for {}", id);
|
||||||
@@ -895,6 +977,55 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(sync) = &self.warp_sync {
|
||||||
|
if sync.is_complete() {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
if let (Some(request), Some(target)) =
|
||||||
|
(sync.next_state_request(), sync.target_block_number())
|
||||||
|
{
|
||||||
|
for (id, peer) in self.peers.iter_mut() {
|
||||||
|
if peer.state.is_available() && peer.best_number >= target {
|
||||||
|
trace!(target: "sync", "New StateRequest for {}", id);
|
||||||
|
peer.state = PeerSyncState::DownloadingState;
|
||||||
|
return Some((id.clone(), request))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a warp sync request, if any.
|
||||||
|
pub fn warp_sync_request(&mut self) -> Option<(PeerId, WarpProofRequest<B>)> {
|
||||||
|
if self
|
||||||
|
.peers
|
||||||
|
.iter()
|
||||||
|
.any(|(_, peer)| peer.state == PeerSyncState::DownloadingWarpProof)
|
||||||
|
{
|
||||||
|
// Only one pending state request is allowed.
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
if let Some(sync) = &self.warp_sync {
|
||||||
|
if sync.is_complete() {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
if let Some(request) = sync.next_warp_poof_request() {
|
||||||
|
let mut targets: Vec<_> = self.peers.values().map(|p| p.best_number).collect();
|
||||||
|
if !targets.is_empty() {
|
||||||
|
targets.sort();
|
||||||
|
let median = targets[targets.len() / 2];
|
||||||
|
// Find a random peer that is synced as much as peer majority.
|
||||||
|
for (id, peer) in self.peers.iter_mut() {
|
||||||
|
if peer.state.is_available() && peer.best_number >= median {
|
||||||
|
trace!(target: "sync", "New WarpProofRequest for {}", id);
|
||||||
|
peer.state = PeerSyncState::DownloadingWarpProof;
|
||||||
|
return Some((id.clone(), request))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1055,7 +1186,8 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
},
|
},
|
||||||
PeerSyncState::Available |
|
PeerSyncState::Available |
|
||||||
PeerSyncState::DownloadingJustification(..) |
|
PeerSyncState::DownloadingJustification(..) |
|
||||||
PeerSyncState::DownloadingState => Vec::new(),
|
PeerSyncState::DownloadingState |
|
||||||
|
PeerSyncState::DownloadingWarpProof => Vec::new(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// When request.is_none() this is a block announcement. Just accept blocks.
|
// When request.is_none() this is a block announcement. Just accept blocks.
|
||||||
@@ -1105,6 +1237,15 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
response.proof.len(),
|
response.proof.len(),
|
||||||
);
|
);
|
||||||
sync.import(response)
|
sync.import(response)
|
||||||
|
} else if let Some(sync) = &mut self.warp_sync {
|
||||||
|
debug!(
|
||||||
|
target: "sync",
|
||||||
|
"Importing state data from {} with {} keys, {} proof nodes.",
|
||||||
|
who,
|
||||||
|
response.entries.len(),
|
||||||
|
response.proof.len(),
|
||||||
|
);
|
||||||
|
sync.import_state(response)
|
||||||
} else {
|
} else {
|
||||||
debug!(target: "sync", "Ignored obsolete state response from {}", who);
|
debug!(target: "sync", "Ignored obsolete state response from {}", who);
|
||||||
return Err(BadPeer(who.clone(), rep::NOT_REQUESTED))
|
return Err(BadPeer(who.clone(), rep::NOT_REQUESTED))
|
||||||
@@ -1112,12 +1253,7 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
|
|
||||||
match import_result {
|
match import_result {
|
||||||
state::ImportResult::Import(hash, header, state) => {
|
state::ImportResult::Import(hash, header, state) => {
|
||||||
let origin = if self.status().state != SyncState::Downloading {
|
let origin = BlockOrigin::NetworkInitialSync;
|
||||||
BlockOrigin::NetworkBroadcast
|
|
||||||
} else {
|
|
||||||
BlockOrigin::NetworkInitialSync
|
|
||||||
};
|
|
||||||
|
|
||||||
let block = IncomingBlock {
|
let block = IncomingBlock {
|
||||||
hash,
|
hash,
|
||||||
header: Some(header),
|
header: Some(header),
|
||||||
@@ -1142,6 +1278,39 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handle a response from the remote to a warp proof request that we made.
|
||||||
|
///
|
||||||
|
/// Returns next request.
|
||||||
|
pub fn on_warp_sync_data(
|
||||||
|
&mut self,
|
||||||
|
who: &PeerId,
|
||||||
|
response: warp::EncodedProof,
|
||||||
|
) -> Result<OnWarpSyncData<B>, BadPeer> {
|
||||||
|
let import_result = if let Some(sync) = &mut self.warp_sync {
|
||||||
|
debug!(
|
||||||
|
target: "sync",
|
||||||
|
"Importing warp proof data from {}, {} bytes.",
|
||||||
|
who,
|
||||||
|
response.0.len(),
|
||||||
|
);
|
||||||
|
sync.import_warp_proof(response)
|
||||||
|
} else {
|
||||||
|
debug!(target: "sync", "Ignored obsolete warp sync response from {}", who);
|
||||||
|
return Err(BadPeer(who.clone(), rep::NOT_REQUESTED))
|
||||||
|
};
|
||||||
|
|
||||||
|
match import_result {
|
||||||
|
warp::WarpProofImportResult::StateRequest(request) =>
|
||||||
|
Ok(OnWarpSyncData::StateRequest(who.clone(), request)),
|
||||||
|
warp::WarpProofImportResult::WarpProofRequest(request) =>
|
||||||
|
Ok(OnWarpSyncData::WarpProofRequest(who.clone(), request)),
|
||||||
|
warp::WarpProofImportResult::BadResponse => {
|
||||||
|
debug!(target: "sync", "Bad proof data received from {}", who);
|
||||||
|
Err(BadPeer(who.clone(), rep::BAD_BLOCK))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn validate_and_queue_blocks(
|
fn validate_and_queue_blocks(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut new_blocks: Vec<IncomingBlock<B>>,
|
mut new_blocks: Vec<IncomingBlock<B>>,
|
||||||
@@ -1308,6 +1477,20 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
self.mode = SyncMode::Full;
|
self.mode = SyncMode::Full;
|
||||||
output.extend(self.restart());
|
output.extend(self.restart());
|
||||||
}
|
}
|
||||||
|
let warp_sync_complete = self
|
||||||
|
.warp_sync
|
||||||
|
.as_ref()
|
||||||
|
.map_or(false, |s| s.target_block_hash() == Some(hash));
|
||||||
|
if warp_sync_complete {
|
||||||
|
info!(
|
||||||
|
target: "sync",
|
||||||
|
"Warp sync is complete ({} MiB), restarting block sync.",
|
||||||
|
self.warp_sync.as_ref().map_or(0, |s| s.progress().total_bytes / (1024 * 1024)),
|
||||||
|
);
|
||||||
|
self.warp_sync = None;
|
||||||
|
self.mode = SyncMode::Full;
|
||||||
|
output.extend(self.restart());
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Err(BlockImportError::IncompleteHeader(who)) =>
|
Err(BlockImportError::IncompleteHeader(who)) =>
|
||||||
if let Some(peer) = who {
|
if let Some(peer) = who {
|
||||||
@@ -1349,6 +1532,7 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
e @ Err(BlockImportError::UnknownParent) | e @ Err(BlockImportError::Other(_)) => {
|
e @ Err(BlockImportError::UnknownParent) | e @ Err(BlockImportError::Other(_)) => {
|
||||||
warn!(target: "sync", "💔 Error importing block {:?}: {:?}", hash, e);
|
warn!(target: "sync", "💔 Error importing block {:?}: {:?}", hash, e);
|
||||||
self.state_sync = None;
|
self.state_sync = None;
|
||||||
|
self.warp_sync = None;
|
||||||
output.extend(self.restart());
|
output.extend(self.restart());
|
||||||
},
|
},
|
||||||
Err(BlockImportError::Cancelled) => {},
|
Err(BlockImportError::Cancelled) => {},
|
||||||
@@ -1828,6 +2012,13 @@ impl<B: BlockT> ChainSync<B> {
|
|||||||
);
|
);
|
||||||
self.mode = SyncMode::Full;
|
self.mode = SyncMode::Full;
|
||||||
}
|
}
|
||||||
|
if matches!(self.mode, SyncMode::Warp) && info.finalized_state.is_some() {
|
||||||
|
log::warn!(
|
||||||
|
target: "sync",
|
||||||
|
"Can't use warp sync mode with a partially synced database. Reverting to full sync mode."
|
||||||
|
);
|
||||||
|
self.mode = SyncMode::Full;
|
||||||
|
}
|
||||||
self.import_existing = false;
|
self.import_existing = false;
|
||||||
self.best_queued_hash = info.best_hash;
|
self.best_queued_hash = info.best_hash;
|
||||||
self.best_queued_number = info.best_number;
|
self.best_queued_number = info.best_number;
|
||||||
@@ -2253,7 +2444,8 @@ mod test {
|
|||||||
let peer_id = PeerId::random();
|
let peer_id = PeerId::random();
|
||||||
|
|
||||||
let mut sync =
|
let mut sync =
|
||||||
ChainSync::new(SyncMode::Full, client.clone(), block_announce_validator, 1).unwrap();
|
ChainSync::new(SyncMode::Full, client.clone(), block_announce_validator, 1, None)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let (a1_hash, a1_number) = {
|
let (a1_hash, a1_number) = {
|
||||||
let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block;
|
let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block;
|
||||||
@@ -2307,6 +2499,7 @@ mod test {
|
|||||||
client.clone(),
|
client.clone(),
|
||||||
Box::new(DefaultBlockAnnounceValidator),
|
Box::new(DefaultBlockAnnounceValidator),
|
||||||
1,
|
1,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -2470,6 +2663,7 @@ mod test {
|
|||||||
client.clone(),
|
client.clone(),
|
||||||
Box::new(DefaultBlockAnnounceValidator),
|
Box::new(DefaultBlockAnnounceValidator),
|
||||||
5,
|
5,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -2584,6 +2778,7 @@ mod test {
|
|||||||
client.clone(),
|
client.clone(),
|
||||||
Box::new(DefaultBlockAnnounceValidator),
|
Box::new(DefaultBlockAnnounceValidator),
|
||||||
5,
|
5,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -2707,6 +2902,7 @@ mod test {
|
|||||||
client.clone(),
|
client.clone(),
|
||||||
Box::new(DefaultBlockAnnounceValidator),
|
Box::new(DefaultBlockAnnounceValidator),
|
||||||
5,
|
5,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -2814,6 +3010,7 @@ mod test {
|
|||||||
client.clone(),
|
client.clone(),
|
||||||
Box::new(DefaultBlockAnnounceValidator),
|
Box::new(DefaultBlockAnnounceValidator),
|
||||||
1,
|
1,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,181 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
|
// Copyright (C) 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/>.
|
||||||
|
|
||||||
|
///! Warp sync support.
|
||||||
|
pub use super::state::ImportResult;
|
||||||
|
use super::state::StateSync;
|
||||||
|
pub use crate::warp_request_handler::{
|
||||||
|
EncodedProof, Request as WarpProofRequest, VerificationResult, WarpSyncProvider,
|
||||||
|
};
|
||||||
|
use crate::{
|
||||||
|
chain::Client,
|
||||||
|
schema::v1::{StateRequest, StateResponse},
|
||||||
|
WarpSyncPhase, WarpSyncProgress,
|
||||||
|
};
|
||||||
|
use sp_finality_grandpa::{AuthorityList, SetId};
|
||||||
|
use sp_runtime::traits::{Block as BlockT, NumberFor, Zero};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
enum Phase<B: BlockT> {
|
||||||
|
WarpProof { set_id: SetId, authorities: AuthorityList, last_hash: B::Hash },
|
||||||
|
State(StateSync<B>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Import warp proof result.
|
||||||
|
pub enum WarpProofImportResult<B: BlockT> {
|
||||||
|
/// Start downloading state data.
|
||||||
|
StateRequest(StateRequest),
|
||||||
|
/// Continue dowloading warp sync proofs.
|
||||||
|
WarpProofRequest(WarpProofRequest<B>),
|
||||||
|
/// Bad proof.
|
||||||
|
BadResponse,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Warp sync state machine. Accumulates warp proofs and state.
|
||||||
|
pub struct WarpSync<B: BlockT> {
|
||||||
|
phase: Phase<B>,
|
||||||
|
client: Arc<dyn Client<B>>,
|
||||||
|
warp_sync_provider: Arc<dyn WarpSyncProvider<B>>,
|
||||||
|
total_proof_bytes: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B: BlockT> WarpSync<B> {
|
||||||
|
/// Create a new instance.
|
||||||
|
pub fn new(
|
||||||
|
client: Arc<dyn Client<B>>,
|
||||||
|
warp_sync_provider: Arc<dyn WarpSyncProvider<B>>,
|
||||||
|
) -> Self {
|
||||||
|
let last_hash = client.hash(Zero::zero()).unwrap().expect("Genesis header always exists");
|
||||||
|
let phase = Phase::WarpProof {
|
||||||
|
set_id: 0,
|
||||||
|
authorities: warp_sync_provider.current_authorities(),
|
||||||
|
last_hash,
|
||||||
|
};
|
||||||
|
WarpSync { client, warp_sync_provider, phase, total_proof_bytes: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validate and import a state reponse.
|
||||||
|
pub fn import_state(&mut self, response: StateResponse) -> ImportResult<B> {
|
||||||
|
match &mut self.phase {
|
||||||
|
Phase::WarpProof { .. } => {
|
||||||
|
log::debug!(target: "sync", "Unexpected state response");
|
||||||
|
return ImportResult::BadResponse
|
||||||
|
},
|
||||||
|
Phase::State(sync) => sync.import(response),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Validate and import a warp proof reponse.
|
||||||
|
pub fn import_warp_proof(&mut self, response: EncodedProof) -> WarpProofImportResult<B> {
|
||||||
|
match &mut self.phase {
|
||||||
|
Phase::State(_) => {
|
||||||
|
log::debug!(target: "sync", "Unexpected warp proof response");
|
||||||
|
WarpProofImportResult::BadResponse
|
||||||
|
},
|
||||||
|
Phase::WarpProof { set_id, authorities, last_hash } => {
|
||||||
|
match self.warp_sync_provider.verify(
|
||||||
|
&response,
|
||||||
|
*set_id,
|
||||||
|
std::mem::take(authorities),
|
||||||
|
) {
|
||||||
|
Err(e) => {
|
||||||
|
log::debug!(target: "sync", "Bad warp proof response: {:?}", e);
|
||||||
|
return WarpProofImportResult::BadResponse
|
||||||
|
},
|
||||||
|
Ok(VerificationResult::Partial(new_set_id, new_authorities, new_last_hash)) => {
|
||||||
|
log::debug!(target: "sync", "Verified partial proof, set_id={:?}", new_set_id);
|
||||||
|
*set_id = new_set_id;
|
||||||
|
*authorities = new_authorities;
|
||||||
|
*last_hash = new_last_hash.clone();
|
||||||
|
self.total_proof_bytes += response.0.len() as u64;
|
||||||
|
WarpProofImportResult::WarpProofRequest(WarpProofRequest {
|
||||||
|
begin: new_last_hash,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
Ok(VerificationResult::Complete(new_set_id, _, header)) => {
|
||||||
|
log::debug!(target: "sync", "Verified complete proof, set_id={:?}", new_set_id);
|
||||||
|
self.total_proof_bytes += response.0.len() as u64;
|
||||||
|
let state_sync = StateSync::new(self.client.clone(), header, false);
|
||||||
|
let request = state_sync.next_request();
|
||||||
|
self.phase = Phase::State(state_sync);
|
||||||
|
WarpProofImportResult::StateRequest(request)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produce next state request.
|
||||||
|
pub fn next_state_request(&self) -> Option<StateRequest> {
|
||||||
|
match &self.phase {
|
||||||
|
Phase::WarpProof { .. } => None,
|
||||||
|
Phase::State(sync) => Some(sync.next_request()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Produce next warp proof request.
|
||||||
|
pub fn next_warp_poof_request(&self) -> Option<WarpProofRequest<B>> {
|
||||||
|
match &self.phase {
|
||||||
|
Phase::State(_) => None,
|
||||||
|
Phase::WarpProof { last_hash, .. } =>
|
||||||
|
Some(WarpProofRequest { begin: last_hash.clone() }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return target block hash if it is known.
|
||||||
|
pub fn target_block_hash(&self) -> Option<B::Hash> {
|
||||||
|
match &self.phase {
|
||||||
|
Phase::State(s) => Some(s.target()),
|
||||||
|
Phase::WarpProof { .. } => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return target block number if it is known.
|
||||||
|
pub fn target_block_number(&self) -> Option<NumberFor<B>> {
|
||||||
|
match &self.phase {
|
||||||
|
Phase::State(s) => Some(s.target_block_num()),
|
||||||
|
Phase::WarpProof { .. } => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the state is complete.
|
||||||
|
pub fn is_complete(&self) -> bool {
|
||||||
|
match &self.phase {
|
||||||
|
Phase::WarpProof { .. } => false,
|
||||||
|
Phase::State(sync) => sync.is_complete(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns state sync estimated progress (percentage, bytes)
|
||||||
|
pub fn progress(&self) -> WarpSyncProgress {
|
||||||
|
match &self.phase {
|
||||||
|
Phase::WarpProof { .. } => WarpSyncProgress {
|
||||||
|
phase: WarpSyncPhase::DownloadingWarpProofs,
|
||||||
|
total_bytes: self.total_proof_bytes,
|
||||||
|
},
|
||||||
|
Phase::State(sync) => WarpSyncProgress {
|
||||||
|
phase: if self.is_complete() {
|
||||||
|
WarpSyncPhase::ImportingState
|
||||||
|
} else {
|
||||||
|
WarpSyncPhase::DownloadingState
|
||||||
|
},
|
||||||
|
total_bytes: self.total_proof_bytes + sync.progress().size,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -186,6 +186,12 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkWorker<B, H> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let default_notif_handshake_message = Roles::from(¶ms.role).encode();
|
let default_notif_handshake_message = Roles::from(¶ms.role).encode();
|
||||||
|
|
||||||
|
let (warp_sync_provider, warp_sync_protocol_config) = match params.warp_sync {
|
||||||
|
Some((p, c)) => (Some(p), Some(c)),
|
||||||
|
None => (None, None),
|
||||||
|
};
|
||||||
|
|
||||||
let (protocol, peerset_handle, mut known_addresses) = Protocol::new(
|
let (protocol, peerset_handle, mut known_addresses) = Protocol::new(
|
||||||
protocol::ProtocolConfig {
|
protocol::ProtocolConfig {
|
||||||
roles: From::from(¶ms.role),
|
roles: From::from(¶ms.role),
|
||||||
@@ -203,6 +209,7 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkWorker<B, H> {
|
|||||||
.collect(),
|
.collect(),
|
||||||
params.block_announce_validator,
|
params.block_announce_validator,
|
||||||
params.metrics_registry.as_ref(),
|
params.metrics_registry.as_ref(),
|
||||||
|
warp_sync_provider,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// List of multiaddresses that we know in the network.
|
// List of multiaddresses that we know in the network.
|
||||||
@@ -346,6 +353,7 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkWorker<B, H> {
|
|||||||
discovery_config,
|
discovery_config,
|
||||||
params.block_request_protocol_config,
|
params.block_request_protocol_config,
|
||||||
params.state_request_protocol_config,
|
params.state_request_protocol_config,
|
||||||
|
warp_sync_protocol_config,
|
||||||
bitswap,
|
bitswap,
|
||||||
params.light_client_request_protocol_config,
|
params.light_client_request_protocol_config,
|
||||||
params.network_config.request_response_protocols,
|
params.network_config.request_response_protocols,
|
||||||
@@ -461,6 +469,7 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkWorker<B, H> {
|
|||||||
total_bytes_inbound: self.total_bytes_inbound(),
|
total_bytes_inbound: self.total_bytes_inbound(),
|
||||||
total_bytes_outbound: self.total_bytes_outbound(),
|
total_bytes_outbound: self.total_bytes_outbound(),
|
||||||
state_sync: status.state_sync,
|
state_sync: status.state_sync,
|
||||||
|
warp_sync: status.warp_sync,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,10 +50,7 @@ fn build_test_full_node(
|
|||||||
impl<B: BlockT> sc_consensus::Verifier<B> for PassThroughVerifier {
|
impl<B: BlockT> sc_consensus::Verifier<B> for PassThroughVerifier {
|
||||||
async fn verify(
|
async fn verify(
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: sp_consensus::BlockOrigin,
|
mut block: sc_consensus::BlockImportParams<B, ()>,
|
||||||
header: B::Header,
|
|
||||||
justifications: Option<sp_runtime::Justifications>,
|
|
||||||
body: Option<Vec<B::Extrinsic>>,
|
|
||||||
) -> Result<
|
) -> Result<
|
||||||
(
|
(
|
||||||
sc_consensus::BlockImportParams<B, ()>,
|
sc_consensus::BlockImportParams<B, ()>,
|
||||||
@@ -61,7 +58,8 @@ fn build_test_full_node(
|
|||||||
),
|
),
|
||||||
String,
|
String,
|
||||||
> {
|
> {
|
||||||
let maybe_keys = header
|
let maybe_keys = block
|
||||||
|
.header
|
||||||
.digest()
|
.digest()
|
||||||
.log(|l| {
|
.log(|l| {
|
||||||
l.try_as_raw(sp_runtime::generic::OpaqueDigestItemId::Consensus(b"aura"))
|
l.try_as_raw(sp_runtime::generic::OpaqueDigestItemId::Consensus(b"aura"))
|
||||||
@@ -75,12 +73,9 @@ fn build_test_full_node(
|
|||||||
vec![(sp_blockchain::well_known_cache_keys::AUTHORITIES, blob.to_vec())]
|
vec![(sp_blockchain::well_known_cache_keys::AUTHORITIES, blob.to_vec())]
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut import = sc_consensus::BlockImportParams::new(origin, header);
|
block.finalized = self.0;
|
||||||
import.body = body;
|
block.fork_choice = Some(sc_consensus::ForkChoiceStrategy::LongestChain);
|
||||||
import.finalized = self.0;
|
Ok((block, maybe_keys))
|
||||||
import.justifications = justifications;
|
|
||||||
import.fork_choice = Some(sc_consensus::ForkChoiceStrategy::LongestChain);
|
|
||||||
Ok((import, maybe_keys))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,6 +127,7 @@ fn build_test_full_node(
|
|||||||
block_request_protocol_config,
|
block_request_protocol_config,
|
||||||
state_request_protocol_config,
|
state_request_protocol_config,
|
||||||
light_client_request_protocol_config,
|
light_client_request_protocol_config,
|
||||||
|
warp_sync: None,
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
+59
-76
@@ -16,58 +16,61 @@
|
|||||||
|
|
||||||
//! Helper for handling (i.e. answering) grandpa warp sync requests from a remote peer.
|
//! Helper for handling (i.e. answering) grandpa warp sync requests from a remote peer.
|
||||||
|
|
||||||
|
use crate::config::{IncomingRequest, OutgoingResponse, ProtocolId, RequestResponseConfig};
|
||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
use futures::{
|
use futures::{
|
||||||
channel::{mpsc, oneshot},
|
channel::{mpsc, oneshot},
|
||||||
stream::StreamExt,
|
stream::StreamExt,
|
||||||
};
|
};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use sc_client_api::Backend;
|
use sp_finality_grandpa::{AuthorityList, SetId};
|
||||||
use sc_finality_grandpa::SharedAuthoritySet;
|
use sp_runtime::traits::Block as BlockT;
|
||||||
use sc_network::config::{IncomingRequest, OutgoingResponse, ProtocolId, RequestResponseConfig};
|
|
||||||
use sc_service::{
|
|
||||||
config::{Configuration, Role},
|
|
||||||
SpawnTaskHandle,
|
|
||||||
};
|
|
||||||
use sp_runtime::traits::{Block as BlockT, NumberFor};
|
|
||||||
use std::{sync::Arc, time::Duration};
|
use std::{sync::Arc, time::Duration};
|
||||||
|
|
||||||
mod proof;
|
/// Scale-encoded warp sync proof response.
|
||||||
|
pub struct EncodedProof(pub Vec<u8>);
|
||||||
|
|
||||||
pub use proof::{WarpSyncFragment, WarpSyncProof};
|
/// Warp sync request
|
||||||
|
#[derive(Encode, Decode, Debug)]
|
||||||
/// Generates the appropriate [`RequestResponseConfig`] for a given chain configuration.
|
pub struct Request<B: BlockT> {
|
||||||
pub fn request_response_config_for_chain<TBlock: BlockT, TBackend: Backend<TBlock> + 'static>(
|
/// Start collecting proofs from this block.
|
||||||
config: &Configuration,
|
pub begin: B::Hash,
|
||||||
spawn_handle: SpawnTaskHandle,
|
|
||||||
backend: Arc<TBackend>,
|
|
||||||
authority_set: SharedAuthoritySet<TBlock::Hash, NumberFor<TBlock>>,
|
|
||||||
) -> RequestResponseConfig
|
|
||||||
where
|
|
||||||
NumberFor<TBlock>: sc_finality_grandpa::BlockNumberOps,
|
|
||||||
{
|
|
||||||
let protocol_id = config.protocol_id();
|
|
||||||
|
|
||||||
if matches!(config.role, Role::Light) {
|
|
||||||
// Allow outgoing requests but deny incoming requests.
|
|
||||||
generate_request_response_config(protocol_id.clone())
|
|
||||||
} else {
|
|
||||||
// Allow both outgoing and incoming requests.
|
|
||||||
let (handler, request_response_config) =
|
|
||||||
GrandpaWarpSyncRequestHandler::new(protocol_id.clone(), backend.clone(), authority_set);
|
|
||||||
spawn_handle.spawn("grandpa-warp-sync", handler.run());
|
|
||||||
request_response_config
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const LOG_TARGET: &str = "finality-grandpa-warp-sync-request-handler";
|
const MAX_RESPONSE_SIZE: u64 = 16 * 1024 * 1024;
|
||||||
|
|
||||||
|
/// Proof verification result.
|
||||||
|
pub enum VerificationResult<Block: BlockT> {
|
||||||
|
/// Proof is valid, but the target was not reached.
|
||||||
|
Partial(SetId, AuthorityList, Block::Hash),
|
||||||
|
/// Target finality is proved.
|
||||||
|
Complete(SetId, AuthorityList, Block::Header),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Warp sync backend. Handles retrieveing and verifying warp sync proofs.
|
||||||
|
pub trait WarpSyncProvider<B: BlockT>: Send + Sync {
|
||||||
|
/// Generate proof starting at given block hash. The proof is accumulated until maximum proof size is reached.
|
||||||
|
fn generate(
|
||||||
|
&self,
|
||||||
|
start: B::Hash,
|
||||||
|
) -> Result<EncodedProof, Box<dyn std::error::Error + Send + Sync>>;
|
||||||
|
/// Verify warp proof agains current set of authorities.
|
||||||
|
fn verify(
|
||||||
|
&self,
|
||||||
|
proof: &EncodedProof,
|
||||||
|
set_id: SetId,
|
||||||
|
authorities: AuthorityList,
|
||||||
|
) -> Result<VerificationResult<B>, Box<dyn std::error::Error + Send + Sync>>;
|
||||||
|
/// Get current list of authorities. This is supposed to be genesis authorities when starting sync.
|
||||||
|
fn current_authorities(&self) -> AuthorityList;
|
||||||
|
}
|
||||||
|
|
||||||
/// Generates a [`RequestResponseConfig`] for the grandpa warp sync request protocol, refusing incoming requests.
|
/// Generates a [`RequestResponseConfig`] for the grandpa warp sync request protocol, refusing incoming requests.
|
||||||
pub fn generate_request_response_config(protocol_id: ProtocolId) -> RequestResponseConfig {
|
pub fn generate_request_response_config(protocol_id: ProtocolId) -> RequestResponseConfig {
|
||||||
RequestResponseConfig {
|
RequestResponseConfig {
|
||||||
name: generate_protocol_name(protocol_id).into(),
|
name: generate_protocol_name(protocol_id).into(),
|
||||||
max_request_size: 32,
|
max_request_size: 32,
|
||||||
max_response_size: proof::MAX_WARP_SYNC_PROOF_SIZE as u64,
|
max_response_size: MAX_RESPONSE_SIZE,
|
||||||
request_timeout: Duration::from_secs(10),
|
request_timeout: Duration::from_secs(10),
|
||||||
inbound_queue: None,
|
inbound_queue: None,
|
||||||
}
|
}
|
||||||
@@ -82,76 +85,59 @@ fn generate_protocol_name(protocol_id: ProtocolId) -> String {
|
|||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Decode)]
|
|
||||||
struct Request<B: BlockT> {
|
|
||||||
begin: B::Hash,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handler for incoming grandpa warp sync requests from a remote peer.
|
/// Handler for incoming grandpa warp sync requests from a remote peer.
|
||||||
pub struct GrandpaWarpSyncRequestHandler<TBackend, TBlock: BlockT> {
|
pub struct RequestHandler<TBlock: BlockT> {
|
||||||
backend: Arc<TBackend>,
|
backend: Arc<dyn WarpSyncProvider<TBlock>>,
|
||||||
authority_set: SharedAuthoritySet<TBlock::Hash, NumberFor<TBlock>>,
|
|
||||||
request_receiver: mpsc::Receiver<IncomingRequest>,
|
request_receiver: mpsc::Receiver<IncomingRequest>,
|
||||||
_phantom: std::marker::PhantomData<TBlock>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<TBlock: BlockT, TBackend: Backend<TBlock>> GrandpaWarpSyncRequestHandler<TBackend, TBlock> {
|
impl<TBlock: BlockT> RequestHandler<TBlock> {
|
||||||
/// Create a new [`GrandpaWarpSyncRequestHandler`].
|
/// Create a new [`RequestHandler`].
|
||||||
pub fn new(
|
pub fn new(
|
||||||
protocol_id: ProtocolId,
|
protocol_id: ProtocolId,
|
||||||
backend: Arc<TBackend>,
|
backend: Arc<dyn WarpSyncProvider<TBlock>>,
|
||||||
authority_set: SharedAuthoritySet<TBlock::Hash, NumberFor<TBlock>>,
|
|
||||||
) -> (Self, RequestResponseConfig) {
|
) -> (Self, RequestResponseConfig) {
|
||||||
let (tx, request_receiver) = mpsc::channel(20);
|
let (tx, request_receiver) = mpsc::channel(20);
|
||||||
|
|
||||||
let mut request_response_config = generate_request_response_config(protocol_id);
|
let mut request_response_config = generate_request_response_config(protocol_id);
|
||||||
request_response_config.inbound_queue = Some(tx);
|
request_response_config.inbound_queue = Some(tx);
|
||||||
|
|
||||||
(
|
(Self { backend, request_receiver }, request_response_config)
|
||||||
Self { backend, request_receiver, _phantom: std::marker::PhantomData, authority_set },
|
|
||||||
request_response_config,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_request(
|
fn handle_request(
|
||||||
&self,
|
&self,
|
||||||
payload: Vec<u8>,
|
payload: Vec<u8>,
|
||||||
pending_response: oneshot::Sender<OutgoingResponse>,
|
pending_response: oneshot::Sender<OutgoingResponse>,
|
||||||
) -> Result<(), HandleRequestError>
|
) -> Result<(), HandleRequestError> {
|
||||||
where
|
|
||||||
NumberFor<TBlock>: sc_finality_grandpa::BlockNumberOps,
|
|
||||||
{
|
|
||||||
let request = Request::<TBlock>::decode(&mut &payload[..])?;
|
let request = Request::<TBlock>::decode(&mut &payload[..])?;
|
||||||
|
|
||||||
let proof = WarpSyncProof::generate(
|
let EncodedProof(proof) = self
|
||||||
&*self.backend,
|
.backend
|
||||||
request.begin,
|
.generate(request.begin)
|
||||||
&self.authority_set.authority_set_changes(),
|
.map_err(HandleRequestError::InvalidRequest)?;
|
||||||
)?;
|
|
||||||
|
|
||||||
pending_response
|
pending_response
|
||||||
.send(OutgoingResponse {
|
.send(OutgoingResponse {
|
||||||
result: Ok(proof.encode()),
|
result: Ok(proof),
|
||||||
reputation_changes: Vec::new(),
|
reputation_changes: Vec::new(),
|
||||||
sent_feedback: None,
|
sent_feedback: None,
|
||||||
})
|
})
|
||||||
.map_err(|_| HandleRequestError::SendResponse)
|
.map_err(|_| HandleRequestError::SendResponse)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run [`GrandpaWarpSyncRequestHandler`].
|
/// Run [`RequestHandler`].
|
||||||
pub async fn run(mut self)
|
pub async fn run(mut self) {
|
||||||
where
|
|
||||||
NumberFor<TBlock>: sc_finality_grandpa::BlockNumberOps,
|
|
||||||
{
|
|
||||||
while let Some(request) = self.request_receiver.next().await {
|
while let Some(request) = self.request_receiver.next().await {
|
||||||
let IncomingRequest { peer, payload, pending_response } = request;
|
let IncomingRequest { peer, payload, pending_response } = request;
|
||||||
|
|
||||||
match self.handle_request(payload, pending_response) {
|
match self.handle_request(payload, pending_response) {
|
||||||
Ok(()) =>
|
Ok(()) =>
|
||||||
debug!(target: LOG_TARGET, "Handled grandpa warp sync request from {}.", peer),
|
debug!(target: "sync", "Handled grandpa warp sync request from {}.", peer),
|
||||||
Err(e) => debug!(
|
Err(e) => debug!(
|
||||||
target: LOG_TARGET,
|
target: "sync",
|
||||||
"Failed to handle grandpa warp sync request from {}: {}", peer, e,
|
"Failed to handle grandpa warp sync request from {}: {}",
|
||||||
|
peer, e,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,7 +145,7 @@ impl<TBlock: BlockT, TBackend: Backend<TBlock>> GrandpaWarpSyncRequestHandler<TB
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, derive_more::Display, derive_more::From)]
|
#[derive(Debug, derive_more::Display, derive_more::From)]
|
||||||
pub enum HandleRequestError {
|
enum HandleRequestError {
|
||||||
#[display(fmt = "Failed to decode request: {}.", _0)]
|
#[display(fmt = "Failed to decode request: {}.", _0)]
|
||||||
DecodeProto(prost::DecodeError),
|
DecodeProto(prost::DecodeError),
|
||||||
#[display(fmt = "Failed to encode response: {}.", _0)]
|
#[display(fmt = "Failed to encode response: {}.", _0)]
|
||||||
@@ -168,11 +154,8 @@ pub enum HandleRequestError {
|
|||||||
DecodeScale(codec::Error),
|
DecodeScale(codec::Error),
|
||||||
Client(sp_blockchain::Error),
|
Client(sp_blockchain::Error),
|
||||||
#[from(ignore)]
|
#[from(ignore)]
|
||||||
InvalidRequest(String),
|
#[display(fmt = "Invalid request {}.", _0)]
|
||||||
#[from(ignore)]
|
InvalidRequest(Box<dyn std::error::Error + Send + Sync>),
|
||||||
InvalidProof(String),
|
|
||||||
#[display(fmt = "Failed to send response.")]
|
#[display(fmt = "Failed to send response.")]
|
||||||
SendResponse,
|
SendResponse,
|
||||||
#[display(fmt = "Missing required data to be able to answer request.")]
|
|
||||||
MissingData,
|
|
||||||
}
|
}
|
||||||
@@ -108,25 +108,19 @@ impl PassThroughVerifier {
|
|||||||
impl<B: BlockT> Verifier<B> for PassThroughVerifier {
|
impl<B: BlockT> Verifier<B> for PassThroughVerifier {
|
||||||
async fn verify(
|
async fn verify(
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
mut block: BlockImportParams<B, ()>,
|
||||||
header: B::Header,
|
|
||||||
justifications: Option<Justifications>,
|
|
||||||
body: Option<Vec<B::Extrinsic>>,
|
|
||||||
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
||||||
let maybe_keys = header
|
let maybe_keys = block
|
||||||
|
.header
|
||||||
.digest()
|
.digest()
|
||||||
.log(|l| {
|
.log(|l| {
|
||||||
l.try_as_raw(OpaqueDigestItemId::Consensus(b"aura"))
|
l.try_as_raw(OpaqueDigestItemId::Consensus(b"aura"))
|
||||||
.or_else(|| l.try_as_raw(OpaqueDigestItemId::Consensus(b"babe")))
|
.or_else(|| l.try_as_raw(OpaqueDigestItemId::Consensus(b"babe")))
|
||||||
})
|
})
|
||||||
.map(|blob| vec![(well_known_cache_keys::AUTHORITIES, blob.to_vec())]);
|
.map(|blob| vec![(well_known_cache_keys::AUTHORITIES, blob.to_vec())]);
|
||||||
let mut import = BlockImportParams::new(origin, header);
|
block.finalized = self.finalized;
|
||||||
import.body = body;
|
block.fork_choice = Some(self.fork_choice.clone());
|
||||||
import.finalized = self.finalized;
|
Ok((block, maybe_keys))
|
||||||
import.justifications = justifications;
|
|
||||||
import.fork_choice = Some(self.fork_choice.clone());
|
|
||||||
|
|
||||||
Ok((import, maybe_keys))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,13 +383,10 @@ where
|
|||||||
block.header.parent_hash,
|
block.header.parent_hash,
|
||||||
);
|
);
|
||||||
let header = block.header.clone();
|
let header = block.header.clone();
|
||||||
let (import_block, cache) = futures::executor::block_on(self.verifier.verify(
|
let mut import_block = BlockImportParams::new(origin, header.clone());
|
||||||
origin,
|
import_block.body = if headers_only { None } else { Some(block.extrinsics) };
|
||||||
header.clone(),
|
let (import_block, cache) =
|
||||||
None,
|
futures::executor::block_on(self.verifier.verify(import_block)).unwrap();
|
||||||
if headers_only { None } else { Some(block.extrinsics) },
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
let cache = if let Some(cache) = cache {
|
let cache = if let Some(cache) = cache {
|
||||||
cache.into_iter().collect()
|
cache.into_iter().collect()
|
||||||
} else {
|
} else {
|
||||||
@@ -631,18 +622,10 @@ struct VerifierAdapter<B: BlockT> {
|
|||||||
impl<B: BlockT> Verifier<B> for VerifierAdapter<B> {
|
impl<B: BlockT> Verifier<B> for VerifierAdapter<B> {
|
||||||
async fn verify(
|
async fn verify(
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: BlockOrigin,
|
block: BlockImportParams<B, ()>,
|
||||||
header: B::Header,
|
|
||||||
justifications: Option<Justifications>,
|
|
||||||
body: Option<Vec<B::Extrinsic>>,
|
|
||||||
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
||||||
let hash = header.hash();
|
let hash = block.header.hash();
|
||||||
self.verifier
|
self.verifier.lock().await.verify(block).await.map_err(|e| {
|
||||||
.lock()
|
|
||||||
.await
|
|
||||||
.verify(origin, header, justifications, body)
|
|
||||||
.await
|
|
||||||
.map_err(|e| {
|
|
||||||
self.failed_verifications.lock().insert(hash, e.clone());
|
self.failed_verifications.lock().insert(hash, e.clone());
|
||||||
e
|
e
|
||||||
})
|
})
|
||||||
@@ -850,6 +833,7 @@ where
|
|||||||
block_request_protocol_config,
|
block_request_protocol_config,
|
||||||
state_request_protocol_config,
|
state_request_protocol_config,
|
||||||
light_client_request_protocol_config,
|
light_client_request_protocol_config,
|
||||||
|
warp_sync: None,
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -939,6 +923,7 @@ where
|
|||||||
block_request_protocol_config,
|
block_request_protocol_config,
|
||||||
state_request_protocol_config,
|
state_request_protocol_config,
|
||||||
light_client_request_protocol_config,
|
light_client_request_protocol_config,
|
||||||
|
warp_sync: None,
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -343,7 +343,10 @@ fn should_query_storage() {
|
|||||||
Err(Error::InvalidBlockRange {
|
Err(Error::InvalidBlockRange {
|
||||||
from: format!("{:?}", genesis_hash),
|
from: format!("{:?}", genesis_hash),
|
||||||
to: format!("{:?}", Some(random_hash1)),
|
to: format!("{:?}", Some(random_hash1)),
|
||||||
details: format!("UnknownBlock: header not found in db: {}", random_hash1),
|
details: format!(
|
||||||
|
"UnknownBlock: Header was not found in the database: {:?}",
|
||||||
|
random_hash1
|
||||||
|
),
|
||||||
})
|
})
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
);
|
);
|
||||||
@@ -356,7 +359,10 @@ fn should_query_storage() {
|
|||||||
Err(Error::InvalidBlockRange {
|
Err(Error::InvalidBlockRange {
|
||||||
from: format!("{:?}", random_hash1),
|
from: format!("{:?}", random_hash1),
|
||||||
to: format!("{:?}", Some(genesis_hash)),
|
to: format!("{:?}", Some(genesis_hash)),
|
||||||
details: format!("UnknownBlock: header not found in db: {}", random_hash1),
|
details: format!(
|
||||||
|
"UnknownBlock: Header was not found in the database: {:?}",
|
||||||
|
random_hash1
|
||||||
|
),
|
||||||
})
|
})
|
||||||
.map_err(|e| e.to_string()),
|
.map_err(|e| e.to_string()),
|
||||||
);
|
);
|
||||||
@@ -369,7 +375,10 @@ fn should_query_storage() {
|
|||||||
Err(Error::InvalidBlockRange {
|
Err(Error::InvalidBlockRange {
|
||||||
from: format!("{:?}", random_hash1),
|
from: format!("{:?}", random_hash1),
|
||||||
to: format!("{:?}", Some(block2_hash)), // Best block hash.
|
to: format!("{:?}", Some(block2_hash)), // Best block hash.
|
||||||
details: format!("UnknownBlock: header not found in db: {}", random_hash1),
|
details: format!(
|
||||||
|
"UnknownBlock: Header was not found in the database: {:?}",
|
||||||
|
random_hash1
|
||||||
|
),
|
||||||
})
|
})
|
||||||
.map_err(|e| e.to_string()),
|
.map_err(|e| e.to_string()),
|
||||||
);
|
);
|
||||||
@@ -382,7 +391,10 @@ fn should_query_storage() {
|
|||||||
Err(Error::InvalidBlockRange {
|
Err(Error::InvalidBlockRange {
|
||||||
from: format!("{:?}", random_hash1), // First hash not found.
|
from: format!("{:?}", random_hash1), // First hash not found.
|
||||||
to: format!("{:?}", Some(random_hash2)),
|
to: format!("{:?}", Some(random_hash2)),
|
||||||
details: format!("UnknownBlock: header not found in db: {}", random_hash1),
|
details: format!(
|
||||||
|
"UnknownBlock: Header was not found in the database: {:?}",
|
||||||
|
random_hash1
|
||||||
|
),
|
||||||
})
|
})
|
||||||
.map_err(|e| e.to_string()),
|
.map_err(|e| e.to_string()),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ use sc_network::{
|
|||||||
config::{OnDemand, Role, SyncMode},
|
config::{OnDemand, 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},
|
||||||
NetworkService,
|
NetworkService,
|
||||||
};
|
};
|
||||||
use sc_telemetry::{telemetry, ConnectionMessage, Telemetry, TelemetryHandle, SUBSTRATE_INFO};
|
use sc_telemetry::{telemetry, ConnectionMessage, Telemetry, TelemetryHandle, SUBSTRATE_INFO};
|
||||||
@@ -354,7 +355,7 @@ where
|
|||||||
wasm_runtime_overrides: config.wasm_runtime_overrides.clone(),
|
wasm_runtime_overrides: config.wasm_runtime_overrides.clone(),
|
||||||
no_genesis: matches!(
|
no_genesis: matches!(
|
||||||
config.network.sync_mode,
|
config.network.sync_mode,
|
||||||
sc_network::config::SyncMode::Fast { .. }
|
sc_network::config::SyncMode::Fast { .. } | sc_network::config::SyncMode::Warp
|
||||||
),
|
),
|
||||||
wasm_runtime_substitutes,
|
wasm_runtime_substitutes,
|
||||||
},
|
},
|
||||||
@@ -843,6 +844,8 @@ pub struct BuildNetworkParams<'a, TBl: BlockT, TExPool, TImpQu, TCl> {
|
|||||||
/// 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>>,
|
||||||
|
/// An optional warp sync provider.
|
||||||
|
pub warp_sync: Option<Arc<dyn WarpSyncProvider<TBl>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build the network service, the network status sinks and an RPC sender.
|
/// Build the network service, the network status sinks and an RPC sender.
|
||||||
@@ -878,6 +881,7 @@ where
|
|||||||
import_queue,
|
import_queue,
|
||||||
on_demand,
|
on_demand,
|
||||||
block_announce_validator_builder,
|
block_announce_validator_builder,
|
||||||
|
warp_sync,
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
let transaction_pool_adapter = Arc::new(TransactionPoolAdapter {
|
let transaction_pool_adapter = Arc::new(TransactionPoolAdapter {
|
||||||
@@ -928,6 +932,20 @@ where
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let warp_sync_params = warp_sync.map(|provider| {
|
||||||
|
let protocol_config = if matches!(config.role, Role::Light) {
|
||||||
|
// Allow outgoing requests but deny incoming requests.
|
||||||
|
warp_request_handler::generate_request_response_config(protocol_id.clone())
|
||||||
|
} else {
|
||||||
|
// Allow both outgoing and incoming requests.
|
||||||
|
let (handler, protocol_config) =
|
||||||
|
WarpSyncRequestHandler::new(protocol_id.clone(), provider.clone());
|
||||||
|
spawn_handle.spawn("warp_sync_request_handler", handler.run());
|
||||||
|
protocol_config
|
||||||
|
};
|
||||||
|
(provider, protocol_config)
|
||||||
|
});
|
||||||
|
|
||||||
let light_client_request_protocol_config = {
|
let light_client_request_protocol_config = {
|
||||||
if matches!(config.role, Role::Light) {
|
if matches!(config.role, Role::Light) {
|
||||||
// Allow outgoing requests but deny incoming requests.
|
// Allow outgoing requests but deny incoming requests.
|
||||||
@@ -965,6 +983,7 @@ where
|
|||||||
metrics_registry: config.prometheus_config.as_ref().map(|config| config.registry.clone()),
|
metrics_registry: config.prometheus_config.as_ref().map(|config| config.registry.clone()),
|
||||||
block_request_protocol_config,
|
block_request_protocol_config,
|
||||||
state_request_protocol_config,
|
state_request_protocol_config,
|
||||||
|
warp_sync: warp_sync_params,
|
||||||
light_client_request_protocol_config,
|
light_client_request_protocol_config,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -769,6 +769,8 @@ where
|
|||||||
{
|
{
|
||||||
let parent_hash = import_headers.post().parent_hash().clone();
|
let parent_hash = import_headers.post().parent_hash().clone();
|
||||||
let status = self.backend.blockchain().status(BlockId::Hash(hash))?;
|
let status = self.backend.blockchain().status(BlockId::Hash(hash))?;
|
||||||
|
let parent_exists = self.backend.blockchain().status(BlockId::Hash(parent_hash))? ==
|
||||||
|
blockchain::BlockStatus::InChain;
|
||||||
match (import_existing, status) {
|
match (import_existing, status) {
|
||||||
(false, blockchain::BlockStatus::InChain) => return Ok(ImportResult::AlreadyInChain),
|
(false, blockchain::BlockStatus::InChain) => return Ok(ImportResult::AlreadyInChain),
|
||||||
(false, blockchain::BlockStatus::Unknown) => {},
|
(false, blockchain::BlockStatus::Unknown) => {},
|
||||||
@@ -815,7 +817,6 @@ where
|
|||||||
if let Some(changes_trie_transaction) = changes_trie_tx {
|
if let Some(changes_trie_transaction) = changes_trie_tx {
|
||||||
operation.op.update_changes_trie(changes_trie_transaction)?;
|
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) => {
|
||||||
@@ -834,10 +835,10 @@ where
|
|||||||
None
|
None
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
// Ensure parent chain is finalized to maintain invariant that
|
||||||
// ensure parent block is finalized to maintain invariant that
|
// finality is called sequentially. This will also send finality
|
||||||
// finality is called sequentially.
|
// notifications for top 250 newly finalized blocks.
|
||||||
if finalized {
|
if finalized && parent_exists {
|
||||||
self.apply_finality_with_block_hash(
|
self.apply_finality_with_block_hash(
|
||||||
operation,
|
operation,
|
||||||
parent_hash,
|
parent_hash,
|
||||||
@@ -868,7 +869,7 @@ where
|
|||||||
NewBlockState::Normal
|
NewBlockState::Normal
|
||||||
};
|
};
|
||||||
|
|
||||||
let tree_route = if is_new_best && info.best_hash != parent_hash {
|
let tree_route = if is_new_best && info.best_hash != parent_hash && parent_exists {
|
||||||
let route_from_best =
|
let route_from_best =
|
||||||
sp_blockchain::tree_route(self.backend.blockchain(), info.best_hash, parent_hash)?;
|
sp_blockchain::tree_route(self.backend.blockchain(), info.best_hash, parent_hash)?;
|
||||||
Some(route_from_best)
|
Some(route_from_best)
|
||||||
@@ -932,21 +933,21 @@ where
|
|||||||
let at = BlockId::Hash(*parent_hash);
|
let at = BlockId::Hash(*parent_hash);
|
||||||
let state_action = std::mem::replace(&mut import_block.state_action, StateAction::Skip);
|
let state_action = std::mem::replace(&mut import_block.state_action, StateAction::Skip);
|
||||||
let (enact_state, storage_changes) = match (self.block_status(&at)?, state_action) {
|
let (enact_state, storage_changes) = match (self.block_status(&at)?, state_action) {
|
||||||
(BlockStatus::Unknown, _) =>
|
|
||||||
return Ok(PrepareStorageChangesResult::Discard(ImportResult::UnknownParent)),
|
|
||||||
(BlockStatus::KnownBad, _) =>
|
(BlockStatus::KnownBad, _) =>
|
||||||
return Ok(PrepareStorageChangesResult::Discard(ImportResult::KnownBad)),
|
return Ok(PrepareStorageChangesResult::Discard(ImportResult::KnownBad)),
|
||||||
(_, StateAction::Skip) => (false, None),
|
|
||||||
(
|
(
|
||||||
BlockStatus::InChainPruned,
|
BlockStatus::InChainPruned,
|
||||||
StateAction::ApplyChanges(sc_consensus::StorageChanges::Changes(_)),
|
StateAction::ApplyChanges(sc_consensus::StorageChanges::Changes(_)),
|
||||||
) => return Ok(PrepareStorageChangesResult::Discard(ImportResult::MissingState)),
|
) => return Ok(PrepareStorageChangesResult::Discard(ImportResult::MissingState)),
|
||||||
|
(_, StateAction::ApplyChanges(changes)) => (true, Some(changes)),
|
||||||
|
(BlockStatus::Unknown, _) =>
|
||||||
|
return Ok(PrepareStorageChangesResult::Discard(ImportResult::UnknownParent)),
|
||||||
|
(_, StateAction::Skip) => (false, None),
|
||||||
(BlockStatus::InChainPruned, StateAction::Execute) =>
|
(BlockStatus::InChainPruned, StateAction::Execute) =>
|
||||||
return Ok(PrepareStorageChangesResult::Discard(ImportResult::MissingState)),
|
return Ok(PrepareStorageChangesResult::Discard(ImportResult::MissingState)),
|
||||||
(BlockStatus::InChainPruned, StateAction::ExecuteIfPossible) => (false, None),
|
(BlockStatus::InChainPruned, StateAction::ExecuteIfPossible) => (false, None),
|
||||||
(_, StateAction::Execute) => (true, None),
|
(_, StateAction::Execute) => (true, None),
|
||||||
(_, StateAction::ExecuteIfPossible) => (true, None),
|
(_, StateAction::ExecuteIfPossible) => (true, None),
|
||||||
(_, StateAction::ApplyChanges(changes)) => (true, Some(changes)),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let storage_changes = match (enact_state, storage_changes, &import_block.body) {
|
let storage_changes = match (enact_state, storage_changes, &import_block.body) {
|
||||||
@@ -1912,8 +1913,14 @@ where
|
|||||||
&mut self,
|
&mut self,
|
||||||
block: BlockCheckParams<Block>,
|
block: BlockCheckParams<Block>,
|
||||||
) -> Result<ImportResult, Self::Error> {
|
) -> Result<ImportResult, Self::Error> {
|
||||||
let BlockCheckParams { hash, number, parent_hash, allow_missing_state, import_existing } =
|
let BlockCheckParams {
|
||||||
block;
|
hash,
|
||||||
|
number,
|
||||||
|
parent_hash,
|
||||||
|
allow_missing_state,
|
||||||
|
import_existing,
|
||||||
|
allow_missing_parent,
|
||||||
|
} = block;
|
||||||
|
|
||||||
// Check the block against white and black lists if any are defined
|
// Check the block against white and black lists if any are defined
|
||||||
// (i.e. fork blocks and bad blocks respectively)
|
// (i.e. fork blocks and bad blocks respectively)
|
||||||
@@ -1955,6 +1962,7 @@ where
|
|||||||
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?
|
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?
|
||||||
{
|
{
|
||||||
BlockStatus::InChainWithState | BlockStatus::Queued => {},
|
BlockStatus::InChainWithState | BlockStatus::Queued => {},
|
||||||
|
BlockStatus::Unknown if allow_missing_parent => {},
|
||||||
BlockStatus::Unknown => return Ok(ImportResult::UnknownParent),
|
BlockStatus::Unknown => return Ok(ImportResult::UnknownParent),
|
||||||
BlockStatus::InChainPruned if allow_missing_state => {},
|
BlockStatus::InChainPruned if allow_missing_state => {},
|
||||||
BlockStatus::InChainPruned => return Ok(ImportResult::MissingState),
|
BlockStatus::InChainPruned => return Ok(ImportResult::MissingState),
|
||||||
|
|||||||
@@ -1209,6 +1209,7 @@ fn import_with_justification() {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.block;
|
.block;
|
||||||
block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap();
|
block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap();
|
||||||
|
client.finalize_block(BlockId::hash(a2.hash()), None).unwrap();
|
||||||
|
|
||||||
// A2 -> A3
|
// A2 -> A3
|
||||||
let justification = Justifications::from((TEST_ENGINE_ID, vec![1, 2, 3]));
|
let justification = Justifications::from((TEST_ENGINE_ID, vec![1, 2, 3]));
|
||||||
@@ -1555,6 +1556,7 @@ fn respects_block_rules() {
|
|||||||
number: 0,
|
number: 0,
|
||||||
parent_hash: block_ok.header().parent_hash().clone(),
|
parent_hash: block_ok.header().parent_hash().clone(),
|
||||||
allow_missing_state: false,
|
allow_missing_state: false,
|
||||||
|
allow_missing_parent: false,
|
||||||
import_existing: false,
|
import_existing: false,
|
||||||
};
|
};
|
||||||
assert_eq!(block_on(client.check_block(params)).unwrap(), ImportResult::imported(false));
|
assert_eq!(block_on(client.check_block(params)).unwrap(), ImportResult::imported(false));
|
||||||
@@ -1570,6 +1572,7 @@ fn respects_block_rules() {
|
|||||||
number: 0,
|
number: 0,
|
||||||
parent_hash: block_not_ok.header().parent_hash().clone(),
|
parent_hash: block_not_ok.header().parent_hash().clone(),
|
||||||
allow_missing_state: false,
|
allow_missing_state: false,
|
||||||
|
allow_missing_parent: false,
|
||||||
import_existing: false,
|
import_existing: false,
|
||||||
};
|
};
|
||||||
if record_only {
|
if record_only {
|
||||||
@@ -1592,6 +1595,7 @@ fn respects_block_rules() {
|
|||||||
number: 1,
|
number: 1,
|
||||||
parent_hash: block_ok.header().parent_hash().clone(),
|
parent_hash: block_ok.header().parent_hash().clone(),
|
||||||
allow_missing_state: false,
|
allow_missing_state: false,
|
||||||
|
allow_missing_parent: false,
|
||||||
import_existing: false,
|
import_existing: false,
|
||||||
};
|
};
|
||||||
if record_only {
|
if record_only {
|
||||||
@@ -1610,6 +1614,7 @@ fn respects_block_rules() {
|
|||||||
number: 1,
|
number: 1,
|
||||||
parent_hash: block_not_ok.header().parent_hash().clone(),
|
parent_hash: block_not_ok.header().parent_hash().clone(),
|
||||||
allow_missing_state: false,
|
allow_missing_state: false,
|
||||||
|
allow_missing_parent: false,
|
||||||
import_existing: false,
|
import_existing: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1676,6 +1681,7 @@ fn returns_status_for_pruned_blocks() {
|
|||||||
number: 0,
|
number: 0,
|
||||||
parent_hash: a1.header().parent_hash().clone(),
|
parent_hash: a1.header().parent_hash().clone(),
|
||||||
allow_missing_state: false,
|
allow_missing_state: false,
|
||||||
|
allow_missing_parent: false,
|
||||||
import_existing: false,
|
import_existing: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1712,6 +1718,7 @@ fn returns_status_for_pruned_blocks() {
|
|||||||
number: 1,
|
number: 1,
|
||||||
parent_hash: a1.header().parent_hash().clone(),
|
parent_hash: a1.header().parent_hash().clone(),
|
||||||
allow_missing_state: false,
|
allow_missing_state: false,
|
||||||
|
allow_missing_parent: false,
|
||||||
import_existing: false,
|
import_existing: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1745,6 +1752,7 @@ fn returns_status_for_pruned_blocks() {
|
|||||||
number: 2,
|
number: 2,
|
||||||
parent_hash: a2.header().parent_hash().clone(),
|
parent_hash: a2.header().parent_hash().clone(),
|
||||||
allow_missing_state: false,
|
allow_missing_state: false,
|
||||||
|
allow_missing_parent: false,
|
||||||
import_existing: false,
|
import_existing: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1779,6 +1787,7 @@ fn returns_status_for_pruned_blocks() {
|
|||||||
number: 0,
|
number: 0,
|
||||||
parent_hash: b1.header().parent_hash().clone(),
|
parent_hash: b1.header().parent_hash().clone(),
|
||||||
allow_missing_state: false,
|
allow_missing_state: false,
|
||||||
|
allow_missing_parent: false,
|
||||||
import_existing: false,
|
import_existing: false,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -492,7 +492,7 @@ sp_api::decl_runtime_apis! {
|
|||||||
/// applied in the runtime after those N blocks have passed.
|
/// applied in the runtime after those N blocks have passed.
|
||||||
///
|
///
|
||||||
/// The consensus protocol will coordinate the handoff externally.
|
/// The consensus protocol will coordinate the handoff externally.
|
||||||
#[api_version(2)]
|
#[api_version(3)]
|
||||||
pub trait GrandpaApi {
|
pub trait GrandpaApi {
|
||||||
/// Get the current GRANDPA authorities and weights. This should not change except
|
/// Get the current GRANDPA authorities and weights. This should not change except
|
||||||
/// for when changes are scheduled and the corresponding delay has passed.
|
/// for when changes are scheduled and the corresponding delay has passed.
|
||||||
@@ -530,5 +530,8 @@ sp_api::decl_runtime_apis! {
|
|||||||
set_id: SetId,
|
set_id: SetId,
|
||||||
authority_id: AuthorityId,
|
authority_id: AuthorityId,
|
||||||
) -> Option<OpaqueKeyOwnershipProof>;
|
) -> Option<OpaqueKeyOwnershipProof>;
|
||||||
|
|
||||||
|
/// Get current GRANDPA authority set id.
|
||||||
|
fn current_set_id() -> SetId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -883,6 +883,10 @@ cfg_if! {
|
|||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn current_set_id() -> sp_finality_grandpa::SetId {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
fn submit_report_equivocation_unsigned_extrinsic(
|
fn submit_report_equivocation_unsigned_extrinsic(
|
||||||
_equivocation_proof: sp_finality_grandpa::EquivocationProof<
|
_equivocation_proof: sp_finality_grandpa::EquivocationProof<
|
||||||
<Block as BlockT>::Hash,
|
<Block as BlockT>::Hash,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ sc-basic-authorship = { path = "../../client/basic-authorship" }
|
|||||||
sc-rpc = { path = "../../client/rpc" }
|
sc-rpc = { path = "../../client/rpc" }
|
||||||
sc-transaction-pool = { path = "../../client/transaction-pool" }
|
sc-transaction-pool = { path = "../../client/transaction-pool" }
|
||||||
grandpa = { package = "sc-finality-grandpa", path = "../../client/finality-grandpa" }
|
grandpa = { package = "sc-finality-grandpa", path = "../../client/finality-grandpa" }
|
||||||
|
sp-finality-grandpa = { path = "../../primitives/finality-grandpa" }
|
||||||
sp-consensus-babe = { path = "../../primitives/consensus/babe" }
|
sp-consensus-babe = { path = "../../primitives/consensus/babe" }
|
||||||
sc-consensus-babe = { path = "../../client/consensus/babe" }
|
sc-consensus-babe = { path = "../../client/consensus/babe" }
|
||||||
sc-consensus = { path = "../../client/consensus/common" }
|
sc-consensus = { path = "../../client/consensus/common" }
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ use sc_transaction_pool_api::TransactionPool;
|
|||||||
use sp_api::{ApiExt, ConstructRuntimeApi, Core, Metadata};
|
use sp_api::{ApiExt, ConstructRuntimeApi, Core, Metadata};
|
||||||
use sp_block_builder::BlockBuilder;
|
use sp_block_builder::BlockBuilder;
|
||||||
use sp_consensus_babe::BabeApi;
|
use sp_consensus_babe::BabeApi;
|
||||||
|
use sp_finality_grandpa::GrandpaApi;
|
||||||
use sp_keyring::sr25519::Keyring::Alice;
|
use sp_keyring::sr25519::Keyring::Alice;
|
||||||
use sp_offchain::OffchainWorkerApi;
|
use sp_offchain::OffchainWorkerApi;
|
||||||
use sp_runtime::traits::{Block as BlockT, Header};
|
use sp_runtime::traits::{Block as BlockT, Header};
|
||||||
@@ -90,7 +91,8 @@ where
|
|||||||
+ TaggedTransactionQueue<T::Block>
|
+ TaggedTransactionQueue<T::Block>
|
||||||
+ BlockBuilder<T::Block>
|
+ BlockBuilder<T::Block>
|
||||||
+ BabeApi<T::Block>
|
+ BabeApi<T::Block>
|
||||||
+ ApiExt<T::Block, StateBackend = <TFullBackend<T::Block> as Backend<T::Block>>::State>,
|
+ ApiExt<T::Block, StateBackend = <TFullBackend<T::Block> as Backend<T::Block>>::State>
|
||||||
|
+ GrandpaApi<T::Block>,
|
||||||
<T::Runtime as frame_system::Config>::Call: From<frame_system::Call<T::Runtime>>,
|
<T::Runtime as frame_system::Config>::Call: From<frame_system::Call<T::Runtime>>,
|
||||||
<<T as ChainInfo>::Block as BlockT>::Hash: FromStr,
|
<<T as ChainInfo>::Block as BlockT>::Hash: FromStr,
|
||||||
<<<T as ChainInfo>::Block as BlockT>::Header as Header>::Number:
|
<<<T as ChainInfo>::Block as BlockT>::Header as Header>::Number:
|
||||||
@@ -151,6 +153,7 @@ where
|
|||||||
import_queue,
|
import_queue,
|
||||||
on_demand: None,
|
on_demand: None,
|
||||||
block_announce_validator_builder: None,
|
block_announce_validator_builder: None,
|
||||||
|
warp_sync: None,
|
||||||
};
|
};
|
||||||
build_network(params)?
|
build_network(params)?
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user