Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

About Event storage codec #759

Closed
yjhmelody opened this issue Apr 9, 2021 · 10 comments
Closed

About Event storage codec #759

yjhmelody opened this issue Apr 9, 2021 · 10 comments
Labels
C-question Further information is requested

Comments

@yjhmelody
Copy link
Contributor

yjhmelody commented Apr 9, 2021

I find erc20 example expanded code is:

    const _: () = {
        #[cfg(not(feature = "ink-as-dependency"))]
        impl<'a> ::ink_lang::EmitEvent<Erc20> for ::ink_lang::EnvAccess<'a, Environment> {
            fn emit_event<E>(self, event: E)
            where
                E: Into<<Erc20 as ::ink_lang::BaseEvent>::Type>,
            {
                ::ink_env::emit_event::<
                    Environment,
                    <Erc20 as ::ink_lang::BaseEvent>::Type,
                >(event.into());
            }
        }
    };

    #[cfg(not(feature = "ink-as-dependency"))]
    pub enum __ink_EventBase {
        Transfer(Transfer),
        Approval(Approval),
    }

Ink! users do not perceive the coding process, but the design has a lot of influence on the combination of different contracts. Other contracts cannot access the generated __ink_EventBase. Contract A may have trouble handling the events of contract B.
Other rust users can only rely on the events type on metadata.json and implement the corresponding event type again to decode the event.

Is it necessary to automatically generate a __ink_EventBase and implement various traits for it? What effect does this have on the mode of event storage ?

Would it be better if the user manually specify the id of the event? Just like selector. In this way, cross-contract events can be resolved more easily.

Example:

    /// Event emitted when a token transfer occurs.
    #[ink(event = 1)]
    pub struct Transfer {
        #[ink(topic)]
        from: Option<AccountId>,
        #[ink(topic)]
        to: Option<AccountId>,
        #[ink(topic)]
        value: Balance,
    }

    /// Event emitted when an approval occurs that `spender` is allowed to withdraw
    /// up to the amount of `value` tokens from `owner`.
    #[ink(event = 2)]
    pub struct Approval {
        #[ink(topic)]
        owner: AccountId,
        #[ink(topic)]
        spender: AccountId,
        #[ink(topic)]
        value: Balance,
    }

User choose a id for contract event. In this way, other contract can reuse these contract events.
However, this method should not encode the event to the enumeration of scale, because the enumeration of scale is very limited (only 1 byte is used), so there will be new problems soon.

@yjhmelody
Copy link
Contributor Author

yjhmelody commented Apr 9, 2021

Another strange place is that self.env() will take the ownership:

pub trait Env {
    /// The access wrapper.
    type EnvAccess;

    /// Accesses the environment with predefined environmental types.
    fn env(self) -> Self::EnvAccess;
}

Why the following erc20 example code works ?

 #[ink(message)]
        pub fn transfer_from(
            &mut self,
            from: AccountId,
            to: AccountId,
            value: Balance,
        ) -> Result<()> {
            let caller = self.env().caller();
            let allowance = self.allowance(from, caller);
            if allowance < value {
                return Err(Error::InsufficientAllowance);
            }
            self.transfer_from_to(from, to, value)?;
            self.allowances.insert((from, caller), allowance - value);
            Ok(())
        }

@yjhmelody yjhmelody changed the title What's the different between Self::env().emit_event and self.env() About Event storage codec Apr 9, 2021
@yjhmelody
Copy link
Contributor Author

yjhmelody commented Apr 9, 2021

About topic :

#[ink(topic)]: Tells the ink! codegen to provide a topic hash for the given field. Every ink! event can only have a limited number of such topic field. Similar semantics as to indexed event arguments in Solidity.

Should this hash appear in metadata.json ? We now only know that some fields are indexd but do not know any other infos.

