/go-ten

Official Golang implementation of the Ten protocol

Primary LanguageGoGNU Affero General Public License v3.0AGPL-3.0

Go Ten

This repository contains the reference implementation of the Ten Protocol.

Note that this is still very much a work in progress, so there are many rough edges and unfinished components.

High level overview

The typical blockchain node runs multiple services in a single process. For example:

  • P2P Service
  • RPC Service
  • Data storage
  • Transaction execution
  • Mempool
  • etc

Ten uses Trusted Execution Environments (TEE), like Intel SGX, to execute transactions in a confidential environment, which means we diverge from the typical architecture. There are three main components of the architecture, each running as a separate process: the Enclave, the Host and the Wallet Extension.

Architecture

I. The Enclave

This is the core component of Ten which runs inside the TEE. See go/enclave

We use EGo, an open source SDK for developing this confidential component.

The Enclave exposes an interface over RPC which attempts to minimise the "trusted computing base"(TCB).

The Enclave component has these main responsibilities:

1. Execute EVM transactions

Ten has the goal to be fully compatible with the EVM, so smart contracts can be ported freely from other EVM compatible chains. To achieve this and minimise the effort and incompatibilities, we depend on go-ethereum.

The dependency on go-ethereum is not straight forward, since transaction execution is coupled with Ethereum specific consensus rules, which had to be mocked out.

See go/enclave/evm

2. Store the state

The dependency on go-ethereum for transaction execution, means that we use the same storage interfaces.

In the current iteration we use EdglessDB, an open source database tailor-made for confidential computing.

go-ethereum uses a key-value store interface, which we implement on top of the SQL database. The reason for this odd choice is that the data needs to be indexed and encrypted before being sent for storage. And the operation of storing data needs to be resistant to side-channel analysis which would allow an attacker to infer information on what calculations are being made based on what data the Enclave is requesting or storing.

In a future iteration, we'll look at alternatives to connect to a performant key-value store designed or modified for confidential computing.

See go/enclave/db

3. Consume Ethereum blocks

The Enclave is fed Ethereum blocks through the RPC interface. These blocks are used as the "Source of Truth", and the Enclave extracts useful information from them, such as published rollups, deposits to the bridge, etc. Ethereum re-orgs have to be detected at this level to rollback the Ten state accordingly.

To avoid the risk of the Enclave being fed invalid blocks which an attacker can use to probe for information, or to shorten the revelation period, the blocks have to be checked for validity, which includes checking that enough "work" went into them. To achieve this we depend on the Blockchain logic.

4. Bridge to Ethereum

One of the key aspects of Ethereum Layer 2 (L2) solutions is to feature a decentralised bridge that is resistant to 51% attacks.

Ten features a L2 side of the bridge that is completely under the control of the platform.

a) Deposits

During processing of the Ethereum blocks, the platform generates synthetic L2 transactions based on every relevant transaction found there. For example when Alice deposits 10ABC from her account to the L1 bridge, Ten will execute a synthetic L2 transaction (that it deterministically generated from the L1 transaction), which moves 10WABC from the L2 bridge to Alice's address on Ten.

This logic is part of the consensus of Ten, every node receiving the same block containing the rollup and the deposits, will generate the exact same synthetic transaction.

b) Withdrawals

Ten ERC20 transactions sent to a special "Bridge" address are interpreted as withdrawals. Which means the wrapped tokens are burned on the Ten side of the bridge and a Withdrawal instruction is added to the rollup header, which will be later executed by the Ethereum side of the bridge.

This happens deterministically in a post-processing phase, after all Ten transactions were executed by the EVM.

See go/enclave/bridge

Note that the current bridge implementation is very primitive and only features two supported hardcoded ERC20 tokens to demonstrate the mechanics.

5. Mempool

The mempool is the component which handles the incoming transactions and is responsible for selecting which transactions to include in the current batch and pick the order.

The big advantage of running the mempool inside the secure Enclave is that the ordering of transactions cannot be gamed by the aggregator, which makes MEV much more difficult.

See go/enclave/mempool

Note that the current mempool implementation is very primitive. It always includes all received transactions that were not already included in a rollup.

6. The rollups and the PoBI protocol

Like in any blockchain the unit of the protocol is the batch of transactions organized in a chain. The Ten blocks have an encrypted payload, which is only visible inside the secure Enclave. All of the logic of maintaining the current state based on incoming data and of producing new rollups is found in the go/enclave/rollupchain package.

