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

Separating transaction signer from transaction deployer #877

Open
wants to merge 3 commits into
base: master
from

Conversation

Projects
None yet
9 participants
@alexvandesande
Contributor

alexvandesande commented Feb 9, 2018

Preamble

EIP: <to be assigned>
Title: Separating transaction signer from transaction deployer
Author: Alex Van de Sande
Type: ERC
Status: Draft
Created: 2018-02-09

Simple Summary

Many contract developers run in the problem of allowing users to interact with their contracts without having ether. This has been addressed by proposed account abstractions which would bring a lot of new features but also brings a lot of complexity. The latest proposed version by Vitalik, while still brings many benefits like quantum resistance, would still not, for example, to pay token transactions with the token themselves, an often requested feature.

This EIP proposes a simple way that enables this by simply separating the transaction signer (the person or entity authorizing the transaction) from the transacion deployer (the person or entity publishing that transaction to the chain and paying its gas). It is not meant to replace account abstraction and the other benefits it might bring.

Read full text on the Pull request

@karalabe

This comment has been minimized.

Member

karalabe commented Feb 9, 2018

If I understand correctly, you're proposal is to have combo transactions, where the outer one is paying for the gas consumption of the inner one.

This would introduce some funky issues around nonces. I guess for the transaction to be valid both nonces need to match and both need to be updated at the same time. This would raise the dimensionality of the transaction propagation/queuing code, as all of a sudden accounts can have inter-dependencies between each other and you can't filter them separately. Nonce gaps also all of a sudden become a 2D problem.

Similar issues might happen with balances, as all of a sudden the transaction validity depends on 2 parties instead of only the sender as currently.

@karalabe

This comment has been minimized.

Member

karalabe commented Feb 9, 2018

This also introduces a potential attack vector against your "deployer" as you can invalidate one of its transaction with a replacement you yourself make directly, causing a nonce gap that the deployer needs to fill.

You can in theory non-stop screw with your deployer this way by constantly making transactions both through the deployer as well as directly, and the deployer cannot punish you in any way as you always have plausible deniability that you are just using multiple dapps at the same time,

@3esmit

This comment has been minimized.

3esmit commented Feb 9, 2018

Thanks for this @alexvandesande, this will be extremeful useful for whats being discussed in status-im/swarms#73 and #865.
I'm working in a contract (using current EVM capabilities) that "abstract gas for other contract calls" using approveAndCallPreSigned, however contracts must take care when implementing ApproveAndCallFallBack, the fields in receiveApproval(_from, _amount, _token, _extraData);.
The main problem with "relay gas with token payment" is that contracts willing to accept relayed calls must trust and implement that tokens, otherwise anyone can make a bad token that fakes address _from.
DAO accepting relayed gas only from their DAO token should be safe, and this should be the main usage of "MiniMeTokenPreSigned", for example Status Network could trust its own Status Network Token for relaying gas of transactions of user calls to their own contracts, requiring users to only hold SNT to use the network.

With this EIP then we can have contracts that accept relayed gas from any token (it does not matters), it would be looking for the transaction signer (msg.signer global at solidity?)
Therefore I see that we have pending ERCs that are related with this EIP: #191 or #712 ERCs about ethereum signed messages.

@AlexeyAkhunov

This comment has been minimized.

Contributor

AlexeyAkhunov commented Feb 9, 2018

I really like the idea. To address the possible issues with nonces presented by @karalabe , I suggest considering these restrictions:

  1. Enveloped (inner) transaction is only valid if the "from" account has nonce 0 and balance 0
  2. Nonces of the "inner" accounts do not get incremented, they always stay 0
  3. Once account have been used for producing "inner" envelope, it cannot receive Ether anymore (to prevent trapped Ether)
  4. The enveloped, or "inner" transaction has "deployer" field that commits it to be used only by the specific deployer.

The consequence of (4) is that the transaction creator giving transaction to the deployer is an irrevocable action. And the nonces, if they are needed will have to be simulated inside smart contracts, if it is desirable to prevent transaction being deployed multiple times. If the nonces are tracked by the smart contract, the deployer does not have incentive to publish transaction multiple times, because it will be just a waste of gas.

## Abstract
Currently, ethereum transactions have the following fields: `nonce`, `gasPrice`, `gasLimit`, `value`, `to` and `data`. This EIP proposes adding a new one: `from` which, if present, indicates that `data` is an ethereum transaction containing `to`, `value`, `to` and its own `data`, but the message is signed by the account represented on `from`.

This comment has been minimized.

@pirapira

pirapira Feb 9, 2018

Member

to, value, to and its own data,

Looks like one to many.

Block validators/miners should check the validity of the signature, and proceed to treat the transaction in sense as if it was being made by the `from` account **except** that in the end, the gas costs (with an added extra cost for the work of checking the validity of the signature) is *deduced from the account of the account deploying the transaction to the chain*.
In higher level languages like solidity, `from`(if present) would be the `msg.sender` as it would be compatible with current contracts, and a new special variable called `tx.sender` could be added to represent the deployer of the transaction (if the code wanted to create incentives).

This comment has been minimized.

@pirapira

pirapira Feb 9, 2018

Member

A space is missing before (if present).

## Abstract
Currently, ethereum transactions have the following fields: `nonce`, `gasPrice`, `gasLimit`, `value`, `to` and `data`. This EIP proposes adding a new one: `from` which, if present, indicates that `data` is an ethereum transaction containing `to`, `value`, `to` and its own `data`, but the message is signed by the account represented on `from`.

This comment has been minimized.

@pirapira

pirapira Feb 9, 2018

Member

