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

Custom signature topic #2031

Merged
merged 22 commits into from
Jan 11, 2024
Merged

Custom signature topic #2031

merged 22 commits into from
Jan 11, 2024

Conversation

SkymanOne
Copy link
Contributor

@SkymanOne SkymanOne commented Dec 21, 2023

Summary

Closes #1838 and follows up #1827

  • [n] y/n | Does it introduce breaking changes?
  • [n] y/n | Is it dependant on the specific version of cargo-contract or pallet-contracts?

Introduces optional attribute signature_topic to the #[ink::event] and #[ink(event)] that takes 32-byte hash string to specify a signature topic for the specific event manually.

Description

It introduces a new macro attribute #[ink(signature_topic = _)], part of Event, and the same argument can be used with inline events, but subject to discussion in #2046.

Example

#[derive(ink::Event, scale::Encode)]
#[ink(signature_topic = "1111111111111111111111111111111111111111111111111111111111111111")]
pub struct MyCustomSignatureEvent {
     pub field: u32,
     pub topic: [u8; 32],
}

assert_eq!(Some([17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17]),
     <MyCustomSignatureEvent as ink::env::Event>::SIGNATURE_TOPIC)

If signature_topic is not specified, then topic will be calculated automatically, otherwise the hash string will be used.
This attribute is appended automatically in #[ink::event].

Breaking change

As part of old discussion

However, when we manually use #[derive(Event, scale::Encode)], we must also implement GetSignatureTopic as implied above. Therefore, we must also use [ink::signature_event].

#[derive(::ink::Event, ::scale::Encode, ::scale::Decode)]
#[cfg_attr(feature = "std", derive(::ink::EventMetadata))]
#[ink::signature_topic]
pub struct Flipped { .. }

However, if #[ink(anonymous)] is used, then #[derive(event) will derive a blank implementation of GetSignatureTopic that returns None, and [ink::signature_event] should not be used.

#[derive(::ink::Event, ::scale::Encode, ::scale::Decode)]
#[cfg_attr(feature = "std", derive(::ink::EventMetadata))]
#[ink(anonymous)]
pub struct Flipped { .. }

Rationale

This approach was followed due to the philosophy behind the [ink::event] macro that simply appends necessary proc macro attributes. Therefore, the user should achieve the same result manually.

Earlier, I have attempted to generate an implementation for GetSignatureTopic inside [ink::event] but this prevented from using #[derive(Event)] independently. Therefore, if use signature_topic = S arg inside #[ink::event] macro that simply passes the string arg into #[ink::signature_topic(hash = S)]

Specifically, GetSignatureTopic provides the flexibility to provide custom calculation of SIGNATURE_TOPIC without the need to implement Event trait.

impl GetSignatureTopic for Flipper {
   const SIGNATURE_TOPIC: Option<[u8; 32]> = <literal value or const fn>
}

#[derive(::ink::Event, ::scale::Encode, ::scale::Decode)]
#[cfg_attr(feature = "std", derive(::ink::EventMetadata))]
pub struct Flipped { .. }

So signature topic can be defined in 6 ways:

  1. Automatically calculated using #[ink(event) or #[ink::event] attributes
  2. Automatically calculated with #[ink::signature_topic] when #[derive(Event) is used directly
  3. Manually specified with #[ink(event, signature_topic = s) for local events
  4. Manually specified with #[ink::event(signature_topic = s)] for shared events
  5. Manually specified with #[ink::signature_topic(hash = s)] for shared events with #[derive(Event)]
  6. Manually specified by implementing GetSignatureTopic for the event struct directly for shared events

No breaking changes have been introduced. The signature_topic argument is totally optional.

Checklist before requesting a review

  • My code follows the style guidelines of this project
  • I have added an entry to CHANGELOG.md
  • I have commented my code, particularly in hard-to-understand areas
  • I have added tests that prove my fix is effective or that my feature works
  • Any dependent changes have been merged and published in downstream modules

@codecov-commenter
Copy link

codecov-commenter commented Dec 22, 2023

Codecov Report

Attention: 62 lines in your changes are missing coverage. Please review.

Comparison is base (db5600e) 53.47% compared to head (c5e5e9f) 53.60%.
Report is 11 commits behind head on master.

