Notes on Facebook Libra's Technical Whitepaper

In this article, I want to talk about Facebook's Libra blockchain from a pure technical point of view. The main objective is to answer the question "What does Libra differ from the existing blockchain systems" based on my reading of its published technical whitepaper.

Note that Libra uses the account based model. Therefore, I will mainly compare it with the existing account-based blockchain systems. This article does not cover discussions on Libra's Move virtual machine and consensus algorithm since I would need further reading of other documents and Libra's source code to understand and comment on them.

A Hybrid Blockchain

The first question people may ask is "What kind of blockchain Libra is?" Well, in my opinion, it is neither a public nor a private/consortium blockchain, but something in the middle, which I would say a "hybrid" blockchain.

As shown in the figure below, we can consider two dimensions when categorizing a blockchain: 1) how the blockchain is accessed (accessibility) and 2) how it is mined (minability). Along each dimension, it is either permissioned or permissionless. A public blockchain sits at the top right corner where anyone is free to access the ledger and join the mining competitions, while a private or consortium blockchain stays on the oppisite side.

Libra is at the bottom right corner. It is permissionless to access and validate the blockchain, however, only 100 authorized parties plus Facebook itself can add new transactions to the ledger according to Libra's whitepaper.

This exact hybrid architecture has also been implemented in the VeChainThor blockchain. Interestingly, VeChainThor has also defined 101 authorized nodes for generating new blocks. The coincided decisions come with no surprise to me since they are probably the most sensible choice for designing a blockchain for mass adoption at the moment.

No More Chained Blocks

There are literally no chained blocks defined in Libra.

We all know that the block structure has unanimously been used to record persistent information that forms the ledger history. Blocks are linked into a chain through putting in one block the hash of its previous block. Such a structure is well suited for a dynamic environment where forks exist, since we can efficiently roll back to some previous block on the trunk and re-sync the ledger if the latest block is found not on the trunk.

On the other hand, Libra uses a BFT-based consensus protocol, LibraBFT, which guarantees that there will be no fork whatsoever. Keeping the structure of a block chain suddenly becomes unnecessary and redundant. Moreover, they want a transaction in the ledger to be verified based on one single outcome of LibraBFT, rather than a chain of blocks.

The figure below (borrowed from the technical whitepaper) illustrates the way Libra stores the ledger history. It first defines a data structure TransactionInfo , as suggested by its name, to hold all the information related to a single transaction. As can be seen, it includes: 1) the transaction itself, 2) the state root after executing the transaction and 3) the Merkel tree root of all the emitted events. It then construct a Merkel tree whose leaves are objects of TransactionInfo to represent the ledger history. The outcome of each LibraBFT round is a particular root agreed and signed by enough validators as required.

Suppose a client wants to verify a particular transaction. What he/she needs are the corresponding TransactionInfo instance and the Merkel tree path up from the leaf to a root that has acquired enough validator signatures.

Digital Assets (Resources)

In Libra, all digital assets (called "resources"), including the Libra coin, are defined by "modules". A module is a piece of executable bytecode stored in the Libra ledger that specifies the asset's data structure, name, and associated functions defining its behaviours, similar to the EVM bytecode of a smart contract. Moreover, it is stored directly under the account that publishes it rather than under a new account created by the system.

Given a deployed module, its associated digital assets are actually instances of the declared data structure and are stored under the accounts own them. It can be seen that the way Libra stores digital assets is fundamentally different from the way Ethereum does. In Libra, all assets belonging to the same account are directly under the account while in Ethereum only the native cryptocurrency, ether, is kept in the account and the others in their respective smart-contract accounts.

Libra distinguishes different types of digital assets following a three-layer top-down naming strategy. At the top is the address of the account that owns the module, followed by the module name and resource name.

Let us have a look at the example in the technical whitepaper as shown below. It includes four accounts and two modules, named Currency and StateChannel . Their resources are both named T in the modules and labelled Currency.T and StateChannel.T , respectively.

Below is what it might look like in Ethereum. We will have two accounts created by the system for contracts Currency and StateChannel . The digital assets are stored within the two accounts in terms of mappings from an account address to some data (e.g., an integer representing the token balance).

Transaction As a Program

A Libra transaction includes the following six data fields: the sender address, sender public key, program, gas price, max gas amount and sequence number. It looks very different from a transaction we are familiar with. Data fields such as the recipient's address, value to be transferred, and payload which we would normally find in a transaction are missing in Libra's transaction model.

In fact, we can consider a Libra transaction as an executable program. Libra allows users to put an executable bytecode script and an optional list of inputs to the script inside the data field "program" of a transaction. The script can invoke multiple functions of modules published in the ledger, use condition logic and perform local computation.

To give you an example, I copy and paste the Move code for transferring Libra coins from one account to another. Here, LibraAccount is a built-in module and its function pay_from_sender is called to conduct the payment. Of course, you will have to compile the code into a bytecode script and provide the payee address and amount as the inputs to construct a valiƒd transaction.

import 0x0.LibraAccount; main(payee: address, amount: u64) { LibraAccount.pay_from_sender(move(payee), move(amount)); return; }