Skip to content

Commit

Permalink
Sync ethereum headers using unsigned (substrate) transactions (parity…
Browse files Browse the repository at this point in the history
…tech#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 <[email protected]>

* Update modules/ethereum/src/verification.rs

Co-Authored-By: Hernando Castano <[email protected]>

* 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 <[email protected]>

* 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 <[email protected]>

* Update modules/ethereum/src/lib.rs

Co-Authored-By: Tomasz Drwięga <[email protected]>

* return err from unsigned_import_header

* get header once

* Update relays/ethereum/src/ethereum_sync.rs

Co-Authored-By: Tomasz Drwięga <[email protected]>

* fix

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

* updated ImportContext::last_signal_block

* cargo fmt --all

* rename runtime calls

Co-authored-by: Hernando Castano <[email protected]>
Co-authored-by: Tomasz Drwięga <[email protected]>
  • Loading branch information
3 people authored and bkchr committed Apr 10, 2024
1 parent b055027 commit c6c4646
Show file tree
Hide file tree
Showing 12 changed files with 1,043 additions and 211 deletions.
2 changes: 1 addition & 1 deletion bridges/bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ construct_runtime!(
Balances: pallet_balances::{Module, Call, Storage, Config<T>, Event<T>},
TransactionPayment: pallet_transaction_payment::{Module, Storage},
Sudo: pallet_sudo::{Module, Call, Config<T>, Storage, Event<T>},
BridgeEthPoA: pallet_bridge_eth_poa::{Module, Call, Config, Storage},
BridgeEthPoA: pallet_bridge_eth_poa::{Module, Call, Config, Storage, ValidateUnsigned},
}
);

Expand Down
51 changes: 31 additions & 20 deletions bridges/modules/ethereum/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,45 +17,49 @@
use sp_runtime::RuntimeDebug;

/// Header import error.
#[derive(RuntimeDebug)]
#[derive(Clone, Copy, RuntimeDebug)]
#[cfg_attr(feature = "std", derive(PartialEq))]
pub enum Error {
/// The header is beyound last finalized and can not be imported.
AncientHeader,
/// The header is beyond last finalized and can not be imported.
AncientHeader = 0,
/// The header is already imported.
KnownHeader,
KnownHeader = 1,
/// Seal has an incorrect format.
InvalidSealArity,
InvalidSealArity = 2,
/// Block number isn't sensible.
RidiculousNumber,
RidiculousNumber = 3,
/// Block has too much gas used.
TooMuchGasUsed,
TooMuchGasUsed = 4,
/// Gas limit header field is invalid.
InvalidGasLimit,
InvalidGasLimit = 5,
/// Extra data is of an invalid length.
ExtraDataOutOfBounds,
ExtraDataOutOfBounds = 6,
/// Timestamp header overflowed.
TimestampOverflow,
TimestampOverflow = 7,
/// The parent header is missing from the blockchain.
MissingParentBlock,
MissingParentBlock = 8,
/// The header step is missing from the header.
MissingStep,
MissingStep = 9,
/// The header signature is missing from the header.
MissingSignature,
MissingSignature = 10,
/// Empty steps are missing from the header.
MissingEmptySteps,
MissingEmptySteps = 11,
/// The same author issued different votes at the same step.
DoubleVote,
DoubleVote = 12,
/// Validation proof insufficient.
InsufficientProof,
InsufficientProof = 13,
/// Difficulty header field is invalid.
InvalidDifficulty,
InvalidDifficulty = 14,
/// The received block is from an incorrect proposer.
NotValidator,
NotValidator = 15,
/// Missing transaction receipts for the operation.
MissingTransactionsReceipts,
MissingTransactionsReceipts = 16,
/// Redundant transaction receipts are provided.
RedundantTransactionsReceipts = 17,
/// Provided transactions receipts are not matching the header.
TransactionsReceiptsMismatch,
TransactionsReceiptsMismatch = 18,
/// Can't accept unsigned header from the far future.
UnsignedTooFarInTheFuture = 19,
}

impl Error {
Expand All @@ -78,7 +82,14 @@ impl Error {
Error::InvalidDifficulty => "Header has invalid difficulty",
Error::NotValidator => "Header is sealed by unexpected validator",
Error::MissingTransactionsReceipts => "The import operation requires transactions receipts",
Error::RedundantTransactionsReceipts => "Redundant transactions receipts are provided",
Error::TransactionsReceiptsMismatch => "Invalid transactions receipts provided",
Error::UnsignedTooFarInTheFuture => "The unsigned header is too far in future",
}
}

