Skip to content

Commit 300b6c4

Browse files
ascjonesGerman
andauthored
Rework fallible constructors (#1491)
* Remove unused trait `InitializerReturnType` * WIP experimenting with static constructor return types * WIP * WIP generating metadata at compile time * WIP using constructor result type * Use generated method for metadata * Generate constructor info using static types * WIP restoring dispatch * WIP adding metadata test to ui test * Fix match as_result * Attempt to infer result type * Constructor dispatch compiles, remove metadata syntax stuff * Fmt * Remove unnecessary braces * Fix up execution for results * Fix up tests * Remove printlns * Explicitly set reverted flag * ReturnTypeSpec accurately reflects ABI * Polished refactor for constructor return type (#1500) * revert instantiating when error * display name + correct return typ when Self * fix dereferencing issue * UI test error variant + cleanup * cargo fmt * cargo fmt * fix another reference issue * fix spec contract test Co-authored-by: Andrew Jones <ascjones@gmail.com> * explicit prelude + fixing test error messages (#1503) * Add constructor to ReturnFlags to not require Default trait impl * Only invoke return_value for fallible constructors * Add e2e fallible constructor value tests * Fmt * Add some reflection tests for constructors returning an error * Check reflection for return types in UI tests * Check reflection in example * Fmt * Add test for fallible constructor succeeding * Add test for fallible constructor failing * Remove unused message * Fully qualify Ok(()) Co-authored-by: German <german@parity.io>
1 parent 6c6e51a commit 300b6c4

File tree

19 files changed

+589
-342
lines changed

19 files changed

+589
-342
lines changed

crates/e2e/src/client.rs

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,34 @@ where
321321
Ok(ret)
322322
}
323323

324+
/// Dry run contract instantiation using the given constructor.
325+
pub async fn instantiate_dry_run<CO: InkConstructor>(
326+
&mut self,
327+
signer: &Signer<C>,
328+
constructor: &CO,
329+
value: E::Balance,
330+
storage_deposit_limit: Option<E::Balance>,
331+
) -> ContractInstantiateResult<C::AccountId, E::Balance>
332+
where
333+
CO: InkConstructor,
334+
{
335+
let mut data = CO::SELECTOR.to_vec();
336+
<CO as scale::Encode>::encode_to(constructor, &mut data);
337+
338+
let code = crate::utils::extract_wasm(CO::CONTRACT_PATH);
339+
let salt = Self::salt();
340+
self.api
341+
.instantiate_with_code_dry_run(
342+
value,
343+
storage_deposit_limit,
344+
code.clone(),
345+
data.clone(),
346+
salt.clone(),
347+
signer,
348+
)
349+
.await
350+
}
351+
324352
/// Executes an `instantiate_with_code` call and captures the resulting events.
325353
async fn exec_instantiate<CO: InkConstructor>(
326354
&mut self,
@@ -337,13 +365,7 @@ where
337365
));
338366
<CO as scale::Encode>::encode_to(constructor, &mut data);
339367

340-
let salt = std::time::SystemTime::now()
341-
.duration_since(std::time::UNIX_EPOCH)
342-
.unwrap_or_else(|err| panic!("unable to get unix time: {}", err))
343-
.as_millis()
344-
.as_u128()
345-
.to_le_bytes()
346-
.to_vec();
368+
let salt = Self::salt();
347369

348370
// dry run the instantiate to calculate the gas limit
349371
let dry_run = self
@@ -429,6 +451,17 @@ where
429451
})
430452
}
431453

454+
/// Generate a unique salt based on the system time.
455+
fn salt() -> Vec<u8> {
456+
std::time::SystemTime::now()
457+
.duration_since(std::time::UNIX_EPOCH)
458+
.unwrap_or_else(|err| panic!("unable to get unix time: {}", err))
459+
.as_millis()
460+
.as_u128()
461+
.to_le_bytes()
462+
.to_vec()
463+
}
464+
432465
/// This function extracts the metadata of the contract at the file path
433466
/// `target/ink/$contract_name.contract`.
434467
///

crates/env/src/backend.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ pub struct ReturnFlags {
3636
}
3737

3838
impl ReturnFlags {
39+
/// Initialize [`ReturnFlags`] with the reverted flag.
40+
pub fn new_with_reverted(has_reverted: bool) -> Self {
41+
Self::default().set_reverted(has_reverted)
42+
}
43+
3944
/// Sets the bit to indicate that the execution is going to be reverted.
4045
#[must_use]
4146
pub fn set_reverted(mut self, has_reverted: bool) -> Self {

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

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -256,16 +256,22 @@ impl Dispatch<'_> {
256256
let payable = constructor.is_payable();
257257
let selector_id = constructor.composed_selector().into_be_u32().hex_padded_suffixed();
258258
let selector_bytes = constructor.composed_selector().hex_lits();
259-
let output_tuple_type = constructor.output().map(quote::ToTokens::to_token_stream)
259+
let output_type = constructor.output().map(quote::ToTokens::to_token_stream)
260260
.unwrap_or_else(|| quote! { () });
261261
let input_bindings = generator::input_bindings(constructor.inputs());
262262
let input_tuple_type = generator::input_types_tuple(constructor.inputs());
263263
let input_tuple_bindings = generator::input_bindings_tuple(constructor.inputs());
264+
let constructor_return_type = quote_spanned!(constructor_span=>
265+
<::ink::reflect::ConstructorOutputValue<#output_type>
266+
as ::ink::reflect::ConstructorOutput<#storage_ident>>
267+
);
264268
quote_spanned!(constructor_span=>
265269
impl ::ink::reflect::DispatchableConstructorInfo<#selector_id> for #storage_ident {
266270
type Input = #input_tuple_type;
267-
type Output = #output_tuple_type;
271+
type Output = #output_type;
268272
type Storage = #storage_ident;
273+
type Error = #constructor_return_type :: Error;
274+
const IS_RESULT: ::core::primitive::bool = #constructor_return_type :: IS_RESULT;
269275

270276
const CALLABLE: fn(Self::Input) -> Self::Output = |#input_tuple_bindings| {
271277
#storage_ident::#constructor_ident(#( #input_bindings ),* )
@@ -442,7 +448,6 @@ impl Dispatch<'_> {
442448
decoded_dispatchable
443449
}
444450
::core::result::Result::Err(_decoding_error) => {
445-
use ::core::default::Default;
446451
let error = ::core::result::Result::Err(::ink::LangError::CouldNotReadInput);
447452

448453
// At this point we're unable to set the `Ok` variant to be the any "real"
@@ -452,7 +457,7 @@ impl Dispatch<'_> {
452457
// This is okay since we're going to only be encoding the `Err` variant
453458
// into the output buffer anyways.
454459
::ink::env::return_value::<::ink::MessageResult<()>>(
455-
::ink::env::ReturnFlags::default().set_reverted(true),
460+
::ink::env::ReturnFlags::new_with_reverted(true),
456461
&error,
457462
);
458463
}
@@ -573,23 +578,58 @@ impl Dispatch<'_> {
573578
}>>::IDS[#index]
574579
}>>::CALLABLE
575580
);
581+
let constructor_output = quote_spanned!(constructor_span=>
582+
<#storage_ident as ::ink::reflect::DispatchableConstructorInfo<{
583+
<#storage_ident as ::ink::reflect::ContractDispatchableConstructors<{
584+
<#storage_ident as ::ink::reflect::ContractAmountDispatchables>::CONSTRUCTORS
585+
}>>::IDS[#index]
586+
}>>::Output
587+
);
576588
let deny_payment = quote_spanned!(constructor_span=>
577589
!<#storage_ident as ::ink::reflect::DispatchableConstructorInfo<{
578590
<#storage_ident as ::ink::reflect::ContractDispatchableConstructors<{
579591
<#storage_ident as ::ink::reflect::ContractAmountDispatchables>::CONSTRUCTORS
580592
}>>::IDS[#index]
581593
}>>::PAYABLE
582594
);
595+
let constructor_value = quote_spanned!(constructor_span=>
596+
<::ink::reflect::ConstructorOutputValue<#constructor_output>
597+
as ::ink::reflect::ConstructorOutput::<#storage_ident>>
598+
);
599+
583600
quote_spanned!(constructor_span=>
584601
Self::#constructor_ident(input) => {
585602
if #any_constructor_accept_payment && #deny_payment {
586603
::ink::codegen::deny_payment::<
587604
<#storage_ident as ::ink::reflect::ContractEnv>::Env>()?;
588605
}
589606

590-
::ink::codegen::execute_constructor::<#storage_ident, _, _>(
591-
move || { #constructor_callable(input) }
592-
)
607+
let result: #constructor_output = #constructor_callable(input);
608+
let output_value = ::ink::reflect::ConstructorOutputValue::new(result);
609+
610+
match #constructor_value :: as_result(&output_value) {
611+
::core::result::Result::Ok(contract) => {
612+
::ink::env::set_contract_storage::<::ink::primitives::Key, #storage_ident>(
613+
&<#storage_ident as ::ink::storage::traits::StorageKey>::KEY,
614+
contract,
615+
);
616+
// only fallible constructors return success `Ok` back to the caller.
617+
if #constructor_value :: IS_RESULT {
618+
::ink::env::return_value::<::core::result::Result<&(), ()>>(
619+
::ink::env::ReturnFlags::new_with_reverted(false),
620+
&::core::result::Result::Ok(&())
621+
)
622+
} else {
623+
::core::result::Result::Ok(())
624+
}
625+
},
626+
::core::result::Result::Err(err) => {
627+
::ink::env::return_value::<::core::result::Result<(), & #constructor_value :: Error>>(
628+
::ink::env::ReturnFlags::new_with_reverted(true),
629+
&::core::result::Result::Err(err)
630+
)
631+
}
632+
}
593633
}
594634
)
595635
});
@@ -768,8 +808,6 @@ impl Dispatch<'_> {
768808

769809
quote_spanned!(message_span=>
770810
Self::#message_ident(input) => {
771-
use ::core::default::Default;
772-
773811
if #any_message_accept_payment && #deny_payment {
774812
::ink::codegen::deny_payment::<
775813
<#storage_ident as ::ink::reflect::ContractEnv>::Env>()?;
@@ -788,14 +826,15 @@ impl Dispatch<'_> {
788826
// intermediate results of the contract - the transaction is going to be
789827
// reverted anyways.
790828
::ink::env::return_value::<::ink::MessageResult::<#message_output>>(
791-
::ink::env::ReturnFlags::default().set_reverted(true), &return_value
829+
::ink::env::ReturnFlags::new_with_reverted(true),
830+
&return_value
792831
)
793832
}
794833

795834
push_contract(contract, #mutates_storage);
796835

797836
::ink::env::return_value::<::ink::MessageResult::<#message_output>>(
798-
::ink::env::ReturnFlags::default(), &return_value
837+
::ink::env::ReturnFlags::new_with_reverted(false), &return_value
799838
)
800839
}
801840
)

0 commit comments

Comments
 (0)