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

Return LangErrors from constructors #1504

Merged
merged 32 commits into from Nov 21, 2022

Conversation

HCastano
Copy link
Contributor

@HCastano HCastano commented Nov 16, 2022

Follow-up to #1450, and closes the constructor part of #1207.

As in #1450, constructors now return a Result<Constructor::Output, LangError>
which can be handled by callers.

If you're curious, click here for a snippet of the new metadata.

    "constructors": [
  {
    "args": [
      {
        "label": "init_value",
        "type": {
          "displayName": [
            "bool"
          ],
          "type": 0
        }
      }
    ],
    "docs": [
      "Infallible constructor"
    ],
    "label": "new",
    "payable": false,
    "returnType": {
      "displayName": [
        "ink_primitives",
        "ConstructorResult"
      ],
      "type": 1
    },
    "selector": "0x9bae9d5e"
  },
                                        
{
  "id": 1,
  "type": {
    "def": {
      "variant": {
        "variants": [
          {
            "fields": [
              {
                "type": 2
              }
            ],
            "index": 0,
            "name": "Ok"
          },
          {
            "fields": [
              {
                "type": 3
              }
            ],
            "index": 1,
            "name": "Err"
          }
        ]
      }
    },
    "params": [
      {
        "name": "T",
        "type": 2
      },
      {
        "name": "E",
        "type": 3
      }
    ],
    "path": [
      "Result"
    ]
  }
},
{
  "id": 2,
  "type": {
    "def": {
      "tuple": []
    }
  }
},
{
  "id": 3,
  "type": {
    "def": {
      "variant": {
        "variants": [
          {
            "index": 1,
            "name": "CouldNotReadInput"
          }
        ]
      }
    },
    "path": [
      "ink_primitives",
      "LangError"
    ]
  }
}

TODOs (Edit: These are gonna be done in #1512)

  • Add test showing how to handle LangError
  • ContractRef implementations

@HCastano HCastano marked this pull request as ready for review November 17, 2022 23:56
@HCastano HCastano requested a review from athei November 18, 2022 00:01
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.

Looks good, we may want to resolve the type of the ReturnValueSpec to be non optional before merging.

};

<<#storage_ident as ::ink::reflect::ContractConstructorDecoder>::Type
as ::ink::reflect::ExecuteDispatchable>::execute_dispatchable(dispatchable)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Looking at this control flow again: from a readability perspective, I wonder whether it would be better to have this inside the Ok arm of the match. It seems unusual because of the semantics of return_value returning immediately !. A casual reader might expect this statement to always be executed. Just a note for something to consider when we get to refactoring this dispatch logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah that seems fair enough. I'll leave it for a follow-up since we'll want to change it for messages too

crates/ink/codegen/src/generator/dispatch.rs Outdated Show resolved Hide resolved
>
>(
"ink_primitives::ConstructorResult"
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I bet we could fit that all on one line to avoid the ugly indentation e.g.
::ink::ConstructorResult<()>>("ink_primitives::ConstructorResult")

)
)
} else {
::core::option::Option::None
::core::option::Option::Some(::ink::metadata::TypeSpec::with_name_str::<
Copy link
Collaborator

Choose a reason for hiding this comment

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

ReturnTypeSpec should now always expect a TypeSpec instead of an Option<TypeSpec>. There is one other usage to take care of for trait output types.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't know enough about all the metadata types to be able to remove this and still get things compiling, so in the interest of time I'm gonna leave it a a follow-up

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Opened #1515

crates/ink/ir/src/ir/item_impl/constructor.rs Outdated Show resolved Hide resolved
@ascjones ascjones mentioned this pull request Nov 18, 2022
1 task
@ascjones
Copy link
Collaborator

Another thing to note is that the contract sizes have gone up very slightly again here: so something to investigate for future betas. e.g. erc20 has gone from 7.6 to 7.7

Copy link
Contributor

@athei athei left a comment

Choose a reason for hiding this comment

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

Metadata and return_value invocations look good.

crates/ink/codegen/src/generator/dispatch.rs Outdated Show resolved Hide resolved
crates/ink/codegen/src/generator/dispatch.rs Outdated Show resolved Hide resolved
crates/ink/codegen/src/generator/dispatch.rs Outdated Show resolved Hide resolved
athei
athei previously requested changes Nov 18, 2022
Copy link
Contributor

@athei athei left a comment

Choose a reason for hiding this comment

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

I think we forgot the reversion

crates/ink/codegen/src/generator/dispatch.rs Outdated Show resolved Hide resolved
@athei
Copy link
Contributor

athei commented Nov 18, 2022

I saw a change to unify those cases and get away with a single return_value. Revert if you don't like.

@ascjones
Copy link
Collaborator

I saw a change to unify those cases and get away with a single return_value. Revert if you don't like.

Side effect of this refactoring has been the cancelling out of this size increase:

Another thing to note is that the contract sizes have gone up very slightly again here: so something to investigate for future betas. e.g. erc20 has gone from 7.6 to 7.7

@athei
Copy link
Contributor

athei commented Nov 18, 2022

Okay then let's be nice to the compiler and also unify it for the call case.

@athei athei dismissed their stale review November 18, 2022 13:49

Fixed the bug myself

@HCastano
Copy link
Contributor Author

The change related to handling LangErrors from constructors ended up being a bit more
annoying that I expected. I've opened up a separate PR for those (#1512).

The current behaviour of this PR is as follows:

  • Instantiations through normal dispatch:
    • These are fine, we never raise LangErrors
  • CreateBuilder or ContractRef instantiations:
    • These work, but we are not able to handle the LangError
    • Instead, we get a ContractReverted error from the Contracts pallet (so kinda like
      the old behaviour)

Since the most important thing here are the metadata changes, we could in principle
merge this. The changes from #1512 would "just" be code level changes.

@athei
Copy link
Contributor

athei commented Nov 19, 2022

Instantiations through normal dispatch:

* These are fine, we never raise `LangError`s

I don't get it. When I use the call builder I define myself what I expect to receive. And I would need to define that I get Result<(), LangError> or the decoding fails because that is what the callee copies into the output buffer for a fallible constructor.

CreateBuilder or ContractRef instantiations:

* These work, but we are not able to handle the `LangError`

* Instead, we get a `ContractReverted` error from the Contracts pallet (so kinda like
  the old behaviour)

Shouldn't it be ContractTrapped because you basically unwrap the outer Result? ContractReverted implies that you called seal_return which doesn't make sense given that you would need to choose an output buffer to return that conforms with the callers return type.

@HCastano
Copy link
Contributor Author

I don't get it. When I use the call builder I define myself what I expect to receive. And I would need to define that I get Result<(), LangError> or the decoding fails because that is what the callee copies into the output buffer for a fallible constructor.

What we want to talk about here isn't the CallBuilder, but the CreateBuilder. At
the moment the CreateBuilder only supports a return type of an AccountId, which is
comes from seal_instantiate. In #1512 I try and add support for reading the LangError
from the output buffer of seal_instatiate.

When I talk about "normal dispatch" I'm referring to calls that go through something like
cargo-contract or the E2E tests . By default neither is able to use an invalid selector
(I guess you could hack the metadata or fiddle at the call site to do this) so these
should not return a LangError.

Shouldn't it be ContractTrapped because you basically unwrap the outer Result? ContractReverted implies that you called seal_return which doesn't make sense given that you would need to choose an output buffer to return that conforms with the callers return type.

If I log the return of the CreateBuilder::instantiate() call I get CalleeReverted. We're failing
to decode the selector and so we hit this:

https://github.com/paritytech/ink/blob/b63ef910b6c02a563cd70b081adbe90ba24ffb12/crates/ink/codegen/src/generator/dispatch.rs#L438-L441

which calls seal_return.

@athei
Copy link
Contributor

athei commented Nov 19, 2022

What we want to talk about here isn't the CallBuilder, but the CreateBuilder. At
the moment the CreateBuilder only supports a return type of an AccountId, which is
comes from seal_instantiate. In #1512 I try and add support for reading the LangError
from the output buffer of seal_instatiate.

Okay I got it. Yes this is a bit more of a high level stuff then. Yes we can leave it that way for the beta.

When I talk about "normal dispatch" I'm referring to calls that go through something like
cargo-contract or the E2E tests . By default neither is able to use an invalid selector
(I guess you could hack the metadata or fiddle at the call site to do this) so these
should not return a LangError.

Yeah those off-chain callers are also not that interesting for ABI. As a matter of fact none of the UIs even displays data coming back from constructors (cause we didn't return anything up until now). We are good here.

If I log the return of the CreateBuilder::instantiate() call I get CalleeReverted. We're failing to decode the selector and so we hit this:

https://github.com/paritytech/ink/blob/b63ef910b6c02a563cd70b081adbe90ba24ffb12/crates/ink/codegen/src/generator/dispatch.rs#L438-L441

This is the callee side. But I thought we are discussing the caller here. How does the caller handle getting a Err(LangError)?

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. Please check my remaining unresolved comments

.await;

assert!(
matches!(result, Err(ink_e2e::Error::InstantiateExtrinsic(_))),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Awesome ❤️

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.

I'm fine to delay the CreateBuilder changes in #1512 to a subsequent beta release, based on the philosophy of stablizing the ABI but allowing breaking code changes.

@HCastano
Copy link
Contributor Author

This is the callee side. But I thought we are discussing the caller here. How does the caller handle getting a Err(LangError)?

Sorry, maybe I'm missing something - but right now they don't. There are two scenarios:

  1. Calling an invalid selector with cargo-contract: ContractReverted, nothing to do
  2. Calling an invalid selector via CreateBuilder: ContractReverted, but can continue its own execution

@HCastano HCastano requested a review from a team as a code owner November 21, 2022 19:17
@athei
Copy link
Contributor

athei commented Nov 21, 2022

Sorry, maybe I'm missing something - but right now they don't. There are two scenarios:

Checked the code. Looks alright. We don't even decode the return buffer for instantiate. So we are safe here. So from my side we can merge this and release the first beta. #1512 is not needed for the beta.

@codecov-commenter
Copy link

Codecov Report

Merging #1504 (ad1cab7) into master (adf8f26) will decrease coverage by 5.85%.
The diff coverage is 93.33%.

@@            Coverage Diff             @@
##           master    #1504      +/-   ##
==========================================
- Coverage   71.70%   65.85%   -5.86%     
==========================================
  Files         204      204              
  Lines        6306     6314       +8     
==========================================
- Hits         4522     4158     -364     
- Misses       1784     2156     +372     
Impacted Files Coverage Δ
crates/e2e/src/client.rs 66.66% <ø> (ø)
crates/ink/codegen/src/generator/dispatch.rs 94.61% <ø> (ø)
crates/ink/codegen/src/generator/metadata.rs 95.20% <ø> (ø)
...i/contract/pass/constructor-return-result-alias.rs 93.87% <93.33%> (-1.00%) ⬇️
crates/ink/ir/src/ir/contract.rs 0.00% <0.00%> (-100.00%) ⬇️
crates/ink/ir/src/ir/ink_test.rs 0.00% <0.00%> (-87.50%) ⬇️
crates/ink/ir/src/ir/trait_def/mod.rs 0.00% <0.00%> (-81.82%) ⬇️
crates/ink/ir/src/ir/storage_item/mod.rs 0.00% <0.00%> (-73.92%) ⬇️
crates/ink/ir/src/ir/storage_item/config.rs 0.00% <0.00%> (-73.34%) ⬇️
crates/ink/ir/src/ir/blake2.rs 20.83% <0.00%> (-58.34%) ⬇️
... and 20 more

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

@HCastano HCastano merged commit 507d01d into master Nov 21, 2022
@HCastano HCastano deleted the hc-impl-lang-error-for-constructors branch November 21, 2022 21:48
Comment on lines +80 to +82
// NOTE: Right now we can't handle any `LangError` from `instantiate`, we can only tell
// that our contract reverted (i.e we see error from the Contracts pallet).
result.ok().map(|id| ink::ToAccountId::to_account_id(&id))
Copy link

Choose a reason for hiding this comment

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

How do you recommend handling instantiating other contracts inside a constructor?

@ascjones ascjones mentioned this pull request Feb 15, 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.

None yet

5 participants