/// Return unique error code.
pub fn code(&self) -> u8 {
*self as u8
}
}
33 changes: 27 additions & 6 deletions bridges/modules/ethereum/src/finality.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,18 @@
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>.

use crate::error::Error;
use crate::{ancestry, Storage};
use crate::Storage;
use primitives::{public_to_address, Address, Header, SealedEmptyStep, H256};
use sp_io::crypto::secp256k1_ecdsa_recover;
use sp_std::collections::{
btree_map::{BTreeMap, Entry},
btree_set::BTreeSet,
vec_deque::VecDeque,
};
use sp_std::prelude::*;
use sp_std::{
collections::{
btree_map::{BTreeMap, Entry},
btree_set::BTreeSet,
vec_deque::VecDeque,
},
iter::from_fn,
};

/// Tries to finalize blocks when given block is imported.
///
Expand Down Expand Up @@ -190,6 +193,24 @@ fn empty_step_signer(empty_step: &SealedEmptyStep, parent_hash: &H256) -> Option
.map(|public| public_to_address(&public))
}

/// Return iterator of given header ancestors.
pub(crate) fn ancestry<'a, S: Storage>(
storage: &'a S,
header: &Header,
) -> impl Iterator<Item = (H256, Header, Option<S::Submitter>)> + 'a {
let mut parent_hash = header.parent_hash.clone();
from_fn(move || {
let (header, submitter) = storage.header(&parent_hash)?;
if header.number == 0 {
return None;
}

let hash = parent_hash.clone();
parent_hash = header.parent_hash.clone();
Some((hash, header, submitter))
})
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
32 changes: 10 additions & 22 deletions bridges/modules/ethereum/src/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
use crate::error::Error;
use crate::finality::finalize_blocks;
use crate::validators::{Validators, ValidatorsConfiguration};
use crate::verification::verify_aura_header;
use crate::{AuraConfiguration, Storage};
use crate::verification::{is_importable_header, verify_aura_header};
use crate::{AuraConfiguration, ChangeToEnact, Storage};
use primitives::{Header, Receipt, H256};
use sp_std::{collections::btree_map::BTreeMap, prelude::*};

Expand Down Expand Up @@ -105,16 +105,22 @@ pub fn import_header<S: Storage>(
let (scheduled_change, enacted_change) = validators.extract_validators_change(&header, receipts)?;

// check if block finalizes some other blocks and corresponding scheduled validators
let validators_set = import_context.validators_set();
let finalized_blocks = finalize_blocks(
storage,
&prev_finalized_hash,
(import_context.validators_start(), import_context.validators()),
(&validators_set.enact_block, &validators_set.validators),
&hash,
import_context.submitter(),
&header,
aura_config.two_thirds_majority_transition,
)?;
let enacted_change = enacted_change.or_else(|| validators.finalize_validators_change(storage, &finalized_blocks));
let enacted_change = enacted_change
.map(|validators| ChangeToEnact {
signal_block: None,
validators,
})
.or_else(|| validators.finalize_validators_change(storage, &finalized_blocks));

// NOTE: we can't return Err() from anywhere below this line
// (because otherwise we'll have inconsistent storage if transaction will fail)
Expand Down Expand Up @@ -157,24 +163,6 @@ pub fn header_import_requires_receipts<S: Storage>(
.unwrap_or(false)
}

/// Checks that we are able to ***try to** import this header.
/// Returns error if we should not try to import this block.
/// Returns hash of the header and number of the last finalized block.
fn is_importable_header<S: Storage>(storage: &S, header: &Header) -> Result<(H256, H256), Error> {
// we never import any header that competes with finalized header
let (finalized_block_number, finalized_block_hash) = storage.finalized_block();
if header.number <= finalized_block_number {
return Err(Error::AncientHeader);
}
// we never import any header with known hash
let hash = header.hash();
if storage.header(&hash).is_some() {
return Err(Error::KnownHeader);
}

Ok((hash, finalized_block_hash))
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
Loading

0 comments on commit c6c4646

Please sign in to comment.