Skip to content

Add another condition to the reject-obsolete-parachain-heads extension#1505

Merged
svyatonik merged 6 commits into
masterfrom
fix-bridge-reject-obsolete-parachain-heads-extension
Jul 14, 2022
Merged

Add another condition to the reject-obsolete-parachain-heads extension#1505
svyatonik merged 6 commits into
masterfrom
fix-bridge-reject-obsolete-parachain-heads-extension

Conversation

@svyatonik
Copy link
Copy Markdown
Contributor

@svyatonik svyatonik commented Jul 13, 2022

So currently this extension only rejects transaction if bundled at-relay-chain-block-number is lesser or equal than the existing one. But there's another condition - when parachain head remains the same (i.e. is not updated) in some relay blocks, then the other relay may use different relay chain block to prove it. The extension won't reject the transaction, even though the pallet will ignore the head (since it is the same). This PR handles this situation.

TODOs:

  • test it;
  • add unit tests;
  • cleanup;
  • more traces?

@svyatonik svyatonik added P-Runtime PR-audit-needed A PR has to be audited before going live. labels Jul 13, 2022
@svyatonik svyatonik marked this pull request as ready for review July 14, 2022 11:34
@svyatonik svyatonik merged commit f6f76d0 into master Jul 14, 2022
@svyatonik svyatonik deleted the fix-bridge-reject-obsolete-parachain-heads-extension branch July 14, 2022 12:18
Copy link
Copy Markdown
Collaborator

@serban300 serban300 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good ! I just have 2 comments. If they make sense I would be happy to implement them.

Comment on lines +75 to +102
let (parachain, parachain_head_hash) = parachains.get(0).expect("verified by match condition; qed");

let bundled_relay_block_number = at_relay_block.0;

let best_parachain_head = $crate::BestParaHeads::<$runtime, $instance>::get(parachain);

match best_parachain_head {
Some(best_parachain_head) if best_parachain_head.at_relay_block_number
>= bundled_relay_block_number =>
sp_runtime::transaction_validity::InvalidTransaction::Stale.into(),
>= bundled_relay_block_number => {
log::trace!(
target: $crate::LOG_TARGET,
"Rejecting obsolete parachain-head {:?} transaction: bundled relay block number: \
{:?} best relay block number: {:?}",
parachain,
bundled_relay_block_number,
best_parachain_head.at_relay_block_number,
);
sp_runtime::transaction_validity::InvalidTransaction::Stale.into()
}
Some(best_parachain_head) if best_parachain_head.head_hash == *parachain_head_hash => {
log::trace!(
target: $crate::LOG_TARGET,
"Rejecting obsolete parachain-head {:?} transaction: head hash {:?}",
parachain,
best_parachain_head.head_hash,
);
sp_runtime::transaction_validity::InvalidTransaction::Stale.into()
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This validation looks very similar with the one here. 2 questions:

  1. The equality case for at_relay_block_number seems to be treated differently. Here it is treated as invalid, in update_parachain_head() it's treated as valid. Is this expected ?
  2. Would it make sense to do the same validation in both places ? In this case would it make sense to deduplicate the code ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Imo in both cases at_relay_block_number leads to error, no (I maybe missing something - sorry)?
fn update_parachain_head() {
    ...
    match stored_best_head {
    	Some(stored_best_head) if stored_best_head.at_relay_block_number <= updated_at_relay_block_number => {
            if updated_head_hash == stored_best_head.head_hash {
                return Err(());
            }

            // else: pass
        }
    ...
}

fn validate() {
	...
	match best_parachain_head {
		Some(best_parachain_head) if best_parachain_head.at_relay_block_number
			>= bundled_relay_block_number => Err(()),
		Some(best_parachain_head) if best_parachain_head.head_hash == *parachain_head_hash => Err(())
	...
}

A few notes on that: the other condition (matching hashes) is actually a separate condition, so maybe in the extension it is expressed better, with a dedicated match branch. In general, we shall ignore tx if it supplies head updated at <= stored_best_head.at_relay_block_number and we shall ignore if it has matching hashes. In practice we can't have two blocks with the same hash (crypto-safe hashing) - there's a lot that will break if we'll see it, so second condition may be checked separately or not.

  1. Yeah - if there's a better way to do that, go ahead! Thank you!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Imo in both cases at_relay_block_number leads to error, no (I maybe missing something - sorry)?

From what I understand, if stored_best_head.at_relay_block_number == updated_at_relay_block_number and the hashes are different, for update_parachain_head() it will go to // else: pass, while the extension will trigger an error. So if I understand correctly this case is treated differently now, even if in practice we shouldn't have to blocks with the same hash.

Anyway, I just wanted to make sure if we should have the same check in both places.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah - correct. It is impossible to have two with blocks the same hashes in practice. And if it is the same block (that is already imported), we shall reject/ignore tx

Comment on lines +164 to +172
if parachains.len() != 1 || parachains[0].0 != P::SOURCE_PARACHAIN_PARA_ID {
return Err(SubstrateError::Custom(format!(
"Trying to prove unexpected parachains {:?}. Expected {:?}",
parachains,
P::SOURCE_PARACHAIN_PARA_ID,
)))
}

let parachain = parachains[0];
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I would do something like:

		let parachain = ParaId(P::SOURCE_PARACHAIN_PARA_ID);
		if parachains != &[parachain] {
			return Err(SubstrateError::Custom(format!(
				"Trying to prove unexpected parachains {:?}. Expected {:?}",
				parachains, parachain,
			)))
		}

@serban300
Copy link
Copy Markdown
Collaborator

#1520 was merged. Marking this PR as reviewed.

svyatonik pushed a commit that referenced this pull request Jul 17, 2023
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.31 to 1.0.32.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](dtolnay/thiserror@1.0.31...1.0.32)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
serban300 pushed a commit to serban300/parity-bridges-common that referenced this pull request Mar 27, 2024
paritytech#1505)

* add another condition to the reject-obsolete-parachain-heads extension

* add tracing to obsolete-tx-extensions

* fix tests

* extension_rejects_header_from_new_relay_block_with_same_hash

* fmt

* fix benchmarks
serban300 pushed a commit to serban300/parity-bridges-common that referenced this pull request Apr 8, 2024
paritytech#1505)

* add another condition to the reject-obsolete-parachain-heads extension

* add tracing to obsolete-tx-extensions

* fix tests

* extension_rejects_header_from_new_relay_block_with_same_hash

* fmt

* fix benchmarks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P-Runtime PR-audit-needed A PR has to be audited before going live.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants