Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Support conditional compilation - [#1707](https://github.com/paritytech/ink/pull/1707)

### Removed
- Remove wildcard selectors [#1706](https://github.com/paritytech/ink/pull/1706)

## Version 4.0.1

### Fixed
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,6 @@ In a module annotated with `#[ink::contract]` these attributes are available:
| `#[ink(topic)]` | Applicable on ink! event field. | 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 fields. Similar semantics as to indexed event arguments in Solidity. |
| `#[ink(payable)]` | Applicable to ink! messages. | Allows receiving value as part of the call of the ink! message. ink! constructors are implicitly payable. |
| `#[ink(selector = S:u32)]` | Applicable to ink! messages and ink! constructors. | Specifies a concrete dispatch selector for the flagged entity. This allows a contract author to precisely control the selectors of their APIs making it possible to rename their API without breakage. |
| `#[ink(selector = _)]` | Applicable to ink! messages. | Specifies a fallback message that is invoked if no other ink! message matches a selector. |
| `#[ink(namespace = N:string)]` | Applicable to ink! trait implementation blocks. | Changes the resulting selectors of all the ink! messages and ink! constructors within the trait implementation. Allows to disambiguate between trait implementations with overlapping message or constructor names. Use only with great care and consideration! |
| `#[ink(impl)]` | Applicable to ink! implementation blocks. | Tells the ink! codegen that some implementation block shall be granted access to ink! internals even without it containing any ink! messages or ink! constructors. |

Expand Down
72 changes: 6 additions & 66 deletions crates/ink/codegen/src/generator/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,25 +98,6 @@ impl GenerateCode for Dispatch<'_> {
}

impl Dispatch<'_> {
/// Returns the index of the ink! message which has a wildcard selector, if existent.
fn query_wildcard_message(&self) -> Option<usize> {
self.contract
.module()
.impls()
.flat_map(|item_impl| item_impl.iter_messages())
.position(|item| item.has_wildcard_selector())
}

/// Returns the index of the ink! constructor which has a wildcard selector, if
/// existent.
fn query_wildcard_constructor(&self) -> Option<usize> {
self.contract
.module()
.impls()
.flat_map(|item_impl| item_impl.iter_constructors())
.position(|item| item.has_wildcard_selector())
}

/// Puts messages and their calculated selector ids in a single data structure
///
/// See [`MessageDispatchable`]
Expand Down Expand Up @@ -520,31 +501,6 @@ impl Dispatch<'_> {
}
)
});
let possibly_wildcard_selector_constructor = match self
.query_wildcard_constructor()
{
Some(wildcard_index) => {
let item = &constructors[wildcard_index];
let constructor_span = item.constructor.span();
let constructor_ident = constructor_variant_ident(wildcard_index);
let constructor_input = expand_constructor_input(
constructor_span,
storage_ident,
item.id.clone(),
);
quote! {
::core::result::Result::Ok(Self::#constructor_ident(
<#constructor_input as ::scale::Decode>::decode(input)
.map_err(|_| ::ink::reflect::DispatchError::InvalidParameters)?
))
}
}
None => {
quote! {
::core::result::Result::Err(::ink::reflect::DispatchError::UnknownSelector)
}
}
};

let constructor_execute = constructors.iter().enumerate().map(|(index, item)| {
let constructor_span = item.constructor.span();
Expand Down Expand Up @@ -621,7 +577,9 @@ impl Dispatch<'_> {
.map_err(|_| ::ink::reflect::DispatchError::InvalidSelector)?
{
#( #constructor_match , )*
_invalid => #possibly_wildcard_selector_constructor
_invalid => ::core::result::Result::Err(
::ink::reflect::DispatchError::UnknownSelector
)
}
}
}
Expand Down Expand Up @@ -725,26 +683,6 @@ impl Dispatch<'_> {
}
)
});
let possibly_wildcard_selector_message = match self.query_wildcard_message() {
Some(wildcard_index) => {
let item = messages.get(wildcard_index).unwrap();
let message_span = item.message.span();
let message_ident = message_variant_ident(wildcard_index);
let message_input =
expand_message_input(message_span, storage_ident, item.id.clone());
quote! {
::core::result::Result::Ok(Self::#message_ident(
<#message_input as ::scale::Decode>::decode(input)
.map_err(|_| ::ink::reflect::DispatchError::InvalidParameters)?
))
}
}
None => {
quote! {
::core::result::Result::Err(::ink::reflect::DispatchError::UnknownSelector)
}
}
};

