Transactions without consensus

"TON's virtual machine (TVM) is applying the concept of distributed microservices to Ethereum EVM's monolith" Tal Kol.

In web3 any message that changes the blockchain state has to go through consensus to be committed. We call them transactions in general. Web3 consensus is a very costly process. In fact, a node can check if a message is valid by checking its signature and wallet state locally. Then, why do we need consensus? To prevent double spend. We need consensus on the order of transaction requests so that we can accept the first of conflicting transaction requests, and reject the rest. In blockchain consensus this is implemented on block level, where no conflicting transactions are allowed within the block.

Getter functions on the other hand do not change state and thus cannot do double spend. Thus they do not need consensus. That is why getter implementation scheme is rather costless and they can be free of charge.

The consensus obligation for transactions has been the unquestioned rule in web3 because the blockchain virtual machines have been monolithic. In EVM for example all transaction regarding an ERC-20 token are done in a single smart contract atomically. All wallet states are in a mapping table residing in the relevant the ERC-20 token contract implementation.

TON on the other hand introduces a microservices approach to Web3 for mass adoption. In TON each wallet is a distinct contract, where the transactions are atomic. However, transactions among wallets (contracts) are asynchronous and not atomic.

This opens up the possibility that some type of transactions that are heavily used in dApps can be committed without dedicated consensus even though they change state.

Instead of sending the staked tokens to the proposal contract, Alice locked her staked tokens locally.

  1. It all happened in her jetton-wallet contract.

  2. She cannot undo locking.

  3. She cannot decrease the lock_balance and end_time of her lock state.

#3 above is very important. All her lock transactions can only increase the lock state. Imagine a double spend attempt as follows:

  1. Alice has 100 DAL tokens in her balance and her lock_balance is zero.

  2. She sends two lock transaction requests to her DAOL-wallet contract as follows:

    1. Message 1: 60 DAOLs to one node.

    2. Message 2: 50 DAOLs to another node.

Notice that the transactions

  1. have different lock amounts with the sum exceeding the balance;

  2. are sent to two different nodes.

We propose that; upon receiving transaction messages the nodes should

  1. Do preliminary check if signature is valid, msg_value has enough $TON amount for the transaction to complete, has enough balance, etc.

  2. Gossip the message to other nodes.

  3. Update wallet balance and lock balance state locally without consensus.

Imagine nodes receive the two transaction messages in the two reverse orders. (This has a very low probability and needs an indredibly short period of time difference between mesages.) The end state would be the same in both cases: balance will be 0, and lock_balance will be 100 $DAOL. Remember that the lock function removes the excess lock_amount in the message to guarantee that total lock cannot exceed balance.

Imagine NodeA receives Message1 (60 $DAOL) and then Message2 (50 $DAOL): It will lock 60 DAOLs and then 40 DAOLs removing the exceess lock-amount (10 $DAOL) requested.

Imagine another node which receives Message2 (50 $DAOL) before Message1 (60 $DAOL). Then it will first lock 50 DAOLs and then lock 50 DAOLs again removing the exceess lock-amount (10$DAOL) requested.

The end state is the same in both scenarios. Alice can use the whole 100 $DAOL she had, for staking purposes in DAO voting. Both validator nodes have temporal and permenant state consistency allowing them to commit both lock transactions, provided that each has enough $TON payment thereto attached. Paying the gas cost of locking transactions, albeit very low, is also anti-DOS.

Then, she will send her vote message to the proposal contract. The proposal contract will call the get_lock getter function on her DAOL-wallet contract and see 100 $DAOLs locked. (It will also check to see that Lock.EndTime >= Porposal.EndTime)

As you can see we did not need consensus in this scheme. Just two local transactions, a getter function and two notificaitons. This is because the user can anly increase her lock with consequitive lock messages, which is against herself (i.e. locking is decreasing your token balance).

We need to make a minor change in the Cathcain consensus implementation to accomodate this scheme though and that is why we need an enhacement proposal. The details are in TEP-X documentation. In a nutshell, the producer of the new block should attach the relevant lock requests from the mem pool to this block. We propose that the transactions in the block are executed before the lock requests. The only conflict possible at this stage is that the lock amount can exceed account balance available after transacations in the block are executed. We offer that instead of rejecting the lock action, TVM should cap the lock_amount and execute the lock function, automatically (i.e. transaction has higher priority than lock within the block). In other words, TON does not implement consensus for lock operations but attach those which pass preliminary validation to the block and execute them all (via automatic capping). We also propose that transaction fees for lock operations should be low and constant becasue

  • Lock operations are local and therefore do not need messages among contracts.

  • They change only the state of their own contract without changing total balance of the jetton-wallet contract and the total supply of the Jetton.

  • Their computation is exactly the same for all possible use cases.

  • Their computational cost is low.

  • We do not want to cause bounces for lock operations. In preliminary check the Wallet code, that will call lock request, can check the TON balance for gas fee, and Jetton balance for available balance, and that's it. The validator node can do the same at the preliminary check for the lock request. Since the gas fee is paid in TON and not in Jetton and the fact that they are distinct tokens providing linearly independent factors, and that the fee is constant for lock requests; the decision for the validity of a lock request can be deterministically reached at preliminary check.

Let us apply this scheme to another smart contract type that is used by token issuers widely. We will use a funnily accurate example: FED's attemp to fight $USD inflation via increasing its interest rates. It would be awesome to see that lockable tokens mechanism is a much better alternative there, too. Then, we can focus on limitations.

Last updated