Skip to content

Commit b802df2

Browse files
committed
add signature_topic macro
1 parent 48cec1f commit b802df2

File tree

17 files changed

+277
-51
lines changed

17 files changed

+277
-51
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ink/codegen/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ blake2 = { workspace = true }
3030
heck = { workspace = true }
3131
scale = { workspace = true }
3232
impl-serde = { workspace = true, default-features = true }
33+
hex = { workspace = true }
3334

3435
serde = { workspace = true, features = ["derive"] }
3536
serde_json = { workspace = true }

crates/ink/codegen/src/generator/event.rs

Lines changed: 6 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
use crate::GenerateCode;
1616
use derive_more::From;
17-
use ir::HexLiteral;
1817
use proc_macro2::TokenStream as TokenStream2;
1918
use quote::quote;
2019
use syn::spanned::Spanned;
@@ -39,13 +38,11 @@ impl GenerateCode for Event<'_> {
3938
let cfg_attrs = self.item.get_cfg_attrs(item.span());
4039

4140
quote::quote! (
42-
#( #cfg_attrs )*
43-
#signature_topic
44-
4541
#( #cfg_attrs )*
4642
#[cfg_attr(feature = "std", derive(::ink::EventMetadata))]
4743
#[derive(::ink::Event)]
4844
#[::ink::scale_derive(Encode, Decode)]
45+
#signature_topic
4946
#anonymous
5047
#item
5148
)
@@ -54,42 +51,17 @@ impl GenerateCode for Event<'_> {
5451