The generated code have the following infos:

 builder
                    .build::<Self>()
                    .push_topic::<::ink_env::topics::PrefixedValue<[u8; 15usize]>>(
                        &::ink_env::topics::PrefixedValue {
                            value: b"Erc20::Transfer",
                            prefix: b"",
                        },
                    )
                    .push_topic::<::ink_env::topics::PrefixedValue<Option<AccountId>>>(
                        &::ink_env::topics::PrefixedValue {
                            value: &self.from,
                            prefix: b"Erc20::Transfer::from",
                        },
                    )
                    .push_topic::<::ink_env::topics::PrefixedValue<Option<AccountId>>>(
                        &::ink_env::topics::PrefixedValue {
                            value: &self.to,
                            prefix: b"Erc20::Transfer::to",
                        },
                    )
                    .push_topic::<::ink_env::topics::PrefixedValue<Balance>>(
                        &::ink_env::topics::PrefixedValue {
                            value: &self.value,
                            prefix: b"Erc20::Transfer::value",
                        },
                    )
                    .finish()
            }

How to use the information of indexed ?
Whether the user should be aware of Implementation Specification about push_topic?

@atenjin
Copy link

atenjin commented Apr 12, 2021

We think if change the events type define from enum to an id mapping could resolve some design problem in #683 , related to event part.

for exmaple we think a standard like "ERC20" should include the event. Then if the standard define some event in an "interface" crate, then the impl crate should know the event type which defined in the "interface" crate. Then the impl crate also may define self event.

The currently event module could not do this thing. So we think the define of event should be marked as id, then it could be composable.

@Robbepop
Copy link
Collaborator

Another strange place is that self.env() will take the ownership:

pub trait Env {
    /// The access wrapper.
    type EnvAccess;

    /// Accesses the environment with predefined environmental types.
    fn env(self) -> Self::EnvAccess;
}

Why the following erc20 example code works ?

 #[ink(message)]
        pub fn transfer_from(
            &mut self,
            from: AccountId,
            to: AccountId,
            value: Balance,
        ) -> Result<()> {
            let caller = self.env().caller();
            let allowance = self.allowance(from, caller);
            if allowance < value {
                return Err(Error::InsufficientAllowance);
            }
            self.transfer_from_to(from, to, value)?;
            self.allowances.insert((from, caller), allowance - value);
            Ok(())
        }

It works since we implement the trait for references of the contract. E.g.

impl<'a> Env for &'a MyContract {
    type EnvAccess = ...;
   
    fn env(self) -> Self::EnvAccess { ... }
}

Then self has the same borrowing semantics as the shared reference and still "counts" as borrow instead of ownership transfer. :)

@Robbepop
Copy link
Collaborator

You can access the generated base event type definition using <Erc20 as ::ink_lang::BaseEvent>::Type where Erc20 is the name of your contract. If you import ink_lang you can simply write <Erc20 as BaseEvent>::Type instead.

@yjhmelody
Copy link
Contributor Author

yjhmelody commented Apr 13, 2021

You can access the generated base event type definition using <Erc20 as ::ink_lang::BaseEvent>::Type where Erc20 is the name of your contract. If you import ink_lang you can simply write <Erc20 as BaseEvent>::Type instead.

But not all users know the detailed generate rule. This makes the cost of using some generated types very high.
Users may only want to reuse some code they see instead of some code generated by the contract. And the metadata.json cannot reflect the detailed generate rule.

@Robbepop
Copy link
Collaborator

We already had discussions about adding yet another way to specify events in the form of an enum directly which would probably resolve your problems. Not implemented, yet though.

@HCastano
Copy link
Contributor

We already had discussions about adding yet another way to specify events in the form of an enum directly which would probably resolve your problems. Not implemented, yet though.

@Robbepop is this in an issue somewhere? If so, I think we should probably close this in favour of that

@HCastano HCastano added the C-question Further information is requested label Jun 14, 2021
@Robbepop
Copy link
Collaborator

There is no issues for that yet from what I can tell. It is a pretty recent idea due to the ideas that came up for library-like contracts.

@SkymanOne
Copy link
Contributor

Closing the issue due to inactivity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-question Further information is requested
Projects
None yet
Development

No branches or pull requests

5 participants