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 ink_primitives::abi::{
18 AbiEncodeWith,
19 Ink,
20 Sol,
21};
22
23/// The concrete implementation that is guided by the topics builder.
24///
25/// To be implemented by the on-chain and off-chain environments respectively.
26#[doc(hidden)]
27pub trait TopicsBuilderBackend<Abi = crate::DefaultAbi> {
28 /// The type of the serialized event topics.
29 type Output;
30
31 /// Pushes another topic for serialization to the backend.
32 fn push_topic<T>(&mut self, topic_value: &T)
33 where
34 T: AbiEncodeWith<Abi>;
35
36 /// Extracts the serialized topics.
37 fn output(self) -> Self::Output;
38}
39
40/// Specifies the topic (i.e. indexed event parameter) encoding implementation for
41/// the given ABI.
42pub trait TopicEncoder: private::Sealed + Sized {
43 /// True if the topic hashing implementation requires a buffer.
44 ///
45 /// (e.g. when hashing requires calling a pre-compile).
46 const REQUIRES_BUFFER: bool;
47
48 /// Encodes the value as a topic (i.e. an indexed event parameter).
49 fn encode_topic<T>(value: &T) -> [u8; 32]
50 where
51 T: AbiEncodeWith<Self>;
52
53 /// Encodes the value as a topic (i.e. an indexed event parameter), utilizing the
54 /// given buffer for hashing (if necessary).
55 fn encode_topic_with_hash_buffer<T>(
56 value: &T,
57 output: &mut [u8; 32],
58 buffer: &mut [u8],
59 ) where
60 T: AbiEncodeWith<Self>;
61}
62
63/// Builder for event topic serialization.
64///
65/// Abstraction to build up event topic serialization with zero-overhead,
66/// no heap-memory allocations and no dynamic dispatch.
67#[doc(hidden)]
68pub struct TopicsBuilder<S, B, Abi = crate::DefaultAbi> {
69 backend: B,
70 #[allow(clippy::type_complexity)]
71 state: core::marker::PhantomData<fn() -> (S, Abi)>,
72}
73
74impl<B, Abi> From<B> for TopicsBuilder<state::Uninit, B, Abi>
75where
76 B: TopicsBuilderBackend<Abi>,
77{
78 fn from(backend: B) -> Self {
79 Self {
80 backend,
81 state: Default::default(),
82 }
83 }
84}
85
86#[doc(hidden)]
87pub mod state {
88 /// The topic builder is uninitialized and needs to be provided with the
89 /// expected number of topics that need to be constructed.
90 pub enum Uninit {}
91 /// There are some remaining topics that need to be provided with some values.
92 pub enum HasRemainingTopics {}
93 /// There are no more remaining topics and the topic builder shall be finalized.
94 pub enum NoRemainingTopics {}
95}
96
97impl<B, Abi> TopicsBuilder<state::Uninit, B, Abi>
98where
99 B: TopicsBuilderBackend<Abi>,
100{
101 /// Initializes the topics builder.
102 ///
103 /// The number of expected topics is given implicitly by the `E` type parameter.
104 pub fn build<Evt: Event<Abi>>(
105 self,
106 ) -> TopicsBuilder<<Evt as Event<Abi>>::RemainingTopics, B, Abi> {
107 TopicsBuilder {
108 backend: self.backend,
109 state: Default::default(),
110 }
111 }
112}
113
114impl<S, B> TopicsBuilder<S, B, Ink>
115where
116 S: SomeRemainingTopics,
117 B: TopicsBuilderBackend<Ink>,
118{
119 /// Pushes another event topic to be serialized through the topics builder.
120 ///
121 /// Returns a topics builder that expects one less event topic for serialization
122 /// than before the call.
123 pub fn push_topic<T>(
124 mut self,
125 value: Option<&T>,
126 ) -> TopicsBuilder<<S as SomeRemainingTopics>::Next, B, Ink>
127 where
128 T: AbiEncodeWith<Ink>,
129 {
130 // Only publish the topic if it is not an `Option::None`.
131 if let Some(topic) = value {
132 self.backend.push_topic::<T>(topic);
133 } else {
134 self.backend.push_topic::<u8>(&0u8);
135 }
136 TopicsBuilder {
137 backend: self.backend,
138 state: Default::default(),
139 }
140 }
141}
142
143impl<S, B> TopicsBuilder<S, B, Sol>
144where
145 S: SomeRemainingTopics,
146 B: TopicsBuilderBackend<Sol>,
147{
148 /// Pushes another event topic to be serialized through the topics builder.
149 ///
150 /// Returns a topics builder that expects one less event topic for serialization
151 /// than before the call.
152 pub fn push_topic<T>(
153 mut self,
154 value: &T,
155 ) -> TopicsBuilder<<S as SomeRemainingTopics>::Next, B, Sol>
156 where
157 T: AbiEncodeWith<Sol>,
158 {
159 self.backend.push_topic::<T>(value);
160 TopicsBuilder {
161 backend: self.backend,
162 state: Default::default(),
163 }
164 }
165}
166
167impl<B, Abi> TopicsBuilder<state::NoRemainingTopics, B, Abi>
168where
169 B: TopicsBuilderBackend<Abi>,
170{
171 /// Finalizes the topics builder.
172 ///
173 /// No more event topics can be serialized afterwards, but the environment will be
174 /// able to extract the information collected by the topics builder in order to
175 /// emit the serialized event.
176 pub fn finish(self) -> <B as TopicsBuilderBackend<Abi>>::Output
177 where
178 B: TopicsBuilderBackend<Abi>,
179 {
180 self.backend.output()
181 }
182}
183
184/// Indicates that there are some remaining topics left for expected serialization.
185#[doc(hidden)]
186pub trait SomeRemainingTopics {
187 /// The type state indicating the amount of the remaining topics afterwards.
188 ///
189 /// Basically trivial sequence of: `N => N - 1` unless `N <= 1`
190 type Next;
191}
192
193/// Indicates the actual amount of expected event topics.
194#[doc(hidden)]
195pub trait EventTopicsAmount {
196 /// The actual amount of remaining topics.
197 const AMOUNT: usize;
198}
199
200macro_rules! impl_some_remaining_for {
201 ( $( $n:literal ),* $(,)? ) => {
202 $(
203 impl SomeRemainingTopics for [state::HasRemainingTopics; $n] {
204 type Next = [state::HasRemainingTopics; $n - 1];
205 }
206
207 impl EventTopicsAmount for [state::HasRemainingTopics; $n] {
208 const AMOUNT: usize = $n;
209 }
210 )*
211 };
212}
213
214impl_some_remaining_for!(2, 3, 4);
215
216impl SomeRemainingTopics for [state::HasRemainingTopics; 1] {
217 type Next = state::NoRemainingTopics;
218}
219
220impl EventTopicsAmount for [state::HasRemainingTopics; 1] {
221 const AMOUNT: usize = 1;
222}
223
224impl EventTopicsAmount for state::NoRemainingTopics {
225 const AMOUNT: usize = 0;
226}
227
228/// Implemented by event types to guide the event topic serialization using the topics
229/// builder.
230///
231/// Normally this trait should be implemented automatically via `#[derive(ink::Event)`.
232pub trait Event<Abi = crate::DefaultAbi>: AbiEncodeWith<Abi> {
233 /// Type state indicating how many event topics are to be expected by the topics
234 /// builder.
235 type RemainingTopics: EventTopicsAmount;
236
237 /// The unique signature topic of the event. `None` for anonymous events.
238 ///
239 /// It can be automatically calculated or manually specified.
240 ///
241 /// Usually this is calculated using the `#[derive(ink::Event)]` derive, which by
242 /// default calculates this as `blake2b("Event(field1_type,field2_type)"`
243 const SIGNATURE_TOPIC: core::option::Option<[u8; 32]>;
244
245 /// Guides event topic serialization using the given topics builder.
246 fn topics<B>(
247 &self,
248 builder: TopicsBuilder<state::Uninit, B, Abi>,
249 ) -> <B as TopicsBuilderBackend<Abi>>::Output
250 where
251 B: TopicsBuilderBackend<Abi>;
252
253 /// ABI encode the dynamic data of this event.
254 fn encode_data(&self) -> ink_prelude::vec::Vec<u8>;
255}
256
257mod private {
258 /// Seals the implementation of `TopicEncoder`.
259 pub trait Sealed {}
260
261 impl Sealed for ink_primitives::abi::Ink {}
262
263 impl Sealed for ink_primitives::abi::Sol {}
264}