Maybe it's worth emphasizing that the inner transaction does not have nonce field.

## Abstract
Currently, ethereum transactions have the following fields: `nonce`, `gasPrice`, `gasLimit`, `value`, `to` and `data`. This EIP proposes adding a new one: `from` which, if present, indicates that `data` is an ethereum transaction containing `to`, `value`, `to` and its own `data`, but the message is signed by the account represented on `from`.

This comment has been minimized.

@pirapira

pirapira Feb 9, 2018

Member

When the inner transaction is a contract creation transaction, the new contract should be deployed to a certain address. How is the address calculated?

(If the nonce of the from account is used, and if the nonce is not incremented, one can deploy to the same address multiple times, and overwrite contracts).

@iurimatias

This comment has been minimized.

Member

iurimatias commented Feb 9, 2018

For reference, TransactionRelay is a early attempt to do this (contract can be found here).

@alexvandesande

This comment has been minimized.

Contributor

alexvandesande commented Feb 9, 2018

@karalabe You're correct, it would be simpler to refer it as two separate transactions: the outer one containing nonce, gasPrice, gasLimit and a new innerTx and the inner one containing nonce, value, to and data.

To simplify matters both inner and outer transactions would need nonces and both would be incremented when the transaction was accepted. I recognize this could create some weird behaviors, for instance if two deployers send the same transaction, the second one should fail – should it throw or simply return?

I would argue that yes, this creates a possibility for inner signers to attack deployers by forcing transactions to fail: but we need to consider that these kinds of transaction deployment is considered voluntary and any incentivization depends on the deployer/signer relationship. In some of the cases mentioned, a deployer could simply stop deploying transactions from a blacklist. Also, since deployment is a second level tier, even if there's some attack vector it doesn't affect the consensus layer.

@iurimatias @3esmit Thanks for the links, I'll add a section about examples

@pirapira thanks for the notes, changed them

@3esmit

This comment has been minimized.

3esmit commented Feb 10, 2018

Maybe we could have something like a signed message struct block, which can be passed through contracts, so we can have pre processed at inclusion and the message a bytes data can carry this presigned verificaton.

In current EVM is already possible for a DAO abstracting the gasPrice to be paid in the token itself, only requiring that the calls that pass address _from came from a trusted token address.
Also, Raiden could implement ApproveAndCalLFallBack properly so the opening and closing channels can be relayed in gasPrice paid in token by approveAndCallPreSigened.

@vbuterin

This comment has been minimized.

Collaborator

vbuterin commented Feb 10, 2018

I think it might actually be possible to do this within my account abstraction proposal. The mechanism is simple: you send a transaction where the target is the transaction deployer, and then the deployer contract verifies the deployer's signature, then calls the sender of the inner transaction, with the intended transaction data. To save on gas verifying deployer signatures, the deployer contract could be designed to do this multiple times with multiple transaction bodies at a time.

Basically, because my proposal reduces a transaction to being little more than just a message call with ENTRY_POINT as sender, doing things more similar to transactions-inside-transactions becomes quite a bit simpler.

@3esmit

This comment has been minimized.

3esmit commented Feb 22, 2018

This would be useful in cases we need presigned messages from contracts. Maybe somehow a contract could be able to sign a message under a predefined multisigning offchain logic?
Ive got into limitations when needing that, for example, a Multisign wallet signing offchain message.

Maybe EVM could have a special layer to dealing with signatures, and they could be calculated all offchain.
For example, we have a special signature member in contracts, that is like a function, but it calculates the signatures for the contracts, which could read from contract state and use data in the signature.
When transaction is included, the required signature calls would be given separatedly for each contract that requires them along the call stack. A contract call that requires this signatures and were not provide would fail.

Seems like some of this could be currently possible by updating compiler only, but not safe under untrusted contracts (because contracts would be assuming that contracts compute signatures correctly), so a change in EVM to tie the precalculated signature result into call stack would be useful for enhancing network between untrusted contracts.

Maybe a new opcode could save the result of a pure call and this call would be somehow injected into callstack, making it available for other contracts that expect it (might be ignored if not used by other contract?)

@alexvandesande

This comment has been minimized.

Contributor

alexvandesande commented Feb 22, 2018

Seems like some of this could be currently possible by updating compiler only

I considered that but I'm not sure. Let's say we add an special flag called "allowSignedMessages" on a solidity function. On compile time the compiler creates a second set of functions for each one of those, with an added "allowSignedMessages" on the name, adding a special line to extract the signature and replacing all instances of msg.owner with the signer. It works on a first level function but what if the function itself calls another external one? You can't replace msg.owner on that one, unless it also provides a allowSignedMessages alternative.

@Arachnid

This comment has been minimized.

Collaborator

Arachnid commented Mar 27, 2018

This is a courtesy notice to let you know that the format for EIPs has been modified slightly. If you want your draft merged, you will need to make some small changes to how your EIP is formatted:

  • Frontmatter is now contained between lines with only a triple dash ('---')
  • Headers in the frontmatter are now lowercase.

If your PR is editing an existing EIP rather than creating a new one, this has already been done for you, and you need only rebase your PR.

In addition, a continuous build has been setup, which will check your PR against the rules for EIP formatting automatically once you update your PR. This build ensures all required headers are present, as well as performing a number of other checks.

Please rebase your PR against the latest master, and edit your PR to use the above format for frontmatter. For convenience, here's a sample header you can copy and adapt:

---
eip: <num>
title: <title>
author: <author>
type: [Standards Track|Informational|Meta]
category: [Core|Networking|Interface|ERC] (for type: Standards Track only)
status: Draft
created: <date>
---
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment