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

Free Transactions Using a Precompiled Contract #166

Open
ajlopez opened this issue Jul 2, 2020 · 2 comments
Open

Free Transactions Using a Precompiled Contract #166

ajlopez opened this issue Jul 2, 2020 · 2 comments

Comments

@ajlopez
Copy link
Contributor

ajlopez commented Jul 2, 2020

Motivation

To promote adoption, many dapps could create blockchain accounts for their users, but without giving them an initial balance (maybe, the dapp could give them some tokens). Then, these users want to interact with the blockchain, to do actions (ie play a game). This proposal describes a way to allow these users to execute actions in the blockchain, without having an available balance to cover the gas costs. Also, it allows them to pay (if needed) that gas cost using their tokens.

Description

In order to solve these use cases, we could use the concept of Ethereum meta transactions (alone, or using Gas Station Network). But that approach has a issue: the contracts to call should be aware that the msg.sender used in those final transaction don't correspond to the original user. This issue also makes difficult to transfer tokens to pay the execution of transactions (usually, the owners SHOULD send such transaction, but in these use cases, they don't not have any available balance).

So, in this proposal is based in a new pre-compiled contract, that given the description of the original sender actions, executes them as such sender, but the gas costs are paid by another account, the sponsor account. Optionally, this sponsor account could receive a payment in tokens from the original sender.

For improved security, the new pre compiled contract could be called only from an external transaction. To improve security, this precompiled contract could be call only from transactions, not via internal message (ie Solidity call or an EVM call). Even in the case the precompiled contract could be invoked internally, the sender nonce is checked, given a quick failure notice.

Detailed Description

The sender is A, without balance. A wants to execute a transaction invoking a smart contract. Then, A declares his/her intentions:

  • Receiver account to invoke (the smart contract address)
  • Value to send (possibly 0)
  • Data to be used

A could declares more than one triplet. After declaring them, A signs the message with contains the list of intentions AND the next sender nonce, to endorse with his/her key that the intentions will be not repudiated.

The proposed format is: intentions, but to discuss if transaction format is needed for hardware wallets interaction (apparently it is not mandatory)

At the execution of intentions, the sender nonce will be incremented using the nonce provided. Transaction validation should check that sponsor nonce is right, AND that the provided (and signed) sender nonce is also right. The recommended logic for sponsor is to DON'T accept advances nonce from a sender, because in this case, it is harder to check the availability of resources to execute anything (ie, the availability of tokens, if the sponsor requires token payments).

