mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 19:21:13 +00:00
decode call after spec_version check (#663)
This commit is contained in:
committed by
Bastian Köcher
parent
44bf84269a
commit
acee5580ca
@@ -134,6 +134,16 @@ pub trait Config<I = DefaultInstance>: frame_system::Config {
|
||||
Origin = <Self as frame_system::Config>::Origin,
|
||||
PostInfo = frame_support::dispatch::PostDispatchInfo,
|
||||
>;
|
||||
/// The type that is used to wrap the `Self::Call` when it is moved over bridge.
|
||||
///
|
||||
/// The idea behind this is to avoid `Call` conversion/decoding until we'll be sure
|
||||
/// that all other stuff (like `spec_version`) is ok. If we would try to decode
|
||||
/// `Call` which has been encoded using previous `spec_version`, then we might end
|
||||
/// up with decoding error, instead of `MessageVersionSpecMismatch`.
|
||||
///
|
||||
/// The `Encode` implementation should match `Encode` implementation of the actual
|
||||
/// `Call`, that (may) have been used to produce signature for `CallOrigin::TargetAccount`.
|
||||
type EncodedCall: Decode + Encode + Into<Result<<Self as Config<I>>::Call, ()>>;
|
||||
/// A type which can be turned into an AccountId from a 256-bit hash.
|
||||
///
|
||||
/// Used when deriving target chain AccountIds from source chain AccountIds.
|
||||
@@ -160,6 +170,8 @@ decl_event!(
|
||||
MessageSignatureMismatch(InstanceId, MessageId),
|
||||
/// Message has been dispatched with given result.
|
||||
MessageDispatched(InstanceId, MessageId, DispatchResult),
|
||||
/// We have failed to decode Call from the message.
|
||||
MessageCallDecodeFailed(InstanceId, MessageId),
|
||||
/// Phantom member, never used. Needed to handle multiple pallet instances.
|
||||
_Dummy(PhantomData<I>),
|
||||
}
|
||||
@@ -174,12 +186,8 @@ decl_module! {
|
||||
}
|
||||
|
||||
impl<T: Config<I>, I: Instance> MessageDispatch<T::MessageId> for Module<T, I> {
|
||||
type Message = MessagePayload<
|
||||
T::SourceChainAccountId,
|
||||
T::TargetChainAccountPublic,
|
||||
T::TargetChainSignature,
|
||||
<T as Config<I>>::Call,
|
||||
>;
|
||||
type Message =
|
||||
MessagePayload<T::SourceChainAccountId, T::TargetChainAccountPublic, T::TargetChainSignature, T::EncodedCall>;
|
||||
|
||||
fn dispatch_weight(message: &Self::Message) -> Weight {
|
||||
message.weight
|
||||
@@ -216,28 +224,6 @@ impl<T: Config<I>, I: Instance> MessageDispatch<T::MessageId> for Module<T, I> {
|
||||
return;
|
||||
}
|
||||
|
||||
// verify weight
|
||||
// (we want passed weight to be at least equal to pre-dispatch weight of the call
|
||||
// because otherwise Calls may be dispatched at lower price)
|
||||
let dispatch_info = message.call.get_dispatch_info();
|
||||
let expected_weight = dispatch_info.weight;
|
||||
if message.weight < expected_weight {
|
||||
frame_support::debug::trace!(
|
||||
"Message {:?}/{:?}: passed weight is too low. Expected at least {:?}, got {:?}",
|
||||
bridge,
|
||||
id,
|
||||
expected_weight,
|
||||
message.weight,
|
||||
);
|
||||
Self::deposit_event(RawEvent::MessageWeightMismatch(
|
||||
bridge,
|
||||
id,
|
||||
expected_weight,
|
||||
message.weight,
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
// prepare dispatch origin
|
||||
let origin_account = match message.origin {
|
||||
CallOrigin::SourceRoot => {
|
||||
@@ -247,8 +233,7 @@ impl<T: Config<I>, I: Instance> MessageDispatch<T::MessageId> for Module<T, I> {
|
||||
target_id
|
||||
}
|
||||
CallOrigin::TargetAccount(source_account_id, target_public, target_signature) => {
|
||||
let digest =
|
||||
account_ownership_digest(message.call.clone(), source_account_id, message.spec_version, bridge);
|
||||
let digest = account_ownership_digest(&message.call, source_account_id, message.spec_version, bridge);
|
||||
|
||||
let target_account = target_public.into_account();
|
||||
if !target_signature.verify(&digest[..], &target_account) {
|
||||
@@ -274,11 +259,43 @@ impl<T: Config<I>, I: Instance> MessageDispatch<T::MessageId> for Module<T, I> {
|
||||
}
|
||||
};
|
||||
|
||||
// now that we have everything checked, let's decode the call
|
||||
let call = match message.call.into() {
|
||||
Ok(call) => call,
|
||||
Err(_) => {
|
||||
frame_support::debug::trace!("Failed to decode Call from message {:?}/{:?}", bridge, id,);
|
||||
Self::deposit_event(RawEvent::MessageCallDecodeFailed(bridge, id));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// verify weight
|
||||
// (we want passed weight to be at least equal to pre-dispatch weight of the call
|
||||
// because otherwise Calls may be dispatched at lower price)
|
||||
let dispatch_info = call.get_dispatch_info();
|
||||
let expected_weight = dispatch_info.weight;
|
||||
if message.weight < expected_weight {
|
||||
frame_support::debug::trace!(
|
||||
"Message {:?}/{:?}: passed weight is too low. Expected at least {:?}, got {:?}",
|
||||
bridge,
|
||||
id,
|
||||
expected_weight,
|
||||
message.weight,
|
||||
);
|
||||
Self::deposit_event(RawEvent::MessageWeightMismatch(
|
||||
bridge,
|
||||
id,
|
||||
expected_weight,
|
||||
message.weight,
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
// finally dispatch message
|
||||
let origin = RawOrigin::Signed(origin_account).into();
|
||||
|
||||
frame_support::debug::trace!("Message being dispatched is: {:?}", &message.call);
|
||||
let dispatch_result = message.call.dispatch(origin);
|
||||
frame_support::debug::trace!("Message being dispatched is: {:?}", &call);
|
||||
let dispatch_result = call.dispatch(origin);
|
||||
let actual_call_weight = extract_actual_weight(&dispatch_result, &dispatch_info);
|
||||
|
||||
frame_support::debug::trace!(
|
||||
@@ -339,7 +356,7 @@ where
|
||||
/// private key. This way, the owner of `source_account_id` on the source chain proves that
|
||||
/// the target chain account private key is also under his control.
|
||||
pub fn account_ownership_digest<Call, AccountId, SpecVersion, BridgeId>(
|
||||
call: Call,
|
||||
call: &Call,
|
||||
source_account_id: AccountId,
|
||||
target_spec_version: SpecVersion,
|
||||
source_instance_id: BridgeId,
|
||||
@@ -471,9 +488,19 @@ mod tests {
|
||||
type TargetChainAccountPublic = TestAccountPublic;
|
||||
type TargetChainSignature = TestSignature;
|
||||
type Call = Call;
|
||||
type EncodedCall = EncodedCall;
|
||||
type AccountIdConverter = AccountIdConverter;
|
||||
}
|
||||
|
||||
#[derive(Decode, Encode)]
|
||||
pub struct EncodedCall(Vec<u8>);
|
||||
|
||||
impl From<EncodedCall> for Result<Call, ()> {
|
||||
fn from(call: EncodedCall) -> Result<Call, ()> {
|
||||
Call::decode(&mut &call.0[..]).map_err(drop)
|
||||
}
|
||||
}
|
||||
|
||||
const TEST_SPEC_VERSION: SpecVersion = 0;
|
||||
const TEST_WEIGHT: Weight = 1_000_000_000;
|
||||
|
||||
@@ -492,7 +519,7 @@ mod tests {
|
||||
spec_version: TEST_SPEC_VERSION,
|
||||
weight: TEST_WEIGHT,
|
||||
origin,
|
||||
call,
|
||||
call: EncodedCall(call.encode()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -617,6 +644,30 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_fail_on_call_decode() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let bridge = b"ethb".to_owned();
|
||||
let id = [0; 4];
|
||||
|
||||
let mut message =
|
||||
prepare_root_message(Call::System(<frame_system::Call<TestRuntime>>::remark(vec![1, 2, 3])));
|
||||
message.call.0 = vec![];
|
||||
|
||||
System::set_block_number(1);
|
||||
CallDispatch::dispatch(bridge, id, Ok(message));
|
||||
|
||||
assert_eq!(
|
||||
System::events(),
|
||||
vec![EventRecord {
|
||||
phase: Phase::Initialization,
|
||||
event: TestEvent::call_dispatch(Event::<TestRuntime>::MessageCallDecodeFailed(bridge, id)),
|
||||
topics: vec![],
|
||||
}],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_dispatch_bridge_message_from_root_origin() {
|
||||
new_test_ext().execute_with(|| {
|
||||
|
||||
Reference in New Issue
Block a user