7. Cryptography

This is where the Obsuro specific cryptography is implemented. See go/enclave/crypto

These are the main components:

a) RPC encryption/decryption

The Ten "Wallet extension" encrypts all requests from users (transactions or smart contract method calls) with the "Ten public key", which is a key derived from the master seed. The response is in turn encrypted with the "Viewing Key" of the requesting user.

This component manages viewing keys and handles the encryption and decryption.

The transactions received from users are gossiped with the other aggregators encrypted with the "Ten Public Key".

Note: In the current implementation, this key is hardcoded.

See: go/enclave/rpcencryptionmanager

b) Transaction blob encryption

Transactions are stored as calldata blobs in ethereum transactions. The component that creates these blobs has to encrypt them with derived keys according to their revelation period.

Note: In the current implementation the payload is encrypted with a single hardcoded key

See: go/enclave/crypto/transaction_blob_crypto.go

8. RPC

The enclave exposes an RPC interface generated with proto-buf.

The interface is described in enclave.proto.

See go/common/rpc

II. The Host

The Host service is the software that is under the control of the operator. It does not run inside a secure Enclave, and there is no attestation on it.

From a threat model point of view, the Host service is seen as an adversary by an Enclave. Any data that it feeds into the Enclave will be verified and considered malicious.

A secure solution that uses confidential computing will generally try to minimize the TCB, and run as much as possible outside the secure Enclave, while still achieving the same security goals.

The Host service is the equivalent of a typical blockchain node, and is responsible for:

  • P2P messaging: Gossiping of encrypted transactions and rollups
  • RPC: Exposing an RPC interface similar to the one exposed by normal Ethereum nodes
  • Communicating with an Ethereum node for retrieving blocks and for submitting transactions with data that was generated inside the Enclave. This means an Ethereum wallet and the control keys to accounts with enough ETH to publish transactions is required.

See go/host

Note: The code for host is currently in an incipient phase. The focus of the first phase of development was on the main building blocks of the Enclave

III. The Wallet Extension

The missing link to achieving fully private transactions while allowing end-users to continue using their favourite wallets (like MetaMask). This is a very thin component that is responsible for encrypting and decrypting traffic between the Ten node and its clients.

See the docs for more information.

Repository Structure

root
├── contracts: : Solidity Management contract, which will be deployed on Ethereum
├── go
│   ├── common: Unstructured package containing base types and utils. Note: It will be cleaned up once more patterns emerged.
│   ├── config: A place where the default configurations are found.
│   ├── enclave: The component that is loaded up inside SGX.
│   │   ├── bridge: The platform side of the decentralised bridge logic.
│   │   ├── core: Base data structures used only inside the enclave. 
│   │   ├── crypto: Implementation of the Ten cryptography.
│   │   ├── db: The database implementations. 
│   │   ├── enclaverunner: The entry point to the standalone enclave process. 
│   │   ├── evm: Ten transaction execution on top of the EVM.
│   │   ├── main: Main
│   │   ├── mempool: The mempool living inside the enclave
│   │   ├── rollupchain: The main logic for calculating the state and the POBI protocol.
│   │   └── rpcencryptionmanager: Responsible for encrypting the communication with the wallet extension.
│   ├── ethadapter: Responsible for interpreting L1 transactions 
│   │   ├── erc20contractlib: Understand ERC20 transactions.
│   │   └── mgmtcontractlib: Understand Ten Management contrract transactions. 
│   ├── host: The standalone host process.
│   │   ├── db: The host's database.
│   │   ├── hostrunner: The entry point.
│   │   ├── main: Main
│   │   ├── node: The host implementation.
│   │   ├── p2p: The P2P communication implementation.
│   │   └── rpc: RPC communications with the enclave and the client.
│   │       ├── clientapi: The API for RPC communications with the client.
│   │       ├── clientrpc: The RPC server for communications with the client.
│   │       └── enclaverpc: The RPC client for communications with the enclave.
│   ├── rpcclientlib: Library to allow go applications to connect to a host via RPC.
│   └── wallet: Logic around wallets. Used both by the node, which is an ethereum wallet, and by the tests
├── integration: Integration tests that spin up Ten networks.
│   ├── simulation: A series of tests that simulate running networks with different setups.
├── testnet: Utilities for deploying a testnet.
└── tools: Peripheral tooling.
    ├── hardhatdeployer: Automates deployment of ERC20 and management contracts to the L1.
    ├── faucet: Faucet for testnet.
    ├── tenscan: Tooling to monitor network transactions.
    └── walletextension: Ensures sensitive messages to and from the Ten node are encrypted.

