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}