Skip to content
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

Implement try_append for StorageNMap #5745

Merged
merged 12 commits into from
Sep 23, 2024
14 changes: 14 additions & 0 deletions prdoc/pr_5745.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json

title: Implement `try_append` for `StorageNMap`

doc:
- audience: Runtime Dev
description: |
This PR introduces the `try_append` api which is available on other storage map types,
but missing on `StorageNMap`.

crates:
- name: frame-support
bump: minor
76 changes: 76 additions & 0 deletions substrate/frame/support/src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1693,6 +1693,46 @@ where
}
}

/// Storage N map that is capable of [`StorageTryAppend`].
pub trait TryAppendNMap<K: KeyGenerator, T: StorageTryAppend<I>, I: Encode> {
/// Try and append the `item` into the storage N map at the given `key`.
///
/// This might fail if bounds are not respected.
fn try_append<
LikeK: EncodeLikeTuple<K::KArg> + TupleToEncodedIter + Clone,
LikeI: EncodeLike<I>,
>(
key: LikeK,
item: LikeI,
) -> Result<(), ()>;
}

impl<K, T, I, StorageNMapT> TryAppendNMap<K, T, I> for StorageNMapT
where
K: KeyGenerator,
T: FullCodec + StorageTryAppend<I>,
I: Encode,
StorageNMapT: generator::StorageNMap<K, T>,
{
fn try_append<
LikeK: EncodeLikeTuple<K::KArg> + TupleToEncodedIter + Clone,
LikeI: EncodeLike<I>,
>(
key: LikeK,
item: LikeI,
) -> Result<(), ()> {
let bound = T::bound();
let current = Self::decode_len(key.clone()).unwrap_or_default();
if current < bound {
let key = Self::storage_n_map_final_key::<K, _>(key);
sp_io::storage::append(&key, item.encode());
Ok(())
} else {
Err(())
}
}
}

/// Returns the storage prefix for a specific pallet name and storage name.
///
/// The storage prefix is `concat(twox_128(pallet_name), twox_128(storage_name))`.
Expand Down Expand Up @@ -2019,6 +2059,17 @@ mod test {
(NMapKey<Twox128, u32>, NMapKey<Twox128, u32>, NMapKey<Twox128, u32>),
u64,
>;
#[crate::storage_alias]
type FooQuadMap = StorageNMap<
Prefix,
(
NMapKey<Twox128, u32>,
NMapKey<Twox128, u32>,
NMapKey<Twox128, u32>,
NMapKey<Twox128, u32>,
),
BoundedVec<u32, ConstU32<7>>,
>;

#[test]
fn contains_prefix_works() {
Expand Down Expand Up @@ -2109,6 +2160,31 @@ mod test {
BoundedVec::<u32, ConstU32<7>>::try_from(vec![4, 5]).unwrap(),
);
});

TestExternalities::default().execute_with(|| {
let bounded: BoundedVec<u32, ConstU32<7>> = vec![1, 2, 3].try_into().unwrap();
FooQuadMap::insert((1, 1, 1, 1), bounded);

assert_ok!(FooQuadMap::try_append((1, 1, 1, 1), 4));
assert_ok!(FooQuadMap::try_append((1, 1, 1, 1), 5));
assert_ok!(FooQuadMap::try_append((1, 1, 1, 1), 6));
assert_ok!(FooQuadMap::try_append((1, 1, 1, 1), 7));
assert_eq!(FooQuadMap::decode_len((1, 1, 1, 1)).unwrap(), 7);
assert!(FooQuadMap::try_append((1, 1, 1, 1), 8).is_err());

// append to a non-existing
assert!(FooQuadMap::get((2, 1, 1, 1)).is_none());
assert_ok!(FooQuadMap::try_append((2, 1, 1, 1), 4));
assert_eq!(
FooQuadMap::get((2, 1, 1, 1)).unwrap(),
BoundedVec::<u32, ConstU32<7>>::try_from(vec![4]).unwrap(),
);
assert_ok!(FooQuadMap::try_append((2, 1, 1, 1), 5));
assert_eq!(
FooQuadMap::get((2, 1, 1, 1)).unwrap(),
BoundedVec::<u32, ConstU32<7>>::try_from(vec![4, 5]).unwrap(),
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

code above uses the same syntax, so i think this is fine

);
});
}

#[crate::storage_alias]
Expand Down
14 changes: 14 additions & 0 deletions substrate/frame/support/src/storage/types/nmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use crate::{
StorageEntryMetadataBuilder, TupleToEncodedIter,
},
KeyGenerator, PrefixIterator, StorageAppend, StorageDecodeLength, StoragePrefixedMap,
StorageTryAppend,
},
traits::{Get, GetDefault, StorageInfo, StorageInstance},
};
Expand Down Expand Up @@ -338,6 +339,19 @@ where
<Self as crate::storage::StorageNMap<Key, Value>>::append(key, item)
}

/// Try and append the given item to the value in the storage.
///
/// Is only available if `Value` of the storage implements [`StorageTryAppend`].
pub fn try_append<KArg, Item, EncodeLikeItem>(key: KArg, item: EncodeLikeItem) -> Result<(), ()>
where
KArg: EncodeLikeTuple<Key::KArg> + TupleToEncodedIter + Clone,
Item: Encode,
EncodeLikeItem: EncodeLike<Item>,
Value: StorageTryAppend<Item>,
{
<Self as crate::storage::TryAppendNMap<Key, Value, Item>>::try_append(key, item)
}

/// Read the length of the storage value without decoding the entire value under the
/// given `key1` and `key2`.
///
Expand Down