Sync ethereum headers using unsigned (substrate) transactions (#45)

* reward submitters on finalization

* PoA -> Substrate: unsigned_import_header API

* fix grumble

* make submitter part of ImportContext

* verify using next validators set + tests

* fix nostd compilation

* add sub-tx-mode argument

* support sub-tx-mode

* impl ValidateUnsigned for Runtime

* do not submit too much transactions to the pool

* cargo fmt

* fix bad merge

* revert license fix

* Update modules/ethereum/src/lib.rs

Co-Authored-By: Hernando Castano <HCastano@users.noreply.github.com>

* Update modules/ethereum/src/verification.rs

Co-Authored-By: Hernando Castano <HCastano@users.noreply.github.com>

* updated comment

* validate receipts before accepting unsigned tx to pool

* cargo fmt

* fix comment

* fix grumbles

* Update modules/ethereum/src/verification.rs

Co-Authored-By: Hernando Castano <HCastano@users.noreply.github.com>

* cargo fmt --all

* struct ChangeToEnact

* updated doc

* fix doc

* add docs to the code method

* simplify fn ancestry

* finish incomplete docs

* Update modules/ethereum/src/lib.rs

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update modules/ethereum/src/lib.rs

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* return err from unsigned_import_header

* get header once

* Update relays/ethereum/src/ethereum_sync.rs

Co-Authored-By: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* fix

* UnsignedTooFarInTheFuture -> Custom(err.code())

* updated ImportContext::last_signal_block

* cargo fmt --all

* rename runtime calls

Co-authored-by: Hernando Castano <HCastano@users.noreply.github.com>
Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
This commit is contained in:
Svyatoslav Nikolsky
2020-04-07 15:53:59 +03:00
committed by Bastian Köcher
parent b055027161
commit c6c46462ab
12 changed files with 1043 additions and 211 deletions
@@ -37,6 +37,9 @@ const SUBSTRATE_TICK_INTERVAL_MS: u64 = 5_000;
/// the subscriber will receive every best header (2) reorg won't always lead to sync
/// stall and restart is a heavy operation (we forget all in-memory headers).
const STALL_SYNC_TIMEOUT_MS: u64 = 30_000;
/// Delay (in milliseconds) after we have seen update of best Ethereum header in Substrate,
/// for us to treat sync stalled. ONLY when relay operates in backup mode.
const BACKUP_STALL_SYNC_TIMEOUT_MS: u64 = 5 * 60_000;
/// Delay (in milliseconds) after connection-related error happened before we'll try
/// reconnection again.
const CONNECTION_ERROR_DELAY_MS: u64 = 10_000;
@@ -57,6 +60,8 @@ pub struct EthereumSyncParams {
pub sub_host: String,
/// Substrate RPC port.
pub sub_port: u16,
/// Substrate transactions submission mode.
pub sub_tx_mode: SubstrateTransactionMode,
/// Substrate transactions signer.
pub sub_signer: sp_core::sr25519::Pair,
/// Maximal number of ethereum headers to pre-download.
@@ -72,6 +77,18 @@ pub struct EthereumSyncParams {
pub prune_depth: u64,
}
/// Substrate transaction mode.
#[derive(Debug, PartialEq)]
pub enum SubstrateTransactionMode {
/// Submit eth headers using signed substrate transactions.
Signed,
/// Submit eth headers using unsigned substrate transactions.
Unsigned,
/// Submit eth headers using signed substrate transactions, but only when we
/// believe that sync has stalled.
Backup,
}
impl std::fmt::Debug for EthereumSyncParams {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("EthereumSyncParams")
@@ -79,6 +96,7 @@ impl std::fmt::Debug for EthereumSyncParams {
.field("eth_port", &self.eth_port)
.field("sub_host", &self.sub_port)
.field("sub_port", &self.sub_port)
.field("sub_tx_mode", &self.sub_tx_mode)
.field("max_future_headers_to_download", &self.max_future_headers_to_download)
.field("max_headers_in_submitted_status", &self.max_headers_in_submitted_status)
.field("max_headers_in_single_submit", &self.max_headers_in_single_submit)
@@ -98,6 +116,7 @@ impl Default for EthereumSyncParams {
eth_port: 8545,
sub_host: "localhost".into(),
sub_port: 9933,
sub_tx_mode: SubstrateTransactionMode::Signed,
sub_signer: sp_keyring::AccountKeyring::Alice.pair(),
max_future_headers_to_download: 128,
max_headers_in_submitted_status: 128,
@@ -112,6 +131,10 @@ impl Default for EthereumSyncParams {
pub fn run(params: EthereumSyncParams) {
let mut local_pool = futures::executor::LocalPool::new();
let mut progress_context = (std::time::Instant::now(), None, None);
let sign_sub_transactions = match params.sub_tx_mode {
SubstrateTransactionMode::Signed | SubstrateTransactionMode::Backup => true,
SubstrateTransactionMode::Unsigned => false,
};
local_pool.run_until(async move {
let eth_uri = format!("http://{}:{}", params.eth_host, params.eth_port);
@@ -120,6 +143,7 @@ pub fn run(params: EthereumSyncParams) {
let mut eth_sync = crate::ethereum_sync::HeadersSync::new(params);
let mut stall_countdown = None;
let mut last_update_time = std::time::Instant::now();
let mut eth_maybe_client = None;
let mut eth_best_block_number_required = false;
@@ -220,6 +244,9 @@ pub fn run(params: EthereumSyncParams) {
sub_best_block,
|sub_best_block| {
let head_updated = eth_sync.substrate_best_header_response(sub_best_block);
if head_updated {
last_update_time = std::time::Instant::now();
}
match head_updated {
// IF head is updated AND there are still our transactions:
// => restart stall countdown timer
@@ -336,7 +363,9 @@ pub fn run(params: EthereumSyncParams) {
sub_existence_status_future
.set(substrate_client::ethereum_header_known(sub_client, parent_id).fuse());
} else if let Some(headers) = eth_sync.select_headers_to_submit() {
} else if let Some(headers) = eth_sync.select_headers_to_submit(
last_update_time.elapsed() > std::time::Duration::from_millis(BACKUP_STALL_SYNC_TIMEOUT_MS),
) {
let ids = match headers.len() {
1 => format!("{:?}", headers[0].id()),
2 => format!("[{:?}, {:?}]", headers[0].id(), headers[1].id()),
@@ -350,7 +379,9 @@ pub fn run(params: EthereumSyncParams) {
);
let headers = headers.into_iter().cloned().collect();
sub_submit_header_future.set(substrate_client::submit_ethereum_headers(sub_client, headers).fuse());
sub_submit_header_future.set(
substrate_client::submit_ethereum_headers(sub_client, headers, sign_sub_transactions).fuse(),
);
// remember that we have submitted some headers
if stall_countdown.is_none() {