Plasma Cash Side Chains For Ethereum

Plasma Cash Side Chains For Ethereum
 
Blockchain and decentralization promise to optimize and improve almost every area of life by using the internet and information technology. They contribute to the reliability and financial efficiency, as well as digitizing real things and goods.
 
Smart contracts bring business logic to decentralized networks, allowing for the construction of new DAPP applications.
 
Execution of smart contracts and rapid operation of applications with a distributed database can only be possible if the condition of high scalability is fulfilled.
 
Modern decentralized blockchains have several drawbacks, the main one being scalability. Ethereum processes about 20 tx/s, which is not enough to accommodate today's financial realities. At the same time, Ethereum has the highest degree of protection against hacking and network failures. Other cryptocurrencies and systems built on the blockchain do not have such a high degree of decentralization, which reduces a network’s credibility.
 
We will discuss the following items in this article:
  • the scalability trilemma and ways to solve it;
  • child-chain data structures and their display in root chains;
  • root chain deposit;
  • withdrawal from a root chain.
We have used JavaScript programming language to implement a child chain, as well as solidity for a root chain. Code examples are written in these languages.
 

Scalability Trilemma

 
A blockchain scalability trilemma includes three components:
  • decentralization;
  • security;
  • scalability.

Decentralization in the Trilemma

 
Decentralization characterizes the degree of ownership diversification in a blockchain, as well as diversification in creating blocks and generating new ledger records.
 
To clarify this term, we should first talk about the most centralized organizations. Usually, a simple database is used instead of a blockchain. The database is managed by special administrators, and all transactions can be canceled via manual intervention.
In a fully decentralized system, each user can participate in network building.
 
The most important feature of decentralization is that a community participating in creating a blockchain enjoys the bulk of its privileges. There is no intermediate managerial team receiving all the benefits, rather than those who generated the basic structure. In fact, most crypto projects belong entirely to their distributors or users, and not to their creators. Decentralization is obviously a more attractive option for non-founders.
 

Security in the Trilemma

 
Security reflects the level of protection that a blockchain has against attacks from external sources. It is a measure of how stable the system should be. Most blockchains are threatened by many adverse factors. We will consider the most common attack vectors and defense options.
 
First of all, decentralization and security go hand in hand. As a rule, the more nodes a network has, the less it depends on a central entity and, consequently, the lower the risk of failure. However, there are many attack vectors that pose a danger to decentralized networks, including,
  • 50% attack - an entity that holds more than 50% of the total number of unpaid tokens essentially owns the network;
  • Sybil Attack - a user can generate multiple identifiers to effectively control a significant share in the ownership and/or decision making of a network;
  • DDoS - Distributed Denial of Service (DDoS) attack - this occurs when traffic is disrupted by filling a network with malicious transactions;
  • Collusion attack - this occurs when one or more objects (or nodes) unite to perform some malicious operations in a network.

Scalability in the trilemma

 
The degree of scalability is important because it determines a network’s ultimate bandwidth. In other words, it defines the upper limit of a network’s volume. The most important issue to consider when evaluating a network is how many users it can serve. Bitcoin currently has between 2.9 and 5.8 million wallet holders; EOS has several thousand wallet holders.
 
Scalability and decentralization may coexist, but when they do, security risks increase. Developers usually choose the platform that best suits their needs, while users choose the one most suitable for them. Some users may be willing to sacrifice security for the sake of scalability, while others are ready to sacrifice scalability in order to increase security.
 

The Holy Grail in blockchain technology

 
The trilemma is that blockchain systems can simultaneously have only two of the following three properties:
 
Decentralization (a system operates according to a scenario where each participant has access only to O (c) resources, that is, to a regular laptop or a small VPS);
 
Scalability (the ability to handle O (n)> O (c) transactions);
 
Security (protection against intruders using O (n) resources).
 
Plasma Cash Side Chains For Ethereum
 
Green: a balanced state of the three conditions.
 
Red: strong security, but decentralization and scalability are limited.
 
Blue: high efficiency, but there are restrictions on security and decentralization.
 
Black: high decentralization, without some scalability and security aspects.
 
Gray: full decentralization, with minimal or no security and scalability.
 
Violet: a balance between security and scalability, decentralization is rejected.
 
In blockchain technology, the Holy Grail is to find solutions that combine all three aspects.
 
Two key features - decentralization and security - are being obtained in most current cryptocurrency projects, while scalability suffers.
 

Promising options for solving the trilemma

 
Proof of Stake (PoS)
 
Proof of Stake (PoS) provides potential scalability improvements and replaces cryptocurrency mining based on the Proof of Work (PoW) system. A validator is selected very quickly, in a deterministic way. At the same time, there are no demands for electricity, reducing costs and protecting the environment.
 
