Skip to content
65 changes: 65 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,40 @@ 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 +2053,12 @@ 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 +2149,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
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
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
15 changes: 14 additions & 1 deletion substrate/frame/support/src/storage/types/nmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::{
EncodeLikeTuple, HasKeyPrefix, HasReversibleKeyPrefix, OptionQuery, QueryKindTrait,
StorageEntryMetadataBuilder, TupleToEncodedIter,
},
KeyGenerator, PrefixIterator, StorageAppend, StorageDecodeLength, StoragePrefixedMap,
KeyGenerator, PrefixIterator, StorageAppend, StorageDecodeLength, StoragePrefixedMap, StorageTryAppend
},
traits::{Get, GetDefault, StorageInfo, StorageInstance},
};
Expand Down Expand Up @@ -338,6 +338,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