Files Patch % Lines
crates/ink/ir/src/ir/event/signature_topic.rs 36.11% 23 Missing ⚠️
crates/ink/macro/src/event/mod.rs 74.35% 20 Missing ⚠️
crates/ink/ir/src/ir/attrs.rs 79.16% 5 Missing ⚠️
crates/ink/ir/src/ir/event/config.rs 75.00% 4 Missing ⚠️
crates/ink/codegen/src/generator/event.rs 0.00% 3 Missing ⚠️
.../ink/tests/ui/event/pass/custom_signature_works.rs 25.00% 3 Missing ⚠️
...sts/ui/event/pass/inline_custom_signature_works.rs 25.00% 3 Missing ⚠️
crates/ink/ir/src/ir/event/mod.rs 91.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2031      +/-   ##
==========================================
+ Coverage   53.47%   53.60%   +0.12%     
==========================================
  Files         221      224       +3     
  Lines        6984     7107     +123     
  Branches     3082     3146      +64     
==========================================
+ Hits         3735     3810      +75     
- Misses       3249     3297      +48     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

github-actions bot commented Dec 22, 2023

🦑 📈 ink! Example Contracts ‒ Changes Report 📉 🦑

These are the results when building the integration-tests/* contracts from this branch with cargo-contract and comparing them to ink! master:

Contract Upstream Size (kB) PR Size (kB) Diff (kB) Diff (%) Change
basic-contract-caller 3.207 3.207 0 0
basic-contract-caller/other-contract 1.581 1.581 0 0
call-builder-return-value 8.904 8.904 0 0
call-runtime 2.013 2.013 0 0
conditional-compilation 1.453 1.453 0 0
contract-storage 7.336 7.336 0 0
contract-terminate 1.336 1.336 0 0
contract-transfer 1.693 1.693 0 0
custom-allocator 7.652 7.652 0 0
dns 7.321 7.321 0 0
e2e-call-runtime 1.302 1.302 0 0
e2e-runtime-only-backend 1.879 1.879 0 0
erc1155 14.123 14.123 0 0
erc20 6.918 6.918 0 0
erc721 9.816 9.816 0 0
events 5.264 5.264 0 0
flipper 1.637 1.637 0 0
incrementer 1.504 1.504 0 0
lang-err-integration-tests/call-builder-delegate 2.561 2.561 0 0
lang-err-integration-tests/call-builder 5.087 5.087 0 0
lang-err-integration-tests/constructors-return-value 1.987 1.987 0 0
lang-err-integration-tests/contract-ref 4.568 4.568 0 0
lang-err-integration-tests/integration-flipper 1.815 1.815 0 0
lazyvec-integration-test 4.553 4.553 0 0
mapping-integration-tests 7.919 7.919 0 0
mother 12.756 12.756 0 0
multi-contract-caller 6.155 6.155 0 0
multi-contract-caller/accumulator 1.378 1.378 0 0
multi-contract-caller/adder 1.908 1.908 0 0
multi-contract-caller/subber 1.928 1.928 0 0
multisig 21.621 21.621 0 0
payment-channel 5.653 5.653 0 0
sr25519-verification 1.148 1.148 0 0
static-buffer 1.649 1.649 0 0
trait-dyn-cross-contract-calls 2.706 2.706 0 0
trait-dyn-cross-contract-calls/contracts/incrementer 1.549 1.549 0 0
trait-erc20 7.294 7.294 0 0
trait-flipper 1.453 1.453 0 0
trait-incrementer 1.614 1.614 0 0
upgradeable-contracts/delegator 3.152 3.152 0 0
upgradeable-contracts/delegator/delegatee 1.613 1.613 0 0
upgradeable-contracts/set-code-hash 1.747 1.747 0 0
upgradeable-contracts/set-code-hash/updated-incrementer 1.726 1.726 0 0
wildcard-selector 2.852 2.852 0 0

Link to the run | Last update: Thu Jan 11 06:13:55 CET 2024

@SkymanOne SkymanOne marked this pull request as ready for review December 22, 2023 15:49
@SkymanOne SkymanOne changed the title [WIP] Custom signature topic Custom signature topic Dec 26, 2023
Copy link
Collaborator

@ascjones ascjones left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of adding a new attribute macro #[::ink::signature_topic], we should look at extending the mechanism we already have for configuring the signature topic.

At the moment we have #[ink(anonymous)] which allows configuring the #[derive(Event)]. This is an idiomatic way to configure a derive macro, so imagine we introduce a new derive configuration attribute #[ink(signature_topic = 0x...)]. To follow this idea further, #[ink(anonymous)] is just an alias for #[ink(signature_topic = None)] (or whatever syntax we choose).

By combining these we might be able to simplify the implementation here, certainly we won't need the additional trait.

crates/ink/ir/src/ir/event/signature_topic.rs Outdated Show resolved Hide resolved
crates/env/src/event.rs Outdated Show resolved Hide resolved
crates/ink/codegen/Cargo.toml Outdated Show resolved Hide resolved
@ascjones
Copy link
Collaborator

ascjones commented Jan 4, 2024

So signature topic can be defined in 6 ways:

I think it should be:just 3 ways:

Automatically calculated using #[ink(event) or #[ink::event] attributes
Automatically calculated with #[ink::signature_topic] when #[derive(Event) is used directly
Manually specified with #[ink(event, signature_topic = s) for local events
Manually specified with #[ink::event(signature_topic = s)] for shared events
Manually specified with #[ink::signature_topic(hash = s)] for shared events with #[derive(Event)]
^^ Instead: #[derive(Event)] with #[ink(signature_topic = 0x)]
Manually specified by implementing GetSignatureTopic for the event struct directly for shared events

So in essence we have the

#[ink::event(signature_topic = 0x...)]]

Which expands to

#[derive(Event)]
#[ink(signature_topic = 0x...)]

The derive then picks up the ink attribute, so exactly how anonymous currently works (IIRC).

crates/ink/macro/src/lib.rs Outdated Show resolved Hide resolved
crates/ink/macro/src/lib.rs Outdated Show resolved Hide resolved
crates/ink/ir/src/ir/event/signature_topic.rs Outdated Show resolved Hide resolved
crates/ink/ir/src/ir/attrs.rs Outdated Show resolved Hide resolved
crates/ink/ir/src/ir/attrs.rs Outdated Show resolved Hide resolved
crates/ink/ir/src/ir/attrs.rs Outdated Show resolved Hide resolved
crates/ink/ir/src/ir/event/config.rs Outdated Show resolved Hide resolved
crates/ink/ir/src/ir/event/signature_topic.rs Outdated Show resolved Hide resolved
@deuszx
Copy link

deuszx commented Jan 10, 2024

A general question - why do we need it? I'm not convinced original argument "we have this for selectors so we can have it for events" . For selectors it makes sense for backwards compatibility - imagine upgrading a contract and want to maintain compatibility with the rest of the ecosystem. For events, signature is based on the event's structure only, right? So there's no problem with backwards compatibility - if event has the same structure, old deserializers will still work.

The way I see it, this feature can actually trick clients to observe, fetch, deserialize, etc. events that are not the ones they're interested in - example I'm observing PSP22::Transfer events with signature XYZ and there are contracts using custom signature topic XYZ even though they're not PSP22 contracts or not emitting PSP22::Transfer events.

@SkymanOne
Copy link
Contributor Author

A general question - why do we need it? I'm not convinced original argument "we have this for selectors so we can have it for events" . For selectors it makes sense for backwards compatibility - imagine upgrading a contract and want to maintain compatibility with the rest of the ecosystem. For events, signature is based on the event's structure only, right? So there's no problem with backwards compatibility - if event has the same structure, old deserializers will still work.

The way I see it, this feature can actually trick clients to observe, fetch, deserialize, etc. events that are not the ones they're interested in - example I'm observing PSP22::Transfer events with signature XYZ and there are contracts using custom signature topic XYZ even though they're not PSP22 contracts or not emitting PSP22::Transfer events.

This is useful for two reasons:

  1. Backward compatibility as you said. The hash function will be configurable in the future, there is an issue Persist hash function in Environment #1820
  2. The event signature topic is calculated by hashing Ident(field_types). If the develop wants to use ident definition from the other contract that is publically accessible, and the event definition is conflicting with the existing one, they can just rename it and specify the signature topic of the original one.

Copy link
Collaborator

@ascjones ascjones left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

(see my final nitpick above, also conflict needs resolving)

@SkymanOne SkymanOne enabled auto-merge (squash) January 11, 2024 04:04
@SkymanOne SkymanOne merged commit bc6f5a9 into master Jan 11, 2024
23 checks passed
@SkymanOne SkymanOne deleted the gn/custom-signature-topic branch January 11, 2024 04:46
@SkymanOne SkymanOne mentioned this pull request Jan 15, 2024
@SkymanOne SkymanOne mentioned this pull request Feb 8, 2024
@SkymanOne SkymanOne mentioned this pull request Mar 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Events 2.0: manually specify signature topic
4 participants