paritytech / ink

@@ -73,6 +73,8 @@
Loading
73 73
/// Configuration for execution of ink! constructor.
74 74
#[derive(Debug, Copy, Clone)]
75 75
pub struct ExecuteConstructorConfig {
76 +
    /// Yields `true` if the ink! constructor accepts payment.
77 +
    pub payable: bool,
76 78
    /// Yields `true` if the dynamic storage allocator has been enabled.
77 79
    ///
78 80
    /// # Note
@@ -93,11 +95,14 @@
Loading
93 95
    f: F,
94 96
) -> Result<(), DispatchError>
95 97
where
96 -
    Contract: SpreadLayout + ContractRootKey,
98 +
    Contract: SpreadLayout + ContractRootKey + ContractEnv,
97 99
    F: FnOnce() -> R,
98 100
    <private::Seal<R> as ConstructorReturnType<Contract>>::ReturnValue: scale::Encode,
99 101
    private::Seal<R>: ConstructorReturnType<Contract>,
100 102
{
103 +
    if !config.payable {
104 +
        deny_payment::<<Contract as ContractEnv>::Env>()?;
105 +
    }
101 106
    if config.dynamic_storage_alloc {
102 107
        alloc::initialize(ContractPhase::Deploy);
103 108
    }
@@ -280,12 +285,6 @@
Loading
280 285
#[derive(Debug, Copy, Clone)]
281 286
pub struct ExecuteMessageConfig {
282 287
    /// Yields `true` if the ink! message accepts payment.
283 -
    ///
284 -
    /// # Note
285 -
    ///
286 -
    /// If no ink! message within the same ink! smart contract
287 -
    /// is payable then this flag will be `true` since the check
288 -
    /// then is moved before the message dispatch as an optimization.
289 288
    pub payable: bool,
290 289
    /// Yields `true` if the ink! message might mutate contract storage.
291 290
    ///

@@ -67,6 +67,8 @@
Loading
67 67
pub struct Constructor {
68 68
    /// The underlying Rust method item.
69 69
    pub(super) item: syn::ImplItemMethod,
70 +
    /// If the ink! constructor can receive funds.
71 +
    is_payable: bool,
70 72
    /// An optional user provided selector.
71 73
    ///
72 74
    /// # Note
@@ -158,15 +160,9 @@
Loading
158 160
            &ir::AttributeArgKind::Constructor,
159 161
            |arg| {
160 162
                match arg.kind() {
161 -
                    ir::AttributeArg::Constructor | ir::AttributeArg::Selector(_) => {
162 -
                        Ok(())
163 -
                    }
164 -
                    ir::AttributeArg::Payable => {
165 -
                        Err(Some(format_err!(
166 -
                            arg.span(),
167 -
                            "constructors are implicitly payable"
168 -
                        )))
169 -
                    }
163 +
                    ir::AttributeArg::Constructor
164 +
                    | ir::AttributeArg::Payable
165 +
                    | ir::AttributeArg::Selector(_) => Ok(()),
170 166
                    _ => Err(None),
171 167
                }
172 168
            },
@@ -182,9 +178,11 @@
Loading
182 178
        Self::ensure_valid_return_type(&method_item)?;
183 179
        Self::ensure_no_self_receiver(&method_item)?;
184 180
        let (ink_attrs, other_attrs) = Self::sanitize_attributes(&method_item)?;
181 +
        let is_payable = ink_attrs.is_payable();
185 182
        let selector = ink_attrs.selector();
186 183
        Ok(Constructor {
187 184
            selector,
185 +
            is_payable,
188 186
            item: syn::ImplItemMethod {
189 187
                attrs: other_attrs,
190 188
                ..method_item
@@ -217,7 +215,7 @@
Loading
217 215
    }
218 216
219 217
    fn is_payable(&self) -> bool {
220 -
        true
218 +
        self.is_payable
221 219
    }
222 220
223 221
    fn visibility(&self) -> Visibility {
@@ -302,6 +300,52 @@
Loading
302 300
        }
303 301
    }
304 302
303 +
    #[test]
304 +
    fn is_payable_works() {
305 +
        let test_inputs: Vec<(bool, syn::ImplItemMethod)> = vec![
306 +
            // Not payable.
307 +
            (
308 +
                false,
309 +
                syn::parse_quote! {
310 +
                    #[ink(constructor)]
311 +
                    fn my_constructor() -> Self {}
312 +
                },
313 +
            ),
314 +
            // Normalized ink! attribute.
315 +
            (
316 +
                true,
317 +
                syn::parse_quote! {
318 +
                    #[ink(constructor, payable)]
319 +
                    pub fn my_constructor() -> Self {}
320 +
                },
321 +
            ),
322 +
            // Different ink! attributes.
323 +
            (
324 +
                true,
325 +
                syn::parse_quote! {
326 +
                    #[ink(constructor)]
327 +
                    #[ink(payable)]
328 +
                    pub fn my_constructor() -> Self {}
329 +
                },
330 +
            ),
331 +
            // Another ink! attribute, separate and normalized attribute.
332 +
            (
333 +
                true,
334 +
                syn::parse_quote! {
335 +
                    #[ink(constructor)]
336 +
                    #[ink(selector = 0xDEADBEEF, payable)]
337 +
                    pub fn my_constructor() -> Self {}
338 +
                },
339 +
            ),
340 +
        ];
341 +
        for (expect_payable, item_method) in test_inputs {
342 +
            let is_payable = <ir::Constructor as TryFrom<_>>::try_from(item_method)
343 +
                .unwrap()
344 +
                .is_payable();
345 +
            assert_eq!(is_payable, expect_payable);
346 +
        }
347 +
    }
348 +
305 349
    #[test]
306 350
    fn visibility_works() {
307 351
        let test_inputs: Vec<(bool, syn::ImplItemMethod)> = vec![
@@ -577,12 +621,6 @@
Loading
577 621
                #[ink(event)]
578 622
                fn my_constructor() -> Self {}
579 623
            },
580 -
            // constructor + payable
581 -
            syn::parse_quote! {
582 -
                #[ink(constructor)]
583 -
                #[ink(payable)]
584 -
                fn my_constructor() -> Self {}
585 -
            },
586 624
        ];
587 625
        for item_method in item_methods {
588 626
            assert_try_from_fails(

@@ -0,0 +1,29 @@
Loading
1 +
use ink_lang as ink;
2 +
3 +
#[ink::contract]
4 +
mod contract {
5 +
    #[ink(storage)]
6 +
    pub struct Contract {}
7 +
8 +
    impl Contract {
9 +
        #[ink(constructor, selector = 0, payable)]
10 +
        pub fn constructor_0() -> Self {
11 +
            Self {}
12 +
        }
13 +
14 +
        #[ink(constructor, selector = 1, payable)]
15 +
        pub fn constructor_1() -> Self {
16 +
            Self {}
17 +
        }
18 +
19 +
        #[ink(message)]
20 +
        pub fn message(&self) {}
21 +
    }
22 +
}
23 +
24 +
use contract::Contract;
25 +
26 +
fn main() {
27 +
    assert!(<Contract as ::ink_lang::reflect::DispatchableConstructorInfo<0>>::PAYABLE);
28 +
    assert!(<Contract as ::ink_lang::reflect::DispatchableConstructorInfo<1>>::PAYABLE);
29 +
}

@@ -72,7 +72,7 @@
Loading
72 72
        let constructor_decoder_type =
73 73
            self.generate_constructor_decoder_type(&constructor_spans);
74 74
        let message_decoder_type = self.generate_message_decoder_type(&message_spans);
75 -
        let entry_points = self.generate_entry_points(&message_spans);
75 +
        let entry_points = self.generate_entry_points(&constructor_spans, &message_spans);
76 76
        quote! {
77 77
            #amount_dispatchables
78 78
            #contract_dispatchable_messages
@@ -267,6 +267,7 @@
Loading
267 267
            .map(|constructor| {
268 268
                let constructor_span = constructor.span();
269 269
                let constructor_ident = constructor.ident();
270 +
                let payable = constructor.is_payable();
270 271
                let selector_id = constructor.composed_selector().into_be_u32().hex_padded_suffixed();
271 272
                let selector_bytes = constructor.composed_selector().hex_lits();
272 273
                let input_bindings = generator::input_bindings(constructor.inputs());
@@ -280,6 +281,7 @@
Loading
280 281
                        const CALLABLE: fn(Self::Input) -> Self::Storage = |#input_tuple_bindings| {
281 282
                            #storage_ident::#constructor_ident( #( #input_bindings ),* )
282 283
                        };
284 +
                        const PAYABLE: ::core::primitive::bool = #payable;
283 285
                        const SELECTOR: [::core::primitive::u8; 4usize] = [ #( #selector_bytes ),* ];
284 286
                        const LABEL: &'static ::core::primitive::str = ::core::stringify!(#constructor_ident);
285 287
                    }
@@ -290,7 +292,7 @@
Loading
290 292
        )
291 293
    }
292 294
293 -
    /// Generate code for the [`ink_lang::DispatchableConstructorInfo`] trait implementations.
295 +
    /// Generate code for the [`ink_lang::DispatchableMessageInfo`] trait implementations.
294 296
    ///
295 297
    /// These trait implementations store relevant dispatch information for every
296 298
    /// dispatchable ink! constructor of the ink! smart contract.
@@ -402,15 +404,27 @@
Loading
402 404
    ///
403 405
    /// This generates the `deploy` and `call` functions with which the smart
404 406
    /// contract runtime mainly interacts with the ink! smart contract.
405 -
    fn generate_entry_points(&self, message_spans: &[proc_macro2::Span]) -> TokenStream2 {
407 +
    fn generate_entry_points(
408 +
        &self,
409 +
        constructor_spans: &[proc_macro2::Span],
410 +
        message_spans: &[proc_macro2::Span],
411 +
    ) -> TokenStream2 {
406 412
        let span = self.contract.module().storage().span();
407 413
        let storage_ident = self.contract.module().storage().ident();
414 +
        let any_constructor_accept_payment =
415 +
            self.any_constructor_accepts_payment_expr(constructor_spans);
408 416
        let any_message_accept_payment =
409 417
            self.any_message_accepts_payment_expr(message_spans);
410 418
        quote_spanned!(span=>
411 419
            #[cfg(not(test))]
412 420
            #[no_mangle]
421 +
            #[allow(clippy::nonminimal_bool)]
413 422
            fn deploy() {
423 +
                if !#any_constructor_accept_payment {
424 +
                    ::ink_lang::codegen::deny_payment::<<#storage_ident as ::ink_lang::reflect::ContractEnv>::Env>()
425 +
                        .unwrap_or_else(|error| ::core::panic!("{}", error))
426 +
                }
427 +
414 428
                ::ink_env::decode_input::<
415 429
                        <#storage_ident as ::ink_lang::reflect::ContractConstructorDecoder>::Type>()
416 430
                    .map_err(|_| ::ink_lang::reflect::DispatchError::CouldNotReadInput)
@@ -431,6 +445,7 @@
Loading
431 445
                    ::ink_lang::codegen::deny_payment::<<#storage_ident as ::ink_lang::reflect::ContractEnv>::Env>()
432 446
                        .unwrap_or_else(|error| ::core::panic!("{}", error))
433 447
                }
448 +
434 449
                ::ink_env::decode_input::<
435 450
                        <#storage_ident as ::ink_lang::reflect::ContractMessageDecoder>::Type>()
436 451
                    .map_err(|_| ::ink_lang::reflect::DispatchError::CouldNotReadInput)
@@ -532,6 +547,7 @@
Loading
532 547
                }
533 548
            }
534 549
        };
550 +
535 551
        let constructor_execute = (0..count_constructors).map(|index| {
536 552
            let constructor_span = constructor_spans[index];
537 553
            let constructor_ident = constructor_variant_ident(index);
@@ -542,14 +558,24 @@
Loading
542 558
                    }>>::IDS[#index]
543 559
                }>>::CALLABLE
544 560
            );
561 +
            let accepts_payment = quote_spanned!(constructor_span=>
562 +
                false ||
563 +
                <#storage_ident as ::ink_lang::reflect::DispatchableConstructorInfo<{
564 +
                    <#storage_ident as ::ink_lang::reflect::ContractDispatchableConstructors<{
565 +
                        <#storage_ident as ::ink_lang::reflect::ContractAmountDispatchables>::CONSTRUCTORS
566 +
                    }>>::IDS[#index]
567 +
                }>>::PAYABLE
568 +
            );
545 569
            let is_dynamic_storage_allocation_enabled = self
546 570
                .contract
547 571
                .config()
548 572
                .is_dynamic_storage_allocator_enabled();
573 +
549 574
            quote_spanned!(constructor_span=>
550 575
                Self::#constructor_ident(input) => {
551 576
                    ::ink_lang::codegen::execute_constructor::<#storage_ident, _, _>(
552 577
                        ::ink_lang::codegen::ExecuteConstructorConfig {
578 +
                            payable: #accepts_payment,
553 579
                            dynamic_storage_alloc: #is_dynamic_storage_allocation_enabled
554 580
                        },
555 581
                        move || { #constructor_callable(input) }
@@ -590,6 +616,7 @@
Loading
590 616
                }
591 617
592 618
                impl ::ink_lang::reflect::ExecuteDispatchable for __ink_ConstructorDecoder {
619 +
                    #[allow(clippy::nonminimal_bool)]
593 620
                    fn execute_dispatchable(self) -> ::core::result::Result<(), ::ink_lang::reflect::DispatchError> {
594 621
                        match self {
595 622
                            #( #constructor_execute ),*
@@ -685,8 +712,7 @@
Loading
685 712
                }
686 713
            }
687 714
        };
688 -
        let any_message_accept_payment =
689 -
            self.any_message_accepts_payment_expr(message_spans);
715 +
690 716
        let message_execute = (0..count_messages).map(|index| {
691 717
            let message_span = message_spans[index];
692 718
            let message_ident = message_variant_ident(index);
@@ -706,7 +732,6 @@
Loading
706 732
            );
707 733
            let accepts_payment = quote_spanned!(message_span=>
708 734
                false ||
709 -
                !#any_message_accept_payment ||
710 735
                <#storage_ident as ::ink_lang::reflect::DispatchableMessageInfo<{
711 736
                    <#storage_ident as ::ink_lang::reflect::ContractDispatchableMessages<{
712 737
                        <#storage_ident as ::ink_lang::reflect::ContractAmountDispatchables>::MESSAGES
@@ -724,6 +749,7 @@
Loading
724 749
                .contract
725 750
                .config()
726 751
                .is_dynamic_storage_allocator_enabled();
752 +
727 753
            quote_spanned!(message_span=>
728 754
                Self::#message_ident(input) => {
729 755
                    let config = ::ink_lang::codegen::ExecuteMessageConfig {
@@ -752,7 +778,6 @@
Loading
752 778
        quote_spanned!(span=>
753 779
            const _: () = {
754 780
                #[allow(non_camel_case_types)]
755 -
                // #[derive(::core::fmt::Debug, ::core::cmp::PartialEq)]
756 781
                pub enum __ink_MessageDecoder {
757 782
                    #( #message_variants ),*
758 783
                }
@@ -826,4 +851,33 @@
Loading
826 851
            { false #( || #message_is_payable )* }
827 852
        )
828 853
    }
854 +
855 +
    /// Generates code to express if any dispatchable ink! constructor accepts payment.
856 +
    ///
857 +
    /// This information can be used to speed-up dispatch since denying of payment
858 +
    /// can be generalized to work before dispatch happens if none of the ink! constructors
859 +
    /// accept payment anyways.
860 +
    fn any_constructor_accepts_payment_expr(
861 +
        &self,
862 +
        constructor_spans: &[proc_macro2::Span],
863 +
    ) -> TokenStream2 {
864 +
        assert_eq!(constructor_spans.len(), self.query_amount_constructors());
865 +
866 +
        let span = self.contract.module().storage().span();
867 +
        let storage_ident = self.contract.module().storage().ident();
868 +
        let count_constructors = self.query_amount_constructors();
869 +
        let constructor_is_payable = (0..count_constructors).map(|index| {
870 +
            let constructor_span = constructor_spans[index];
871 +
            quote_spanned!(constructor_span=>
872 +
                <#storage_ident as ::ink_lang::reflect::DispatchableConstructorInfo<{
873 +
                    <#storage_ident as ::ink_lang::reflect::ContractDispatchableConstructors<{
874 +
                        <#storage_ident as ::ink_lang::reflect::ContractAmountDispatchables>::CONSTRUCTORS
875 +
                    }>>::IDS[#index]
876 +
                }>>::PAYABLE
877 +
            )
878 +
        });
879 +
        quote_spanned!(span=>
880 +
            { false #( || #constructor_is_payable )* }
881 +
        )
882 +
    }
829 883
}

@@ -0,0 +1,23 @@
Loading
1 +
use ink_lang as ink;
2 +
3 +
#[ink::contract]
4 +
mod contract {
5 +
    #[ink(storage)]
6 +
    pub struct Contract {}
7 +
8 +
    impl Contract {
9 +
        #[ink(constructor, selector = 0)]
10 +
        pub fn constructor() -> Self {
11 +
            Self {}
12 +
        }
13 +
14 +
        #[ink(message)]
15 +
        pub fn message(&self) {}
16 +
    }
17 +
}
18 +
19 +
use contract::Contract;
20 +
21 +
fn main() {
22 +
    assert!(!<Contract as ::ink_lang::reflect::DispatchableConstructorInfo<0>>::PAYABLE);
23 +
}

@@ -0,0 +1,29 @@
Loading
1 +
use ink_lang as ink;
2 +
3 +
#[ink::contract]
4 +
mod contract {
5 +
    #[ink(storage)]
6 +
    pub struct Contract {}
7 +
8 +
    impl Contract {
9 +
        #[ink(constructor, selector = 0)]
10 +
        pub fn constructor_0() -> Self {
11 +
            Self {}
12 +
        }
13 +
14 +
        #[ink(constructor, selector = 1)]
15 +
        pub fn constructor_1() -> Self {
16 +
            Self {}
17 +
        }
18 +
19 +
        #[ink(message)]
20 +
        pub fn message(&self) {}
21 +
    }
22 +
}
23 +
24 +
use contract::Contract;
25 +
26 +
fn main() {
27 +
    assert!(!<Contract as ::ink_lang::reflect::DispatchableConstructorInfo<0>>::PAYABLE);
28 +
    assert!(!<Contract as ::ink_lang::reflect::DispatchableConstructorInfo<1>>::PAYABLE);
29 +
}

@@ -19,7 +19,7 @@
Loading
19 19
/// # Note
20 20
///
21 21
/// - This is automatically implemented by all ink! smart contracts.
22 -
/// - All ink! constructors and ink! messages of an ink! smart contract are dispatchables.  
22 +
/// - All ink! constructors and ink! messages of an ink! smart contract are dispatchables.
23 23
///   This explicitly includes ink! messages from ink! trait implementations.
24 24
///
25 25
/// # Usage
@@ -339,8 +339,12 @@
Loading
339 339
    /// The closure that can be used to dispatch into the dispatchable ink! constructor.
340 340
    const CALLABLE: fn(Self::Input) -> Self::Storage;
341 341
342 +
    /// Yields `true` if the dispatchable ink! constructor is payable.
343 +
    const PAYABLE: bool;
344 +
342 345
    /// The selectors of the dispatchable ink! constructor.
343 346
    const SELECTOR: [u8; 4];
347 +
344 348
    /// The label of the dispatchable ink! constructor.
345 349
    const LABEL: &'static str;
346 350
}
347 351
imilarity index 86%
348 352
ename from crates/lang/tests/ui/contract/fail/constructor-payable.rs
349 353
ename to crates/lang/tests/ui/contract/fail/constructor-payable-invalid-1.rs

@@ -0,0 +1,23 @@
Loading
1 +
use ink_lang as ink;
2 +
3 +
#[ink::contract]
4 +
mod contract {
5 +
    #[ink(storage)]
6 +
    pub struct Contract {}
7 +
8 +
    impl Contract {
9 +
        #[ink(constructor, selector = 0, payable)]
10 +
        pub fn constructor() -> Self {
11 +
            Self {}
12 +
        }
13 +
14 +
        #[ink(message)]
15 +
        pub fn message(&self) {}
16 +
    }
17 +
}
18 +
19 +
use contract::Contract;
20 +
21 +
fn main() {
22 +
    assert!(<Contract as ::ink_lang::reflect::DispatchableConstructorInfo<0>>::PAYABLE);
23 +
}
Files Coverage
crates 78.83%
Project Totals (252 files) 78.83%

No yaml found.

Create your codecov.yml to customize your Codecov experience

Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading