diff --git a/Cargo.lock b/Cargo.lock index eee4c44d081..24ac29979d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9555,6 +9555,7 @@ dependencies = [ "bp-runtime", "bp-test-utils", "bridge-runtime-common", + "env_logger", "finality-grandpa", "frame-support", "futures", diff --git a/deployments/bridges/rococo-westend/dashboard/grafana/bridge-rococo-westend-alerts.json b/deployments/bridges/rococo-westend/dashboard/grafana/bridge-rococo-westend-alerts.json index a25a0a79931..2dfa9316e24 100644 --- a/deployments/bridges/rococo-westend/dashboard/grafana/bridge-rococo-westend-alerts.json +++ b/deployments/bridges/rococo-westend/dashboard/grafana/bridge-rococo-westend-alerts.json @@ -88,9 +88,9 @@ } } ], - "updated": "2023-11-22T09:28:26Z", + "updated": "2023-12-08T06:04:42Z", "intervalSeconds": 60, - "version": 128, + "version": 130, "uid": "r41otJp4k", "namespace_uid": "eblDiw17z", "namespace_id": 140, @@ -186,9 +186,9 @@ } } ], - "updated": "2023-11-22T09:28:26Z", + "updated": "2023-12-08T06:04:42Z", "intervalSeconds": 60, - "version": 126, + "version": 128, "uid": "wqmPtJpVz", "namespace_uid": "eblDiw17z", "namespace_id": 140, @@ -284,9 +284,9 @@ } } ], - "updated": "2023-11-22T09:28:26Z", + "updated": "2023-12-08T06:04:42Z", "intervalSeconds": 60, - "version": 122, + "version": 124, "uid": "z4h3pJtVz", "namespace_uid": "eblDiw17z", "namespace_id": 140, @@ -382,9 +382,9 @@ } } ], - "updated": "2023-11-22T09:28:26Z", + "updated": "2023-12-08T06:04:42Z", "intervalSeconds": 60, - "version": 121, + "version": 123, "uid": "Kj_z21t4k", "namespace_uid": "eblDiw17z", "namespace_id": 140, @@ -480,9 +480,9 @@ } } ], - "updated": "2023-11-22T09:28:26Z", + "updated": "2023-12-08T06:04:42Z", "intervalSeconds": 60, - "version": 116, + "version": 118, "uid": "hw_a21pVk", "namespace_uid": "eblDiw17z", "namespace_id": 140, @@ -578,9 +578,9 @@ } } ], - "updated": "2023-11-22T09:28:26Z", + "updated": "2023-12-08T06:04:42Z", "intervalSeconds": 60, - "version": 115, + "version": 117, "uid": "daN62Jt4z", "namespace_uid": "eblDiw17z", "namespace_id": 140, @@ -676,9 +676,9 @@ } } ], - "updated": "2023-11-22T09:28:26Z", + "updated": "2023-12-08T06:04:42Z", "intervalSeconds": 60, - "version": 112, + "version": 114, "uid": "BzBDb1pVz", "namespace_uid": "eblDiw17z", "namespace_id": 140, @@ -774,9 +774,9 @@ } } ], - "updated": "2023-11-22T09:28:26Z", + "updated": "2023-12-08T06:04:42Z", "intervalSeconds": 60, - "version": 111, + "version": 113, "uid": "1W6lb1p4z", "namespace_uid": "eblDiw17z", "namespace_id": 140, @@ -872,9 +872,9 @@ } } ], - "updated": "2023-11-22T09:28:26Z", + "updated": "2023-12-08T06:04:42Z", "intervalSeconds": 60, - "version": 110, + "version": 112, "uid": "Y5Dm-1tVz", "namespace_uid": "eblDiw17z", "namespace_id": 140, @@ -970,9 +970,9 @@ } } ], - "updated": "2023-11-22T09:28:26Z", + "updated": "2023-12-08T06:04:42Z", "intervalSeconds": 60, - "version": 109, + "version": 111, "uid": "yUl4a1tVz", "namespace_uid": "eblDiw17z", "namespace_id": 140, @@ -1111,9 +1111,9 @@ } } ], - "updated": "2023-11-22T09:28:26Z", + "updated": "2023-12-08T06:04:42Z", "intervalSeconds": 60, - "version": 78, + "version": 80, "uid": "R6GKwNA4z", "namespace_uid": "eblDiw17z", "namespace_id": 140, @@ -1252,9 +1252,9 @@ } } ], - "updated": "2023-11-22T09:28:26Z", + "updated": "2023-12-08T06:04:42Z", "intervalSeconds": 60, - "version": 76, + "version": 78, "uid": "rAM1QHAVk", "namespace_uid": "eblDiw17z", "namespace_id": 140, @@ -1283,16 +1283,16 @@ "data": [ { "refId": "B", - "queryType": "range", + "queryType": "instant", "relativeTimeRange": { "from": 600, "to": 0 }, - "datasourceUid": "P03E52D76DFE188C3", + "datasourceUid": "P3572579027B246FE", "model": { "datasource": { "type": "loki", - "uid": "P03E52D76DFE188C3" + "uid": "P3572579027B246FE" }, "editorMode": "code", "expr": "count_over_time({container=\"bridges-common-relay\"} |= `Aborting relay` [1m])", @@ -1300,7 +1300,7 @@ "intervalMs": 1000, "legendFormat": "Aborts per minute", "maxDataPoints": 43200, - "queryType": "range", + "queryType": "instant", "refId": "B" } }, @@ -1392,9 +1392,9 @@ } } ], - "updated": "2023-11-22T09:28:26Z", + "updated": "2023-12-08T06:04:42Z", "intervalSeconds": 60, - "version": 75, + "version": 77, "uid": "TwWPeN04z", "namespace_uid": "eblDiw17z", "namespace_id": 140, @@ -1533,9 +1533,9 @@ } } ], - "updated": "2023-11-22T09:28:26Z", + "updated": "2023-12-08T06:04:42Z", "intervalSeconds": 60, - "version": 70, + "version": 72, "uid": "08-5gv04k", "namespace_uid": "eblDiw17z", "namespace_id": 140, @@ -1674,9 +1674,9 @@ } } ], - "updated": "2023-11-22T09:28:26Z", + "updated": "2023-12-08T06:04:42Z", "intervalSeconds": 60, - "version": 69, + "version": 71, "uid": "Esj2gD0Vk", "namespace_uid": "eblDiw17z", "namespace_id": 140, @@ -1717,7 +1717,7 @@ "uid": "PC96415006F908B67" }, "editorMode": "code", - "expr": "increase(BridgeHubRococo_to_BridgeHubWestend_MessageLane_00000002_lane_state_nonces{domain=\"parity-testnet\", type=~\"source_latest_generated\"}[24h])", + "expr": "max by(container) (increase(BridgeHubRococo_to_BridgeHubWestend_MessageLane_00000002_lane_state_nonces{domain=\"parity-testnet\", type=~\"source_latest_generated\"}[24h]))", "hide": true, "instant": false, "interval": "", @@ -1816,9 +1816,9 @@ } } ], - "updated": "2023-11-22T09:28:26Z", + "updated": "2023-12-08T06:04:42Z", "intervalSeconds": 60, - "version": 41, + "version": 43, "uid": "ry1K5SB4k", "namespace_uid": "eblDiw17z", "namespace_id": 140, @@ -1958,9 +1958,9 @@ } } ], - "updated": "2023-11-22T09:28:26Z", + "updated": "2023-12-08T06:04:42Z", "intervalSeconds": 60, - "version": 39, + "version": 41, "uid": "9YAdEUB4z", "namespace_uid": "eblDiw17z", "namespace_id": 140, diff --git a/modules/grandpa/src/call_ext.rs b/modules/grandpa/src/call_ext.rs index e33fb0c8245..cc7339489bc 100644 --- a/modules/grandpa/src/call_ext.rs +++ b/modules/grandpa/src/call_ext.rs @@ -16,7 +16,8 @@ use crate::{weights::WeightInfo, BridgedBlockNumber, BridgedHeader, Config, Error, Pallet}; use bp_header_chain::{ - justification::GrandpaJustification, ChainWithGrandpa, SubmitFinalityProofInfo, + justification::GrandpaJustification, max_expected_submit_finality_proof_arguments_size, + ChainWithGrandpa, GrandpaConsensusLogReader, SubmitFinalityProofInfo, }; use bp_runtime::{BlockNumberOf, OwnedBridgeModule}; use codec::Encode; @@ -145,28 +146,28 @@ pub(crate) fn submit_finality_proof_info_from_args, I: 'static>( Weight::zero() }; + // check if the `finality_target` is a mandatory header. If so, we are ready to refund larger + // size + let is_mandatory_finality_target = + GrandpaConsensusLogReader::>::find_scheduled_change( + finality_target.digest(), + ) + .is_some(); + // we can estimate extra call size easily, without any additional significant overhead let actual_call_size: u32 = finality_target .encoded_size() .saturating_add(justification.encoded_size()) .saturated_into(); - let max_expected_call_size = max_expected_call_size::(required_precommits); + let max_expected_call_size = max_expected_submit_finality_proof_arguments_size::( + is_mandatory_finality_target, + required_precommits, + ); let extra_size = actual_call_size.saturating_sub(max_expected_call_size); SubmitFinalityProofInfo { block_number, extra_weight, extra_size } } -/// Returns maximal expected size of `submit_finality_proof` call arguments. -fn max_expected_call_size, I: 'static>(required_precommits: u32) -> u32 { - let max_expected_justification_size = - GrandpaJustification::>::max_reasonable_size::( - required_precommits, - ); - - // call arguments are header and justification - T::BridgedChain::MAX_HEADER_SIZE.saturating_add(max_expected_justification_size) -} - #[cfg(test)] mod tests { use crate::{ diff --git a/modules/grandpa/src/mock.rs b/modules/grandpa/src/mock.rs index 17518305dd9..866e5f95a7c 100644 --- a/modules/grandpa/src/mock.rs +++ b/modules/grandpa/src/mock.rs @@ -91,8 +91,8 @@ impl ChainWithGrandpa for TestBridgedChain { const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; const MAX_AUTHORITIES_COUNT: u32 = MAX_BRIDGED_AUTHORITIES; const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; - const MAX_HEADER_SIZE: u32 = 256; - const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64; + const MAX_MANDATORY_HEADER_SIZE: u32 = 256; + const AVERAGE_HEADER_SIZE: u32 = 64; } /// Return test externalities to use in tests. diff --git a/modules/messages/src/tests/mock.rs b/modules/messages/src/tests/mock.rs index 36c5b74c75f..efe23b02251 100644 --- a/modules/messages/src/tests/mock.rs +++ b/modules/messages/src/tests/mock.rs @@ -134,8 +134,8 @@ impl ChainWithGrandpa for BridgedChain { const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "WithBridgedChainBridgeGrandpa"; const MAX_AUTHORITIES_COUNT: u32 = 16; const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 4; - const MAX_HEADER_SIZE: u32 = 4096; - const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 4096; + const AVERAGE_HEADER_SIZE: u32 = 4096; + const MAX_MANDATORY_HEADER_SIZE: u32 = 8192; } impl ChainWithMessages for BridgedChain { diff --git a/modules/parachains/src/mock.rs b/modules/parachains/src/mock.rs index efc02d7508e..866f68cfd8d 100644 --- a/modules/parachains/src/mock.rs +++ b/modules/parachains/src/mock.rs @@ -272,8 +272,8 @@ impl ChainWithGrandpa for TestBridgedChain { const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; const MAX_AUTHORITIES_COUNT: u32 = 16; const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; - const MAX_HEADER_SIZE: u32 = 256; - const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64; + const MAX_MANDATORY_HEADER_SIZE: u32 = 256; + const AVERAGE_HEADER_SIZE: u32 = 64; } #[derive(Debug)] @@ -307,8 +307,8 @@ impl ChainWithGrandpa for OtherBridgedChain { const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; const MAX_AUTHORITIES_COUNT: u32 = 16; const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; - const MAX_HEADER_SIZE: u32 = 256; - const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64; + const MAX_MANDATORY_HEADER_SIZE: u32 = 256; + const AVERAGE_HEADER_SIZE: u32 = 64; } /// Return test externalities to use in tests. diff --git a/modules/relayers/src/mock.rs b/modules/relayers/src/mock.rs index 46d0e742266..b527b460ebb 100644 --- a/modules/relayers/src/mock.rs +++ b/modules/relayers/src/mock.rs @@ -146,8 +146,8 @@ impl ChainWithGrandpa for BridgedUnderlyingChain { const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; const MAX_AUTHORITIES_COUNT: u32 = 16; const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 8; - const MAX_HEADER_SIZE: u32 = 256; - const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 64; + const AVERAGE_HEADER_SIZE: u32 = 64; + const MAX_MANDATORY_HEADER_SIZE: u32 = 128; } impl ChainWithMessages for BridgedUnderlyingChain { diff --git a/modules/xcm-bridge-hub/Cargo.toml b/modules/xcm-bridge-hub/Cargo.toml index 8d167df94b8..235b261fbb3 100644 --- a/modules/xcm-bridge-hub/Cargo.toml +++ b/modules/xcm-bridge-hub/Cargo.toml @@ -2,9 +2,8 @@ name = "pallet-xcm-bridge-hub" description = "Module that adds dynamic bridges/lanes support to XCM infrastucture at the bridge hub." version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +authors.workspace = true +edition.workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.1.5", default-features = false } diff --git a/modules/xcm-bridge-hub/src/exporter.rs b/modules/xcm-bridge-hub/src/exporter.rs index 31982da8c53..39793601f90 100644 --- a/modules/xcm-bridge-hub/src/exporter.rs +++ b/modules/xcm-bridge-hub/src/exporter.rs @@ -74,6 +74,18 @@ where let bridge_destination_universal_location = destination.clone().take().ok_or(SendError::MissingArgument)?; + // prepare the origin relative location + let bridge_origin_relative_location = + bridge_origin_universal_location.relative_to(&T::UniversalLocation::get()); + + // then we are able to compute the lane id used to send messages + let locations = Self::bridge_locations( + Box::new(bridge_origin_relative_location), + Box::new(bridge_destination_universal_location.into()), + ) + .map_err(|_| SendError::NotApplicable)?; + let bridge = Self::bridge(locations.bridge_id).ok_or(SendError::NotApplicable)?; + // check if we are able to route the message. We use existing `HaulBlobExporter` for that. // It will make all required changes and will encode message properly, so that the // `DispatchBlob` at the bridged bridge hub will be able to decode it @@ -85,18 +97,6 @@ where message, )?; - // prepare the origin relative location - let bridge_origin_relative_location = - bridge_origin_universal_location.relative_to(&T::UniversalLocation::get()); - - // then we are able to compute the lane id used to send messages - let locations = Self::bridge_locations( - Box::new(bridge_origin_relative_location), - Box::new(bridge_destination_universal_location.into()), - ) - .map_err(|_| SendError::Unroutable)?; - let bridge = Self::bridge(locations.bridge_id).ok_or(SendError::Unroutable)?; - Ok(((locations.bridge_id, bridge, blob, id), price)) } diff --git a/primitives/chain-kusama/src/lib.rs b/primitives/chain-kusama/src/lib.rs index 3ed9772e9f4..2e9085634d0 100644 --- a/primitives/chain-kusama/src/lib.rs +++ b/primitives/chain-kusama/src/lib.rs @@ -58,8 +58,8 @@ impl ChainWithGrandpa for Kusama { const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; - const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE; - const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; + const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE; + const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; } // The SignedExtension used by Kusama. diff --git a/primitives/chain-polkadot-bulletin/src/lib.rs b/primitives/chain-polkadot-bulletin/src/lib.rs index 0d7bceabfaa..e283490056f 100644 --- a/primitives/chain-polkadot-bulletin/src/lib.rs +++ b/primitives/chain-polkadot-bulletin/src/lib.rs @@ -44,8 +44,8 @@ use sp_runtime::{ // This chain reuses most of Polkadot primitives. pub use bp_polkadot_core::{ AccountAddress, AccountId, Balance, Block, BlockNumber, Hash, Hasher, Header, Nonce, Signature, - SignedBlock, UncheckedExtrinsic, AVERAGE_HEADER_SIZE_IN_JUSTIFICATION, - EXTRA_STORAGE_PROOF_SIZE, MAX_HEADER_SIZE, REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY, + SignedBlock, UncheckedExtrinsic, AVERAGE_HEADER_SIZE, EXTRA_STORAGE_PROOF_SIZE, + MAX_MANDATORY_HEADER_SIZE, REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY, }; /// Maximal number of GRANDPA authorities at Polkadot Bulletin chain. @@ -213,8 +213,8 @@ impl ChainWithGrandpa for PolkadotBulletin { const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; - const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE; - const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; + const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE; + const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; } impl ChainWithMessages for PolkadotBulletin { diff --git a/primitives/chain-polkadot/src/lib.rs b/primitives/chain-polkadot/src/lib.rs index 9087246fae8..93783e153d4 100644 --- a/primitives/chain-polkadot/src/lib.rs +++ b/primitives/chain-polkadot/src/lib.rs @@ -60,8 +60,8 @@ impl ChainWithGrandpa for Polkadot { const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; - const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE; - const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; + const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE; + const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; } /// The SignedExtension used by Polkadot. diff --git a/primitives/chain-rococo/src/lib.rs b/primitives/chain-rococo/src/lib.rs index fe0ba92ec64..32436a221df 100644 --- a/primitives/chain-rococo/src/lib.rs +++ b/primitives/chain-rococo/src/lib.rs @@ -58,8 +58,8 @@ impl ChainWithGrandpa for Rococo { const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; - const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE; - const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; + const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE; + const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; } // The SignedExtension used by Rococo. diff --git a/primitives/chain-westend/src/lib.rs b/primitives/chain-westend/src/lib.rs index 0a8517b6414..0b001885121 100644 --- a/primitives/chain-westend/src/lib.rs +++ b/primitives/chain-westend/src/lib.rs @@ -58,8 +58,8 @@ impl ChainWithGrandpa for Westend { const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; - const MAX_HEADER_SIZE: u32 = MAX_HEADER_SIZE; - const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; + const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE; + const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; } // The SignedExtension used by Westend. diff --git a/primitives/header-chain/src/justification/mod.rs b/primitives/header-chain/src/justification/mod.rs index 72a5f68918d..b32d8bdb5f1 100644 --- a/primitives/header-chain/src/justification/mod.rs +++ b/primitives/header-chain/src/justification/mod.rs @@ -82,8 +82,8 @@ impl GrandpaJustification { .saturating_add(BlockNumberOf::::max_encoded_len().saturated_into()) .saturating_add(HashOf::::max_encoded_len().saturated_into()); - let max_expected_votes_ancestries_size = C::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY - .saturating_mul(C::AVERAGE_HEADER_SIZE_IN_JUSTIFICATION); + let max_expected_votes_ancestries_size = + C::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY.saturating_mul(C::AVERAGE_HEADER_SIZE); // justification is round number (u64=8b), a signed GRANDPA commit and the // `votes_ancestries` vector diff --git a/primitives/header-chain/src/lib.rs b/primitives/header-chain/src/lib.rs index 434e7d995dc..d3d372718fb 100644 --- a/primitives/header-chain/src/lib.rs +++ b/primitives/header-chain/src/lib.rs @@ -24,8 +24,8 @@ use crate::justification::{ GrandpaJustification, JustificationVerificationContext, JustificationVerificationError, }; use bp_runtime::{ - BasicOperatingMode, Chain, HashOf, HasherOf, StorageProofError, UnderlyingChainProvider, - UnverifiedStorageProof, VerifiedStorageProof, + BasicOperatingMode, Chain, HashOf, HasherOf, HeaderOf, StorageProofError, + UnderlyingChainProvider, UnverifiedStorageProof, VerifiedStorageProof, }; use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen}; use core::{clone::Clone, cmp::Eq, default::Default, fmt::Debug}; @@ -270,23 +270,28 @@ pub trait ChainWithGrandpa: Chain { /// to submitter. const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32; - /// Maximal size of the chain header. The header may be the header that enacts new GRANDPA - /// authorities set (so it has large digest inside). + /// Maximal size of the mandatory chain header. Mandatory header is the header that enacts new + /// GRANDPA authorities set (so it has large digest inside). /// /// This isn't a strict limit. The relay may submit larger headers and the pallet will accept /// the call. The limit is only used to compute maximal refund amount and doing calls which /// exceed the limit, may be costly to submitter. - const MAX_HEADER_SIZE: u32; + const MAX_MANDATORY_HEADER_SIZE: u32; - /// Average size of the chain header from justification ancestry. We don't expect to see there - /// headers that change GRANDPA authorities set (GRANDPA will probably be able to finalize at - /// least one additional header per session on non test chains), so this is average size of - /// headers that aren't changing the set. + /// Average size of the chain header. We don't expect to see there headers that change GRANDPA + /// authorities set (GRANDPA will probably be able to finalize at least one additional header + /// per session on non test chains), so this is average size of headers that aren't changing the + /// set. /// - /// This isn't a strict limit. The relay may submit justifications with larger headers in its - /// ancestry and the pallet will accept the call. The limit is only used to compute maximal - /// refund amount and doing calls which exceed the limit, may be costly to submitter. - const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32; + /// This isn't a strict limit. The relay may submit justifications with larger headers and the + /// pallet will accept the call. However, if the total size of all `submit_finality_proof` + /// arguments exceeds the maximal size, computed using this average size, relayer will only get + /// partial refund. + /// + /// We expect some headers on production chains that are above this size. But they are rare and + /// if rellayer cares about its profitability, we expect it'll select other headers for + /// submission. + const AVERAGE_HEADER_SIZE: u32; } impl ChainWithGrandpa for T @@ -299,7 +304,70 @@ where const MAX_AUTHORITIES_COUNT: u32 = ::MAX_AUTHORITIES_COUNT; const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = ::REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY; - const MAX_HEADER_SIZE: u32 = ::MAX_HEADER_SIZE; - const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = - ::AVERAGE_HEADER_SIZE_IN_JUSTIFICATION; + const MAX_MANDATORY_HEADER_SIZE: u32 = + ::MAX_MANDATORY_HEADER_SIZE; + const AVERAGE_HEADER_SIZE: u32 = ::AVERAGE_HEADER_SIZE; +} + +/// Returns maximal expected size of `submit_finality_proof` call arguments. +pub fn max_expected_submit_finality_proof_arguments_size( + is_mandatory_finality_target: bool, + precommits: u32, +) -> u32 { + let max_expected_justification_size = + GrandpaJustification::>::max_reasonable_size::(precommits); + + // call arguments are header and justification + let max_expected_finality_target_size = if is_mandatory_finality_target { + C::MAX_MANDATORY_HEADER_SIZE + } else { + C::AVERAGE_HEADER_SIZE + }; + max_expected_finality_target_size.saturating_add(max_expected_justification_size) +} + +#[cfg(test)] +mod tests { + use super::*; + use bp_runtime::ChainId; + use frame_support::weights::Weight; + use sp_runtime::{testing::H256, traits::BlakeTwo256, MultiSignature, StateVersion}; + + struct TestChain; + + impl Chain for TestChain { + const ID: ChainId = *b"test"; + const STATE_VERSION: StateVersion = StateVersion::V1; + type BlockNumber = u32; + type Hash = H256; + type Hasher = BlakeTwo256; + type Header = sp_runtime::generic::Header; + type AccountId = u64; + type Balance = u64; + type Nonce = u64; + type Signature = MultiSignature; + + fn max_extrinsic_size() -> u32 { + 0 + } + fn max_extrinsic_weight() -> Weight { + Weight::zero() + } + } + + impl ChainWithGrandpa for TestChain { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "Test"; + const MAX_AUTHORITIES_COUNT: u32 = 128; + const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 2; + const MAX_MANDATORY_HEADER_SIZE: u32 = 100_000; + const AVERAGE_HEADER_SIZE: u32 = 1_024; + } + + #[test] + fn max_expected_submit_finality_proof_arguments_size_respects_mandatory_argument() { + assert!( + max_expected_submit_finality_proof_arguments_size::(true, 100) > + max_expected_submit_finality_proof_arguments_size::(false, 100), + ); + } } diff --git a/primitives/polkadot-core/src/lib.rs b/primitives/polkadot-core/src/lib.rs index 4fec1e3660d..6f0360e5db9 100644 --- a/primitives/polkadot-core/src/lib.rs +++ b/primitives/polkadot-core/src/lib.rs @@ -67,30 +67,28 @@ pub const MAX_AUTHORITIES_COUNT: u32 = 1_256; /// /// See [`bp-header-chain::ChainWithGrandpa`] for more details. /// -/// This value comes from recent (February, 2023) Kusama and Polkadot headers. There are no +/// This value comes from recent (December, 2023) Kusama and Polkadot headers. There are no /// justifications with any additional headers in votes ancestry, so reasonable headers may /// be set to zero. But we assume that there may be small GRANDPA lags, so we're leaving some /// reserve here. pub const REASONABLE_HEADERS_IN_JUSTIFICATON_ANCESTRY: u32 = 2; -/// Approximate average header size in `votes_ancestries` field of justification on Polkadot-like +/// Average header size in `votes_ancestries` field of justification on Polkadot-like /// chains. /// /// See [`bp-header-chain::ChainWithGrandpa`] for more details. /// -/// This value comes from recent (February, 2023) Kusama headers. Average is `336` there, but some -/// non-mandatory headers has size `40kb` (they contain the BABE epoch descriptor with all -/// authorities - just like our mandatory header). Since we assume `2` headers in justification -/// votes ancestry, let's set average header to `40kb / 2`. -pub const AVERAGE_HEADER_SIZE_IN_JUSTIFICATION: u32 = 20 * 1024; +/// This value comes from recent (December, 2023) Kusama headers. Most of headers are `327` bytes +/// there, but let's have some reserve and make it 1024. +pub const AVERAGE_HEADER_SIZE: u32 = 1024; /// Approximate maximal header size on Polkadot-like chains. /// /// See [`bp-header-chain::ChainWithGrandpa`] for more details. /// -/// This value comes from recent (February, 2023) Kusama headers. Maximal header is a mandatory -/// header. In its SCALE-encoded form it is `80348` bytes. Let's have some reserve here. -pub const MAX_HEADER_SIZE: u32 = 90_000; +/// This value comes from recent (December, 2023) Kusama headers. Maximal header is a mandatory +/// header. In its SCALE-encoded form it is `113407` bytes. Let's have some reserve here. +pub const MAX_MANDATORY_HEADER_SIZE: u32 = 120 * 1024; /// Number of extra bytes (excluding size of storage value itself) of storage proof, built at /// Polkadot-like chain. This mostly depends on number of entries in the storage trie. diff --git a/primitives/xcm-bridge-hub/Cargo.toml b/primitives/xcm-bridge-hub/Cargo.toml index 7b2ea491abc..58ae340725a 100644 --- a/primitives/xcm-bridge-hub/Cargo.toml +++ b/primitives/xcm-bridge-hub/Cargo.toml @@ -2,8 +2,8 @@ name = "bp-xcm-bridge-hub" description = "Primitives of the xcm-bridge-hub pallet." version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" +authors.workspace = true +edition.workspace = true license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] diff --git a/relays/bin-substrate/Cargo.toml b/relays/bin-substrate/Cargo.toml index 51155cb651d..7793d7ea2cc 100644 --- a/relays/bin-substrate/Cargo.toml +++ b/relays/bin-substrate/Cargo.toml @@ -10,6 +10,7 @@ anyhow = "1.0" async-std = "1.9.0" async-trait = "0.1" codec = { package = "parity-scale-codec", version = "3.1.5" } +env_logger = "0.10" futures = "0.3.29" hex = "0.4" log = "0.4.20" diff --git a/relays/lib-substrate-relay/src/finality_base/engine.rs b/relays/lib-substrate-relay/src/finality_base/engine.rs index f3ddd108595..72df3ebb145 100644 --- a/relays/lib-substrate-relay/src/finality_base/engine.rs +++ b/relays/lib-substrate-relay/src/finality_base/engine.rs @@ -23,8 +23,9 @@ use bp_header_chain::{ verify_and_optimize_justification, GrandpaEquivocationsFinder, GrandpaJustification, JustificationVerificationContext, }, - AuthoritySet, ConsensusLogReader, FinalityProof, FindEquivocations, GrandpaConsensusLogReader, - HeaderFinalityInfo, HeaderGrandpaInfo, StoredHeaderGrandpaInfo, + max_expected_submit_finality_proof_arguments_size, AuthoritySet, ConsensusLogReader, + FinalityProof, FindEquivocations, GrandpaConsensusLogReader, HeaderFinalityInfo, + HeaderGrandpaInfo, StoredHeaderGrandpaInfo, }; use bp_runtime::{BasicOperatingMode, HeaderIdProvider, OperatingMode}; use codec::{Decode, Encode}; @@ -36,9 +37,22 @@ use relay_substrate_client::{ }; use sp_consensus_grandpa::{AuthorityList as GrandpaAuthoritiesSet, GRANDPA_ENGINE_ID}; use sp_core::{storage::StorageKey, Bytes}; -use sp_runtime::{scale_info::TypeInfo, traits::Header, ConsensusEngineId}; +use sp_runtime::{scale_info::TypeInfo, traits::Header, ConsensusEngineId, SaturatedConversion}; use std::{fmt::Debug, marker::PhantomData}; +/// Result of checking maximal expected call size. +pub enum MaxExpectedCallSizeCheck { + /// Size is ok and call will be refunded. + Ok, + /// The call size exceeds the maximal expected and relayer will only get partial refund. + Exceeds { + /// Actual call size. + call_size: u32, + /// Maximal expected call size. + max_call_size: u32, + }, +} + /// Finality engine, used by the Substrate chain. #[async_trait] pub trait Engine: Send { @@ -111,6 +125,14 @@ pub trait Engine: Send { proof: &mut Self::FinalityProof, ) -> Result<(), SubstrateError>; + /// Checks whether the given `header` and its finality `proof` fit the maximal expected + /// call size limit. If result is `MaxExpectedCallSizeCheck::Exceeds { .. }`, this + /// submission won't be fully refunded and relayer will spend its own funds on that. + fn check_max_expected_call_size( + header: &C::Header, + proof: &Self::FinalityProof, + ) -> MaxExpectedCallSizeCheck; + /// Prepare initialization data for the finality bridge pallet. async fn prepare_initialization_data( client: impl Client, @@ -224,6 +246,24 @@ impl Engine for Grandpa { }) } + fn check_max_expected_call_size( + header: &C::Header, + proof: &Self::FinalityProof, + ) -> MaxExpectedCallSizeCheck { + let is_mandatory = Self::ConsensusLogReader::schedules_authorities_change(header.digest()); + let call_size: u32 = + header.encoded_size().saturating_add(proof.encoded_size()).saturated_into(); + let max_call_size = max_expected_submit_finality_proof_arguments_size::( + is_mandatory, + proof.commit.precommits.len().saturated_into(), + ); + if call_size > max_call_size { + MaxExpectedCallSizeCheck::Exceeds { call_size, max_call_size } + } else { + MaxExpectedCallSizeCheck::Ok + } + } + /// Prepare initialization data for the GRANDPA verifier pallet. async fn prepare_initialization_data( source_client: impl Client, diff --git a/relays/lib-substrate-relay/src/on_demand/headers.rs b/relays/lib-substrate-relay/src/on_demand/headers.rs index ac88f432049..fbab3c8a77a 100644 --- a/relays/lib-substrate-relay/src/on_demand/headers.rs +++ b/relays/lib-substrate-relay/src/on_demand/headers.rs @@ -16,14 +16,16 @@ //! On-demand Substrate -> Substrate header finality relay. -use crate::finality::SubmitFinalityProofCallBuilder; +use crate::{ + finality::SubmitFinalityProofCallBuilder, finality_base::engine::MaxExpectedCallSizeCheck, +}; use async_std::sync::{Arc, Mutex}; use async_trait::async_trait; use bp_header_chain::ConsensusLogReader; use bp_runtime::HeaderIdProvider; use futures::{select, FutureExt}; -use num_traits::{One, Zero}; +use num_traits::{One, Saturating, Zero}; use sp_runtime::traits::Header; use finality_relay::{FinalitySyncParams, TargetClient as FinalityTargetClient}; @@ -142,30 +144,61 @@ impl< &self, required_header: BlockNumberOf, ) -> Result<(HeaderIdOf, Vec>), SubstrateError> { - // first find proper header (either `required_header`) or its descendant - let finality_source = - SubstrateFinalitySource::::new(self.source_client.clone(), None); - let (header, mut proof) = finality_source.prove_block_finality(required_header).await?; - let header_id = header.id(); + const MAX_ITERATIONS: u32 = 4; + let mut iterations = 0; + let mut current_required_header = required_header; + loop { + // first find proper header (either `current_required_header`) or its descendant + let finality_source = + SubstrateFinalitySource::::new(self.source_client.clone(), None); + let (header, mut proof) = + finality_source.prove_block_finality(current_required_header).await?; + let header_id = header.id(); + + // optimize justification before including it into the call + P::FinalityEngine::optimize_proof(&self.target_client, &header, &mut proof).await?; + + // now we have the header and its proof, but we want to minimize our losses, so let's + // check if we'll get the full refund for submitting this header + let check_result = P::FinalityEngine::check_max_expected_call_size(&header, &proof); + if let MaxExpectedCallSizeCheck::Exceeds { call_size, max_call_size } = check_result { + iterations += 1; + current_required_header = header_id.number().saturating_add(One::one()); + if iterations < MAX_ITERATIONS { + log::debug!( + target: "bridge", + "[{}] Requested to prove {} head {:?}. Selected to prove {} head {:?}. But it is too large: {} vs {}. \ + Going to select next header", + self.relay_task_name, + P::SourceChain::NAME, + required_header, + P::SourceChain::NAME, + header_id, + call_size, + max_call_size, + ); - // optimize justification before including it into the call - P::FinalityEngine::optimize_proof(&self.target_client, &header, &mut proof).await?; + continue; + } + } - log::debug!( - target: "bridge", - "[{}] Requested to prove {} head {:?}. Selected to prove {} head {:?}", - self.relay_task_name, - P::SourceChain::NAME, - required_header, - P::SourceChain::NAME, - header_id, - ); + log::debug!( + target: "bridge", + "[{}] Requested to prove {} head {:?}. Selected to prove {} head {:?} (after {} iterations)", + self.relay_task_name, + P::SourceChain::NAME, + required_header, + P::SourceChain::NAME, + header_id, + iterations, + ); - // and then craft the submit-proof call - let call = - P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call(header, proof); + // and then craft the submit-proof call + let call = + P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call(header, proof); - Ok((header_id, vec![call])) + return Ok((header_id, vec![call])); + } } }