A Close Look at Libra's Source Code

Do Libra’s Intentions Line Up With What Its Source Code Tells Us?

Thursday, 18th of July 2019 · by

The Libra project has had no shortage of deep analyses, long and short-term theories, tweets, and all sorts of other media coverage. Despite the buzz, many of these analyses focus on either the greater vision behind the project when compared to Facebook's track record, or the research behind the Libra whitepaper itself. However, little has been written about what's hidden inside of Libra's source code. This report aims to peek into the Libra project's repository and understand whether its code reflects Facebook's intentions towards gradually "decentralizing" the Libra project.

The Basics

The Libra project was built in a fully centralized manner by a large team with likely a limitless budget using Facebook's highest standards of development and project management. As such, Facebook engineers, starting from a clean slate, spent a chunk of time understanding the development landscape of other cryptocurrency projects along with their trade-offs and design decisions.

The task wasn't "how do we build Libra" but rather "what are the best-in-class technologies, such as peer-to-peer networking packages, programming languages, and RPC communication libraries, Facebook should use to build Libra even at the expense of sacrificing decentralization".

Unlike Ethereum that is built to handle a maximum of 4 million validators, Libra does not have to operate under this constraint. Hence, Libra's code had to be designed to be more of a performant server rather than a fully decentralized, byzantine-fault-tolerant system. To fill those gaps, Libra was also free to make its own smart contract programming language named "Move", as well as build its own internal software and tooling to get the job done.

Why These Decisions Matter

These decisions matter because they paint a stark contrast between different software governance ideologies. Facebook does not need consensus from a community or set of core developers to decide what technology to incorporate into Libra. Facebook has no need for a multi-client network or conformity testing between unrelated teams building the same protocol when building Libra. If Libra PM's reach consensus that a feature must be implemented and certain technologies must be used, they will surely be built by their engineers.

Other projects operate under different constraints. Newer crypto projects find themselves having to raise an ICO, a seed round, or simply build their project as a volunteer effort, the latter suffering from the tragedy of the commons. If a project does manage to make it through the funding gauntlet, it must now cater to a community of public/private investors, gain adoption for its network, acquire a slice of the pie of the miner/validator community, and release a product all in a short timespan. For projects that have already been built to be permissionless, coordination is often difficult when launching software improvements, often needing many back and forths between unrelated teams to implement an RFC. Granted, this coordination difficulty comes at the benefit of permissionless innovation, where any engineer can suggest improvements to a protocol, garner support through the right arguments and reasons, and include it in a future hard fork via consensus from the community. If Facebook indeed decides to go this route, and the Libra project has a larger community behind it, it may face the same governance pains other crypto projects encounter.

Given permissionless improvements to a protocol promise inclusions of features based on merit and consensus, why don't the best-in-class technologies often make it into these projects in production? The main reason has to do with how difficult it is to make executive decisions in decentralized projects. Once a lot of teams implement a protocol, it is really difficult to create abstractions which please everyone, leading to more governance pain. For example, it may be a lot more difficult to implement and standardize a generic serialization library in Go than in a language such as Rust, which has access to generic types.

Given Facebook only has to worry about maintaining one client and has been working on its protocol for a while, it can couple as many implementation decisions as it wants with its protocol design. The verdict is that it is exceedingly difficult to build truly decentralized projects in an efficient manner. Facebook, not bound by this constraint, may have an upper hand in launching a robust codebase to production compared to other crypto projects.

Validators vs. Common Node Operators

The Libra association website states:

one of the association's directives will be to work with the community to research and implement the transition to a permissionless network over time.

Although this should absolutely be the goal of the project, it's worth our time to peek into its code base and see if its foundations are built for this transition or whether this would require a massive departure from its current implementation.

The second thing to look at, and perhaps the one which will give us the most insight into the first matter, is whether or not privileged validators have a different view of the network than regular users operating a node. The most critical aspect of this is whether any user, not just privileged validators, will be able to freely download and replicate the entire state of the ledger to independently verify transactions.

The most comprehensive information we can find on the subject outside of the source code is Libra's official documentation, which gives a bird's eye view of the project as well as instructions on how to run and connect to its running testnet. Libra offers two options to users at the moment: (1) Running a local client that connects to a validator running the testnet (2) Running a local validator node on a local chain.

Running a Local Client Connected to a Validator

For option (1), Libra gives us access to a CLI (Command-Line Interface) which lets us create new accounts, transfer coins, and view state/transaction information corresponding to accounts on the testnet.

That is, as a user, I am only able to query limited transaction/state information which I receive from a validator, but there is no option to view information about the full state or as new transactions are committed to the ledger.