let message_execute = messages
.iter()
Expand Down Expand Up @@ -818,7 +756,9 @@ impl Dispatch<'_> {
.map_err(|_| ::ink::reflect::DispatchError::InvalidSelector)?
{
#( #message_match , )*
_invalid => #possibly_wildcard_selector_message
_invalid => ::core::result::Result::Err(
::ink::reflect::DispatchError::UnknownSelector
)
}
}
}
Expand Down
149 changes: 44 additions & 105 deletions crates/ink/ir/src/ir/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ impl InkAttribute {
}

/// Returns the selector of the ink! attribute if any.
pub fn selector(&self) -> Option<SelectorOrWildcard> {
pub fn selector(&self) -> Option<Selector> {
self.args().find_map(|arg| {
if let ir::AttributeArg::Selector(selector) = arg.kind() {
return Some(*selector)
Expand All @@ -287,16 +287,6 @@ impl InkAttribute {
.any(|arg| matches!(arg.kind(), AttributeArg::Payable))
}

/// Returns `true` if the ink! attribute contains the wildcard selector.
pub fn has_wildcard_selector(&self) -> bool {
self.args().any(|arg| {
matches!(
arg.kind(),
AttributeArg::Selector(SelectorOrWildcard::Wildcard)
)
})
}

/// Returns `true` if the ink! attribute contains the `anonymous` argument.
pub fn is_anonymous(&self) -> bool {
self.args()
Expand Down Expand Up @@ -351,7 +341,6 @@ pub enum AttributeArgKind {
Constructor,
/// `#[ink(payable)]`
Payable,
/// `#[ink(selector = _)]`
/// `#[ink(selector = 0xDEADBEEF)]`
Selector,
/// `#[ink(extension = N: u32)]`
Expand Down Expand Up @@ -403,13 +392,10 @@ pub enum AttributeArg {
/// Applied on ink! constructors or messages in order to specify that they
/// can receive funds from callers.
Payable,
/// Can be either one of:
/// `#[ink(selector = 0xDEADBEEF)]`
///
/// - `#[ink(selector = 0xDEADBEEF)]` Applied on ink! constructors or messages to
/// manually control their selectors.
/// - `#[ink(selector = _)]` Applied on ink! messages to define a fallback messages
/// that is invoked if no other ink! message matches a given selector.
Selector(SelectorOrWildcard),
/// Applied on ink! constructors or messages to manually control their selectors.
Selector(Selector),
/// `#[ink(namespace = "my_namespace")]`
///
/// Applied on ink! trait implementation blocks to disambiguate other trait
Expand Down Expand Up @@ -497,7 +483,7 @@ impl core::fmt::Display for AttributeArg {
Self::Message => write!(f, "message"),
Self::Constructor => write!(f, "constructor"),
Self::Payable => write!(f, "payable"),
Self::Selector(selector) => core::fmt::Display::fmt(&selector, f),
Self::Selector(selector) => core::fmt::Debug::fmt(&selector, f),
Self::Extension(extension) => {
write!(f, "extension = {:?}", extension.into_u32())
}
Expand All @@ -510,76 +496,6 @@ impl core::fmt::Display for AttributeArg {
}
}

/// Either a wildcard selector or a specified selector.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SelectorOrWildcard {
/// A wildcard selector. If no other selector matches, the message/constructor
/// annotated with the wildcard selector will be invoked.
Wildcard,
/// A user provided selector.
UserProvided(ir::Selector),
}

impl SelectorOrWildcard {
/// Create a new `SelectorOrWildcard::Selector` from the supplied bytes.
fn selector(bytes: [u8; 4]) -> SelectorOrWildcard {
SelectorOrWildcard::UserProvided(Selector::from(bytes))
}
}

impl TryFrom<&ast::PathOrLit> for SelectorOrWildcard {
type Error = syn::Error;

fn try_from(value: &ast::PathOrLit) -> Result<Self, Self::Error> {
match value {
ast::PathOrLit::Lit(lit) => {
if let syn::Lit::Str(_) = lit {
return Err(format_err_spanned!(
lit,
"#[ink(selector = ..)] attributes with string inputs are deprecated. \
use an integer instead, e.g. #[ink(selector = 1)] or #[ink(selector = 0xC0DECAFE)]."
));
}
if let syn::Lit::Int(lit_int) = lit {
let selector_u32 = lit_int.base10_parse::<u32>()
.map_err(|error| {
format_err_spanned!(
lit_int,
"selector value out of range. selector must be a valid `u32` integer: {}",
error
)
})?;
let selector = Selector::from(selector_u32.to_be_bytes());
return Ok(SelectorOrWildcard::UserProvided(selector))
}
Err(format_err_spanned!(
value,
"expected 4-digit hexcode for `selector` argument, e.g. #[ink(selector = 0xC0FEBABE]"
))
}
ast::PathOrLit::Path(path) => {
if path.is_ident("_") {
Ok(SelectorOrWildcard::Wildcard)
} else {
Err(format_err_spanned!(
path,
"expected `selector` argument to be either a 4-digit hexcode or `_`"
))
}
}
}
}
}

impl core::fmt::Display for SelectorOrWildcard {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
match self {
Self::UserProvided(selector) => core::fmt::Debug::fmt(&selector, f),
Self::Wildcard => write!(f, "_"),
}
}
}

/// An ink! namespace applicable to a trait implementation block.
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Namespace {
Expand Down Expand Up @@ -902,8 +818,7 @@ impl Parse for AttributeFrag {
})?;
match ident.to_string().as_str() {
"selector" => {
SelectorOrWildcard::try_from(&name_value.value)
.map(AttributeArg::Selector)
selector_from_value(&name_value.value).map(AttributeArg::Selector)
}
"namespace" => {
Namespace::try_from(&name_value.value)
Expand Down Expand Up @@ -995,6 +910,42 @@ impl Parse for AttributeFrag {
}
}

/// Extract the 4 byte selector from the given `value` of the `#[ink(selector = ..)]`
/// attribute.
fn selector_from_value(value: &ast::PathOrLit) -> Result<Selector, syn::Error> {
if value.as_string().is_some() {
return Err(format_err_spanned!(
value,
"#[ink(selector = ..)] attributes with string inputs are deprecated. \
use an integer instead, e.g. #[ink(selector = 1)] or #[ink(selector = 0xC0DECAFE)]."
))
}
if let Some(lit_int) = value.as_lit_int() {
let selector_u32 = lit_int.base10_parse::<u32>().map_err(|error| {
format_err_spanned!(
lit_int,
"selector value out of range. selector must be a valid `u32` integer: {}",
error
)
})?;
let selector = Selector::from(selector_u32.to_be_bytes());
return Ok(selector)
}
if let ast::PathOrLit::Path(path) = value {
if path.is_ident("_") {
return Err(format_err_spanned!(
path,
"#[ink(selector = _)] wildcard attributes are no longer supported. For upgradeable \
contracts use `set_code_hash` instead."
))
}
}
Err(format_err_spanned!(
value,
"expected 4-digit hexcode for `selector` argument, e.g. #[ink(selector = 0xC0FEBABE]"
))
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -1156,27 +1107,15 @@ mod tests {
#[ink(selector = 42)]
},
Ok(test::Attribute::Ink(vec![AttributeArg::Selector(
SelectorOrWildcard::UserProvided(Selector::from([0, 0, 0, 42])),
Selector::from([0, 0, 0, 42]),
)])),
);
assert_attribute_try_from(
syn::parse_quote! {
#[ink(selector = 0xDEADBEEF)]
},
Ok(test::Attribute::Ink(vec![AttributeArg::Selector(
SelectorOrWildcard::selector([0xDE, 0xAD, 0xBE, 0xEF]),
)])),
);
}

#[test]
fn wildcard_selector_works() {
assert_attribute_try_from(
syn::parse_quote! {
#[ink(selector = _)]
},
Ok(test::Attribute::Ink(vec![AttributeArg::Selector(
SelectorOrWildcard::Wildcard,
Selector::from([0xDE, 0xAD, 0xBE, 0xEF]),
)])),
);
}
Expand Down
7 changes: 0 additions & 7 deletions crates/ink/ir/src/ir/item_impl/callable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,6 @@ where
<C as Callable>::is_payable(self.callable)
}

fn has_wildcard_selector(&self) -> bool {
<C as Callable>::has_wildcard_selector(self.callable)
}

fn visibility(&self) -> Visibility {
<C as Callable>::visibility(self.callable)
}
Expand Down Expand Up @@ -166,9 +162,6 @@ pub trait Callable {
/// Flagging as payable is done using the `#[ink(payable)]` attribute.
fn is_payable(&self) -> bool;

/// Returns `true` if the ink! callable is flagged as a wildcard selector.
fn has_wildcard_selector(&self) -> bool;

/// Returns the visibility of the ink! callable.
fn visibility(&self) -> Visibility;

Expand Down
Loading