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

Handle LangError from instantiate #1512

Merged
merged 90 commits into from
Jan 23, 2023

Conversation

HCastano
Copy link
Contributor

@HCastano HCastano commented Nov 19, 2022

This is a follow up to #1504, in which LangErrors were returned from constructors. In
that PR we had no way to read the output buffer from seal_instantiate and bubble any
information back up to the contract caller.

This PR adds support for reading this output buffer for both infallible and fallible
constructors.

Because of type checking problems I've had to add a new set of APIs for handling fallible
constructors. I'm still looking for a way to unify them, as mentioned in this comment.
If you have any ideas for improvements here I'd be happy to hear them.

I've changed the create_builder API slightly to be more in-line with the call_builder
one (i.e removing the generic in favour of a returns method).

For infallible constructors, the main changes are:

For fallible constructors:

  • We've added intantiate_fallible and try_instantiate_fallible CreateBuilder methods
  • We've added a new environment API, ink_env::instantiate_fallible_contract

I've also updated the codegen so that ContractRefs can be used to instantiate contracts,
as an alternative to the "raw" CreateBuilder API.

Notes for reviewers:

  • Keep in mind that there are two ways that the output buffer of seal_instantiate can
    be written to:
    • By the ink! codegen
    • Manually by the callee contract using ink_env::return_value
      • This second one is the one we need to worry about since people can (on purpose
        or by accident) encode erroneous values in there
  • Related to the above, make sure to take a close look at the different output buffer
    decoding scenarios
  • Take a look at the E2E tests to see if they make sense
  • Right now the E2E test fail in the CI, not because they're actually broken but because
    of E2E test "panicked at 'error on call sign_and_submit_then_watch_default" #1498
    (run them locally if you don't believe me 😆)

Base automatically changed from hc-impl-lang-error-for-constructors to master November 21, 2022 21:48
This commit lets us grab what's in the output buffer after our call to
`instantiate`, however we are unable to succesfully decode the
`AccountId` from the success case.
@HCastano HCastano force-pushed the hc-get-lang-error-from-create-builder branch from acc3f48 to 8796a62 Compare November 25, 2022 14:56
@HCastano HCastano changed the title Handle LangError from instantiate (fails for success case) Handle LangError from instantiate Nov 29, 2022
@HCastano HCastano marked this pull request as ready for review November 29, 2022 00:20
@codecov-commenter
Copy link

codecov-commenter commented Nov 29, 2022

Codecov Report

Merging #1512 (aa4f897) into master (bf2de8f) will decrease coverage by 0.10%.
The diff coverage is 0.00%.

❗ Current head aa4f897 differs from pull request most recent head cf2516f. Consider uploading reports for the commit cf2516f to get more accurate results

@@            Coverage Diff             @@
##           master    #1512      +/-   ##
==========================================
- Coverage   71.67%   71.57%   -0.11%     
==========================================
  Files         204      204              
  Lines        6320     6321       +1     
==========================================
- Hits         4530     4524       -6     
- Misses       1790     1797       +7     
Impacted Files Coverage Δ
crates/env/src/api.rs 36.92% <ø> (ø)
crates/env/src/backend.rs 78.12% <ø> (ø)
crates/env/src/call/create_builder.rs 0.00% <0.00%> (ø)
crates/env/src/engine/off_chain/impls.rs 47.64% <ø> (ø)
...odegen/src/generator/as_dependency/contract_ref.rs 100.00% <ø> (ø)
crates/ink/src/env_access.rs 9.09% <ø> (ø)
crates/allocator/src/bump.rs 85.95% <0.00%> (-3.97%) ⬇️
crates/metadata/src/layout/mod.rs 75.83% <0.00%> (-1.67%) ⬇️
crates/ink/ir/src/ir/item/storage.rs 80.00% <0.00%> (-0.65%) ⬇️
crates/ink/ir/src/ir/item/event.rs 86.56% <0.00%> (-0.20%) ⬇️
... and 14 more

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

Copy link
Collaborator

@ascjones ascjones left a comment

Choose a reason for hiding this comment

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

Just the question mark about the handling of the decoding of the LangError, other than that it looks good.

crates/env/src/call/create_builder.rs Outdated Show resolved Hide resolved
crates/env/src/call/create_builder.rs Outdated Show resolved Hide resolved
crates/env/src/call/create_builder.rs Show resolved Hide resolved
crates/env/src/call/create_builder.rs Outdated Show resolved Hide resolved
Err(ext::Error::CalleeReverted) => {
// We don't wrap manually with an extra `Err` like we do in the `Ok` case since the
// buffer already comes back in the form of `Err(LangError)`
let out = scale::Decode::decode(&mut &out_return_value[..])?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Couldn't there exist a contract that would revert the contract but return Ok(()) e.g.

::ink::env::return_value::<::ink::ConstructorResult<()>>(
    ::ink::env::ReturnFlags::new_with_reverted(true),
    Ok(()), 
);

This could mislead the caller into handling the Ok case?

Copy link
Contributor Author

@HCastano HCastano Nov 29, 2022

Choose a reason for hiding this comment

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

Hmm yeah this is a good observation. I've added a test that shows that you can indeed revert from a constructor and continue execution by returning Ok(AccountId).

I've also added a quick patch which asserts that the content in the buffer is an error.

I'm not entirely sure what the implications of either case are though...

  • In the Revert + Ok case, misleading the caller seems like the main reason to do this, but maybe there are legit reasons to do encode an Ok back? I can't think of any though.
  • In the Revert + Err case, the callee can encode the "wrong" LangError into the output buffer, potentially misleading the caller too, and there's nothing we can do to help there.

Either way it'll be up to the contract developer to make sure they understand that there are risks to calling other contracts.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@athei would like to hear your opinion here

Copy link
Collaborator

@ascjones ascjones Dec 1, 2022

Choose a reason for hiding this comment

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

The assumption is that the user is not calling return_value directly, and if they are they are using it in an ABI compatible way which is either Revert + Err(LangError) or Revert + Ok(Err(ContracErr)).

So perhaps we could return an Err(LangError) here if this ABI is not satisfied, e.g. as mentioned in #1512 (comment)

Copy link
Collaborator

@ascjones ascjones Dec 1, 2022

Choose a reason for hiding this comment

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

Okay maybe what we want is something along the lines of

// note changed from `AccountId` to `()`
let out = ink_primitives::ConstructorResult::<()>::decode(
    &mut &out_return_value[..],
)?;
match out {
    // This branch when a fallible constructor returns `Err`
    Ok(()) => Err(ext::Error::CalleeReverted),
    /// This branch handles an actual `LangErr`
    Err(lang_err) => Ok(Err(lang_err))
}

@ascjones
Copy link
Collaborator

ascjones commented Jan 3, 2023

Ping me when you're back and we can catch up on this and get it merged

@ascjones
Copy link
Collaborator

ascjones commented Jan 4, 2023

See use-ink/ink-workshop#31

Edit, sorry I realize this is for a call and not an instantiate. Still, it highlights a potential footgun if we are requiring the user to explicitly specify the Result<_, LangError>

@ascjones ascjones mentioned this pull request Jan 12, 2023
@HCastano
Copy link
Contributor Author

See paritytech/ink-workshop#31

Edit, sorry I realize this is for a call and not an instantiate. Still, it highlights a potential footgun if we are requiring the user to explicitly specify the Result<_, LangError>

Yeah, this is something I wanted to address with #1525

@ascjones ascjones force-pushed the hc-get-lang-error-from-create-builder branch from 09af6a2 to 9a14e21 Compare January 20, 2023 15:30
Copy link
Collaborator

@ascjones ascjones left a comment

Choose a reason for hiding this comment

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

LGTM!

The dual method API with fallible alternatives is a bit clunky, however #1591 provides a solution to that.

@HCastano HCastano merged commit cdfb7fe into master Jan 23, 2023
@HCastano HCastano deleted the hc-get-lang-error-from-create-builder branch January 23, 2023 22:02
@HCastano HCastano mentioned this pull request Jan 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants