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}