Sidechains
 
It is possible to create a side network in the Ethereum virtual machine (EVM). A project can process its specific transactions there and then record only initial and final results. This reduces the load on EVM but undermines trust in sidechain management. The active involvement of third parties reduces decentralization.
 
Sharding
 
Sharding breaks transactions into smaller data pieces. Instead of each individual node processing entire transactions in the network, nodes are divided into groups, and these groups process certain data pieces. These data pieces are later reassimilated for permanent storage in a blockchain.
 
Increasing the block size
 
Litecoin and Bitcoin Cash (BCH) are forks for a Bitcoin blockchain. Forking basically copies a single blockchain. After branching, you can make changes to the blockchain. Both LTC and BCH increase each block’s size, which allows for the storage of more transactions per block, thereby increasing transaction processing speed.
 
Lightning Network
 
Lightning was the very first sidechain solution. The main idea of Lightning Network is that not all transactions should be recorded in a blockchain since this overloads the network. If users transfer funds to one another multiple times, it is not necessary to register each transfer. It is enough to open a sort of payment channel and record the data on its opening in the blockchain. This channel would then remain open as long as necessary. When the time comes to close it, the results of all the transactions made in this channel are simply written in the blockchain. Following this idea, you could create a whole network of payment channels, and transactions would be used less often in the blockchain.
 
A payment channel is just a merger of several transactions. The channel can be closed by any of its members. This action is akin to opening a safe. It allows participants to withdraw funds and record their transfer data to the blockchain.
 
This technology is powerful when several similar channels are used jointly in the Lightning Network. This network was built especially for Bitcoin.
 
Raiden network
 
The Raiden network is the Lightning equivalent for Ethereum.
 
It is used for scaling outside the main blockchain, which is compatible with transferring ERC-20 tokens in bidirectional payment channels.
 
Its basic architecture is complex, but Raiden requires developers to interact only with API to create scalable applications on Raiden.
 
This system provides instant payments, increases the confidentiality of transactions, enables micropayments, and has low fees. Most payment channels exist outside a network and form transactions within the root network only occasionally, which significantly reduces the throughput of the root network.
 

The solution

 
Lightning ideologists have gone further and created a new concept solving blockchain speed problems for child chains.
 
We implement the Plasma and Plasma Cash concepts in practice. Plasma is a set of smart contracts that run on top of an Ethereum root chain, and consist of a child chain network connected to a root chain in a hierarchical tree structure.
 
The Ethereum root chain’s security is used to optimize scalability.
 
Plasma Cash Side Chains For Ethereum
 

Our Plasma Cash implementation

 
We have implemented the first version of Plasma Cash in our project.
 
Plasma Cash is the most effective option in terms of scalability. Its construction is based on applying unique identifiers for each token in the Plasma chain. NFT is used, and online tokens are given unique serial numbers.
 
Due to sharded client-side validation, clients need only to follow their Plasma chain to get their tokens. This increases transaction throughput without increasing the burden on individual users.
 
Mass exits pose little threat to the Plasma Cash network because a thief would have to provide a unique exit transaction to steal each token.
 
With Plasma Cash, there are no two-way confirmations. Transactions no longer require two-step sending plus confirmation. Instead, as soon as a transaction is included in the main chain, it can be spent.
 
However, there is one disadvantage to Plasma Cash: Large token values.
 
Since each token must be assigned a unique serial number, it is impossible to arbitrarily create small tokens, because at some mark, the cost of buying a token would exceed the value of the token itself.
 

Plasma Cash transaction structure

 
We have used Javascript to implement a child chain. Each transaction in Plasma Cash has the following structure,
  1. const transactionFields = [  
  2.   {name: 'prevHash'},  
  3.   {name: 'prevBlock'inttruedefault: 0},  
  4.   {name: 'tokenId', isDecimal: true},  
  5.   {name: 'newOwner'},  
  6.   {name: 'type'},  
  7.   {name: 'signature'},  
  8. ]  
The most important elements are a link to a previous block prevBlock allowing movement along a blockchain and the unique tokenId token identifier, as well as newOwner - the final token owner.
 
A special type of Patricia Merkle Tree is used to create a block and get a root hash. The same tree is used in Ethereum. It has a compressed structure, but it is still possible to get proofs of inclusion or non-inclusion of transactions in a block.
 
Signature is a mark on elliptic curves.
 
A transaction that spends a token with a given tokenId is valid only if it is included in the Merkle Tree, in the tokenId position. That is, each token in the Merkle Tree has only one “place” where a transaction may be conducted to spend this token. This format allows users to check the complete Plasma chain history, as well as prove and disprove ownership of specific tokens.
 
To spend a token, it is necessary to validate the chain, check it for missing blocks, and then re-sign the transaction along with its whole history.
 
