ink_env 3.3.1

[ink!] Low-level interface for interacting with the smart contract Wasm executor.
Documentation
// Copyright 2018-2022 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Docs

use crate::Environment;

/// The concrete implementation that is guided by the topics builder.
///
/// To be implemented by the on-chain and off-chain environments respectively.
#[doc(hidden)]
pub trait TopicsBuilderBackend<E>
where
    E: Environment,
{
    /// The type of the serialized event topics.
    type Output;

    /// Initialized the backend with the expected number of event topics.
    fn expect(&mut self, expected_topics: usize);

    /// Pushes another topic for serialization to the backend.
    fn push_topic<T>(&mut self, topic_value: &T)
    where
        T: scale::Encode;

    /// Extracts the serialized topics.
    fn output(self) -> Self::Output;
}

/// Builder for event topic serialization.
///
/// Abstraction to build up event topic serialization with zero-overhead,
/// no heap-memory allocations and no dynamic dispatch.
#[doc(hidden)]
pub struct TopicsBuilder<S, E, B> {
    backend: B,
    state: core::marker::PhantomData<fn() -> (S, E)>,
}

impl<E, B> From<B> for TopicsBuilder<state::Uninit, E, B>
where
    E: Environment,
    B: TopicsBuilderBackend<E>,
{
    fn from(backend: B) -> Self {
        Self {
            backend,
            state: Default::default(),
        }
    }
}

#[doc(hidden)]
pub mod state {
    /// The topic builder is uninitialized and needs to be provided with the
    /// expected number of topics that need to be constructed.
    pub enum Uninit {}
    /// There are some remaining topics that need to be provided with some values.
    pub enum HasRemainingTopics {}
    /// There are no more remaining topics and the topic builder shall be finalized.
    pub enum NoRemainingTopics {}
}

impl<E, B> TopicsBuilder<state::Uninit, E, B>
where
    E: Environment,
    B: TopicsBuilderBackend<E>,
{
    /// Initializes the topics builder and informs it about how many topics it must expect to serialize.
    ///
    /// The number of expected topics is given implicitly by the `E` type parameter.
    pub fn build<Event: Topics>(
        mut self,
    ) -> TopicsBuilder<<Event as Topics>::RemainingTopics, E, B> {
        self.backend
            .expect(<<Event as Topics>::RemainingTopics as EventTopicsAmount>::AMOUNT);
        TopicsBuilder {
            backend: self.backend,
            state: Default::default(),
        }
    }
}

impl<E, S, B> TopicsBuilder<S, E, B>
where
    E: Environment,
    S: SomeRemainingTopics,
    B: TopicsBuilderBackend<E>,
{
    /// Pushes another event topic to be serialized through the topics builder.
    ///
    /// Returns a topics builder that expects one less event topic for serialization
    /// than before the call.
    pub fn push_topic<T>(
        mut self,
        value: &T,
    ) -> TopicsBuilder<<S as SomeRemainingTopics>::Next, E, B>
    where
        T: scale::Encode,
    {
        self.backend.push_topic(value);
        TopicsBuilder {
            backend: self.backend,
            state: Default::default(),
        }
    }
}

impl<E, B> TopicsBuilder<state::NoRemainingTopics, E, B>
where
    E: Environment,
    B: TopicsBuilderBackend<E>,
{
    /// Finalizes the topics builder.
    ///
    /// No more event topics can be serialized afterwards, but the environment will be
    /// able to extract the information collected by the topics builder in order to
    /// emit the serialized event.
    pub fn finish(self) -> <B as TopicsBuilderBackend<E>>::Output
    where
        B: TopicsBuilderBackend<E>,
    {
        self.backend.output()
    }
}

/// Indicates that there are some remaining topics left for expected serialization.
#[doc(hidden)]
pub trait SomeRemainingTopics {
    /// The type state indicating the amount of the remaining topics afterwards.
    ///
    /// Basically trivial sequence of: `N => N - 1` unless `N <= 1`
    type Next;
}

/// Indicates the actual amount of expected event topics.
#[doc(hidden)]
pub trait EventTopicsAmount {
    /// The actual amount of remaining topics.
    const AMOUNT: usize;
}

macro_rules! impl_some_remaining_for {
    ( $( $n:literal ),* $(,)? ) => {
        $(
            impl SomeRemainingTopics for [state::HasRemainingTopics; $n] {
                type Next = [state::HasRemainingTopics; $n - 1];
            }

            impl EventTopicsAmount for [state::HasRemainingTopics; $n] {
                const AMOUNT: usize = $n;
            }
        )*
    };
}
#[rustfmt::skip]
impl_some_remaining_for!(
             2,  3,  4,  5,  6,  7,  8,  9,
    10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
    20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
    30, 31, 32,
);

impl SomeRemainingTopics for [state::HasRemainingTopics; 1] {
    type Next = state::NoRemainingTopics;
}

impl EventTopicsAmount for [state::HasRemainingTopics; 1] {
    const AMOUNT: usize = 1;
}

impl EventTopicsAmount for state::NoRemainingTopics {
    const AMOUNT: usize = 0;
}

/// Implemented by event types to guide the event topic serialization using the topics builder.
///
/// Normally this trait should be implemented automatically via the ink! codegen.
pub trait Topics {
    /// Type state indicating how many event topics are to be expected by the topics builder.
    type RemainingTopics: EventTopicsAmount;

    /// Guides event topic serialization using the given topics builder.
    fn topics<E, B>(
        &self,
        builder: TopicsBuilder<state::Uninit, E, B>,
    ) -> <B as TopicsBuilderBackend<E>>::Output
    where
        E: Environment,
        B: TopicsBuilderBackend<E>;
}

/// For each topic a hash is generated. This hash must be unique
/// for a field and its value. The `prefix` is concatenated
/// with the `value`. This result is then hashed.
/// The `prefix` is typically set to the path a field has in
/// an event struct plus the identifier of the event struct.
///
/// For example, in the case of our ERC-20 example contract the
/// prefix `Erc20::Transfer::from` is concatenated with the
/// field value of `from` and then hashed.
/// In this example `Erc20` would be the contract identified,
/// `Transfer` the event identifier, and `from` the field identifier.
#[doc(hidden)]
pub struct PrefixedValue<'a, 'b, T> {
    pub prefix: &'a [u8],
    pub value: &'b T,
}

impl<X> scale::Encode for PrefixedValue<'_, '_, X>
where
    X: scale::Encode,
{
    #[inline]
    fn size_hint(&self) -> usize {
        self.prefix.size_hint() + self.value.size_hint()
    }

    #[inline]
    fn encode_to<T: scale::Output + ?Sized>(&self, dest: &mut T) {
        self.prefix.encode_to(dest);
        self.value.encode_to(dest);
    }
}