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

contracts: submit pre-signed transaction #175

Merged
merged 20 commits into from
Oct 18, 2023
Merged

Conversation

CedarMist
Copy link
Member

@CedarMist CedarMist commented Sep 6, 2023

Fixes #179:

Error: Un-enveloped data was passed to sendRawTransaction, which is likely incorrect. Is the dapp using the Sapphire compat lib correctly?

I have added tests transaction envelope tests:

✔ Wrapper encrypts self-signed transaction calldata (4038ms)
✔ Other-Signed transaction submission via un-wrapped provider (4223ms)
✔ Other-Signed transaction submission via wrapped provider (4115ms)
✔ Self-Signed transaction submission via wrapped provider (4061ms)

This slightly modifies sapphire.wrap behavior so if it receives a signed but unencrypted transaction from an address which it can't sign for the transaction will pass through as-is.

@CedarMist
Copy link
Member Author

The repackRawTx function rejects things that it could easily handle.

And if you CBOR encode the envelope you end up with the contract receiving CBOR encoded data, rather than the calldata...

@CedarMist CedarMist self-assigned this Sep 6, 2023
@CedarMist CedarMist added p:1 Priority: high js labels Sep 6, 2023
@CedarMist
Copy link
Member Author

This will fix #179

@CedarMist
Copy link
Member Author

CedarMist commented Sep 17, 2023

There are a few scenarios we want to handle with sendRawTransaction:

  • User submits signed but unencrypted transaction
    • Will be unable to encrypt, as that requires re-signing
    • Submits unencrypted transaction to RPC endpoint
  • User submits signed & encrypted transaction
    • Pass it verbatim to RPC endpoint
  • User submits unsigned unencrypted transaction
    • Sign it, encrypt it & submit to RPC endpoint

However, currently the wrapper will raise an error if you submit a transaction signed by somebody else. This matches Ethers behavior.

If you submit a pre-signed but unencrypted transaction to a provider, it won't be able to encrypt it because no signer exists. (Error: signing transactions is unsupported (operation="signTransaction", code=UNSUPPORTED_OPERATION, version=providers/5.7.2)

The Ethers API doesn't let you submit pre-signed transactions to a signer, it requires you decode it and re-submit it in the Deferrable<TransactionRequest> format.

Realistically, this means the sapphire wrapper, with Ethers, cannot re-encrypt pre-signed transactions even when the tx signer matches the address of the Ethers signer.

And the contract is unable to get the calldata public key, so it can't trust that if it encrypts something with a key provided by the user that they won't be able to intercept it.

@CedarMist CedarMist force-pushed the CedarMist/submit-pre-signed branch from 35e7a90 to 20282fa Compare September 17, 2023 12:44
@CedarMist CedarMist marked this pull request as ready for review September 17, 2023 12:48
@CedarMist
Copy link
Member Author

So... looks like the error I want to fix is fundamentally unfixable.

But, instead I added a test to make sure the EIP155 module can sign and submit transactions, and that transactions submitted by the wrapped provider are encrypted.

@CedarMist CedarMist requested review from nhynes and matevz September 17, 2023 12:49
@CedarMist
Copy link
Member Author

So I can fix this error where we submit pre-signed transactions through the provider which is attached to a signer with a different address. But we can't automatically encrypt pre-signed transactions.

@matevz
Copy link
Member

matevz commented Sep 18, 2023

So I can fix this error where we submit pre-signed transactions through the provider which is attached to a signer with a different address. But we can't automatically encrypt pre-signed transactions.

Why not? Encryption with an ephemeral key derived from the runtime and the client key has nothing to do with who signed the tx.

@CedarMist
Copy link
Member Author

Why not? Encryption with an ephemeral key derived from the runtime and the client key has nothing to do with who signed the tx.

The calldata is signed, modifying the calldata by wrapping it in an encrypted envelope would invalidate the existing signature.

When I use:

const [owner] = await ethers.getSigners();
const provider = testContract.provider;
const wallet = sapphire.wrap(owner.connect(provider));

The repackRawTx function encrypts the calldata then asks the signer to re-sign the modified transaction, however with the JSON-RPC Signer it's

Error: cannot alter JSON-RPC Signer connection (operation="connect", code=UNSUPPORTED_OPERATION, version=providers/5.7.2)

If I wrap the owner directly, e.g. const wallet = sapphire.wrap(owner); I get:

Error: signing transactions is unsupported (operation="signTransaction", code=UNSUPPORTED_OPERATION, version=providers/5.7.2)

And only via creating a new ethers.Wallet directly and connect() it to the provider does it work because it can sign the transaction.

If this happens via MetaMask and the dApp signs a transaction without encrypting it then sends it through a wrapped signer it will try to encrypt it before submission.

I have slightly modified the behavior and added test cases so it you pass a transaction not signed by the signers address it will not attempt to encrypt it and pass it on. This makes submitting the gasless transactions easier because you don't need to setup a different unwrapped provider to submit it.


@CedarMist
Copy link
Member Author

This may be relevant: #106

clients/js/src/compat.ts Outdated Show resolved Hide resolved
contracts/test/eip155.ts Outdated Show resolved Hide resolved
@CedarMist CedarMist force-pushed the CedarMist/submit-pre-signed branch from 3e801d7 to bc36df9 Compare October 10, 2023 08:27
@CedarMist CedarMist requested a review from aefhm October 10, 2023 10:12
Copy link
Contributor

@aefhm aefhm left a comment

Choose a reason for hiding this comment

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

Non-blocking comments.

clients/js/src/compat.ts Outdated Show resolved Hide resolved
contracts/test/eip155.ts Outdated Show resolved Hide resolved
contracts/test/eip155.ts Outdated Show resolved Hide resolved
contracts/test/eip155.ts Outdated Show resolved Hide resolved
contracts/test/eip155.ts Outdated Show resolved Hide resolved
contracts/test/eip155.ts Outdated Show resolved Hide resolved
@CedarMist CedarMist force-pushed the CedarMist/submit-pre-signed branch from 2c01068 to 988e06f Compare October 18, 2023 16:43
@CedarMist CedarMist merged commit efc9ce0 into main Oct 18, 2023
16 checks passed
@CedarMist CedarMist deleted the CedarMist/submit-pre-signed branch October 18, 2023 16:59
github-actions bot added a commit that referenced this pull request Oct 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
p:1 Priority: high
Projects
None yet
Development

Successfully merging this pull request may close these issues.

client-js: Encrypt plain transactions in sendTransaction()
3 participants