ink_env/
event.rs

1// Copyright (C) Use Ink (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! This module contains the implementation for the event topic logic.
16
17use crate::types::Environment;
18
19/// The concrete implementation that is guided by the topics builder.
20///
21/// To be implemented by the on-chain and off-chain environments respectively.
22#[doc(hidden)]
23pub trait TopicsBuilderBackend<E>
24where
25    E: Environment,
26{
27    /// The type of the serialized event topics.
28    type Output;
29
30    #[cfg(feature = "unstable-hostfn")]
31    /// Pushes another topic for serialization to the backend.
32    fn push_topic<T>(&mut self, topic_value: &T)
33    where
34        T: scale::Encode;
35
36    /// Extracts the serialized topics.
37    fn output(self) -> Self::Output;
38}
39
40/// Builder for event topic serialization.
41///
42/// Abstraction to build up event topic serialization with zero-overhead,
43/// no heap-memory allocations and no dynamic dispatch.
44#[doc(hidden)]
45pub struct TopicsBuilder<S, E, B> {
46    backend: B,
47    state: core::marker::PhantomData<fn() -> (S, E)>,
48}
49
50impl<E, B> From<B> for TopicsBuilder<state::Uninit, E, B>
51where
52    E: Environment,
53    B: TopicsBuilderBackend<E>,
54{
55    fn from(backend: B) -> Self {
56        Self {
57            backend,
58            state: Default::default(),
59        }
60    }
61}
62
63#[doc(hidden)]
64pub mod state {
65    /// The topic builder is uninitialized and needs to be provided with the
66    /// expected number of topics that need to be constructed.
67    pub enum Uninit {}
68    /// There are some remaining topics that need to be provided with some values.
69    pub enum HasRemainingTopics {}
70    /// There are no more remaining topics and the topic builder shall be finalized.
71    pub enum NoRemainingTopics {}
72}
73
74impl<E, B> TopicsBuilder<state::Uninit, E, B>
75where
76    E: Environment,
77    B: TopicsBuilderBackend<E>,
78{
79    /// Initializes the topics builder.
80    ///
81    /// The number of expected topics is given implicitly by the `E` type parameter.
82    pub fn build<Evt: Event>(
83        self,
84    ) -> TopicsBuilder<<Evt as Event>::RemainingTopics, E, B> {
85        TopicsBuilder {
86            backend: self.backend,
87            state: Default::default(),
88        }
89    }
90}
91
92impl<E, S, B> TopicsBuilder<S, E, B>
93where
94    E: Environment,
95    S: SomeRemainingTopics,
96    B: TopicsBuilderBackend<E>,
97{
98    /// Pushes another event topic to be serialized through the topics builder.
99    ///
100    /// Returns a topics builder that expects one less event topic for serialization
101    /// than before the call.
102    #[cfg(feature = "unstable-hostfn")]
103    pub fn push_topic<T>(
104        mut self,
105        value: Option<&T>,
106    ) -> TopicsBuilder<<S as SomeRemainingTopics>::Next, E, B>
107    where
108        T: scale::Encode,
109    {
110        // Only publish the topic if it is not an `Option::None`.
111        if let Some(topic) = value {
112            self.backend.push_topic::<T>(topic);
113        } else {
114            self.backend.push_topic::<u8>(&0u8);
115        }
116        TopicsBuilder {
117            backend: self.backend,
118            state: Default::default(),
119        }
120    }
121}
122
123impl<E, B> TopicsBuilder<state::NoRemainingTopics, E, B>
124where
125    E: Environment,
126    B: TopicsBuilderBackend<E>,
127{
128    /// Finalizes the topics builder.
129    ///
130    /// No more event topics can be serialized afterwards, but the environment will be
131    /// able to extract the information collected by the topics builder in order to
132    /// emit the serialized event.
133    pub fn finish(self) -> <B as TopicsBuilderBackend<E>>::Output
134    where
135        B: TopicsBuilderBackend<E>,
136    {
137        self.backend.output()
138    }
139}
140
141/// Indicates that there are some remaining topics left for expected serialization.
142#[doc(hidden)]
143pub trait SomeRemainingTopics {
144    /// The type state indicating the amount of the remaining topics afterwards.
145    ///
146    /// Basically trivial sequence of: `N => N - 1` unless `N <= 1`
147    type Next;
148}
149
150/// Indicates the actual amount of expected event topics.
151#[doc(hidden)]
152pub trait EventTopicsAmount {
153    /// The actual amount of remaining topics.
154    const AMOUNT: usize;
155}
156
157macro_rules! impl_some_remaining_for {
158    ( $( $n:literal ),* $(,)? ) => {
159        $(
160            impl SomeRemainingTopics for [state::HasRemainingTopics; $n] {
161                type Next = [state::HasRemainingTopics; $n - 1];
162            }
163
164            impl EventTopicsAmount for [state::HasRemainingTopics; $n] {
165                const AMOUNT: usize = $n;
166            }
167        )*
168    };
169}
170#[rustfmt::skip]
171impl_some_remaining_for!(
172             2,  3,  4,  5,  6,  7,  8,  9,
173    10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
174    20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
175    30, 31, 32,
176);
177
178impl SomeRemainingTopics for [state::HasRemainingTopics; 1] {
179    type Next = state::NoRemainingTopics;
180}
181
182impl EventTopicsAmount for [state::HasRemainingTopics; 1] {
183    const AMOUNT: usize = 1;
184}
185
186impl EventTopicsAmount for state::NoRemainingTopics {
187    const AMOUNT: usize = 0;
188}
189
190/// Implemented by event types to guide the event topic serialization using the topics
191/// builder.
192///
193/// Normally this trait should be implemented automatically via `#[derive(ink::Event)`.
194pub trait Event: scale::Encode {
195    /// Type state indicating how many event topics are to be expected by the topics
196    /// builder.
197    type RemainingTopics: EventTopicsAmount;
198
199    /// The unique signature topic of the event. `None` for anonymous events.
200    ///
201    /// It can be automatically calculated or manually specified.
202    ///
203    /// Usually this is calculated using the `#[derive(ink::Event)]` derive, which by
204    /// default calculates this as `blake2b("Event(field1_type,field2_type)"`
205    const SIGNATURE_TOPIC: core::option::Option<[u8; 32]>;
206
207    /// Guides event topic serialization using the given topics builder.
208    fn topics<E, B>(
209        &self,
210        builder: TopicsBuilder<state::Uninit, E, B>,
211    ) -> <B as TopicsBuilderBackend<E>>::Output
212    where
213        E: Environment,
214        B: TopicsBuilderBackend<E>;
215}