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

Transaction Encapsulation Discussion #80

Open
SergioDemianLerner opened this issue Jul 10, 2018 · 0 comments
Open

Transaction Encapsulation Discussion #80

SergioDemianLerner opened this issue Jul 10, 2018 · 0 comments

Comments

@SergioDemianLerner
Copy link
Collaborator

There are several techniques that can be used to pay fees in fiat currencies:

  1. The "fee" field is expanded to included the token id (ERC20 contract) and miners accept fiat currencies.

This approach has several problems: miners may not know about or be interested in such tokens. Therefore users do not have any means of discovering miner currency choices. A new field: acceptedCurrencies must be added to each block to signal it. But there are many more problems:

  • the REMASC contract may need to handle many different tokens.

  • Each network node must know the current majority accepted currencies to decide whether to forward a transaction or not.

  • Miners must vote for minimum transaction fees for every currency supported.

  1. Multi-input transactions are enabled.

The last input of the transaction is the one which pays the fees. ERC20 contract are expanded to support at least two transactions in a single method call.

Therefore a user Alice can:

  1. Contact an exchange E and ask for the current conversion rate.

  2. If the conversion rate is accepted (Big question here: how to detect it without user help?) the user asks E for its address.

  3. The user builds a transaction T with a ERC20 method Transfer() with the following arguments::

    1. a payment of X cryptoUSD to the receiver

    2. a payment of Y cryptoUSD to E

  4. The transaction T has two inputs:

    1. the first from Alice does not specify any value.

    2. the second from E specifies a gasPrice/gasCount computed to allow the call

  5. Alice signs T creating Ta.

  6. Alice sends Ta to E.

  7. E verifies that the Y value in cryptoUSD pays for the gas specified.

  8. E signs Ts obtaining Tse

  9. E broadcasts Tse

This change is not hard at the core level, but renders all existent block explorers useless because they can’t correctly interpret the new transaction format.

There are several benefits:

  1. This solution is that is does not have the double-spend problem of the transaction encapsulation solution.

  2. This solution is low cost: verifying another signature costs 4000 gas. So the overhead is 81260 /77260 =1.05 (5%) if T pays in cryptoUSD and 53314/ 49314=1.08 (8%) if Alice has a "debit account" with E.

  3. This solution allows creating a new type of all-or-nothing payment engagement in a single transaction (without multi-input transaction, this requires one message by each member).

  4. Transaction Encapsulation

A new opcode is added that allows the VM to accept a transaction as it were included in the transaction trie. The flow would be the following:

  1. Alice contacts an exchange E and ask for the current conversion rate.

  2. If the conversion rate is accepted the user asks E for its address.

  3. The user builds a transaction T with a ERC20 method Transfer() with the following arguments::

    1. a payment of X cryptoUSD to the receiver

    2. a payment of Y cryptoUSD to E

  4. The transaction T is signed and forwarded to E.

  5. E encapsulates T as data of a transaction Te. Te transfers no value and contains gasPrice/gasCount computed to allow the call. The destination of Te is a contract R owned by T. The contract R simply takes the payload an executes the payload (via a new opcode EXECUTE).

  6. When Alice wants to do another transaction, without the help of E, she can create a transaction T2 with using the following nonce of the specified in T.

This solution has some problems:

  1. As the network has not really seen T (because it’s encapsulated) it may not allow T2 to be broadcast. But this problem disappears as a new block containing T is executed, and the nonce changes in the account state. Therefore users that use encapsulation can not switch immediately to non-encapsulated transactions: they have to wait the encapsulated transaction to be mined.

  2. A related problem is that a user may double-spend by propagating a transaction Tp with the same nonce as T, while E is propagating T. Clearly only one of them will be executed successfully. However, E has invested resources (transaction fees) to propagate T, and those fees will not be paid back by Alice in transaction T. Therefore there must be a minimal trust relation between Alice and E. That minimal trust extends to the cost of an ERC20 payment, which may be around 2 US cents, or 30 cents of a peso argentino.

  3. The cost of an additional micro-transaction of ERC20 tokens to pay for the transaction fees may increase the cost of the fees itself considerably. We tested this implementing a methods transfer2() in a standard ERC20 contract. The appendix A shows the program used for this test. The results are the following:

transfer(0,1)

Transaction cost: 49314 gas.

Execution cost: 27722 gas.

doNothing()

Transaction cost: 21506 gas.

Execution cost: 234 gas.

transfer2(0,1,1,2)

Transaction cost: 77260 gas.

Execution cost: 55284 gas.

This means the the execution cost has almost doubled. Since the external transaction pays 21K gas, adding a new ERC20 destination represents 77260 /49314 =156.66 (56% price increase).

Encapsulation also adds other costs:

  • that the sender must pay for the payload: (TX_NO_ZERO_DATA = 68). So a 100 byte transaction will pay approximately 6800 gas.

  • the cost of EXECUTE opcode. This should be approximately equal to the cost of a CALL (700) and the cost of an ECDSA public key recovery (EC_RECOVER = 3000). So we can assume a cost of 4000.

Considering the last two costs, the cost of encapsulation+double ERC20 payment reaches 88060/49314 =1.78 (78% more)

Clearly it is better that Alice has a cryptoUSD debit account in E (in other words E gives the service of transaction broadcast, and the account is topped with certain cryptoUSD). Therefore the additional cost of Te is 10800 gas, which gives 60114/49314 =1.21 (21% more).

Benefits of encapsulation:

  1. Encapsulation can be used to prevent easy censorship of transactions. Because the payload of transactions is not so easily analyzed by nodes, a censored account can use encapsulation to prevent miners detecting the use of this account, without executing first executing the transaction that encapsulates, which implies certain amount of work.

  2. We can also implement the all-or-nothing (AoN) functionality using a new field in transactions: "caller" which specifies which contract can execute this transaction. This also requires a new contract to be created specifically to handle the all-or-nothing functionality. It requires two steps: first the AoN contract is created. This contract would have a single aon_execute(byte[] data) method, where data is the RLP encoding of several transactions. Then the AoN would check some fields of each transaction (e.g. the source and value to transfer) and if all are correct, execute EXECUTE for each transaction.

After the AoN contract is created, each party would build the transaction with a caller field set to the AoN contract address, they will concatenate all transactions using RLP, and submit them to aon_execute(). Note this requires an instruction to parse RLP or a contract library to do so.

  1. Another way to allow all-or-nothing is by adding a new field "bundle" to each transaction, and extending EXECUTE opcode to execute more than one transaction. bundle would be the hash of all transactions, except signatures. The bundle only succeeds if all transactions in the bundle succeed. The idea of transaction bundles is more powerful, and could be used for external transactions also. A bundle could easily allow an AoN operation by including transactions to creating a contract, sending money to this contract from different sources, doing something else with the funds, and killing the contract.

While multi-input transactions can serve several use-cases (specially if each input can contribute with a different value) encapsulation serves a only few use-cases:

  • Adding transaction fees to a pre-existing transaction

  • Dispatching many transactions where the source is an account, with much less cost than executing each of the transactions separately. This is because the overhead of execute is 10K gas, while each independent external transaction costs 21K gas. Note that this can also be done by creating a dispatching contract and sending a single transaction carring the data of all the receivers and amounts.

Encapsulation, seems to be the best solution in terms of simplicity and backward-compatibility.

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

No branches or pull requests

1 participant