-
Notifications
You must be signed in to change notification settings - Fork 131
Signed extension to refund relayer at the target chain #1657
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
svyatonik
merged 25 commits into
master
from
signed-extension-to-refund-relayer-at-target-chain
Dec 9, 2022
Merged
Changes from 4 commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
8999d63
add utlity pallet to the Millau runtime
svyatonik 89a9268
RefundRelayerForMessagesDeliveryFromParachain prototype
svyatonik 20f90aa
Merge branch 'master' into signed-extension-to-refund-relayer-at-targ…
svyatonik dc35853
done with RefundRelayerForMessagesDeliveryFromParachain::post_dispatch
svyatonik 25e4c09
parse calls
svyatonik e72c723
check batch for obsolete headers/messages
svyatonik 4bb3766
fmt
svyatonik 54e350b
shorten generic arg names + add parachain id generic arg
svyatonik 92cc156
check lane_id
svyatonik 5dc098a
impl all state read functions
svyatonik 29dc969
Merge branch 'master' into signed-extension-to-refund-relayer-at-targ…
svyatonik e581c4d
fix typos from review
svyatonik d067da3
renamed extension + reference issue from TODO
svyatonik 8ab7c9f
tests for pre-dispaytch
svyatonik da8699e
renamed extension source file
svyatonik d2596ab
tests for post-dispatch
svyatonik 3f5e08d
abstract fee calculation
svyatonik 7d31015
clippy
svyatonik 3258a8e
actually fix clippy
svyatonik ecf5f7c
Merge branch 'master' into signed-extension-to-refund-relayer-at-targ…
svyatonik 9b3b5f3
Update bin/runtime-common/src/refund_relayer_extension.rs
svyatonik 557fce8
Update bin/runtime-common/src/refund_relayer_extension.rs
svyatonik 16686ee
Update bin/runtime-common/src/refund_relayer_extension.rs
svyatonik 054d4e8
Update bin/runtime-common/src/refund_relayer_extension.rs
svyatonik 948db62
Merge branch 'master' into signed-extension-to-refund-relayer-at-targ…
svyatonik File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,297 @@ | ||
// Copyright 2021 Parity Technologies (UK) Ltd. | ||
// This file is part of Parity Bridges Common. | ||
|
||
// Parity Bridges Common is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
|
||
// Parity Bridges Common is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
|
||
// You should have received a copy of the GNU General Public License | ||
// along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
//! Signed extension that refunds relayer if he has delivered some new messages. | ||
//! It also refunds transacation cost if the transaction is an `utility.batchAll()` | ||
//! with calls that are: delivering new messsage and all necessary underlying headers | ||
//! (parachain or relay chain). | ||
|
||
use bp_messages::{LaneId, MessageNonce}; | ||
use bp_polkadot_core::parachains::ParaId; | ||
use codec::{Decode, Encode}; | ||
use frame_support::{ | ||
dispatch::{DispatchInfo, Dispatchable, PostDispatchInfo}, | ||
RuntimeDebugNoBound, | ||
}; | ||
use pallet_bridge_parachains::RelayBlockNumber; | ||
use pallet_transaction_payment::OnChargeTransaction; | ||
use scale_info::TypeInfo; | ||
use sp_runtime::{ | ||
traits::{DispatchInfoOf, PostDispatchInfoOf, SignedExtension, Zero}, | ||
transaction_validity::{TransactionValidity, TransactionValidityError, ValidTransaction}, | ||
DispatchResult, FixedPointOperand, | ||
}; | ||
use sp_std::marker::PhantomData; | ||
|
||
// TODO: is it possible to impl it for several bridges at once? Like what we have in | ||
// `BridgeRejectObsoleteHeadersAndMessages`? If it is hard to do now - just submit an issue | ||
|
||
#[derive(Decode, Encode, RuntimeDebugNoBound, TypeInfo)] | ||
#[scale_info(skip_type_params(Runtime, GrandpaInstance, ParachainsInstance, MessagesInstance))] | ||
pub struct RefundRelayerForMessagesDeliveryFromParachain< | ||
Runtime, | ||
GrandpaInstance, | ||
ParachainsInstance, | ||
MessagesInstance, | ||
>(PhantomData<(Runtime, GrandpaInstance, ParachainsInstance, MessagesInstance)>); | ||
|
||
impl<Runtime, GrandpaInstance, ParachainsInstance, MessagesInstance> Clone | ||
for RefundRelayerForMessagesDeliveryFromParachain< | ||
Runtime, | ||
GrandpaInstance, | ||
ParachainsInstance, | ||
MessagesInstance, | ||
> | ||
{ | ||
fn clone(&self) -> Self { | ||
RefundRelayerForMessagesDeliveryFromParachain(PhantomData) | ||
} | ||
} | ||
|
||
impl<Runtime, GrandpaInstance, ParachainsInstance, MessagesInstance> Eq | ||
for RefundRelayerForMessagesDeliveryFromParachain< | ||
Runtime, | ||
GrandpaInstance, | ||
ParachainsInstance, | ||
MessagesInstance, | ||
> | ||
{ | ||
} | ||
|
||
impl<Runtime, GrandpaInstance, ParachainsInstance, MessagesInstance> PartialEq | ||
for RefundRelayerForMessagesDeliveryFromParachain< | ||
Runtime, | ||
GrandpaInstance, | ||
ParachainsInstance, | ||
MessagesInstance, | ||
> | ||
{ | ||
fn eq(&self, _other: &Self) -> bool { | ||
true | ||
} | ||
} | ||
|
||
/// Data that is crafted in `pre_dispatch` method and used at `post_dispatch`. | ||
#[derive(PartialEq)] | ||
pub struct PreDispatchData<AccountId> { | ||
/// Transaction submitter (relayer) account. | ||
pub relayer: AccountId, | ||
/// Type of the call. | ||
pub call_type: CallType, | ||
} | ||
|
||
/// Type of the call that the extension recognizing. | ||
#[derive(Clone, Copy, PartialEq)] | ||
pub enum CallType { | ||
/// Relay chain finality + parachain finality + message delivery calls. | ||
AllFinalityAndDelivery(ExpectedRelayChainState, ExpectedParachainState, MessagesState), | ||
/// Parachain finality + message delivery calls. | ||
ParachainFinalityAndDelivery(ExpectedParachainState, MessagesState), | ||
/// Standalone message delivery call. | ||
Delivery(MessagesState), | ||
} | ||
|
||
impl CallType { | ||
/// Returns the pre-dispatch messages pallet state. | ||
fn pre_dispatch_messages_state(&self) -> MessagesState { | ||
match *self { | ||
Self::AllFinalityAndDelivery(_, _, messages_state) => messages_state, | ||
Self::ParachainFinalityAndDelivery(_, messages_state) => messages_state, | ||
Self::Delivery(messages_state) => messages_state, | ||
} | ||
} | ||
} | ||
|
||
/// Expected post-dispatch state of the relay chain pallet. | ||
#[derive(Clone, Copy, PartialEq)] | ||
pub struct ExpectedRelayChainState { | ||
/// Best known relay chain block number. | ||
pub best_block_number: RelayBlockNumber, | ||
} | ||
|
||
/// Expected post-dispatch state of the parachain pallet. | ||
#[derive(Clone, Copy, PartialEq)] | ||
pub struct ExpectedParachainState { | ||
/// Parachain identifier. | ||
pub para: ParaId, | ||
/// At which relay block the parachain head has been updated? | ||
pub at_relay_block_number: RelayBlockNumber, | ||
} | ||
|
||
/// Pre-dispatch state of messages pallet. | ||
/// | ||
/// This struct is for pre-dispatch state of the pallet, not the expected post-dispatch state. | ||
/// That's because message delivery transaction may deliver some of messages that it brings. | ||
/// If this happens, we consider it "helpful" and refund its cost. If transaction fails to | ||
/// deliver at least one message, it is considered wrong and is not refunded. | ||
#[derive(Clone, Copy, PartialEq)] | ||
pub struct MessagesState { | ||
/// Message lane identifier. | ||
pub lane: LaneId, | ||
/// Best delivered message nonce. | ||
pub last_delivered_nonce: MessageNonce, | ||
} | ||
|
||
// without this typedef rustfmt fails with internal err | ||
type BalanceOf<Runtime> = | ||
<<Runtime as pallet_transaction_payment::Config>::OnChargeTransaction as OnChargeTransaction< | ||
Runtime, | ||
>>::Balance; | ||
|
||
impl<Runtime, GrandpaInstance, ParachainsInstance, MessagesInstance> SignedExtension | ||
for RefundRelayerForMessagesDeliveryFromParachain< | ||
Runtime, | ||
GrandpaInstance, | ||
ParachainsInstance, | ||
MessagesInstance, | ||
> where | ||
Runtime: 'static | ||
+ Send | ||
+ Sync | ||
+ frame_system::Config | ||
+ pallet_transaction_payment::Config | ||
+ pallet_bridge_grandpa::Config<GrandpaInstance> | ||
+ pallet_bridge_parachains::Config<ParachainsInstance> | ||
+ pallet_bridge_messages::Config<MessagesInstance> | ||
+ pallet_bridge_relayers::Config<Reward = BalanceOf<Runtime>>, | ||
GrandpaInstance: 'static + Send + Sync, | ||
ParachainsInstance: 'static + Send + Sync, | ||
MessagesInstance: 'static + Send + Sync, | ||
<Runtime as frame_system::Config>::RuntimeCall: | ||
Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>, | ||
BalanceOf<Runtime>: FixedPointOperand, | ||
{ | ||
const IDENTIFIER: &'static str = "RefundRelayerForMessagesDeliveryFromParachain"; | ||
type AccountId = Runtime::AccountId; | ||
type Call = Runtime::RuntimeCall; | ||
type AdditionalSigned = (); | ||
type Pre = PreDispatchData<Runtime::AccountId>; | ||
|
||
fn additional_signed(&self) -> Result<(), TransactionValidityError> { | ||
Ok(()) | ||
} | ||
|
||
fn validate( | ||
&self, | ||
_who: &Self::AccountId, | ||
_call: &Self::Call, | ||
_info: &DispatchInfoOf<Self::Call>, | ||
_len: usize, | ||
) -> TransactionValidity { | ||
Ok(ValidTransaction::default()) | ||
} | ||
|
||
fn pre_dispatch( | ||
self, | ||
_who: &Self::AccountId, | ||
_call: &Self::Call, | ||
_post_info: &DispatchInfoOf<Self::Call>, | ||
_len: usize, | ||
) -> Result<Self::Pre, TransactionValidityError> { | ||
// TODO: for every call from the batch - call the `BridgesExtension` to ensure that every | ||
// call transaction brings something new and reject obsolete transactions | ||
unimplemented!("TODO") // TODO: return actual call type | ||
} | ||
|
||
fn post_dispatch( | ||
pre: Option<Self::Pre>, | ||
info: &DispatchInfoOf<Self::Call>, | ||
post_info: &PostDispatchInfoOf<Self::Call>, | ||
len: usize, | ||
result: &DispatchResult, | ||
) -> Result<(), TransactionValidityError> { | ||
// we never refund anything if that is not bridge transaction or if it is a bridge | ||
svyatonik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// transaction that we do not support here | ||
let (relayer, call_type) = match pre { | ||
Some(pre) => (pre.relayer, pre.call_type), | ||
None => return Ok(()), | ||
}; | ||
|
||
// we never refund anything if transaction has failed | ||
if result.is_err() { | ||
return Ok(()) | ||
} | ||
|
||
// check if relay chain state has been updated | ||
if let CallType::AllFinalityAndDelivery(expected_relay_chain_state, _, _) = call_type { | ||
let actual_relay_chain_state = relay_chain_state::<Runtime, GrandpaInstance>(); | ||
if actual_relay_chain_state != expected_relay_chain_state { | ||
// we only refund relayer if all calls have updated chain state | ||
return Ok(()) | ||
} | ||
|
||
// there's a conflict between how bridge GRANDPA pallet works and the | ||
// `AllFinalityAndDelivery` transaction. If relay cahin header is mandatory, the GRANDPA | ||
svyatonik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// pallet returns `Pays::No`, because such transaction is mandatory for operating the | ||
// bridge. But `utility.batchAll` transaction always requires payment. But in both cases | ||
// we'll refund relayer - either explicitly here, or using `Pays::No` if he's choosing | ||
// to submit dedicated transaction. | ||
} | ||
|
||
// check if parachain state has been updated | ||
match call_type { | ||
CallType::AllFinalityAndDelivery(_, expected_parachain_state, _) | | ||
CallType::ParachainFinalityAndDelivery(expected_parachain_state, _) => { | ||
let actual_parachain_state = parachain_state::<Runtime, ParachainsInstance>(); | ||
if expected_parachain_state != actual_parachain_state { | ||
// we only refund relayer if all calls have updated chain state | ||
return Ok(()) | ||
} | ||
}, | ||
_ => (), | ||
} | ||
|
||
// check if messages have been delivered | ||
let actual_messages_state = messages_state::<Runtime, MessagesInstance>(); | ||
let pre_dispatch_messages_state = call_type.pre_dispatch_messages_state(); | ||
if actual_messages_state == pre_dispatch_messages_state { | ||
// we only refund relayer if all calls have updated chain state | ||
return Ok(()) | ||
} | ||
|
||
// regarding the tip - refund that happens here (at this side of the bridge) isn't the whole | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's an important thing - we don't refund the whole transaction cost here. Tip is not included in refund. Reasoning is in the code |
||
// relayer compensation. He'll receive some amount at the other side of the bridge. It shall | ||
// (in theory) cover the tip here. Otherwise, if we'll be compensating tip here, some | ||
// malicious relayer may use huge tips, effectively depleting account that pay rewards. The | ||
// cost of this attack is nothing. Hence we use zero as tip here. | ||
let tip = Zero::zero(); | ||
|
||
// compute the relayer reward | ||
let reward = pallet_transaction_payment::Pallet::<Runtime>::compute_actual_fee( | ||
len as _, info, post_info, tip, | ||
); | ||
|
||
// finally - regiater reward in relayers pallet | ||
svyatonik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
pallet_bridge_relayers::Pallet::<Runtime>::register_relayer_reward(&relayer, reward); | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
/// Returns relay chain state that we are interested in. | ||
fn relay_chain_state<Runtime, GrandpaInstance>() -> ExpectedRelayChainState { | ||
unimplemented!("TODO") | ||
} | ||
|
||
/// Returns parachain state that we are interested in. | ||
fn parachain_state<Runtime, ParachainsInstance>() -> ExpectedParachainState { | ||
unimplemented!("TODO") | ||
} | ||
|
||
/// Returns messages state that we are interested in. | ||
fn messages_state<Runtime, MessagesInstance>() -> MessagesState { | ||
unimplemented!("TODO") | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.