Skip to content

Commit

Permalink
add signature_topic macro
Browse files Browse the repository at this point in the history
  • Loading branch information
SkymanOne committed Dec 21, 2023
1 parent 48cec1f commit b802df2
Show file tree
Hide file tree
Showing 17 changed files with 277 additions and 51 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/ink/codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ blake2 = { workspace = true }
heck = { workspace = true }
scale = { workspace = true }
impl-serde = { workspace = true, default-features = true }
hex = { workspace = true }

serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
Expand Down
40 changes: 6 additions & 34 deletions crates/ink/codegen/src/generator/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

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

quote::quote! (
#( #cfg_attrs )*
#signature_topic

#( #cfg_attrs )*
#[cfg_attr(feature = "std", derive(::ink::EventMetadata))]
#[derive(::ink::Event)]
#[::ink::scale_derive(Encode, Decode)]
#signature_topic
#anonymous
#item
)
Expand All @@ -54,42 +51,17 @@ impl GenerateCode for Event<'_> {

impl Event<'_> {
fn generate_signature_topic(&self) -> TokenStream2 {
let item_ident = &self.item.item().ident;
let signature_topic = if let Some(bytes) = self.item.signature_topic() {
let hash_bytes = bytes.map(|b| b.hex_padded_suffixed());
let hash_string = hex::encode(bytes);
quote! {
::core::option::Option::Some([ #( #hash_bytes ),* ])
#[::ink::signature_topic(hash = #hash_string)]
}
} else if self.item.anonymous() {
quote! { ::core::option::Option::None }
quote! {}
} else {
let calculated_topic = signature_topic(&self.item.item().fields, item_ident);
quote! { ::core::option::Option::Some(#calculated_topic) }
quote! { #[::ink::signature_topic] }
};

quote! {
impl ::ink::env::GetSignatureTopic for #item_ident {
fn signature_topic() -> Option<[u8; 32]> {
#signature_topic
}
}
}
quote! { #signature_topic }
}
}

/// The signature topic of an event variant.
///
/// Calculated with `blake2b("Event(field1_type,field2_type)")`.
fn signature_topic(fields: &syn::Fields, event_ident: &syn::Ident) -> TokenStream2 {
let fields = fields
.iter()
.map(|field| {
quote::ToTokens::to_token_stream(&field.ty)
.to_string()
.replace(' ', "")
})
.collect::<Vec<_>>()
.join(",");
let topic_str = format!("{}({fields})", event_ident);
quote!(::ink::blake2x256!(#topic_str))
}
2 changes: 2 additions & 0 deletions crates/ink/codegen/src/generator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ mod ink_test;
mod item_impls;
mod metadata;
mod selector;
mod signature_topic;
mod storage;
mod storage_item;
mod trait_def;
Expand Down Expand Up @@ -67,6 +68,7 @@ pub use self::{
SelectorBytes,
SelectorId,
},
signature_topic::SignatureTopic,
storage::Storage,
storage_item::StorageItem,
trait_def::TraitDefinition,
Expand Down
80 changes: 80 additions & 0 deletions crates/ink/codegen/src/generator/signature_topic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (C) 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.

use crate::GenerateCode;
use derive_more::From;
use ir::HexLiteral;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::spanned::Spanned;

/// Generates code to generate signature topic.
#[derive(From, Copy, Clone)]
pub struct SignatureTopic<'a> {
/// The event item to generate code for.
item: &'a ir::SignatureTopic,
}

impl GenerateCode for SignatureTopic<'_> {
/// Generates ink! signature topic item code.
fn generate_code(&self) -> TokenStream2 {
let item = self.item.item();
let signature_topic = self.generate_signature_topic();
let cfg_attrs = self.item.get_cfg_attrs(item.span());

quote::quote! (
#( #cfg_attrs )*
#signature_topic
#item
)
}
}

impl SignatureTopic<'_> {
fn generate_signature_topic(&self) -> TokenStream2 {
let item_ident = &self.item.item().ident;
let signature_topic = if let Some(bytes) = self.item.signature_topic() {
let hash_bytes = bytes.map(|b| b.hex_padded_suffixed());
quote! { ::core::option::Option::Some([ #( #hash_bytes ),* ]) }
} else {
let calculated_topic = signature_topic(&self.item.item().fields, item_ident);
quote! { ::core::option::Option::Some(#calculated_topic) }
};

quote! {
impl ::ink::env::GetSignatureTopic for #item_ident {
fn signature_topic() -> Option<[u8; 32]> {
#signature_topic
}
}
}
}
}

/// The signature topic of an event variant.
///
/// Calculated with `blake2b("Event(field1_type,field2_type)")`.
fn signature_topic(fields: &syn::Fields, event_ident: &syn::Ident) -> TokenStream2 {
let fields = fields
.iter()
.map(|field| {
quote::ToTokens::to_token_stream(&field.ty)
.to_string()
.replace(' ', "")
})
.collect::<Vec<_>>()
.join(",");
let topic_str = format!("{}({fields})", event_ident);
quote!(::ink::blake2x256!(#topic_str))
}
4 changes: 4 additions & 0 deletions crates/ink/codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ impl<'a> CodeGenerator for &'a ir::Event {
type Generator = generator::Event<'a>;
}

impl<'a> CodeGenerator for &'a ir::SignatureTopic {
type Generator = generator::SignatureTopic<'a>;
}

impl<'a> CodeGenerator for &'a ir::StorageItem {
type Generator = generator::StorageItem<'a>;
}
Expand Down
12 changes: 6 additions & 6 deletions crates/ink/ir/src/ir/event/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use syn::spanned::Spanned;

use crate::{
ast,
utils::duplicate_config_err,
Expand Down Expand Up @@ -58,12 +60,10 @@ impl TryFrom<ast::AttributeArgs> for EventConfig {
));
}

if let Some(lit_str) = anonymous {
return Err(duplicate_config_err(
lit_str,
arg,
"signature_topic",
"event",
if signature_topic.is_some() {
return Err(format_err!(
arg.span(),
"encountered duplicate ink! configuration argument"
));
}
signature_topic = Some(SignatureTopicArg::try_from(&arg.value)?);
Expand Down
5 changes: 4 additions & 1 deletion crates/ink/ir/src/ir/event/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ use crate::{
utils::extract_cfg_attributes,
};

pub use signature_topic::SignatureTopicArg;
pub use signature_topic::{
SignatureTopic,
SignatureTopicArg,
};

/// A checked ink! event with its configuration.
#[derive(Debug, PartialEq, Eq)]
Expand Down
95 changes: 92 additions & 3 deletions crates/ink/ir/src/ir/event/signature_topic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,22 @@
// limitations under the License.

use hex::decode_to_slice;
use proc_macro2::{
Span,
TokenStream as TokenStream2,
};
use quote::ToTokens;
use syn::spanned::Spanned;

use crate::ast;
use crate::{
ast,
error::ExtError,
utils::extract_cfg_attributes,
};

/// The signature topic of an event variant.
/// The signature topic argument of an event variant.
///
/// Calculated with `blake2b("Event(field1_type,field2_type)")`.
/// Used as part of `ink::event` macro.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SignatureTopicArg {
topic: [u8; 32],
Expand Down Expand Up @@ -66,3 +76,82 @@ impl TryFrom<&ast::MetaValue> for SignatureTopicArg {
}
}
}

impl TryFrom<ast::AttributeArgs> for Option<SignatureTopicArg> {
type Error = syn::Error;

fn try_from(args: ast::AttributeArgs) -> Result<Self, Self::Error> {
let mut signature_topic: Option<SignatureTopicArg> = None;
for arg in args.into_iter() {
if arg.name.is_ident("hash") {
if signature_topic.is_some() {
return Err(format_err!(
arg.span(),
"encountered duplicate ink! event configuration argument"
));
}
signature_topic = Some(SignatureTopicArg::try_from(&arg.value)?);
} else {
return Err(format_err_spanned!(
arg,
"encountered unknown or unsupported ink! storage item configuration argument",
));
}
}
Ok(signature_topic)
}
}

/// The signature topic argument of an event variant.
///
/// Used as part of `ink::signature_topic` macro.
///
/// Calculated with `blake2b("Event(field1_type,field2_type)")`.
#[derive(Debug, PartialEq, Eq)]
pub struct SignatureTopic {
item: syn::ItemStruct,
arg: Option<SignatureTopicArg>,
}

impl SignatureTopic {
pub fn new(config: TokenStream2, item: TokenStream2) -> Result<Self, syn::Error> {
let item = syn::parse2::<syn::ItemStruct>(item.clone()).map_err(|err| {
err.into_combine(format_err_spanned!(
item,
"event definition must be a `struct`",
))
})?;
let parsed_config = syn::parse2::<crate::ast::AttributeArgs>(config)?;
let arg = Option::<SignatureTopicArg>::try_from(parsed_config)?;

for attr in &item.attrs {
if attr
.path()
.to_token_stream()
.to_string()
.contains("signature_topic")
{
return Err(format_err_spanned!(
attr,
"only one `ink::signature_topic` is allowed",
));
}
}
Ok(Self { item, arg })
}

/// Returns the event definition .
pub fn item(&self) -> &syn::ItemStruct {
&self.item
}

/// Return manually specified signature topic hash.
pub fn signature_topic(&self) -> Option<[u8; 32]> {
self.arg.map(|a| a.signature_topic())
}

/// Returns a list of `cfg` attributes if any.
pub fn get_cfg_attrs(&self, span: Span) -> Vec<TokenStream2> {
extract_cfg_attributes(&self.item.attrs, span)
}
}
1 change: 1 addition & 0 deletions crates/ink/ir/src/ir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ pub use self::{
contract::Contract,
event::{
Event,
SignatureTopic,
SignatureTopicArg,
},
ink_test::InkTest,
Expand Down
1 change: 1 addition & 0 deletions crates/ink/ir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ pub use self::{
Receiver,
Selector,
SelectorMacro,
SignatureTopic,
Storage,
StorageItem,
Visibility,
Expand Down
Loading

0 comments on commit b802df2

Please sign in to comment.