The signed messages (intentions triples and sender nonce) are sent to an sponsor S application (possible a public exposed API), with optional fields: the gas cost estimated for all operations, the suggested gas price (it's up to the dapp how to decide this values, but the straight way is to use the estimated gas JSON RPC for normal transactions). The sponsor logic checks the valid signature, could examine the intentions, could accept or change the gas price (if sponsor accepts token payments, could check suggested gas price with tokens provided), and approve the intentions. Then, S builds a valid blockchain transaction:

  • Sender: sponsor S
  • Receiver: the dedicated pre compiled contract C
  • Nonce: sponsor nonce
  • Value: any additional value needed, if the inner intentions have values > 0
  • Data: the signed intentions plus sender nonce payload
  • Gas and Gas Price: determined by sponsor logic, according to sender estimation

S signs this transaction T and send it to the blockchain. The transaction hash is informed to the sender A. In this way, A could verify the existence of the transaction in the blockchain, and its result.

The transaction T is a normal transaction. To the rest of the logic, it does not involves consensus or validation changes. Except that it needs the existence of a new pre compiled contract C

When transaction T is executed (in miner logic, or in other nodes), the usual logic is applied: it invokes a contract, and the gas used * gas price will be pay by the sender (the sponsor S). The key logic is in the pre compiled contract C:

  • It verifies the payload (triple list, sender nonce and signature), and then, it starts to execute each intention, using internal message to call for each intention:

-- Sender: the original sender (identified by the payload signature)
-- Receiver: the receiver of the intention under process
-- Value: the value of the intention under process
-- Data: the data of the intention under process

At the end of transaction execution, gas costs are covered by the sponsor S account, as usual in other transactions: the sender pays.

If one the internal message execution is reverted, all the full sponsor transaction is reverted.

Use Cases

Promote Game Adoption

A Dapp exposes a game experience (ie spaceship battles) using the web, but recording player moves and items into the blockchain. To have an easy onboarding, each new user is assigned with a blockchain account, without balance. When the user plays a move, the dapp construct the intention (invocation to the game contract) and sends this information to the sponsor site (maybe a public API). Sponsor logic checks_

  • Receiver is the game contract
  • The move in valid
  • etc

and then the sponsor builds, sends and pays the blockchain transaction. The game is promoted, so the business model covers the gas costs. The original user don't need to pay in anyway to the sponsor.

Social Network

A social network wants to reward their users by their activities, and gives them some utility tokens. When the user wants to exchange this tokens, or do any action in the blockchain, he/she could pay with such tokens the execution of transaction (usually the user has no available balance). The social network browser logic (like a dapp) builds the user intention. In this case, the intentions are two:

  • Pay with n tokens to sponsor
  • Play a game

The signed intentions are sent to the sponsor site/logic. The sponsor validates that the amount of tokens is available, and that it is enough to cover the total gas costs. If sponsor S accepts the intentions, it builds the blockchain transaction, with receiver the dedicated pre compiled smart contract.

Out of scope

To discuss detailed sponsor check logic. Also, to solve any possible attack to sponsor (we could leverage the current solutions and proposals, for Ethereum, relayers in Gas Station Network, and meta transactions in general).

Implementing the solution

Only a simple pre compiled contract is needed. The current code allows to build this extension, as a hard fork, using a bounded effort, and complying with time to market.

Intentions are executed as internal calls. The gas costs added is the internal call cost. The gas limit to provide, is the current gas limit BEFORE the internal call, using current RSK behavior for internal call (AFAIR there is a rule to pass a bit less gas limit).

The total gas cost is the internal call gas costs (including the called code gas cost), check the result (if false revert), check sender signature, and the transaction (invoke and payload) cost.

The format of the message to receive as payload:

  • RLP encoded (it's better than ABI to represent hierachical heterogeneus data)
  • Three items: a list, a two single items. The first single item is the sender nonce. The second single item is the sender signature as bytes
  • The list item has items that are list. Each item has three subitems: receiver address, value, data (array of bytes)

The message to sign is a list with the above first two items: list of intentions, and sender nonce. Alternative: the hash of that message could be signed. Alternative: a message compatible with EIP 712 could be signed (having a domain identification, ie, the precompiled contract C address)

All the specification allows the change of format is needed or if there are other format more appropiate for lower payload gas cost, o easiness for dapps. The key things are in place: list of intentions (triples), sender nonce, sender signature.

Some special cases to cover:

  • Allows trace_transaction gets the appropriate information about internal calls and transfers
  • The same for debug_transaction

As mentioned, some logic it's up to dapp and sponsor logic, like the negotiation of gas limit and gas price to use. If it is really needed, each triple could an additional gas limit (being then a quadruplet), but having sponsor checked the current status of the blockchain and checked the full gas limit, this additional field could be not needed.

The use of the sender nonce (signed after the list of intentions) is to avoid replay the same intentions by the same or different sponsor. Also it could disallow sender sending the same intentions to different sponsors: only one will be accepted and executed if they share the same sender nonce.

DApp and Sponsor Logic

Along this proposal, the logic for sponsor and dapp was mentioned, but it is only a recommendation, to avoid some rogue behavior from dapp or sponsor. I think that a detailed logic could be specified in a separate proposal, and even could be relative to the real use case. Ie, it the real use case is to have initially ONLY one kind of sponsor, for a social network, and only one dapp, more specific logic could be applied. An example: in that case, the sponsor could check that the sender account corresponds to a social network user account, and enforce that existence, even when the blockchain account still does not exist in the blockchain.

Some recommendations:

  • The sponsor should check and recalculate the gas needed even if the sender specifies it
  • In case of token payments, the sponsor should evaluate the gas price to pay according to his/her valuation of token given.
  • In case of payment with tokens, the intention declares the sponsor payment account as the receiver of the token transfer. So, they cannot be stolen by other sponsor.
  • It is recommended that sponsor use many accounts to sign the final transactions, in order to avoid his/her nonce could be a bottleneck in the advance of the operations
  • Senders could send the same intentions (except token payment if any) to different sponsor. But if they use the same nonce, only one final transaction will be executed. If they use different nonce, maybe both are executed, in predetermined order, according to senders instructions
  • Signing only intentions with sender nonce according to the next nonce to use by the sender, enforces sponsor logic to be sure that the resources are available (maybe after invoking a test transaction using estimate gas JSON RPC entry point).

Exiting the solution

In case it is needed, the sponsors could stop to accept intentions at any moment. And the pre compiled contract could be suspended (with a hard fork at heigh H).

@SergioDemianLerner
Copy link
Collaborator

The specification suggests that the payload format is:
payload = (nonce,signature, (list_of_intentions) )
where
list_of_intentions = list of (receiver address, value, data)

What does the signature signs ? There are several possibilities. I think you mean the payload but replacing the signature by something else. It is the empty field?

@SergioDemianLerner
Copy link
Collaborator

The use of the account nonce in a non-free transaction brings several problems regarding mempool.
One can think of an attack where the attacker fills the mempool with a huge transaction (and the network propagates it) but the attacker cancels it with a single call to the precompiled contract, where the call was hidden in another previous transaction in the block, under multiple recursive calls ?
To be more clear, both the call to the precompile and the free-transaction call share the same nonce.
This attack can be repeated with hundreds of accounts simultaneously, as a DoS attack to miners and the network.
That's why Patrick McCorry's proposal uses a different nonce queue.

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

2 participants