libra% query txn_acc_seq 0 0 true
>> Getting committed transaction by account and sequence number
Committed transaction: SignedTransaction {
 { raw_txn: RawTransaction {
    sender: 3ed8e5fafae4147b2a105a0be2f81972883441cfaaadf93fc0868e7a0253c4a8,
    sequence_number: 0,
    payload: {,
      transaction: peer_to_peer_transaction,
      args: [
        {ADDRESS: 8337aac709a41fe6be03cad8878a0d4209740b1608f8a81566c9a7d4b95a2ec7},
        {U64: 10000000},
      ]
    },

Despite what the CLI above tells us, this may just be the software obfuscating information from the regular user which may be there. That is, the functionality to read every transaction in the network may be there but could be hidden from the CLI tool. Perhaps it is possible to fork the project and modify its code to give us read access to the ledger itself? Could it be possible for my local client to sync fully with the testnet? Let's take a look.

libra% h
Connected to validator at: ac.testnet.libra.org:8000
usage: \<command\> \<args\>

Use the following commands:
account | a
 Account operations
query | q
 Query operations
transfer | transferb | t | tb
 Suffix 'b' is for blocking.
 Transfer coins (in libra) from account to another.
submit | submitb | s | sb
 \<signer\_account\_address\>|\<signer\_account\_ref\_id\> \<path\_to\_raw\_transaction\> Suffix 'b' is for blocking.
 Load a RawTransaction from file and submit to the network
help | h
 Prints this help
quit | q!
 Exit this client

And if we want to query events from the network, the only information we have access to is

libra% q
usage: q <arg>

Use the following args for this command:

balance | b <account_ref_id>|<account_address>
    Get the current balance of an account
sequence | s <account_ref_id>|<account_address>
    Get the current sequence number for an account
account_state | as <account_ref_id>|<account_address>
    Get the latest state for an account
txn_acc_seq | ts <account_ref_id>|<account_address> 
    Get the committed transaction by account and sequence number.
txn_range | tr <start_version> <limit>
    Get the committed transactions by version range.
event | ev <account_ref_id>|<account_address>
    Get events by account and event type (sent|received).

Hence, after inspecting the multiple commands that can be executed using the CLI tool, we found that the tool can only perform account creation, transfers and queries to a validator node to pull specific information. The queries that can be performed only pulls information about a specific account such as balance, last, state, committed transactions and types of transactions.

Granted, when we connect to the testnet, our node does not sync. Instead, it sends RPC requests to a remote node found at ac.testnet.libra.org which returns the information, requiring us to fully trust the data we receive with no potential to independently verify. For now, that's all that a user using the Libra CLI can view.

Running a Local Validator

Libra specifies the life of a transaction as encapsulated within the validator runtime, with the client simply functioning as a single point of entry.

Source: Libra Life of a Transaction

Libra nodes connect to a set of trusted consensus peers which are verified against public keys. Currently, there is no known way to spawn a node that can peer with other live, testnet validators as the trusted_peers configuration option is currently empty.

The only endpoint exposed by Libra is a public access-controlled RPC url owned by Facebook which likely runs a load balancer on some cloud infrastructure. Although there does not seem to be access control in terms of downloading state/block information, there are mechanisms to verify consensus peers at the network level based on configuration options. It is unclear whether peer ID's and IP addresses will be obfuscated at mainnet release, and authenticated validators could easily block peers that are not from known, authenticated validators in production. That is, it is highly likely regular users may only have access to the libra client, giving access to RPC submissions to authenticated validators after passing access controls. It is also not clear if validators will even be incentivized to share their ledger information, as they paid a large premium for said privilege.

Libra's nomenclature frequently alludes to network peers as "validators", further pushing the notion of users only running RPC clients.

pub fn gen_genesis_transaction<P: AsRef<Path>>(
    path: P,
    faucet_account_keypair: &KeyPair,
    trusted_peer_config: &TrustedPeersConfig,
) -> Result<()> {
    let validator_set = trusted_peer_config
        .peers
        .iter()
        .map(|(peer_id, peer)| {
            ValidatorPublicKeys::new(
                AccountAddress::try_from(peer_id.clone()).expect("[config] invalid peer_id"),
                peer.get_consensus_public(),
                peer.get_network_signing_public(),
                peer.get_network_identity_public(),
            )
        })

Source: libra/config/config_builder/src/util.rs

And

 /// Returns the key that establishes a validator's identity in the p2p network
    pub fn network_identity_public_key(&self) -> &X25519PublicKey {
        &self.network_identity_public_key
    }

Source: libra/types/src/validator_public_keys.rs

The validator_set above is constructed from the list of trusted p2p peers, alluding to a network and identity signing public keys, which can likely be used for access control at the networking level. This would mean only authenticated validators could likely be treated as trusted peers by other validators in the network.

Conclusion

In conclusion, the verdict holds that Libra has different read permissions for its blockchain between regular users and validators, the former not being able to access information regarding the full ledger at the moment. As a consequence, regular users cannot fully replicate the Libra state machine, as their clients serve more as sidecars that connect to validator nodes that actually run the network. We do not know if this will be the case in production or is merely a consequence of the Libra project still thinking about this decision. Additionally, Libra's source code frequently names full nodes as validators, adding further evidence of the direction Facebook might take the project. Once the network is running in production this way, there is also little incentive for privileged nodes, who each paid $10MM, to allow access control to ledger/state data from peers who are not authenticated validators.

Sources

  1. https://libra.org/en-US/
  2. https://developers.libra.org/docs/move-paper
Stay up to date with the latest in blockchain and cryptocurrency

The Token Daily newsletter is the best way to keep up to date on important happenings in all things blockchain and cryptocurrency. Subscribe below and you’ll also receive exclusive token analysis articles from our team.

No thanks.
Author

Raul Jordan
Research Partner at TD Research