Under The Hood : Blockchain Transaction
This article is part of the Under The Hood series, where we will take an in-depth look at blockchain architecture and how blockchains work under the hood.
In this article, we will focus on how transactions are implemented by different blockchain protocols and the impact it has on the whole project.
The code of the Bitcoin blockchain is, without a doubt, the most widespread among the blockchain industry. The Unspent Transaction Output (UTxO) model used by Bitcoin is very singular, as it is a computationally inexpensive way to solve the double spend problem.
A Bitcoin transaction is basically just a list of input and outputs, where inputs can be split and recombined at will between any outputs.
A Bitcoin transaction output contains an amount of BTC and a script that describes how this amount can be spent.
A Bitcoin transaction input contains a reference to the output it tries to spend and the redemption script that unlocks the script of the output. In most cases, the redemption script contains the signatures required to spend the output.
Because every unspent output can be considered as a coin, it looks like bitcoins are not belonging to anyone. What we would consider a transfer of value in the real world, would just be like changing the lock of the box containing the bitcoin, and who ever has the key would be considered the owner.
The UTXO model used by Bitcoin is really effective at isolating each transaction from the global state and preventing concurrency issues, as each transaction output lives in its own scope, dependently of the global state of the blockchain.
On Bitcoin, most of the transaction logic is in fact handled by the scripting language from the simplest to the most advanced features. The scripting language, however, is limited to the scope of the output and cannot interact directly with other parts of the blockchain.
The code of the Ethereum blockchain and its Virtual Machine is the second most widespread among the blockchain industry, especially for entreprise blockchains willing to use smart-contracts. The main focus of Ethereum is to provide the best environment for the smart-contract enabled features. Unlike Bitcoin, Ethereum is using an account based model where accounts can be representing a standard wallet or a smart-contract with code that can be executed.
Ethereum has adopted the We Have No Features stance, which means they made the choice not to provide any built-in functionalities in the transaction, so all the logic must be handled by smart contracts. In this model, each transaction has basically been given two distinct responsibilities, sending ETH to an address and potentially calling a smart contract’s method.
The recipient address is going to determine if the transaction is an ETH transfer from one wallet to another, or if the transaction is calling a smart-contract. The transfer is quite straight forward and indeed, really limited.
In the case of a call to a smart-contract, the only way to have an idea of what the transaction is doing, is to fully execute the contract using the data embedded in the transaction.
Another interesting point to note, is that fees are handled as gas to fuel the smart-contracts, where the gas price is the price the user is willing to pay per computation step and the gas limit is the maximum amount the user is willing to pay fully execute the transaction. This point emphasizes even more the fact that it is not possible to assume the result of an Ethereum transaction before its full execution.
Like most blockchains using the account model, each Ethereum account has a field called nonce to keep track of the total number of transactions that account has executed. The nonce is incremented for every new transaction and this allows the network to know the order in which the transactions need to be executed. The nonce is also used for the replay protection, as only transactions with a nonce greater than the last one are considered valid.
This nonce model is quite straight forward but comes with the drawback that every confirmed transaction will invalidate any previously created transaction that has not been confirmed yet. This can be a real problem if, for any reason, you need to delay publishing a transaction to the network, as it can potentially become invalid for purely technical reasons.
Ripple is one the first multi-currency layer-two blockchain to be released, when the ecosystem was still very immature and the scaling problem was not a thing, yet. The main feature of Ripple, is to allow anyone to issue their own asset backed currency and transaction with it seamlessly on the network.
Because anyone can issue any asset without having to prove it is actually backed by a real world asset, Ripple uses a mechanism based on trust to ensure the users only receive payments in currencies issued by people they trust. This mechanism is called Rippling and uses a pathfinding algorithm that uses an internal orderbook system to automatically convert the amount sent in the preferred currency of the recipient.
On Ripple, transactions are the only way to modify the ledger, the state of the blockchain. They are several types of transactions to handle the different features built-in the protocol such as payments, order books, payment channels, etc…
A transaction is initiated by a single account, which can be either a simple account or a multi-signature account. In the case of a simple account, SigningPubKey and TxnSignature must be filled, otherwise, the Signers field must be set.
Some small features are implemented in order to better integrate the transaction in the global flow of transactions. The transaction is only valid if the sending account’s previously-sent transaction matches a given hash (AccountTxnID). The transaction can also specify the last ledger it can be included in (LastLedgerSequence), making it an effective deadline and preventing transactions to be pending forever.
Several different features where implemented in order to add arbitrary data to the transaction, in the form of an integer (SourceTag) or more complex objects (Memos). The lack of a generic way to add data to a transaction leads to a plethora of specialized implementations in different parts of the code.
Ripple implemented a really simple payment transaction, where an account can send an amount to another account. In fact, we are speaking about a single account spending a single amount of a specific currency to a single account. This design is so simplistic, that is it only suitable for a single use case and will not support any advanced uses cases that could bring more value to the chain.
The best part of this design is that it does not contain the information of what the sender agrees to send. The only information given is the amount that should be received, Ripple automatically deduct from the sender balance what is needed to pay for the specified amount. Currencies in Ripple are really special, they have a currency code like BTC or USD, but they also have to specify the issuer.
The rippling algorithm can look like magic for the users, but it has really concrete impacts on the technical side. Even if a powerful pathfinding algorithm will be used to get the best rate when the conversion happens, additional features are required to give the user more control over what happens, especially on the slippage that happens when you convert one currency to another. The sender can specify which paths will be used to do the conversion, as well as how much he is willing to pay (SendMax) for the transaction to happen and the minimum amount that should be received (DeliverMin), to cancel the payment in case the slippage is not acceptable.
Again, more fields were added to attach additional data to the payment, the destination flag and the invoice id. In total, there are 4 fields where arbitrary data can be added in a payment transaction…
The architecture of the code behind the Ripple protocol is a complete mess and it has been poorly designed. The features offered by the protocol are great, but suffers from a rigid and a near-sighted implementation and a lot of redundant fields have been used to compensate and make the product actually usable.
The Caasiope Network is a layer-two protocol using a lightweight blockchain with a brand new code base. The blockchain was designed to process multi-currency transactions and support most common advanced uses cases. While Caasiope is using the account model, it is also using the input and outputs model for transactions.
The transaction contains inputs and outputs to describe which accounts are debited or credited. Whereas Bitcoin determines the fees as being the remaining between inputs minus outputs, Caasiope requires the sum of inputs to exactly match the sum of outputs and the user must explicitly set the fees to the desired amount.
The declarations are used to handle advanced features such as multi-signature, time-lock and hash-lock.
There are no real differences between an input and an output, it is just a book keeping entry where we increase or decrease the balance of the accounts.
The most interesting part is that a transaction can handle different currencies, allowing people to exchange currency in a single transaction.
Unlike on Ripple, where anyone can issue their own currency, on Cassiope, only the consortium can issue currency. Because the consortium can be audited by anyone at anytime, we are making sure that 1 BTC on Caasiope will always be worth 1 BTC.
To create a multi-signature account, you only have to provide the addresses of the signers and the number of signatures required to spend from it.
Unlike Ethereum, the Caasiope Network has made the choice to natively support advanced features in order to provide a better experience to the users when they use it. This design also increases the security by giving the correct implementation of the feature to every user instead of putting them at risk by asking them to write a script that could have known bugs.
Looking for an opensource blockchain community ? Join us on www.caasiope.net