rsksmart / RSKIPs

RSK Improvement Proposals
51 stars 28 forks source link

Free Transactions Using a Precompiled Contract #166

Open ajlopez opened 4 years ago

ajlopez commented 4 years ago

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:

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:

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:

-- 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_

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:

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:

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:

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:

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 commented 4 years ago

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 commented 4 years ago

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.