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

Make #[ink(selector = ..)] take a u32 parameter instead of a string #928

Merged
merged 7 commits into from
Sep 20, 2021
Merged
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: 2 additions & 1 deletion .config/cargo_spellcheck.dic
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
90
100

ABI
AST
Expand Down Expand Up @@ -91,3 +91,4 @@ runtime/S
struct/S
vec/S
vector/S
wildcard/S
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,8 @@ In a module annotated with `#[ink::contract]` these attributes are available:
| `#[ink(message)]` | Applicable to methods. | Flags a method for the ink! storage struct as message making it available to the API for calling the contract. |
| `#[ink(constructor)]` | Applicable to method. | Flags a method for the ink! storage struct as constructor making it available to the API for instantiating the contract. |
| `#[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 = "..")]` | 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(namespace = "..")]` | 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(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(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! |
Copy link
Collaborator

Choose a reason for hiding this comment

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

What do S and N refer to here? We should add some context above the table to clarify.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

S and N are the parameter names with respect to normal Rust syntax respectively.
S is the input selector of type u32 and N is the input namespace of type string.

| `#[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. |

See [here](https://paritytech.github.io/ink/ink_lang/attr.contract.html) for a more detailed description of those and also for details on the `#[ink::contract]` macro.
Expand Down
12 changes: 12 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
# Version 3.0-rc6 (UNRELEASED)

This is the 6th release candidate for ink! 3.0.

## Changed

- Message and constructor selectors no longer take their inputs as string but as `u32` decodable integer. For example:

- It is no longer possible to specify a selector as `#[ink(selector = "0xC0DECAFE")]`.
- The newly allowed formats are `#[ink(selector = 0xC0DECAFE)]` and `#[ink(selector = 42)]`.
- Smart contract authors are required to update their smart contracts for this change.

# Version 3.0-rc5 (2021-09-08)

This is the 5th release candidate for ink! 3.0.
Expand Down
1 change: 0 additions & 1 deletion crates/lang/codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ proc-macro2 = "1.0"
derive_more = { version = "0.99", default-features = false, features = ["from"] }
itertools = "0.10"
either = { version = "1.5", default-features = false }
regex = "1.3"
blake2 = "0.9"
heck = "0.3.1"
scale = { package = "parity-scale-codec", version = "2.1", default-features = false, features = ["derive", "full"] }
Expand Down
1 change: 0 additions & 1 deletion crates/lang/ir/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ syn = { version = "1.0", features = ["parsing", "full", "visit", "extra-traits"]
proc-macro2 = "1.0"
itertools = { version = "0.10", default-features = false }
either = { version = "1.5", default-features = false }
regex = "1.3"
blake2 = "0.9"

[features]
Expand Down
96 changes: 35 additions & 61 deletions crates/lang/ir/src/ir/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ use proc_macro2::{
Ident,
Span,
};
use regex::Regex;
use std::collections::HashMap;
use syn::spanned::Spanned;

Expand Down Expand Up @@ -98,12 +97,12 @@ impl Attrs for syn::Item {
///
/// An attribute with a parameterized flag:
/// ```no_compile
/// #[ink(selector = "0xDEADBEEF")]
/// #[ink(selector = 0xDEADBEEF)]
/// ```
///
/// An attribute with multiple flags:
/// ```no_compile
/// #[ink(message, payable, selector = "0xDEADBEEF")]
/// #[ink(message, payable, selector = 0xDEADBEEF)]
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct InkAttribute {
Expand Down Expand Up @@ -314,7 +313,7 @@ pub enum AttributeArgKind {
Constructor,
/// `#[ink(payable)]`
Payable,
/// `#[ink(selector = "0xDEADBEEF")]`
/// `#[ink(selector = 0xDEADBEEF)]`
Selector,
/// `#[ink(extension = N: u32)]`
Extension,
Expand Down Expand Up @@ -367,7 +366,7 @@ pub enum AttributeArg {
/// Applied on ink! constructors or messages in order to specify that they
/// can receive funds from callers.
Payable,
/// `#[ink(selector = "0xDEADBEEF")]`
/// `#[ink(selector = 0xDEADBEEF)]`
///
/// Applied on ink! constructors or messages to manually control their
/// selectors.
Expand Down Expand Up @@ -732,19 +731,6 @@ impl InkAttribute {
}
}

/// Returns an error to notify about non-hex digits at a position.
fn err_non_hex(meta: &syn::Meta, pos: usize) -> syn::Error {
format_err_spanned!(meta, "encountered non-hex digit at position {}", pos)
}

/// Returns an error to notify about an invalid ink! selector.
fn invalid_selector_err_regex(meta: &syn::Meta) -> syn::Error {
format_err_spanned!(
meta,
"invalid selector - a selector must consist of four bytes in hex (e.g. `selector = \"0xCAFEBABE\"`)"
)
}

impl TryFrom<syn::NestedMeta> for AttributeFrag {
type Error = syn::Error;

Expand All @@ -754,46 +740,28 @@ impl TryFrom<syn::NestedMeta> for AttributeFrag {
match &meta {
syn::Meta::NameValue(name_value) => {
if name_value.path.is_ident("selector") {
if let syn::Lit::Str(lit_str) = &name_value.lit {
let regex = Regex::new(r"0x([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})([\da-fA-F]{2})")
.map_err(|_| {
invalid_selector_err_regex(&meta)
})?;
let str = lit_str.value();
let cap =
regex.captures(&str).ok_or_else(|| {
invalid_selector_err_regex(&meta)
if let syn::Lit::Int(lit_int) = &name_value.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
)
})?;
if !regex.is_match(&str) {
return Err(invalid_selector_err_regex(
&meta,
))
}
let len_digits = (str.as_bytes().len() - 2) / 2;
if len_digits != 4 {
return Err(format_err!(
name_value,
"expected 4-digit hexcode for `selector` argument, found {} digits",
len_digits,
))
}
let selector_bytes = [
u8::from_str_radix(&cap[1], 16)
.map_err(|_| err_non_hex(&meta, 0))?,
u8::from_str_radix(&cap[2], 16)
.map_err(|_| err_non_hex(&meta, 1))?,
u8::from_str_radix(&cap[3], 16)
.map_err(|_| err_non_hex(&meta, 2))?,
u8::from_str_radix(&cap[4], 16)
.map_err(|_| err_non_hex(&meta, 3))?,
];
let selector = Selector::from_bytes(selector_u32.to_be_bytes());
return Ok(AttributeFrag {
ast: meta,
arg: AttributeArg::Selector(Selector::from_bytes(
selector_bytes,
)),
arg: AttributeArg::Selector(selector),
})
}
if let syn::Lit::Str(_) = &name_value.lit {
return Err(format_err!(
name_value,
"#[ink(selector = ..)] attributes with string inputs are deprecated. \
use an integer instead, e.g. #[ink(selector = 1)] or #[ink(selector = 0xC0DECAFE)]."
))
}
return Err(format_err!(name_value, "expecteded 4-digit hexcode for `selector` argument, e.g. #[ink(selector = 0xC0FEBABE]"))
}
if name_value.path.is_ident("namespace") {
Expand Down Expand Up @@ -1066,7 +1034,7 @@ mod tests {
fn selector_works() {
assert_attribute_try_from(
syn::parse_quote! {
#[ink(selector = "0xDEADBEEF")]
#[ink(selector = 0xDEADBEEF)]
},
Ok(test::Attribute::Ink(vec![AttributeArg::Selector(
Selector::from_bytes([0xDE, 0xAD, 0xBE, 0xEF]),
Expand All @@ -1075,30 +1043,36 @@ mod tests {
}

#[test]
fn selector_non_hexcode() {
fn selector_negative_number() {
assert_attribute_try_from(
syn::parse_quote! {
#[ink(selector = "0xhelloworld")]
#[ink(selector = -1)]
},
Err("invalid selector - a selector must consist of four bytes in hex (e.g. `selector = \"0xCAFEBABE\"`)"),
Err(
"selector value out of range. selector must be a valid `u32` integer: \
invalid digit found in string",
),
);
}

#[test]
fn selector_too_long() {
fn selector_out_of_range() {
assert_attribute_try_from(
syn::parse_quote! {
#[ink(selector = "0xDEADBEEFC0FEBABE")]
#[ink(selector = 0xFFFF_FFFF_FFFF_FFFF)]
},
Err("expected 4-digit hexcode for `selector` argument, found 8 digits"),
Err(
"selector value out of range. \
selector must be a valid `u32` integer: number too large to fit in target type"
),
);
}

#[test]
fn selector_invalid_type() {
assert_attribute_try_from(
syn::parse_quote! {
#[ink(selector = 42)]
#[ink(selector = true)]
},
Err("expecteded 4-digit hexcode for `selector` argument, e.g. #[ink(selector = 0xC0FEBABE]"),
);
Expand Down
4 changes: 2 additions & 2 deletions crates/lang/ir/src/ir/item_impl/callable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ pub trait Callable {
///
/// ```no_compile
/// impl MyStorage {
/// #[ink(message, selector = "0xDEADBEEF")]
/// #[ink(message, selector = 0xDEADBEEF)]
/// fn my_message(&self) {}
/// }
/// ```
Expand Down Expand Up @@ -611,7 +611,7 @@ mod tests {
impl MyTrait for MyStorage {}
},
syn::parse_quote! {
#[ink(message, selector = "0xDEADBEEF")]
#[ink(message, selector = 0xDEADBEEF)]
fn my_message(&self) {}
},
[0xDE, 0xAD, 0xBE, 0xEF],
Expand Down
2 changes: 1 addition & 1 deletion crates/lang/ir/src/ir/item_impl/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ mod tests {
true,
syn::parse_quote! {
#[ink(message)]
#[ink(selector = "0xDEADBEEF", payable)]
#[ink(selector = 0xDEADBEEF, payable)]
pub fn my_message(&self) {}
},
),
Expand Down
31 changes: 14 additions & 17 deletions crates/lang/ir/src/ir/item_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@ impl ItemMod {
format_err!(
second_span,
"encountered ink! {}s with overlapping selectors (= {:02X?})\n\
hint: use #[ink(selector = \"0x...\")] on the callable or \
#[ink(namespace = \"...\")] on the implementation block to \
hint: use #[ink(selector = S:u32)] on the callable or \
#[ink(namespace = N:string)] on the implementation block to \
disambiguate overlapping selectors.",
kind,
selector.as_bytes(),
Expand Down Expand Up @@ -713,20 +713,19 @@ mod tests {
#[ink(constructor)]
pub fn my_constructor() -> Self {}

#[ink(message, selector = "0xDEADBEEF")]
#[ink(message, selector = 0xDEADBEEF)]
pub fn my_message_1(&self) {}
}

impl MyStorage {
#[ink(message, selector = "0xDEADBEEF")]
#[ink(message, selector = 0xDEADBEEF)]
pub fn my_message_2(&self) {}
}
}
},
"encountered ink! messages with overlapping selectors (= [DE, AD, BE, EF])\n\
hint: use #[ink(selector = \"0x...\")] on the callable or \
#[ink(namespace = \"...\")] on the implementation block to \
disambiguate overlapping selectors.",
hint: use #[ink(selector = S:u32)] on the callable or #[ink(namespace = N:string)] \
on the implementation block to disambiguate overlapping selectors.",
);
}

Expand All @@ -739,23 +738,22 @@ mod tests {
pub struct MyStorage {}

impl MyStorage {
#[ink(constructor, selector = "0xDEADBEEF")]
#[ink(constructor, selector = 0xDEADBEEF)]
pub fn my_constructor_1() -> Self {}

#[ink(message)]
pub fn my_message_1(&self) {}
}

impl MyStorage {
#[ink(constructor, selector = "0xDEADBEEF")]
#[ink(constructor, selector = 0xDEADBEEF)]
pub fn my_constructor_2() -> Self {}
}
}
},
"encountered ink! constructors with overlapping selectors (= [DE, AD, BE, EF])\n\
hint: use #[ink(selector = \"0x...\")] on the callable or \
#[ink(namespace = \"...\")] on the implementation block to \
disambiguate overlapping selectors.",
hint: use #[ink(selector = S:u32)] on the callable or #[ink(namespace = N:string)] \
on the implementation block to disambiguate overlapping selectors.",
);
}

Expand All @@ -782,9 +780,8 @@ mod tests {
}
},
"encountered ink! messages with overlapping selectors (= [04, C4, 94, 46])\n\
hint: use #[ink(selector = \"0x...\")] on the callable or \
#[ink(namespace = \"...\")] on the implementation block to \
disambiguate overlapping selectors.",
hint: use #[ink(selector = S:u32)] on the callable or #[ink(namespace = N:string)] \
on the implementation block to disambiguate overlapping selectors.",
);
}

Expand Down Expand Up @@ -824,10 +821,10 @@ mod tests {
pub struct MyStorage {}

impl MyStorage {
#[ink(constructor, selector = "0xDEADBEEF")]
#[ink(constructor, selector = 0xDEADBEEF)]
pub fn my_constructor() -> Self {}

#[ink(message, selector = "0xDEADBEEF")]
#[ink(message, selector = 0xDEADBEEF)]
pub fn my_message(&self) {}
}
}
Expand Down
6 changes: 3 additions & 3 deletions crates/lang/macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,20 +356,20 @@ use proc_macro::TokenStream;
/// # }
/// impl Flipper {
/// #[ink(constructor)]
/// #[ink(selector = "0xDEADBEEF")] // Works on constructors as well.
/// #[ink(selector = 0xDEADBEEF)] // Works on constructors as well.
/// pub fn new(initial_value: bool) -> Self {
/// Flipper { value: false }
/// }
///
/// # /// Flips the current value.
/// # #[ink(message)]
/// # #[ink(selector = "0xCAFEBABE")] // You can either specify selector out-of-line.
/// # #[ink(selector = 0xCAFEBABE)] // You can either specify selector out-of-line.
/// # pub fn flip(&mut self) {
/// # self.value = !self.value;
/// # }
/// #
/// /// Returns the current value.
/// #[ink(message, selector = "0xFEEDBEEF")] // ... or specify selector inline.
/// #[ink(message, selector = 0xFEEDBEEF)] // ... or specify selector inline.
/// pub fn get(&self) -> bool {
/// self.value
/// }
Expand Down
1 change: 1 addition & 0 deletions crates/lang/macro/tests/compile_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ fn compile_tests() {
t.compile_fail("tests/ui/fail/M-03-message-returns-self.rs");
t.compile_fail("tests/ui/fail/M-04-message-returns-non-codec.rs");
t.compile_fail("tests/ui/fail/M-05-message-invalid-selector.rs");
t.compile_fail("tests/ui/fail/M-06-message-invalid-selector-type.rs");
t.compile_fail("tests/ui/fail/M-10-method-unknown-ink-marker.rs");

t.compile_fail("tests/ui/fail/S-01-missing-storage-struct.rs");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ mod message_invalid_selector {
Self {}
}

#[ink(message, selector = "0x00")]
#[ink(message, selector = "0xC0DECAFE")]
pub fn invalid_selector(&self) {}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: invalid selector - a selector must consist of four bytes in hex (e.g. `selector = "0xCAFEBABE"`)
error: #[ink(selector = ..)] attributes with string inputs are deprecated. use an integer instead, e.g. #[ink(selector = 1)] or #[ink(selector = 0xC0DECAFE)].
--> $DIR/M-05-message-invalid-selector.rs:14:24
|
14 | #[ink(message, selector = "0x00")]
| ^^^^^^^^^^^^^^^^^
14 | #[ink(message, selector = "0xC0DECAFE")]
| ^^^^^^^^^^^^^^^^^^^^^^^
Loading