Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion frame/ismp-messaging/src/inbound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,24 @@ impl<T: Config> IsmpModule for IsmpModuleCallback<T> {
// An absent value emits nothing. It proves the receipt was not there at `height`,
// which is indistinguishable from asking too early, and there is no event in this
// pallet that can claim a message failed to arrive.
//
// The value is SCALE-decoded, not taken raw. `pallet-ismp` writes the receipt with
// `child::put` (`child_trie.rs:154`), which encodes, so a 32-byte account is stored
// as 33 bytes: a compact length prefix (`0x80`) followed by the account. Emitting
// that verbatim published a relayer nothing could match against an account, and a
// length that belongs to the encoding rather than to the data.
//
// A value that does not decode is dropped rather than emitted raw: a malformed
// relayer is worse than none, and the receipt's mere presence is already the proof
// of delivery — the account is who, not whether.
if let Some(confirmed) = crate::receipts::confirmed_commitment(&response.get.context) {
let want = crate::receipts::request_receipt_key(confirmed);
if let Some(relayer) = response
.values
.iter()
.find(|v| v.key == want)
.and_then(|v| v.value.clone())
.and_then(|v| v.value.as_ref())
.and_then(|raw| alloc::vec::Vec::<u8>::decode(&mut &raw[..]).ok())
{
Pallet::<T>::deposit_event(Event::DeliveryConfirmed {
commitment: confirmed,
Expand Down
6 changes: 6 additions & 0 deletions frame/ismp-messaging/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,12 @@ pub mod pallet {
NoKeysRequested,
/// Exceeded [`Config::MaxGetKeys`].
TooManyKeys,
/// A single GET key exceeded [`Config::MaxBodyLen`].
///
/// Distinct from [`Error::TooManyKeys`]: the count was fine, one key was not. A
/// storage key is short; a long one is a payload wearing a key's name, and the
/// weight charged per key does not cover it.
KeyTooLarge,
/// A GET must name the height to read at, and `0` is never a real one.
///
/// The response handler requires the proof height to equal the requested height
Expand Down
10 changes: 10 additions & 0 deletions frame/ismp-messaging/src/outbound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,16 @@ pub fn get<T: Config>(
Error::<T>::TooManyKeys
);

// And each key's LENGTH, which the count alone does not bound. `dispatch_get`'s weight
// is charged per key, on the stated assumption that a key is a storage key rather than
// a payload (`weights.rs:212-214`); sixteen megabyte-long keys would be priced as
// sixteen short ones. Reuses `MaxBodyLen` for the same reason `context` does — opaque
// bytes we agree to carry, bounded by the one constant that means that.
ensure!(
keys.iter().all(|k| k.len() as u32 <= T::MaxBodyLen::get()),
Error::<T>::KeyTooLarge
);

// `context` travels on the wire and comes back inside `GetResponse.get`, so it is
// attacker-visible bytes whose cost nothing else bounds: `dispatch_get`'s weight is
// measured per KEY, not per context byte. Reuses `MaxBodyLen` because it is the same
Expand Down
Loading
Loading