What is the cost of event storage in substrate?
Asked Answered
F

2

7

While implementing my chain logic I was wondering whether to use events at all since they might cost the node extra storage for the event logs. What is the actual storage cost involved here? Do the logs get purged automatically at some point?

Franconian answered 26/7, 2019 at 12:12 Comment(3)
you need to be a lot more specific on SOMalinda
This kind of discussion should probably be made on a substrate specific forum.Hotpress
This is the appropriate place to ask this question (with the [substrate] tag). You just need to remove other tags.Borodino
B
13

Runtime events are handled by the System module. Within your own module, you normally implement the default deposit_event function:

From the in-code documentation:

deposit_event: Helper function for depositing an event. The default behavior is to call deposit_event from the System module. However, you can write your own implementation for events in your runtime. To use the default behavior, add fn deposit_event<T>() = default; to your Module.

If you look at the System module code, you will find that ultimately a helper function is called which stores the event:

/// Deposits an event into this block's event record adding this event
/// to the corresponding topic indexes.
///
/// This will update storage entries that correspond 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) { ... }

This function modifies three storage items which you can find in the decl_storage for the System module:

/// Events deposited for the current block.
Events get(events): Vec<EventRecord<T::Event, T::Hash>>;

/// The number of events in the `Events<T>` list.
EventCount get(event_count): EventIndex;

/// Mapping between a topic (represented by T::Hash) and a vector of indexes
/// of events in the `<Events<T>>` list.
EventTopics get(event_topics): double_map hasher(blake2_256) (), blake2_256(T::Hash)
            => Vec<(T::BlockNumber, EventIndex)>;

The final part of the event story can be found in the initialize function in the System module, where all three of these items are "cleaned up":

pub fn initialize( ... ) {
    ...
    <Events<T>>::kill();
    EventCount::kill();
    <EventTopics<T>>::remove_prefix(&());
}

This initialize function is called in the Executive module at the beginning of every block, before on_initialize is called for any module:

fn initialize_block_impl(
    block_number: &System::BlockNumber,
    parent_hash: &System::Hash,
    extrinsics_root: &System::Hash,
    digest: &Digest<System::Hash>,
) {
    <system::Module<System>>::initialize(block_number, parent_hash, extrinsics_root, digest);
    <AllModules as OnInitialize<System::BlockNumber>>::on_initialize(*block_number);
}

In conclusion, the cost of adding a single Event in the runtime is:

  • Running the deposit_event_indexed function.
  • Adding a new item to two vectors in runtime storage.
  • ... which are cleaned up at the beginning of the next block so the storage cost is not compounding.
Borodino answered 27/7, 2019 at 19:24 Comment(1)
Is this answer still valid today? Are events pruned after every block?Dix
E
2

Events are created the same as any other storage item and consume the same amount of storage, based on the type of the inner data being emitted.

Elrod answered 26/7, 2019 at 20:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.