Our block looks like this,
  1. const blockFields = [  
  2.   {name: 'prevHash'},  
  3.   {name: 'blockNum', isDecimal: true},  
  4.   {name: 'transactions'},  
  5.   {name: 'merkleRoot'},  
  6.   {name: 'time'}  
  7. ]  
At a fundamental level, a blockchain is simply a set of blocks with reference to a previous block. To avoid rewriting the immutability history. merkleRoot allows us to write checkpoints in a root chain.
 
A root chain at the smart contract level looks like this (solidity language),
  1. /* 
  2.      * Block structure (represents one block in a chain) 
  3.      */  
  4.     struct Block {  
  5.         uint block_num;  
  6.         bytes32 merkle_root;  
  7.         uint time;  
  8.     }  
  9.   
  10.     /* 
  11.      * Transaction structure (decoded from RLP form) 
  12.      */  
  13.     struct Transaction {  
  14.         bytes32 prevhash;  
  15.         uint prev_block;  
  16.         uint token_id;  
  17.         address new_owner;  
  18.     }  
Coding is performed using encoding/decoding - serialization/deserialization RLP.
 

Deposit methods in Plasma Cash

 
Anyone can enter a deposit into Plasma Cash, simply by transferring Ethereum to a smart contract. As a result, an OPP token will be received in a certain tokenID position.
 
Here is an implementation in the solidity language,
  1. function deposit() public payable {  
  2.         uint token_id = uint(keccak256(msg.sender, msg.value, deposit_blk));  
  3.         // token.index = deposit_blk;  
  4.         tokens[token_id] = msg.value;  
  5.         deposit_blk += 1;  
  6.         emit DepositAdded(msg.sender, msg.value, token_id, current_blk);  
  7.  }  
The tokenID is simply generated as a random number (hash). Then, an event is generated and scanned in the child chain.
 
Plasma Cash Side Chains For Ethereum
 

Exit methods in Plasma Cash

 
Anyone can withdraw their tokens by providing the last two transactions in each token’s ownership history.
 
Here is how the exit from a root chain may be implemented,
  1. function startExit(uint block_num, bytes tx1, bytes tx0, bytes proof1, bytes proof0) public returns (uint exit_id) {  
  2.    require(checkPatriciaProof(keccak256(tx1), childChain[block_num].merkle_root, proof1));  
  3.   
  4.         bytes32 prev_hash;  
  5.         uint prev_blk;  
  6.         uint token_id;  
  7.         address new_owner;  
  8.        (prev_hash, prev_blk, token_id, new_owner,) = getTransactionFromRLP(tx1);  
  9.       
  10.         require(msg.sender == new_owner);  
  11.           
  12.         require(tokens[token_id] > 0);  
  13.         bytes32 hashPrevTx = keccak256(tx0);  
  14.     require(checkPatriciaProof(hashPrevTx, childChain[prev_blk].merkle_root, proof0));  
  15.         require(prev_hash == hashPrevTx);  
  16.   
  17.         Exit storage record = exitRecords[token_id];  
  18.         require(record.block_num == 0);  
  19.   
  20.         record.block_num = block_num;  
  21.         record.new_owner = msg.sender;  
  22.         record.prev_block = prev_blk;  
  23.   
  24.         if (childChain[block_num].time > block.timestamp - week)  
  25.             record.priority = childChain[block_num].time;  
  26.         else  
  27.             record.priority = block.timestamp - week;  
  28.   
  29.         exits.add(record.priority);  
  30.         exit_ids[record.priority].push(token_id);  
  31.   
  32.         emit ExitAdded(msg.sender, record.priority, token_id);  
  33.         return token_id;  
  34.     }  
First of all, the presence of the two transactions is checked. If a current user is a transaction owner, the withdrawal is simply added to the structure, and two weeks are allotted to challenge the withdrawal if necessary.
 
An exit can be challenged in three ways.
 
Providing a spend confirmation on transactions,
  1. function challengeSpent(uint exit_id, uint blk_num, bytes tx1, bytes proof) public {   
  2.         require(checkPatriciaProof(keccak256(tx1), childChain[blk_num].merkle_root, proof));  
  3.   
  4.         Exit memory record = exitRecords[exit_id];  
  5.         require(record.block_num > 0);  
  6.         uint prev_block;  
  7.         uint token_id;  
  8.         (, prev_block , token_id, ) = getTransactionFromRLP(tx1);  
  9.         require(tokens[token_id] > 0);  
  10.         require(prev_block == record.block_num && record.block_num < blk_num);  
  11.         require(token_id == exit_id);  
  12.   
  13.         exit_ids[record.priority].remove(exit_id);  
  14.         delete exitRecords[exit_id];  
  15.         emit ExitChallengedEvent(exit_id);  
  16.     }  
