A way to send new transactions in KT contracts
tldr; Currently you can only send new transactions with implicit accounts. With this proposal you’ll be able to do it in KT accounts and solve some current Tezos limitations.
The following idea is early, it came to me few hours ago.
Edit 2020-09-14 15:16: I modified the proposal to make it technically more valid
:: 'p : mutez : contract 'p : mutez %gas : 'S -> operation : 'S NEW_TRANSACTION_TRANSFER_TOKENS
The instruction NEW_TRANSACTION_TRANSFER_TOKENS
is a lot like TRANSFER_TOKENS
except the transfer is done in a completely new transaction. As a result, the calling contract gives the fee for the new transaction.
This instruction and the corresponding operation can never fail. If the new transaction failed for any reason the NEW_TRANSACTION_TRANSFER_TOKENS
instruction is still considered passed.
The TRANSFER_TOKENS
operation is executed at the end of the whole current transaction only if the current transaction passed.
The number of NEW_TRANSACTION_TRANSFER_TOKENS
allowed is only limited by the current transaction gas limit.
Scheduling
Scheduling is a hot topic on TezosAgora.
I don’t have any definitive answer to that at the moment. The bakers that accept to include a transaction with a NEW_TRANSACTION_TRANSFER_TOKENS
instruction could guarantee that the callback will be included in at most n blocks. In exchange for this guarantee, the entry_points of the callback and the resulting operations should be guaranteed small.
Apart from this consideration, I don’t know if the new transactions could or not be executed in parallel. I would say yes but I feel that the answer is no (because they should act more like TRANSFER_TOKENS
). The new transactions shall be able to run in different blocks (see below why).
Feedback callback
As the NEW_TRANSACTION_TRANSFER_TOKENS
and NEW_TRANSACTION_TRANSFER_TOKENS
operation always pass you can’t know if the induced transaction have been able to transfer the tokens.
This information is needed is almost every use cases, this is why we should find a solution.
It could be a callback parameter. The callback would be automatically called at the end of the induced transaction with a boolean parameter equals to true if the induced transaction passes, false otherwise.
This brings another question: what happen if the callback transaction is never included in a block?
I don’t have any definitive answer to that at the moment. The bakers that accept to include a transaction with a NEW_TRANSACTION_TRANSFER_TOKENS
instruction could garantee that the callback will be included in at most n blocks. In exchange for this garantee, the entry_points for the callback and the resulting operations should be garanteed small.
About tickets
The tickets (see MR) should be kept along the transaction chain.
Question that needed answer: Is it technically possible taking into account the fact that the transactions can occur in different blocks?
About reentrancy (and tickets)
I thought that it could be interesting to add something like a time to live feature.
It would be a nat parameter.
A new condition and a reason to fail would be added to NEW_TRANSACTION_TRANSFER_TOKENS
. The instruction fails if the number of times you executed it with the same parameters and contract sender (including the entry_points) in this chains is equal to the nat parameter.
This would avoid the possibility of infinite loop until the balance of the contract is empty.
Ex:
- Contract A send a NEW_TRANSACTION_TRANSFER_TOKENS to contract B with a
%TTL
parameter equal to 1 - As a result B do a TRANSFER_TOKEN or a NEW_TRANSACTION_TRANSFER_TOKENS to contract A (or to a chains that finish by doing a TRANSFER_TOKEN/NEW_TRANSACTION_TRANSFER_TOKENS to contract A)
- Contract A want to send a NEW_TRANSACTION_TRANSFER_TOKENS to contract B but the number of time this already happened is 1. 1 equals the
%TTL
so the instruction fail and the current transaction fail.
This feature could nearly be implemented with tickets.
IMO a new parameter would simplify contract developers and force them to think about this infinite loop question. The drawback is that it adds another data to attach to the whole chains of transactions.
About total gas limit
Should there be a limit of the total number of gas spent on the whole chains of transactions? I don’t find any reason but there could be.
This proposal answers actual problems
Refunding to a list of contracts problem
We can read in the Tezos Developer Documentation: Michelson Anti-Patterns one of the current limit of Tezos that can disappear with this new instruction.
One common pattern in contracts is to refund a group of people’s funds at once. This is problematic if you accepted arbitrary contracts as a malicious user can do cause various issues for you.
Possible issues:
- One contract swallows all the gas through a series of callbacks
- One contract writes transactions until the block is full
- Reentrancy bugs. Michelson intentionally makes these difficult to write, but it is still possible if you try.
- A contract calls the
FAIL
instruction, stopping all computation.
Resolving with NEW_TRANSACTION_TRANSFER_TOKENS
The solution is straightforward: iterates through the list of addresses to refund and use the NEW_TRANSACTION_TRANSFER_TOKENS
instruction to refund them. This way you can separate your computation from that of the recipient in the destiny of your transaction.
This solution prevents the “gas spending”, “block full” and “Fail
computation” problems.
Question that needed answer: What are the remaining reentrancy bugs?
Notice that you should still permit people to pull their fund individually in case their payment receiver failed.
Make others pay for their computation
One feature I’d like in Tezos would be to pay the fee of the computation for someone else or to ask for someone else to pay my fees. It would act as an on chain gaz relay.
This proposal permits that.
In the previous refund example when you use the NEW_TRANSACTION_TRANSFER_TOKENS
in your refund contract you don’t need much gas even if the people want to do complex transaction in response.
Why? Because they can use NEW_TRANSACTION_TRANSFER_TOKENS
in their own contract to do the complex operations.
You’ll pay the fee for a very simple transaction and let them pay the fee to have their complex computation included in the block.
Increase the interactivity possibilities
In the current situation implicit accounts are the only one able to create new transaction. It means that only humans or off chain codes can prolong the interactivity beyond the gas limit.
With this proposal you can create a new transaction on KT1 accounts and start on a fresh new gas limit counter.
I’m waiting for your comments.