mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 17:01:09 +00:00
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.
This commit is contained in:
committed by
Gavin Wood
parent
d974189e3c
commit
21773b3a07
@@ -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![],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -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![],
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -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![],
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -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![],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -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<T::BlockNumber>;
|
||||
pub AppendableDM: double_map u32, blake2_256(T::BlockNumber) => Vec<u32>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Test>;
|
||||
|
||||
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<u32>"),
|
||||
key2_hasher: DecodeDifferent::Encode("blake2_256"),
|
||||
},
|
||||
default: DecodeDifferent::Encode(
|
||||
DefaultByteGetter(&__GetByteStructGenericData2DM(PhantomData::<Test>))
|
||||
),
|
||||
documentation: DecodeDifferent::Encode(&[]),
|
||||
},
|
||||
])
|
||||
};
|
||||
|
||||
|
||||
@@ -401,6 +401,20 @@ pub trait StorageDoubleMap<K1: Codec, K2: Codec, V: Codec> {
|
||||
KArg1: Borrow<K1>,
|
||||
KArg2: Borrow<K2>,
|
||||
F: FnOnce(&mut Self::Query) -> R;
|
||||
|
||||
/// Append the given items to the value under the key specified.
|
||||
///
|
||||
/// `V` is required to implement `codec::EncodeAppend<Item=I>`.
|
||||
fn append<KArg1, KArg2, I>(
|
||||
k1: KArg1,
|
||||
k2: KArg2,
|
||||
items: &[I],
|
||||
) -> Result<(), &'static str>
|
||||
where
|
||||
KArg1: Borrow<K1>,
|
||||
KArg2: Borrow<K2>,
|
||||
I: codec::Encode,
|
||||
V: EncodeAppend<Item=I>;
|
||||
}
|
||||
|
||||
impl<K1: Codec, K2: Codec, V: Codec, U> StorageDoubleMap<K1, K2, V> for U
|
||||
@@ -453,6 +467,20 @@ where
|
||||
{
|
||||
U::mutate(k1.borrow(), k2.borrow(), f, &RuntimeStorage)
|
||||
}
|
||||
|
||||
fn append<KArg1, KArg2, I>(
|
||||
k1: KArg1,
|
||||
k2: KArg2,
|
||||
items: &[I],
|
||||
) -> Result<(), &'static str>
|
||||
where
|
||||
KArg1: Borrow<K1>,
|
||||
KArg2: Borrow<K2>,
|
||||
I: codec::Encode,
|
||||
V: EncodeAppend<Item=I>,
|
||||
{
|
||||
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
|
||||
|
||||
@@ -150,4 +150,24 @@ pub trait StorageDoubleMap<K1: codec::Codec, K2: codec::Codec, V: codec::Codec>
|
||||
|
||||
/// Mutate the value under a key.
|
||||
fn mutate<R, F: FnOnce(&mut Self::Query) -> 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<I, S: UnhashedStorage>(
|
||||
k1: &K1,
|
||||
k2: &K2,
|
||||
items: &[I],
|
||||
storage: &S,
|
||||
) -> Result<(), &'static str>
|
||||
where
|
||||
I: codec::Encode,
|
||||
V: codec::EncodeAppend<Item=I>,
|
||||
{
|
||||
let key = Self::key_for(k1, k2);
|
||||
let new_val = <V as codec::EncodeAppend>::append(
|
||||
storage.get_raw(&key).unwrap_or_default(),
|
||||
items,
|
||||
).ok_or_else(|| "Could not append given item")?;
|
||||
storage.put_raw(&key, &new_val);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<T: Trait> 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<T>` 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 <Events<T>>::append(&events).is_err() {
|
||||
let [event] = events;
|
||||
<Events<T>>::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<E: Parameter + Member> {
|
||||
pub struct EventRecord<E: Parameter + Member, T> {
|
||||
/// 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<T>,
|
||||
}
|
||||
|
||||
decl_event!(
|
||||
@@ -295,6 +289,12 @@ fn hash69<T: AsMut<[u8]> + 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<T>`
|
||||
/// which can't contain more than `u32::max_value()` items.
|
||||
type EventIndex = u32;
|
||||
|
||||
decl_storage! {
|
||||
trait Store for Module<T: Trait> 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<EventRecord<T::Event>>;
|
||||
Events get(events): Vec<EventRecord<T::Event, T::Hash>>;
|
||||
/// The number of events in the `Events<T>` 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<EventIndex>)>`, 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 `<Events<T>>` 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<ChangesTrieConfiguration>;
|
||||
@@ -378,6 +401,54 @@ pub fn ensure_none<OuterOrigin, AccountId>(o: OuterOrigin) -> Result<(), &'stati
|
||||
}
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
/// 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::<Vec<_>>(),
|
||||
};
|
||||
|
||||
// Index of the to be added event.
|
||||
let event_idx = {
|
||||
let old_event_count = <EventCount<T>>::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,
|
||||
};
|
||||
<EventCount<T>>::put(new_event_count);
|
||||
old_event_count
|
||||
};
|
||||
|
||||
// Appending can only fail if `Events<T>` 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<T>` seems to be corrupted. Also, this has happened after the start of execution
|
||||
// (since the event list is cleared at the block initialization).
|
||||
if <Events<T>>::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 <EventTopics<T>>::append(&(), topic, &[(block_no, event_idx)]).is_err() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the index of extrinsic that is currently executing.
|
||||
pub fn extrinsic_index() -> Option<u32> {
|
||||
storage::unhashed::get(well_known_keys::EXTRINSIC_INDEX)
|
||||
@@ -408,6 +479,8 @@ impl<T: Trait> Module<T> {
|
||||
*index = (*index + 1) % 81;
|
||||
});
|
||||
<Events<T>>::kill();
|
||||
<EventCount<T>>::kill();
|
||||
<EventTopics<T>>::remove_prefix(&());
|
||||
}
|
||||
|
||||
/// Remove temporary "environment" entries in storage.
|
||||
@@ -430,7 +503,13 @@ impl<T: Trait> Module<T> {
|
||||
digest.push(item);
|
||||
}
|
||||
|
||||
// <Events<T>> stays to be inspected by the client.
|
||||
// The following fields
|
||||
//
|
||||
// - <Events<T>>
|
||||
// - <EventCount<T>>
|
||||
// - <EventTopics<T>>
|
||||
//
|
||||
// stay to be inspected by the client and will be cleared by `Self::initialize`.
|
||||
|
||||
<T::Header as traits::Header>::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)],
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user