If there is a transaction already spending a withdrawn token, the withdrawal would be canceled.
 
Providing proof of costs of an earlier transaction,
  1. /* 
  2.      * Challenge exit by providing 
  3.      * a proof of a transaction spending P(C) that appears before C 
  4.      */  
  5.     function challengeDoubleSpend(uint exit_id, uint blk_num, bytes tx1, bytes proof) public {   
  6.   require(checkPatriciaProof(keccak256(tx1), childChain[blk_num].merkle_root, proof));  
  7.         Exit memory record = exitRecords[exit_id];  
  8.         require(record.block_num > 0);  
  9.        // bytes32 prev_hash;   
  10.         uint prev_block;  
  11.         uint token_id;   
  12.         (, prev_block , token_id, ) = getTransactionFromRLP(tx1);  
  13.         require(tokens[token_id] > 0);  
  14.         // check if token double spent  
  15.         require(prev_block == record.prev_block && blk_num < record.block_num);  
  16.        // require(token_id == exit_id);  
  17.         exit_ids[record.priority].remove(exit_id);  
  18.         delete exitRecords[exit_id];  
  19.         emit ExitChallengedEvent(exit_id);  
  20.     }  
This is the same check made when a token was spent before its withdrawal. The transaction in a root hash is first checked, and we delete the withdrawal if it has already been spent.
 
Providing a transaction in the token transaction history preceding it;
 
This may be the wrong history, so you need to confirm it with a child transaction,
  1. //  */  
  2.    function challengeInvalidHistory(uint exit_id, uint blk_num, bytes tx0, bytes proof) public {   
  3.        // check if proof is valid  
  4. require(checkPatriciaProof(keccak256(tx0), childChain[blk_num].merkle_root, proof));  
  5.          
  6.        Exit memory record = exitRecords[exit_id];  
  7.        require(record.block_num > 0);  
  8.   
  9.        bytes32 prev_hash;   
  10.        uint token_id;   
  11.        (prev_hash, , token_id, ) = getTransactionFromRLP(tx0);  
  12.   
  13.        //require(exit_id == token_id);  
  14.        require(tokens[token_id] > 0);  
  15.   
  16.        // transaction should be before exit tx in history  
  17.        require(blk_num < record.block_num - 1);  
  18.   
  19.        challenged[exit_id] = blk_num;  
  20.        emit ChallengedInvalidHistory(exit_id, token_id);  
  21.    }  
Using the first and second algorithms immediately blocks the exit. The third method may be validated by providing direct descendant. It should be equal to or precede a parent transaction.
  1.   /* 
  2.      * Respond to invalid history challenge by providing 
  3.      * the direct child of C*, which must be either equal to or before P( C ) 
  4.      */  
  5.     function respondChallenge(uint exit_id, uint blk_num, bytes childtx, bytes proof) public {  
  6.         require(challenged[exit_id] > 0);  
  7.         Exit memory record = exitRecords[exit_id];  
  8.         require(record.block_num > 0);  
  9. require(checkPatriciaProof(keccak256(childtx), childChain[blk_num].merkle_root, proof));  
  10.         // get transaction from rlpencoded form  
  11.         bytes32 prev_hash;   
  12.         uint prev_block;  
  13.         uint token_id;   
  14.         (prev_hash, prev_block, token_id, ) = getTransactionFromRLP(childtx);  
  15.         // if direct child  
  16.         if (prev_block == challenged[exit_id] ) {  
  17.             if (blk_num <= record.prev_block && token_id == exit_id ) {  
  18.                 delete challenged[exit_id];  
  19.                 emit ExitRespondedEvent(exit_id);  
  20.             } else {  
  21.                 exit_ids[record.priority].remove(exit_id);  
  22.                 delete exitRecords[exit_id];  
  23.                 emit ExitChallengedEvent(exit_id);  
  24.             }  
  25.         }  
  26.     }  
If a correct child transaction is obtained, the withdrawal is challenged and remains in a queue.
 
To summarize the above information,
 
We looked at the part of Plasma Cash protocol developed as a part of our project. It increases security at the expense of the Ethereum root chain.
 
We reviewed the complexities of deposit and withdrawal from a root chain and state (transaction blocks) compression and analyzed all methods of withdrawal from and deposit into a root chain. Also, basic data structures — transactions and blocks — were reviewed.
 
Using an Ethereum-based sidechain significantly increases transaction speed. We achieved up to 300,000 transactions per second on one operator, far surpassing other current payment systems.
 
Despite some data availability problems, the operator provides a high level of blockchain stability and makes it possible to perform effective international business transactions. Plasma Cash adds a tremendous increase in scalability, makes money exchange smooth and easy.
 

Links


Similar Articles