5552
impl Event<'_> {
5653
fn generate_signature_topic(&self) -> TokenStream2 {
57-
let item_ident = &self.item.item().ident;
5854
let signature_topic = if let Some(bytes) = self.item.signature_topic() {
59-
let hash_bytes = bytes.map(|b| b.hex_padded_suffixed());
55+
let hash_string = hex::encode(bytes);
6056
quote! {
61-
::core::option::Option::Some([ #( #hash_bytes ),* ])
57+
#[::ink::signature_topic(hash = #hash_string)]
6258
}
6359
} else if self.item.anonymous() {
64-
quote! { ::core::option::Option::None }
60+
quote! {}
6561
} else {
66-
let calculated_topic = signature_topic(&self.item.item().fields, item_ident);
67-
quote! { ::core::option::Option::Some(#calculated_topic) }
62+
quote! { #[::ink::signature_topic] }
6863
};
6964

70-
quote! {
71-
impl ::ink::env::GetSignatureTopic for #item_ident {
72-
fn signature_topic() -> Option<[u8; 32]> {
73-
#signature_topic
74-
}
75-
}
76-
}
65+
quote! { #signature_topic }
7766
}
7867
}
79-
80-
/// The signature topic of an event variant.
81-
///
82-
/// Calculated with `blake2b("Event(field1_type,field2_type)")`.
83-
fn signature_topic(fields: &syn::Fields, event_ident: &syn::Ident) -> TokenStream2 {
84-
let fields = fields
85-
.iter()
86-
.map(|field| {
87-
quote::ToTokens::to_token_stream(&field.ty)
88-
.to_string()
89-
.replace(' ', "")
90-
})
91-
.collect::<Vec<_>>()
92-
.join(",");
93-
let topic_str = format!("{}({fields})", event_ident);
94-
quote!(::ink::blake2x256!(#topic_str))
95-
}

crates/ink/codegen/src/generator/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ mod ink_test;
3838
mod item_impls;
3939
mod metadata;
4040
mod selector;
41+
mod signature_topic;
4142
mod storage;
4243
mod storage_item;
4344
mod trait_def;
@@ -67,6 +68,7 @@ pub use self::{
6768
SelectorBytes,
6869
SelectorId,
6970
},
71+
signature_topic::SignatureTopic,
7072
storage::Storage,
7173
storage_item::StorageItem,
7274
trait_def::TraitDefinition,
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Copyright (C) Parity Technologies (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+
use crate::GenerateCode;
16+
use derive_more::From;
17+
use ir::HexLiteral;
18+
use proc_macro2::TokenStream as TokenStream2;
19+
use quote::quote;
20+
use syn::spanned::Spanned;
21+
22+
/// Generates code to generate signature topic.
23+
#[derive(From, Copy, Clone)]
24+
pub struct SignatureTopic<'a> {
25+
/// The event item to generate code for.
26+
item: &'a ir::SignatureTopic,
27+
}
28+
29+
impl GenerateCode for SignatureTopic<'_> {
30+
/// Generates ink! signature topic item code.
31+
fn generate_code(&self) -> TokenStream2 {
32+
let item = self.item.item();
33+
let signature_topic = self.generate_signature_topic();
34+
let cfg_attrs = self.item.get_cfg_attrs(item.span());
35+
36+
quote::quote! (
37+
#( #cfg_attrs )*
38+
#signature_topic
39+
#item
40+
)
41+
}
42+
}
43+
44+
impl SignatureTopic<'_> {
45+
fn generate_signature_topic(&self) -> TokenStream2 {
46+
let item_ident = &self.item.item().ident;
47+
let signature_topic = if let Some(bytes) = self.item.signature_topic() {
48+
let hash_bytes = bytes.map(|b| b.hex_padded_suffixed());
49+
quote! { ::core::option::Option::Some([ #( #hash_bytes ),* ]) }
50+
} else {
51+
let calculated_topic = signature_topic(&self.item.item().fields, item_ident);
52+
quote! { ::core::option::Option::Some(#calculated_topic) }
53+
};
54+
55+
quote! {
56+
impl ::ink::env::GetSignatureTopic for #item_ident {
57+
fn signature_topic() -> Option<[u8; 32]> {
58+
#signature_topic
59+
}
60+
}
61+
}
62+
}
63+
}
64+
65+
/// The signature topic of an event variant.
66+
///
67+
/// Calculated with `blake2b("Event(field1_type,field2_type)")`.
68+
fn signature_topic(fields: &syn::Fields, event_ident: &syn::Ident) -> TokenStream2 {
69+
let fields = fields
70+
.iter()
71+
.map(|field| {
72+
quote::ToTokens::to_token_stream(&field.ty)
73+
.to_string()
74+
.replace(' ', "")
75+
})
76+
.collect::<Vec<_>>()
77+
.join(",");
78+
let topic_str = format!("{}({fields})", event_ident);
79+
quote!(::ink::blake2x256!(#topic_str))
80+
}

crates/ink/codegen/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ impl<'a> CodeGenerator for &'a ir::Event {
6262
type Generator = generator::Event<'a>;
6363
}
6464

65+
impl<'a> CodeGenerator for &'a ir::SignatureTopic {
66+
type Generator = generator::SignatureTopic<'a>;
67+
}
68+
6569
impl<'a> CodeGenerator for &'a ir::StorageItem {
6670
type Generator = generator::StorageItem<'a>;
6771
}

crates/ink/ir/src/ir/event/config.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
use syn::spanned::Spanned;
16+
1517
use crate::{
1618
ast,
1719
utils::duplicate_config_err,
@@ -58,12 +60,10 @@ impl TryFrom<ast::AttributeArgs> for EventConfig {
5860
));
5961
}
6062

61-
if let Some(lit_str) = anonymous {
62-
return Err(duplicate_config_err(
63-
lit_str,
64-
arg,
65-
"signature_topic",
66-
"event",
63+
if signature_topic.is_some() {
64+
return Err(format_err!(
65+
arg.span(),
66+
"encountered duplicate ink! configuration argument"
6767
));
6868
}
6969
signature_topic = Some(SignatureTopicArg::try_from(&arg.value)?);

crates/ink/ir/src/ir/event/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ use crate::{
2929
utils::extract_cfg_attributes,
3030
};
3131

32-
pub use signature_topic::SignatureTopicArg;
32+
pub use signature_topic::{
33+
SignatureTopic,
34+
SignatureTopicArg,
35+
};
3336

3437
/// A checked ink! event with its configuration.
3538
#[derive(Debug, PartialEq, Eq)]

crates/ink/ir/src/ir/event/signature_topic.rs

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,22 @@
1313
// limitations under the License.
1414

1515
use hex::decode_to_slice;
16+
use proc_macro2::{
17+
Span,
18+
TokenStream as TokenStream2,
19+
};
20+
use quote::ToTokens;
21+
use syn::spanned::Spanned;
1622

17-
use crate::ast;
23+
use crate::{
24+
ast,
25+
error::ExtError,
26+
utils::extract_cfg_attributes,
27+
};
1828

19-
/// The signature topic of an event variant.
29+
/// The signature topic argument of an event variant.
2030
///
21-
/// Calculated with `blake2b("Event(field1_type,field2_type)")`.
31+
/// Used as part of `ink::event` macro.
2232
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
2333
pub struct SignatureTopicArg {
2434
topic: [u8; 32],
@@ -66,3 +76,82 @@ impl TryFrom<&ast::MetaValue> for SignatureTopicArg {
6676
}
6777
}
6878
}
79+
80+
impl TryFrom<ast::AttributeArgs> for Option<SignatureTopicArg> {
81+
type Error = syn::Error;
82+
83+
fn try_from(args: ast::AttributeArgs) -> Result<Self, Self::Error> {
84+
let mut signature_topic: Option<SignatureTopicArg> = None;
85+
for arg in args.into_iter() {
86+
if arg.name.is_ident("hash") {
87+
if signature_topic.is_some() {
88+
return Err(format_err!(
89+
arg.span(),
90+
"encountered duplicate ink! event configuration argument"
91+
));
92+
}
93+
signature_topic = Some(SignatureTopicArg::try_from(&arg.value)?);
94+
} else {
95+
return Err(format_err_spanned!(
96+
arg,
97+
"encountered unknown or unsupported ink! storage item configuration argument",
98+
));
99+
}
100+
}
101+
Ok(signature_topic)
102+
}
103+
}
104+
105+
/// The signature topic argument of an event variant.
106+
///
107+
/// Used as part of `ink::signature_topic` macro.
108+
///
109+
/// Calculated with `blake2b("Event(field1_type,field2_type)")`.
110+
#[derive(Debug, PartialEq, Eq)]
111+
pub struct SignatureTopic {
112+
item: syn::ItemStruct,
113+
arg: Option<SignatureTopicArg>,
114+
}
115+
116+
impl SignatureTopic {
117+
pub fn new(config: TokenStream2, item: TokenStream2) -> Result<Self, syn::Error> {
118+
let item = syn::parse2::<syn::ItemStruct>(item.clone()).map_err(|err| {
119+
err.into_combine(format_err_spanned!(
120+
item,
121+
"event definition must be a `struct`",
122+
))
123+
})?;
124+
let parsed_config = syn::parse2::<crate::ast::AttributeArgs>(config)?;
125+
let arg = Option::<SignatureTopicArg>::try_from(parsed_config)?;
126+
127+
for attr in &item.attrs {
128+
if attr
129+
.path()
130+
.to_token_stream()
131+
.to_string()
132+
.contains("signature_topic")
133+
{
134+
return Err(format_err_spanned!(
135+
attr,
136+
"only one `ink::signature_topic` is allowed",
137+
));
138+
}
139+
}
140+
Ok(Self { item, arg })
141+
}
142+
143+
/// Returns the event definition .
144+
pub fn item(&self) -> &syn::ItemStruct {
145+
&self.item
146+
}
147+
148+
/// Return manually specified signature topic hash.
149+
pub fn signature_topic(&self) -> Option<[u8; 32]> {
150+
self.arg.map(|a| a.signature_topic())
151+
}
152+
153+
/// Returns a list of `cfg` attributes if any.
154+
pub fn get_cfg_attrs(&self, span: Span) -> Vec<TokenStream2> {
155+
extract_cfg_attributes(&self.item.attrs, span)
156+
}
157+
}

crates/ink/ir/src/ir/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ pub use self::{
7272
contract::Contract,
7373
event::{
7474
Event,
75+
SignatureTopic,
7576
SignatureTopicArg,
7677
},
7778
ink_test::InkTest,

0 commit comments

Comments
 (0)