Testing

The Ten integration tests are found in: integration/simulation.

The main tests are "simulations", which means they spin up both an L1 network and an L2 network, and then inject random transactions. Due to the non-determinism of both the "mining" protocol in the L1 network and the nondeterminism of POBI, coupled with the random traffic, it allows the tests to capture many corner cases without having to explicitly write individual tests for them.

The first simulation_in_mem_test runs fully in one single process on top of a mocked L1 network and with the networking components of the Ten node swapped out, and is just focused on producing random L1 blocks at very short intervals. The ethereummock implementation is based on the ethereum protocol with the individual nodes gossiping with each other with random latencies, producing blocks at a random interval distributed around a configured AvgBlockDuration, and making decisions about the canonical head based on the longest chain. The L2 nodes are each connected to one of these mocked L1 nodes, and receive a slightly different view. If this test is run long enough, it verifies the POBI protocol.

There are a number of simulations that gradually become more realistic, but at the cost of a reduction in the number of blocks that can be generated.

The simulation_geth_in_mem_test replaces the mocked ethereum nodes with a network of geth nodes started in clique mode. The lowest unit of time of producing blocks in that mode is 1 second.

The simulation_full_network_test starts standalone local processes for both the enclave and the ten node connected to real geth nodes.

The simulation_docker_test goes a step further and runs the enclave in "Simulation mode" in a docker container with the "EGo" library.

The simulation_azure_enclaves_test is the ultimate test where the enclaves are deployed in "Real mode" on SGX enabled VMs on Azure.

A transaction injector is able to create and inject random transactions in any of these setups by receiving RPC handles to the nodes.

Getting Started

The following section describes building the reference implementation of the Ten protocol, running the unit and integration tests, and deploying a local testnet for end-to-end testing. The reference implementation of Ten is written in go. Unless otherwise stated, all paths stated herein are relative to the root of the go-ten checkout.

Dependencies

The following dependencies are required to be installed locally;

Building

To create the build artifacts local to the checkout of the repository the easiest approach is to build each component separately for the host, enclave, and wallet extension i.e.

cd ./go/host/main && go build && cd -
cd ./go/enclave/main && go build && cd -
cd ./tools/walletextension/main && go build && cd -

Running go build ./... to build all packages at the root level will build all packages, but it will discard the resulting artifacts; it therefore serves only as a check that the packages can be built. Note that building the enclave using go will compile it for a non-SGX mode and allow it to be run for test purposes. Compiling for SGX mode requires ego-go from Ego to be used in placement. This is done using a docker image as defined in dockerfiles/enclave.Dockerfile. Note that building the host and enclave is included here for information only; when building to run a local or remote component, docker is used and the creation of the docker images automated as described in [Building and running a local testnet](#Building and running a local testnet).

Running the tests

The tests require an Ten enclave to be locally running, and as such the image should first be created and added to the docker images repository. Building the image is described in dockerfiles and can be performed using the below in the root of the project;

docker build -t enclave -f ./dockerfiles/enclave.Dockerfile .

To run all unit, integration and simulation tests locally, run the below in the root of the project;

go test ./...

Building and running a local testnet

A local testnet is started from docker images that have all executables built, installed and available for running. The images are created from the base directory of the go-ten repository; to build the images and start all required components clone the repository and use the below;

cd go-ten
./testnet/testnet-local-build_images.sh 
go run ./testnet/launcher/cmd 

The network is started running both a sequencer and a validator node (SGX simulated). It will also start a faucet server to fund accounts on the network, and a local instance of the Ten Gateway to mediate connections to the network. The faucet server is started on http://127.0.0.1:99 and the gateway on http://127.0.0.1:3000. To request funds for a given account use the below command;

curl --location --request POST 'http://127.0.0.1:99/fund/eth' --header 'Content-Type: application/json' \
--data-raw '{ "address":"<address>" }'

Note that relevant contract addresses on the network can be found from running the below command;

curl -X POST 127.0.0.1:80  -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"obscuro_config","params":[],"id":1}'

Community

Development is discussed by the team and the community on discord