From df4fba5bfa40728af0b802b7598fef5d1f9335ca Mon Sep 17 00:00:00 2001 From: Sergei Pepyakin Date: Mon, 13 May 2019 20:56:01 +0200 Subject: [PATCH] Light client friendly events (#2491) * Sketch of indexed events. * Get EventIndex by holding another variable. * Add some docs. * Use DoubleMap to store reverse topic index * Implement StorageDoubleMap::append * Use append for EventTopics. * Refactor. * Avoid `mutate` * Docs. * Add topics to EventRecord * Update tests. * Rebuild. * Bump version. * Event topics test. * Mix in BlockNumber to distinguish updates * Fix srml-system test. * Post merge fixes. * Comments/TODO. --- node/executor/src/lib.rs | 45 +++-- srml/contract/src/tests.rs | 38 +++- srml/council/src/motions.rs | 30 ++- srml/grandpa/src/tests.rs | 2 + srml/support/src/lib.rs | 31 +++ srml/support/src/storage/mod.rs | 28 +++ .../support/src/storage/unhashed/generator.rs | 20 ++ srml/system/src/lib.rs | 187 ++++++++++++++++-- 8 files changed, 325 insertions(+), 56 deletions(-) diff --git a/node/executor/src/lib.rs b/node/executor/src/lib.rs index fac04de6e7f91..c6903529c4cd1 100644 --- a/node/executor/src/lib.rs +++ b/node/executor/src/lib.rs @@ -490,7 +490,8 @@ mod tests { assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: Event::system(system::Event::ExtrinsicSuccess) + event: Event::system(system::Event::ExtrinsicSuccess), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(1), @@ -499,23 +500,28 @@ mod tests { bob().into(), 69, 0 - )) + )), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(1), - event: Event::system(system::Event::ExtrinsicSuccess) + event: Event::system(system::Event::ExtrinsicSuccess), + topics: vec![], }, EventRecord { phase: Phase::Finalization, - event: Event::treasury(treasury::RawEvent::Spending(0)) + event: Event::treasury(treasury::RawEvent::Spending(0)), + topics: vec![], }, EventRecord { phase: Phase::Finalization, - event: Event::treasury(treasury::RawEvent::Burnt(0)) + event: Event::treasury(treasury::RawEvent::Burnt(0)), + topics: vec![], }, EventRecord { phase: Phase::Finalization, - event: Event::treasury(treasury::RawEvent::Rollover(0)) + event: Event::treasury(treasury::RawEvent::Rollover(0)), + topics: vec![], }, ]); }); @@ -537,7 +543,8 @@ mod tests { assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: Event::system(system::Event::ExtrinsicSuccess) + event: Event::system(system::Event::ExtrinsicSuccess), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(1), @@ -548,11 +555,13 @@ mod tests { 5, 0 ) - ) + ), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(1), - event: Event::system(system::Event::ExtrinsicSuccess) + event: Event::system(system::Event::ExtrinsicSuccess), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(2), @@ -563,27 +572,33 @@ mod tests { 15, 0 ) - ) + ), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(2), - event: Event::system(system::Event::ExtrinsicSuccess) + event: Event::system(system::Event::ExtrinsicSuccess), + topics: vec![], }, EventRecord { phase: Phase::Finalization, - event: Event::treasury(treasury::RawEvent::Spending(0)) + event: Event::treasury(treasury::RawEvent::Spending(0)), + topics: vec![], }, EventRecord { phase: Phase::Finalization, - event: Event::treasury(treasury::RawEvent::Burnt(0)) + event: Event::treasury(treasury::RawEvent::Burnt(0)), + topics: vec![], }, EventRecord { phase: Phase::Finalization, - event: Event::treasury(treasury::RawEvent::Rollover(0)) + event: Event::treasury(treasury::RawEvent::Rollover(0)), + topics: vec![], }, EventRecord { phase: Phase::Finalization, - event: Event::session(session::RawEvent::NewSession(1)) + event: Event::session(session::RawEvent::NewSession(1)), + topics: vec![], }, ]); }); diff --git a/srml/contract/src/tests.rs b/srml/contract/src/tests.rs index a428018f8941f..149dc4b26bc0e 100644 --- a/srml/contract/src/tests.rs +++ b/srml/contract/src/tests.rs @@ -363,28 +363,34 @@ fn instantiate_and_call_and_deposit_event() { EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances(balances::RawEvent::NewAccount(1, 1_000_000)), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::contract(RawEvent::CodeStored(HASH_RETURN_FROM_START_FN.into())), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances( balances::RawEvent::NewAccount(BOB, 100) - ) + ), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)) + event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Contract(BOB, vec![1, 2, 3, 4])) + event: MetaEvent::contract(RawEvent::Contract(BOB, vec![1, 2, 3, 4])), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)) + event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)), + topics: vec![], } ]); @@ -434,10 +440,12 @@ fn dispatch_call() { EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances(balances::RawEvent::NewAccount(1, 1_000_000)), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::contract(RawEvent::CodeStored(HASH_DISPATCH_CALL.into())), + topics: vec![], }, ]); @@ -461,24 +469,29 @@ fn dispatch_call() { EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances(balances::RawEvent::NewAccount(1, 1_000_000)), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::contract(RawEvent::CodeStored(HASH_DISPATCH_CALL.into())), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances( balances::RawEvent::NewAccount(BOB, 100) - ) + ), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)) + event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)) + event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)), + topics: vec![], }, // Dispatching the call. @@ -486,19 +499,22 @@ fn dispatch_call() { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances( balances::RawEvent::NewAccount(CHARLIE, 50) - ) + ), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances( balances::RawEvent::Transfer(BOB, CHARLIE, 50, 0) - ) + ), + topics: vec![], }, // Event emited as a result of dispatch. EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Dispatched(BOB, true)) + event: MetaEvent::contract(RawEvent::Dispatched(BOB, true)), + topics: vec![], } ]); }, @@ -644,10 +660,12 @@ fn set_rent_hash_and_code() { EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances(balances::RawEvent::NewAccount(1, 1_000_000)), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::contract(RawEvent::CodeStored(HASH_SET_RENT.into())), + topics: vec![], }, ]); } diff --git a/srml/council/src/motions.rs b/srml/council/src/motions.rs index 393ebce4f890d..e560fadb9c5a4 100644 --- a/srml/council/src/motions.rs +++ b/srml/council/src/motions.rs @@ -234,7 +234,8 @@ mod tests { assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Proposed(1, 0, hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), 3)) + event: OuterEvent::motions(RawEvent::Proposed(1, 0, hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), 3)), + topics: vec![], } ]); }); @@ -287,11 +288,13 @@ mod tests { assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Proposed(1, 0, hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), 2)) + event: OuterEvent::motions(RawEvent::Proposed(1, 0, hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), 2)), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Voted(1, hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), false, 0, 1)) + event: OuterEvent::motions(RawEvent::Voted(1, hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), false, 0, 1)), + topics: vec![], } ]); }); @@ -309,15 +312,18 @@ mod tests { assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Proposed(1, 0, hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), 3)) + event: OuterEvent::motions(RawEvent::Proposed(1, 0, hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), 3)), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Voted(2, hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), false, 1, 1)) + event: OuterEvent::motions(RawEvent::Voted(2, hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), false, 1, 1)), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Disapproved(hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into())) + event: OuterEvent::motions(RawEvent::Disapproved(hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into())), + topics: vec![], } ]); }); @@ -335,19 +341,23 @@ mod tests { assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Proposed(1, 0, hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), 2)) + event: OuterEvent::motions(RawEvent::Proposed(1, 0, hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), 2)), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Voted(2, hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), true, 2, 0)) + event: OuterEvent::motions(RawEvent::Voted(2, hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), true, 2, 0)), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Approved(hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into())) + event: OuterEvent::motions(RawEvent::Approved(hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into())), + topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: OuterEvent::motions(RawEvent::Executed(hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), false)) + event: OuterEvent::motions(RawEvent::Executed(hex!["cd0b662a49f004093b80600415cf4126399af0d27ed6c185abeb1469c17eb5bf"].into(), false)), + topics: vec![], } ]); }); diff --git a/srml/grandpa/src/tests.rs b/srml/grandpa/src/tests.rs index 3050b6a572e0d..560766f8c62ad 100644 --- a/srml/grandpa/src/tests.rs +++ b/srml/grandpa/src/tests.rs @@ -47,6 +47,7 @@ fn authorities_change_logged() { EventRecord { phase: Phase::Finalization, event: RawEvent::NewAuthorities(vec![(4, 1), (5, 1), (6, 1)]).into(), + topics: vec![], }, ]); }); @@ -77,6 +78,7 @@ fn authorities_change_logged_after_delay() { EventRecord { phase: Phase::Finalization, event: RawEvent::NewAuthorities(vec![(4, 1), (5, 1), (6, 1)]).into(), + topics: vec![], }, ]); }); diff --git a/srml/support/src/lib.rs b/srml/support/src/lib.rs index be8d4fb73a517..9d1bbff73a178 100644 --- a/srml/support/src/lib.rs +++ b/srml/support/src/lib.rs @@ -238,6 +238,7 @@ mod tests { pub DataDM config(test_config) build(|_| vec![(15u32, 16u32, 42u64)]): double_map hasher(twox_64_concat) u32, blake2_256(u32) => u64; pub GenericDataDM: double_map T::BlockNumber, twox_128(T::BlockNumber) => T::BlockNumber; pub GenericData2DM: double_map T::BlockNumber, twox_256(T::BlockNumber) => Option; + pub AppendableDM: double_map u32, blake2_256(T::BlockNumber) => Vec; } } @@ -367,6 +368,21 @@ mod tests { assert_eq!(DoubleMap::get(key1, key2+1), 0u64); assert_eq!(DoubleMap::get(key1+1, key2), 4u64); assert_eq!(DoubleMap::get(key1+1, key2+1), 4u64); + + }); + } + + #[test] + fn double_map_append_should_work() { + with_externalities(&mut new_test_ext(), || { + type DoubleMap = AppendableDM; + + let key1 = 17u32; + let key2 = 18u32; + + DoubleMap::insert(key1, key2, vec![1]); + DoubleMap::append(key1, key2, &[2, 3]); + assert_eq!(DoubleMap::get(key1, key2), vec![1, 2, 3]); }); } @@ -453,6 +469,21 @@ mod tests { ), documentation: DecodeDifferent::Encode(&[]), }, + StorageFunctionMetadata { + name: DecodeDifferent::Encode("AppendableDM"), + modifier: StorageFunctionModifier::Default, + ty: StorageFunctionType::DoubleMap{ + hasher: StorageHasher::Blake2_256, + key1: DecodeDifferent::Encode("u32"), + key2: DecodeDifferent::Encode("T::BlockNumber"), + value: DecodeDifferent::Encode("Vec"), + key2_hasher: DecodeDifferent::Encode("blake2_256"), + }, + default: DecodeDifferent::Encode( + DefaultByteGetter(&__GetByteStructGenericData2DM(PhantomData::)) + ), + documentation: DecodeDifferent::Encode(&[]), + }, ]) }; diff --git a/srml/support/src/storage/mod.rs b/srml/support/src/storage/mod.rs index fb23383c9d113..426e480f0c262 100644 --- a/srml/support/src/storage/mod.rs +++ b/srml/support/src/storage/mod.rs @@ -401,6 +401,20 @@ pub trait StorageDoubleMap { KArg1: Borrow, KArg2: Borrow, F: FnOnce(&mut Self::Query) -> R; + + /// Append the given items to the value under the key specified. + /// + /// `V` is required to implement `codec::EncodeAppend`. + fn append( + k1: KArg1, + k2: KArg2, + items: &[I], + ) -> Result<(), &'static str> + where + KArg1: Borrow, + KArg2: Borrow, + I: codec::Encode, + V: EncodeAppend; } impl StorageDoubleMap for U @@ -453,6 +467,20 @@ where { U::mutate(k1.borrow(), k2.borrow(), f, &RuntimeStorage) } + + fn append( + k1: KArg1, + k2: KArg2, + items: &[I], + ) -> Result<(), &'static str> + where + KArg1: Borrow, + KArg2: Borrow, + I: codec::Encode, + V: EncodeAppend, + { + U::append(k1.borrow(), k2.borrow(), items, &RuntimeStorage) + } } /// child storage NOTE could replace unhashed by having only one kind of storage (root being null storage diff --git a/srml/support/src/storage/unhashed/generator.rs b/srml/support/src/storage/unhashed/generator.rs index 25592dce6d944..7e9e5a21993f4 100644 --- a/srml/support/src/storage/unhashed/generator.rs +++ b/srml/support/src/storage/unhashed/generator.rs @@ -150,4 +150,24 @@ pub trait StorageDoubleMap /// Mutate the value under a key. fn mutate R, S: UnhashedStorage>(k1: &K1, k2: &K2, f: F, storage: &S) -> R; + + /// Append the given items to the value under the key specified. + fn append( + k1: &K1, + k2: &K2, + items: &[I], + storage: &S, + ) -> Result<(), &'static str> + where + I: codec::Encode, + V: codec::EncodeAppend, + { + let key = Self::key_for(k1, k2); + let new_val = ::append( + storage.get_raw(&key).unwrap_or_default(), + items, + ).ok_or_else(|| "Could not append given item")?; + storage.put_raw(&key, &new_val); + Ok(()) + } } diff --git a/srml/system/src/lib.rs b/srml/system/src/lib.rs index 0eccd3d0145f5..b009785011fb6 100644 --- a/srml/system/src/lib.rs +++ b/srml/system/src/lib.rs @@ -83,7 +83,10 @@ use primitives::traits::{self, CheckEqual, SimpleArithmetic, SimpleBitOps, One, #[cfg(any(feature = "std", test))] use primitives::traits::Zero; use substrate_primitives::storage::well_known_keys; -use srml_support::{storage, StorageValue, StorageMap, Parameter, decl_module, decl_event, decl_storage}; +use srml_support::{ + storage, decl_module, decl_event, decl_storage, StorageDoubleMap, StorageValue, + StorageMap, Parameter, +}; use safe_mix::TripletMix; use parity_codec::{Encode, Decode}; @@ -182,18 +185,7 @@ decl_module! { pub struct Module for enum Call where origin: T::Origin { /// Deposits an event into this block's event record. pub fn deposit_event(event: T::Event) { - let extrinsic_index = Self::extrinsic_index(); - let phase = extrinsic_index.map_or(Phase::Finalization, |c| Phase::ApplyExtrinsic(c)); - let event = EventRecord { phase, event }; - - // Appending can only fail if `Events` can not be decoded or - // when we try to insert more than `u32::max_value()` events. - // If one of these conditions is met, we just insert the new event. - let events = [event]; - if >::append(&events).is_err() { - let [event] = events; - >::put(vec![event]); - } + Self::deposit_event_indexed(&[], event); } } } @@ -211,11 +203,13 @@ pub enum Phase { /// Record of an event happening. #[derive(Encode, Decode)] #[cfg_attr(feature = "std", derive(Serialize, PartialEq, Eq, Clone, Debug))] -pub struct EventRecord { +pub struct EventRecord { /// The phase of the block it happened in. pub phase: Phase, /// The event itself. pub event: E, + /// The list of the topics this event has. + pub topics: Vec, } decl_event!( @@ -295,6 +289,12 @@ fn hash69 + Default>() -> T { h } +/// This type alias represents an index of an event. +/// +/// We use `u32` here because this index is used as index for `Events` +/// which can't contain more than `u32::max_value()` items. +type EventIndex = u32; + decl_storage! { trait Store for Module as System { /// Extrinsics nonce for accounts. @@ -319,7 +319,30 @@ decl_storage! { /// Digest of the current block, also part of the block header. Digest get(digest): T::Digest; /// Events deposited for the current block. - Events get(events): Vec>; + Events get(events): Vec>; + /// The number of events in the `Events` list. + EventCount get(event_count): EventIndex; + + // TODO: https://github.com/paritytech/substrate/issues/2553 + // Possibly, we can improve it by using something like: + // `Option<(BlockNumber, Vec)>`, however in this case we won't be able to use + // `EventTopics::append`. + + /// Mapping between a topic (represented by T::Hash) and a vector of indexes + /// of events in the `>` list. + /// + /// The first key serves no purpose. This field is declared as double_map just + /// for convenience of using `remove_prefix`. + /// + /// All topic vectors have deterministic storage locations depending on the topic. This + /// allows light-clients to leverage the changes trie storage tracking mechanism and + /// in case of changes fetch the list of events of interest. + /// + /// The value has the type `(T::BlockNumber, EventIndex)` because if we used only just + /// the `EventIndex` then in case if the topic has the same contents on the next block + /// no notification will be triggered thus the event might be lost. + EventTopics get(event_topics): double_map hasher(blake2_256) (), blake2_256(T::Hash) + => Vec<(T::BlockNumber, EventIndex)>; } add_extra_genesis { config(changes_trie_config): Option; @@ -378,6 +401,54 @@ pub fn ensure_none(o: OuterOrigin) -> Result<(), &'stati } impl Module { + /// Deposits an event into this block's event record adding this event + /// to the corresponding topic indexes. + /// + /// This will update storage entries that correpond to the specified topics. + /// It is expected that light-clients could subscribe to this topics. + pub fn deposit_event_indexed(topics: &[T::Hash], event: T::Event) { + let extrinsic_index = Self::extrinsic_index(); + let phase = extrinsic_index.map_or(Phase::Finalization, |c| Phase::ApplyExtrinsic(c)); + let event = EventRecord { + phase, + event, + topics: topics.iter().cloned().collect::>(), + }; + + // Index of the to be added event. + let event_idx = { + let old_event_count = >::get(); + let new_event_count = match old_event_count.checked_add(1) { + // We've reached the maximum number of events at this block, just + // don't do anything and leave the event_count unaltered. + None => return, + Some(nc) => nc, + }; + >::put(new_event_count); + old_event_count + }; + + // Appending can only fail if `Events` can not be decoded or + // when we try to insert more than `u32::max_value()` events. + // + // We perform early return if we've reached the maximum capacity of the event list, + // so `Events` seems to be corrupted. Also, this has happened after the start of execution + // (since the event list is cleared at the block initialization). + if >::append(&[event]).is_err() { + // The most sensible thing to do here is to just ignore this event and wait until the + // new block. + return; + } + + let block_no = Self::block_number(); + for topic in topics { + // The same applies here. + if >::append(&(), topic, &[(block_no, event_idx)]).is_err() { + return; + } + } + } + /// Gets the index of extrinsic that is currently executing. pub fn extrinsic_index() -> Option { storage::unhashed::get(well_known_keys::EXTRINSIC_INDEX) @@ -408,6 +479,8 @@ impl Module { *index = (*index + 1) % 81; }); >::kill(); + >::kill(); + >::remove_prefix(&()); } /// Remove temporary "environment" entries in storage. @@ -430,7 +503,13 @@ impl Module { digest.push(item); } - // > stays to be inspected by the client. + // The following fields + // + // - > + // - > + // - > + // + // stay to be inspected by the client and will be cleared by `Self::initialize`. ::new(number, extrinsics_root, storage_root, parent_hash, digest) } @@ -649,7 +728,13 @@ mod tests { System::finalize(); assert_eq!( System::events(), - vec![EventRecord { phase: Phase::Finalization, event: 1u16 }] + vec![ + EventRecord { + phase: Phase::Finalization, + event: 1u16, + topics: vec![], + } + ] ); System::initialize(&2, &[0u8; 32].into(), &[0u8; 32].into()); @@ -660,11 +745,71 @@ mod tests { System::deposit_event(3u16); System::finalize(); assert_eq!(System::events(), vec![ - EventRecord { phase: Phase::ApplyExtrinsic(0), event: 42u16 }, - EventRecord { phase: Phase::ApplyExtrinsic(0), event: 100u16 }, - EventRecord { phase: Phase::ApplyExtrinsic(1), event: 101u16 }, - EventRecord { phase: Phase::Finalization, event: 3u16 } + EventRecord { phase: Phase::ApplyExtrinsic(0), event: 42u16, topics: vec![] }, + EventRecord { phase: Phase::ApplyExtrinsic(0), event: 100u16, topics: vec![] }, + EventRecord { phase: Phase::ApplyExtrinsic(1), event: 101u16, topics: vec![] }, + EventRecord { phase: Phase::Finalization, event: 3u16, topics: vec![] } ]); }); } + + #[test] + fn deposit_event_topics() { + with_externalities(&mut new_test_ext(), || { + const BLOCK_NUMBER: u64 = 1; + + System::initialize(&BLOCK_NUMBER, &[0u8; 32].into(), &[0u8; 32].into()); + System::note_finished_extrinsics(); + + let topics = vec![ + H256::repeat_byte(1), + H256::repeat_byte(2), + H256::repeat_byte(3), + ]; + + // We deposit a few events with different sets of topics. + System::deposit_event_indexed(&topics[0..3], 1u16); + System::deposit_event_indexed(&topics[0..1], 2u16); + System::deposit_event_indexed(&topics[1..2], 3u16); + + System::finalize(); + + // Check that topics are reflected in the event record. + assert_eq!( + System::events(), + vec![ + EventRecord { + phase: Phase::Finalization, + event: 1u16, + topics: topics[0..3].to_vec(), + }, + EventRecord { + phase: Phase::Finalization, + event: 2u16, + topics: topics[0..1].to_vec(), + }, + EventRecord { + phase: Phase::Finalization, + event: 3u16, + topics: topics[1..2].to_vec(), + } + ] + ); + + // Check that the topic-events mapping reflects the deposited topics. + // Note that these are indexes of the events. + assert_eq!( + System::event_topics(&(), &topics[0]), + vec![(BLOCK_NUMBER, 0), (BLOCK_NUMBER, 1)], + ); + assert_eq!( + System::event_topics(&(), &topics[1]), + vec![(BLOCK_NUMBER, 0), (BLOCK_NUMBER, 2)], + ); + assert_eq!( + System::event_topics(&(), &topics[2]), + vec![(BLOCK_NUMBER, 0)], + ); + }); + } }