Skip to content
This repository has been archived by the owner on Nov 29, 2023. It is now read-only.

Cross chain execution and payments via UserOps #1

Merged
merged 20 commits into from
Nov 29, 2023
Merged

Conversation

lalexgap
Copy link
Contributor

@lalexgap lalexgap commented Nov 10, 2023

See markdown doc

TODO

  • Add more thorough testing
  • Implement proper nonce support
  • Turn PR description into readme.md

@lalexgap lalexgap marked this pull request as ready for review November 10, 2023 22:08
@lalexgap lalexgap closed this Nov 10, 2023
@lalexgap lalexgap reopened this Nov 10, 2023
@lalexgap lalexgap marked this pull request as draft November 10, 2023 22:37
@lalexgap lalexgap marked this pull request as ready for review November 10, 2023 23:48
if (
validateSignature(userOpHash, ownerSig, owner) != SIG_VALIDATION_SUCCEEDED
) {
return SIG_VALIDATION_FAILED;
Copy link
Contributor Author

@lalexgap lalexgap Nov 12, 2023

Choose a reason for hiding this comment

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

The ERC 4337 spec specifies that the wallet SHOULD return SIG_VALIDATION_FAILED (and not revert) on signature mismatch, so I've changed this from a revert to return SIG_VALIDATION_FAILED

@lalexgap lalexgap changed the title Cross chain execution using signed UserOps Cross chain execution and payments via UserOps Nov 12, 2023
@lalexgap lalexgap marked this pull request as draft November 13, 2023 00:12
@lalexgap lalexgap marked this pull request as ready for review November 14, 2023 18:43
@bitwiseguy
Copy link
Contributor

bitwiseguy commented Nov 14, 2023

Thanks for all of the details in the PR description. It might be useful to turn this PR description into a .md file within the repo. That way the documentation is not lost when this PR is merged and it is easy to leave comments on specific parts of the writeup as we are reviewing

@lalexgap
Copy link
Contributor Author

Thanks for all of the details in the PR description. It might be useful to turn this PR description into a .md file within the repo. That way the documentation is not lost when this PR is merged and it is easy to leave comments on specific parts of the writeup as we are reviewing

8babf50

contracts/SCBridgeWallet.sol Outdated Show resolved Hide resolved
docs/cross-chain-payments-and-execution.md Outdated Show resolved Hide resolved
docs/cross-chain-payments-and-execution.md Outdated Show resolved Hide resolved
docs/cross-chain-payments-and-execution.md Outdated Show resolved Hide resolved
docs/cross-chain-payments-and-execution.md Outdated Show resolved Hide resolved

# Multihop

Since a UserOp can contain multiple `PaymentChainInfo`s and `ExecuteChainInfos`, this approach can be used for multi-hop execution or payments. We just require the `owner` and `intermediary` of every BridgeWallet involved to sign the UserOp.
Copy link
Contributor

Choose a reason for hiding this comment

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

When we go beyond a single hop, we have "intermediary-intermediary" channels. Is this a bit awkward with the "owner" / "intermediary" roles of the bridge wallet? Will one of the parties just arbitrarily take the role of "owner"?

Copy link
Contributor Author

@lalexgap lalexgap Nov 16, 2023

Choose a reason for hiding this comment

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

I think right now it might be a bit awkward, as once intermediary would play the role of the owner and one intermediary would play the role of intermediary in the BridgeWallet.

To solve this we could implement a specific Intermediary<->Intermediary mode (or separate contract?) for the BridgeWallet that handles the "intermediary-intermediary" use case.


It's important that we're not vulnerable to replay attacks, where a `UserOp` is submitted again using a different entrypoint or chain.

If we're working on one chain, it's fairly easy to prevent replay attacks. The `userOpHash` [provided by the Entrypoint](https://github.com/magmo/Bridge-Wallet/blob/ad6d24fa2435f449751d1b61e24d12faff1f83a9/contracts/core/EntryPoint.sol#L298) to `validateUserOp` is hashed against the current chain and entrypoint. This means that if the UserOp is run against a different chain or entrypoint, there will be a different `userOpHash` causing all the signature checks to fail.
Copy link
Contributor

Choose a reason for hiding this comment

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

This feels a little suboptimal. I suppose the entrypoint has not been designed with cross chain applications in mind, meaning that we have to make multiple hashes and a signature on each one (and as you say validate all of those combinations).

A "cross chain entrypoint" would ease this, but would be a departure from 4337 norms (I guess?).

Copy link
Contributor Author

@lalexgap lalexgap Nov 15, 2023

Choose a reason for hiding this comment

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

With this change I don't think it's too bad. Now instead of generating a unique hash for each chain, all participants just sign the hash based on the first chain. This means we can generate one hash and check all the signatures against that.

I think it's safe to have everyone sign the same UserOpHash since we perform a separate check on the chainId and entrypoint based on the calldata in the UserOp. If you try submitting the UserOp, it will only work if the chain and entrypoint match a ExecuteChainInfo or PaymentChainInfo.

So the cost of validateCrossChain would be generating one hash, and then checking 2 signatures for every BridgeWallet involved. IE: If we have a multi-hop UserOp with Alice,Irene,Iris,Bob we'd expect the UserOp to have the following signatures: [AliceSig,IreneSig,IreneSig,IrisSig,IrisSig,BobSig]. We'd generate one hash and check the6 signatures against that hash.

It would also be an easy optimization to omit or skip duplicate signatures so we could get the cost down to generating one hash and checking 1 signature per participant.

Comment on lines +465 to +466
ownerSig,
intermediarySig,
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we extend the test to work with two different chains (and chain ids) somehow?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, I've created #4 for that. We'll need to rig up a test that works with multiple chains.

Comment on lines +143 to +144
// Only the entrypoint should trigger this by excecuting a UserOp
require(msg.sender == entrypoint, "account: not EntryPoint");
Copy link
Contributor

Choose a reason for hiding this comment

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

Curious if this is necessary -- should we have had this check before? Should all 4337 wallets have this check?

Copy link
Contributor

Choose a reason for hiding this comment

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

It also seems to be replicated below?

Copy link
Contributor Author

@lalexgap lalexgap Nov 15, 2023

Choose a reason for hiding this comment

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

This is needed since we want to enforce that crossChain is only triggered through submitting a UserOp. Otherwise anyone could call with crossChain, using a transaction from an EoA, and whatever PaymentChainInfo or ExecutionChainInfo they wanted. Since challenge is allowed to be called at any time, someone could call crossChain with a bogus unsigned state and trigger a challenge using that.

Should all 4337 wallets have this check?

It depends on the function and the 4337 wallet! In a lot of cases there will be functions restricted to only some owner or entrypoint(IE: A function to send user funds)

 require(
        msg.sender == entrypoint || msg.sender == owner,
        "account: not Owner or EntryPoint"
      );

This allows the owner of the wallet to call the function using either an EoA account or through submitting a UserOp.

In our case we need to be a bit stricter, we only want to trigger crossChain if a UserOp has been submitted signed by all the participants. We're relying on the signature validation of the UserOp to protect the call to crossChain.

It also seems to be replicated below?

I think that's in a separate execute function, which can be triggered outside of crossChain. So we need to implement the check in both functions.

contracts/SCBridgeWallet.sol Outdated Show resolved Hide resolved
@geoknee
Copy link
Contributor

geoknee commented Nov 15, 2023

Adding a multi-chain sequence diagram would be really good.


# Payments

We embed payment information in the UserOp using a `PaymentChainInfo`
Copy link
Contributor

Choose a reason for hiding this comment

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

Could the structs be renamed to PaymentInfo and ExecuteInfo for simplicity? Or does the use of Chain in those names help clarify what they are?

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 think the use of Chain helps clarify that info in these structs is specific to a given chain, and should only be honoured on that chain. I don't feel too strongly about it though so I'm happy to change it if others feel differently.

lalexgap and others added 2 commits November 15, 2023 10:35
Co-authored-by: Colin Kennedy <NiloCK@users.noreply.github.com>
Co-authored-by: George C. Knee <georgeknee@googlemail.com>
Co-authored-by: Sam Stokes <35908605+bitwiseguy@users.noreply.github.com>
@lalexgap
Copy link
Contributor Author

lalexgap commented Nov 15, 2023

Adding a multi-chain sequence diagram would be really good.
image

image

@lalexgap lalexgap merged commit 99c7c3c into main Nov 29, 2023
1 check passed
@lalexgap lalexgap deleted the cross-chain-exec branch November 29, 2023 18:10
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants