Skip to content

Commit 141760a

Browse files
Reset fee multiplier and bump telemetry (#1343)
* Reset fee multiplier and bump telemetry * Fix fees and force multiplier reset * Updated the fork block height constant to 23_202_222 * remove pre-commit hook * restore chain specs * add back pre-commit hook * restore old chain specs * refine queue handling * update for CI
1 parent 1910fc8 commit 141760a

File tree

16 files changed

+494
-52
lines changed

16 files changed

+494
-52
lines changed

.github/workflows/static_analysis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ jobs:
5050
shell: bash
5151

5252
- name: Upload SARIF file
53-
if: always()
53+
if: ${{ always() && hashFiles('results.sarif') != '' }}
5454
# v2.24.2
5555
uses: github/codeql-action/upload-sarif@49abf0ba24d0b7953cb586944e918a0b92074c80
5656
with:

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
# The cache for chain data in container
1010
.local
11+
.cargo/*
12+
.cargo*
1113

1214
local-test
1315

BRIDGE.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,16 @@ Easiest way is to use `ethApp.burn` and `erc20App.burn` via polkadot.js/apps
153153

154154
### Ethereum to Sora
155155
Example placed in `bridge-scripts/transfer-eth.sh`
156+
157+
## Operational Runbook
158+
159+
### Clearing Stalled Outgoing Requests
160+
161+
If an outgoing request collects signatures but cannot finalize (for example because the bridge account lacks funds), the pallet keeps the existing approvals. Once the underlying issue is fixed, clear the stale signatures before peers vote again:
162+
163+
1. Use a root session (sudo in polkadot.js/apps) and call the extrinsic
164+
`ethBridge.resetRequestSignatures(network_id, request_hash)`.
165+
2. Confirm the runtime emits `ethBridge.RequestSignaturesCleared`.
166+
3. The request status should remain `Failed` or `Broken`; resubmit or re-run the approval flow as appropriate.
167+
168+
This explicit reset replaces the old behaviour where signatures were wiped automatically on failure, preventing accidental loss of evidence while still giving operators a deterministic recovery step.

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node/Cargo.toml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "framenode"
3-
version = "4.7.0"
3+
version = "4.7.1"
44
authors = ["Parity Technologies <admin@parity.io>"]
55
build = "build.rs"
66
edition = "2021"
@@ -103,8 +103,6 @@ rayon = "=1.10.0"
103103
rayon-core = "=1.12.1"
104104
url = "=2.5.2"
105105
static_init = "=1.0.3"
106-
netlink-proto = "=0.10.0"
107-
if-watch = "=3.2.0"
108106
backtrace = "=0.3.74"
109107
derive_more = "=0.99.18"
110108
scale-info = "=2.11.3"
@@ -127,6 +125,11 @@ pallet-sudo = { git = "https://github.com/sora-xor/substrate.git", branch = "pol
127125

128126
framenode-chain-spec = { path = "chain_spec", features = ["test"] }
129127

128+
# Linux-only netlink dependencies (do not compile on macOS)
129+
[target.'cfg(not(target_os = "macos"))'.dependencies]
130+
netlink-proto = "=0.10.0"
131+
if-watch = "=3.2.0"
132+
130133
[features]
131134
include-real-files = []
132135

pallets/eth-bridge/src/lib.rs

Lines changed: 87 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -393,10 +393,12 @@ pub mod pallet {
393393
>;
394394

395395
type Denominator: common::Denominator<Self::AssetId, Balance>;
396+
/// Maximum number of requests that can be queued per network.
397+
type MaxRequestsPerQueue: Get<u32>;
396398
}
397399

398400
/// The current storage version.
399-
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
401+
const STORAGE_VERSION: StorageVersion = StorageVersion::new(2);
400402

401403
#[pallet::pallet]
402404
#[pallet::generate_store(pub(super) trait Store)]
@@ -1038,6 +1040,36 @@ pub mod pallet {
10381040
SidechainAssetPrecision::<T>::insert(network_id, &asset_id, precision);
10391041
Ok(().into())
10401042
}
1043+
1044+
/// Explicitly clear collected signatures for a request.
1045+
///
1046+
/// Used by operators to recover from failed finalization without implicitly wiping
1047+
/// approvals on-chain.
1048+
#[pallet::call_index(17)]
1049+
#[pallet::weight(EXTRINSIC_FIXED_WEIGHT)]
1050+
pub fn reset_request_signatures(
1051+
origin: OriginFor<T>,
1052+
network_id: BridgeNetworkId<T>,
1053+
hash: H256,
1054+
) -> DispatchResultWithPostInfo {
1055+
ensure_root(origin)?;
1056+
let status =
1057+
RequestStatuses::<T>::get(network_id, &hash).ok_or(Error::<T>::UnknownRequest)?;
1058+
ensure!(
1059+
matches!(
1060+
status,
1061+
RequestStatus::Failed(_) | RequestStatus::Broken(_, _)
1062+
),
1063+
Error::<T>::RequestStatusNotResettable
1064+
);
1065+
ensure!(
1066+
Requests::<T>::contains_key(network_id, &hash),
1067+
Error::<T>::UnknownRequest
1068+
);
1069+
Self::clear_request_signatures(network_id, &hash);
1070+
Self::deposit_event(Event::RequestSignaturesCleared(hash));
1071+
Ok(().into())
1072+
}
10411073
}
10421074

10431075
#[pallet::event]
@@ -1059,6 +1091,8 @@ pub mod pallet {
10591091
CancellationFailed(H256),
10601092
/// The request registration has been failed. [Request Hash, Error]
10611093
RegisterRequestFailed(H256, DispatchError),
1094+
/// Operators cleared stored signatures for a request. [Request Hash]
1095+
RequestSignaturesCleared(H256),
10621096
}
10631097

10641098
#[cfg_attr(test, derive(PartialEq, Eq))]
@@ -1224,6 +1258,10 @@ pub mod pallet {
12241258
Other,
12251259
/// Expected pending request.
12261260
ExpectedPendingRequest,
1261+
/// Too many requests queued for the network.
1262+
RequestsQueueFull,
1263+
/// Signatures can only be reset when a request failed or is broken.
1264+
RequestStatusNotResettable,
12271265
/// Expected Ethereum network.
12281266
ExpectedEthNetwork,
12291267
/// Request was removed and refunded.
@@ -1309,6 +1347,18 @@ pub mod pallet {
13091347
ValueQuery,
13101348
>;
13111349

1350+
/// Outgoing request approval authors.
1351+
#[pallet::storage]
1352+
pub(super) type RequestApprovers<T: Config> = StorageDoubleMap<
1353+
_,
1354+
Twox64Concat,
1355+
BridgeNetworkId<T>,
1356+
Identity,
1357+
H256,
1358+
BTreeSet<T::AccountId>,
1359+
ValueQuery,
1360+
>;
1361+
13121362
/// Requests made by an account.
13131363
#[pallet::storage]
13141364
#[pallet::getter(fn account_requests)]
@@ -1574,11 +1624,17 @@ impl<T: Config> Pallet<T> {
15741624
Error::<T>::DuplicatedRequest
15751625
);
15761626
}
1627+
let queue_len = RequestsQueue::<T>::get(net_id).len();
1628+
ensure!(
1629+
queue_len < T::MaxRequestsPerQueue::get() as usize,
1630+
Error::<T>::RequestsQueueFull
1631+
);
15771632
request.validate()?;
15781633
request.prepare()?;
1634+
Self::clear_request_signatures(net_id, &hash);
15791635
AccountRequests::<T>::mutate(&request.author(), |vec| vec.push((net_id, hash)));
15801636
Requests::<T>::insert(net_id, &hash, request);
1581-
RequestsQueue::<T>::mutate(net_id, |v| v.push(hash));
1637+
RequestsQueue::<T>::mutate(net_id, |queue| queue.push(hash));
15821638
RequestStatuses::<T>::insert(net_id, &hash, RequestStatus::Pending);
15831639
let block_number = frame_system::Pallet::<T>::current_block_number();
15841640
RequestSubmissionHeight::<T>::insert(net_id, &hash, block_number);
@@ -1603,13 +1659,6 @@ impl<T: Config> Pallet<T> {
16031659
!Requests::<T>::contains_key(network_id, incoming_request_hash),
16041660
Error::<T>::RequestIsAlreadyRegistered
16051661
);
1606-
Self::remove_request_from_queue(network_id, &sidechain_tx_hash);
1607-
RequestStatuses::<T>::insert(network_id, sidechain_tx_hash, RequestStatus::Done);
1608-
LoadToIncomingRequestHash::<T>::insert(
1609-
network_id,
1610-
sidechain_tx_hash,
1611-
incoming_request_hash,
1612-
);
16131662
if let Err(e) = incoming_request
16141663
.validate()
16151664
.and_then(|_| incoming_request.prepare())
@@ -1623,12 +1672,24 @@ impl<T: Config> Pallet<T> {
16231672
Self::deposit_event(Event::RegisterRequestFailed(incoming_request_hash, e));
16241673
return Ok(incoming_request_hash);
16251674
}
1675+
let queue_len = RequestsQueue::<T>::get(network_id).len();
1676+
ensure!(
1677+
queue_len < T::MaxRequestsPerQueue::get() as usize,
1678+
Error::<T>::RequestsQueueFull
1679+
);
16261680
Requests::<T>::insert(network_id, &incoming_request_hash, incoming_request);
1627-
RequestsQueue::<T>::mutate(network_id, |v| v.push(incoming_request_hash));
1681+
RequestsQueue::<T>::mutate(network_id, |queue| queue.push(incoming_request_hash));
16281682
RequestStatuses::<T>::insert(network_id, incoming_request_hash, RequestStatus::Pending);
16291683
AccountRequests::<T>::mutate(request_author, |v| {
16301684
v.push((network_id, incoming_request_hash))
16311685
});
1686+
Self::remove_request_from_queue(network_id, &sidechain_tx_hash);
1687+
RequestStatuses::<T>::insert(network_id, sidechain_tx_hash, RequestStatus::Done);
1688+
LoadToIncomingRequestHash::<T>::insert(
1689+
network_id,
1690+
sidechain_tx_hash,
1691+
incoming_request_hash,
1692+
);
16321693
Ok(incoming_request_hash)
16331694
}
16341695

@@ -1669,6 +1730,12 @@ impl<T: Config> Pallet<T> {
16691730
});
16701731
}
16711732

1733+
#[inline]
1734+
pub(crate) fn clear_request_signatures(network_id: T::NetworkId, hash: &H256) {
1735+
RequestApprovals::<T>::remove(network_id, hash);
1736+
RequestApprovers::<T>::remove(network_id, hash);
1737+
}
1738+
16721739
/// Registers new sidechain asset and grants mint permission to the bridge account.
16731740
fn register_sidechain_asset(
16741741
token_address: EthAddress,
@@ -1785,6 +1852,7 @@ impl<T: Config> Pallet<T> {
17851852
}
17861853
info!("Verified request approve {:?}", request_encoded);
17871854
let mut approvals = RequestApprovals::<T>::get(net_id, &hash);
1855+
let mut approvers = RequestApprovers::<T>::get(net_id, &hash);
17881856
let pending_peers_len = if Self::is_additional_signature_needed(net_id, &request) {
17891857
1
17901858
} else {
@@ -1793,8 +1861,17 @@ impl<T: Config> Pallet<T> {
17931861
let need_sigs = majority(Self::peers(net_id).len()) + pending_peers_len;
17941862
let current_status =
17951863
RequestStatuses::<T>::get(net_id, &hash).ok_or(Error::<T>::UnknownRequest)?;
1864+
if !approvers.insert(author.clone()) {
1865+
debug!(
1866+
"Peer {:?} attempted to resubmit approval for {:?}",
1867+
author, hash
1868+
);
1869+
RequestApprovers::<T>::insert(net_id, &hash, &approvers);
1870+
return Ok(None);
1871+
}
17961872
approvals.insert(signature_params);
17971873
RequestApprovals::<T>::insert(net_id, &hash, &approvals);
1874+
RequestApprovers::<T>::insert(net_id, &hash, &approvers);
17981875
if current_status == RequestStatus::Pending && approvals.len() == need_sigs {
17991876
if let Err(err) = request.finalize(hash) {
18001877
error!("Outgoing request finalization failed: {:?}", err);

pallets/eth-bridge/src/macros.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ macro_rules! cancel {
1313
debug_assert!(false, "unexpected cancellation error {:?}", e);
1414
} else {
1515
crate::RequestStatuses::<T>::insert($net_id, $hash, RequestStatus::Failed($err));
16+
Self::clear_request_signatures($net_id, &$hash);
1617
}
1718
};
1819
}

pallets/eth-bridge/src/requests/incoming.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ impl<T: Config> IncomingCancelOutgoingRequest<T> {
477477
hash,
478478
RequestStatus::Failed(Error::<T>::Cancelled.into()),
479479
);
480-
crate::RequestApprovals::<T>::take(net_id, hash);
480+
crate::Pallet::<T>::clear_request_signatures(net_id, hash);
481481
Ok(self.initial_request_hash)
482482
}
483483

0 commit comments

Comments
 (0)