sharding: merge with master

Former-commit-id: eafe01eb22f8e478dacb6b07a17feeae4a7e0697 [formerly 2fe0af9b331c81eff468cb3adda6784af74d2a0f]
Former-commit-id: 93720d0e2f005bf1edfb141ffbf3e735fcbca7df
This commit is contained in:
Terence Tsao 2018-05-09 11:56:59 -07:00
commit 8b5cafd7a6
12 changed files with 217 additions and 447 deletions

View File

@ -3,16 +3,16 @@ go_import_path: github.com/ethereum/go-ethereum
sudo: false
matrix:
include:
- os: linux
dist: trusty
sudo: required
go: 1.9.x
script:
- sudo modprobe fuse
- sudo chmod 666 /dev/fuse
- sudo chown root:$USER /etc/fuse.conf
- go run build/ci.go install
- go run build/ci.go test -coverage
#- os: linux
# dist: trusty
# sudo: required
# go: 1.9.x
# script:
# - sudo modprobe fuse
# - sudo chmod 666 /dev/fuse
# - sudo chown root:$USER /etc/fuse.conf
# - go run build/ci.go install
# - go run build/ci.go test -coverage
# These are the latest Go versions.
- os: linux
@ -26,15 +26,15 @@ matrix:
- go run build/ci.go install
- go run build/ci.go test -coverage
- os: osx
go: "1.10"
script:
- unset -f cd # workaround for https://github.com/travis-ci/travis-ci/issues/8703
- brew update
- brew install caskroom/cask/brew-cask
- brew cask install osxfuse
- go run build/ci.go install
- go run build/ci.go test -coverage
#- os: osx
# go: "1.10"
# script:
# - unset -f cd # workaround for https://github.com/travis-ci/travis-ci/issues/8703
# - brew update
# - brew install caskroom/cask/brew-cask
# - brew cask install osxfuse
# - go run build/ci.go install
# - go run build/ci.go test -coverage
# This builder only tests code linters on latest version of Go
- os: linux

View File

@ -16,8 +16,8 @@ Interested in contributing? Check out our [Contribution Guidelines](#contributio
- [Sharding Instructions](#sharding)
- [Running a Local Geth Node](#running-a-local-geth-node)
- [Transaction Generator](#transaction-generator)
- [Becoming a Collator](#becoming-a-collator)
- [Becoming a Proposer](#becoming-a-proposer)
- [Becoming a Notary](#becoming-a-notary)
- [Running a Collation Proposal Client](#running-a-collation-proposal-client)
- [Testing](#testing)
- [Contribution Guidelines](#contribution-guidelines)
- [License](#license)
@ -56,7 +56,7 @@ $ make all
# Sharding Instructions
To get started with running the project, follow the instructions to initialize your own private Ethereum blockchain and geth node, as they will be required to run before you can become a collator or a proposer.
To get started with running the project, follow the instructions to initialize your own private Ethereum blockchain and geth node, as they will be required to run before you can begin proposing collations into shard chains.
## Running a Local Geth Node
@ -98,28 +98,29 @@ Now, save the passphrase you used in the geth node into a text file called passw
## Transaction Generator
Work in Progress. To track our current draft of the tx generator cli spec, visit this [link](https://docs.google.com/document/d/1YohsW4R9dIRo0u5RqfNOYjCkYKVCmzjgoBDBYDdu5m0/edit?usp=drive_web&ouid=105756662967435769870).
Work in Progress. To track our current draft of the tx generator cli spec, visit this [link](https://docs.google.com/document/d/1YohsW4R9dIRo0u5RqfNOYjCkYKVCmzjgoBDBYDdu5m0/edit?usp=drive_web&ouid=105756662967435769870). Generating test transactions on a local network will allow for benchmarking of tx throughput within our system.
## Becoming a Collator
## Becoming a Notary
To deposit ETH and join as a collator in the Sharding Manager Contract, run the following command:
Our system outlined below follows the [Minimal Sharding Protocol](https://ethresear.ch/t/a-minimal-sharding-protocol-that-may-be-worthwhile-as-a-development-target-now/1650) as outlined by Vitalik on ETHResearch where any actor can submit collation headers via the SMC, but only a selected committee of notaries is allowed to vote on collations in each period. Notaries are in charge of data availability checking and consensus is reached upon a collation header receiving >= 2/3 votes in a period.
To deposit ETH and join as a notary in the Sharding Manager Contract, run the following command:
```
geth sharding-collator --deposit --datadir /path/to/your/datadir --password /path/to/your/password.txt --networkid 12345
geth sharding-notary --deposit --datadir /path/to/your/datadir --password /path/to/your/password.txt --networkid 12345
```
This will extract 100ETH from your account balance and insert you into the SMC's collator pool. Then, the program will listen for incoming block headers and notify you when you have been selected as an eligible collator for a certain shard in a given period. Once you are selected, the collator will request collations created by proposer nodes. We will need to run a proposer node concurrently in a separate terminal window as follows:
This will extract 1000ETH from your account balance and insert you into the SMC's notaries. Then, the program will listen for incoming block headers and notify you when you have been selected as to vote on proposals for a certain shard in a given period. Once you are selected, your sharding client will download collation information to check for data availability on vote on proposals that have been submitted via the `addHeader` function on the SMC.
## Becoming a Proposer
Concurrently, you will need to run another client that is tasked with processing transactions into collations and submitting them to the SMC via the `addHeader` function.
## Running a Collation Proposal Client
```
geth sharding-proposer --datadir /path/to/your/datadir --password /path/to/your/password.txt --networkid 12345
```
Proposers are tasked with processing pending transactions into blobs within collations. They are responsible for submitting proposals (collation headers) to collators currently assigned to a period along with an ETH bid.
Collators then subscribe to incoming proposals and fetch the collation headers that offer the highest ETH deposit. Once a collator issues a commitment to a certain proposal, the proposer can be confident the collator will download the body. After this countersigning occurs, the collator can add the collation header to the Sharding Manager Contract.
Once this is done, the full, end-to-end phase 1 sharding example is complete and another iteration can occur.
This client is tasked with processing pending transactions into blobs within collations by serializing data into collation bodies. It is responsible for submitting proposals (collation headers) to the SMC via the `addHeader` function.
# Making Changes

View File

@ -14,31 +14,22 @@ This document serves as a main reference for Prysmatic Labs' sharding implementa
- [System Architecture](#system-architecture)
- [System Start and User Entrypoint](#system-start-and-user-entrypoint)
- [The Sharding Manager Contract](#the-sharding-manager-contract)
- [Necessary Functionality](#necessary-functionality)
- [Registering Collators and Proposers](#registering-collators-and-proposers)
- [Determining an Eligible Collator for a Period on a Shard](#determining-an-eligible-collator-for-a-period-on-a-shard)
- [Processing and Verifying a Collation Header](#processing-and-verifying-a-collation-header)
- [Collator Sampling](#collator-sampling)
- [Collation Header Approval](#collation-header-approval)
- [Event Logs](#event-logs)
- [The Collator Client](#the-collator-client)
- [Notary Sampling](#notary-sampling)
- [The Notary Client](#the-notary-client)
- [Local Shard Storage](#local-shard-storage)
- [The Proposer Client](#the-proposer-client)
- [Collation Headers](#collation-headers)
- [Peer Discovery and Shard Wire Protocol](#peer-discovery-and-shard-wire-protocol)
- [Protocol Modifications](#protocol-modifications)
- [Protocol Primitives: Collations, Blocks, Transactions, Accounts](#protocol-primitives-collations-blocks-transactions-accounts)
- [The EVM: What You Need to Know](#the-evm-what-you-need-to-know)
- [Sharding In-Practice](#sharding-in-practice)
- [Fork Choice Rule](#fork-choice-rule)
- [Use-Case Stories: Proposers](#use-case-stories-proposers)
- [Use-Case Stories: Collators](#use-case-stories-collators)
- [Use-Case Stories: Notaries](#use-case-stories-notaries)
- [Current Status](#current-status)
- [Security Considerations](#security-considerations)
- [Not Included in Ruby Release](#not-included-in-ruby-release)
- [Bribing, Coordinated Attack Models](#bribing-coordinated-attack-models)
- [Enforced Windback](#enforced-windback)
- [Explicit Finality for Stateless Clients](#explicit-finality-for-stateless-clients)
- [The Data Availability Problem](#the-data-availability-problem)
- [Introduction and Background](#introduction-and-background)
- [On Uniquely Attributable Faults](#on-uniquely-attributable-faults)
@ -51,8 +42,6 @@ This document serves as a main reference for Prysmatic Labs' sharding implementa
- [Transparent Sharding](#transparent-sharding)
- [Tightly-Coupled Sharding (Fork-Free Sharding)](#tightly-coupled-sharding-fork-free-sharding)
- [Active Questions and Research](#active-questions-and-research)
- [Separation of Proposals and Consensus](#separation-of-proposals-and-consensus)
- [Selecting Eligible Collators Off-Chain](#selecting-eligible-collators-off-chain)
- [Community Updates and Contributions](#community-updates-and-contributions)
- [Acknowledgements](#acknowledgements)
- [References](#references)
@ -67,51 +56,43 @@ An approach to solving the scalability trilemma is the idea of blockchain shardi
## Basic Sharding Idea and Design
A sharded blockchain system is made possible by having nodes store “signed metadata” in the main chain of latest changes within each shard chain. Through this, we manage to create a layer of abstraction that tells us enough information about the global, synced state of parallel shard chains. These messages are called **collation headers**, which are specific structures that encompass important information about the chainstate of a shard in question. Collations are created by actors known as **proposer nodes** that are tasked with packaging transactions and “offering” them to collator nodes through cryptoeconomic incentive mechanisms. These collators are randomly selected for particular periods of time and are then tasked into adding these collations into particular shards through a **proof of stake** system occurring through a smart contract on the Ethereum main chain.
A sharded blockchain system is made possible by having nodes store “signed metadata” in the main chain of latest changes within each shard chain. Through this, we manage to create a layer of abstraction that tells us enough information about the global, synced state of parallel shard chains. These messages are called **collation headers**, which are specific structures that encompass important information about the chainstate of a shard in question. Collations are created by actors known as **proposers** that are tasked with packaging transactions into collation bodies. These collations are then voted on by a party of actors known as **notaries**. These notaries are randomly selected for particular periods of time in certain shards and are then tasked into reaching consensus on these chains via a **proof of stake** system occurring through a smart contract on the Ethereum main chain.
These collations are holistic descriptions of the state and transactions on a certain shard. A collation header at its most basic, high level summary contains the following information:
- Information about what shard the collation corresponds to (lets say shard 10)
- Information about the current state of the shard before all transactions are applied
- Information about what the state of the shard will be after all transactions are applied
- Information about the cryptoeconomic incentive game the successful collator and proposer played
For detailed information on protocol primitives including collations, see: [Protocol Primitives](#protocol-primitives). We will have two types of nodes that do the heavy lifting of our sharding logic: **proposers and collators**. The basic role of proposers is to fetch pending transactions from the txpool, wrap them into collations, and submit them along with an ETH deposit to a **proposals pool**.
For detailed information on protocol primitives including collations, see: [Protocol Primitives](#protocol-primitives). We will have two types of nodes that do the heavy lifting of our sharding logic: **proposers and notaries**. The basic role of proposers is to fetch pending transactions from the txpool, wrap them into collations, and submit them to a smart contract on the Ethereum main chain.
<!--[Proposer{bg:wheat}]fetch txs-.->[TXPool], [TXPool]-.->[Proposer{bg:wheat}], [Proposer{bg:wheat}]-package txs>[Collation|header|ETH Deposit], [Collation|header|ETH Deposit]-submit>[Proposals Pool], [Collator{bg:wheat}]subscribe to-.->[Proposals Pool]-->
![proposers](https://yuml.me/6da583d7.png)
<!--[Proposer{bg:wheat}]fetch txs-.->[TXPool], [TXPool]-.->[Proposer{bg:wheat}], [Proposer{bg:wheat}]-package txs>[Collation|header|body], [Collation|header|body]-submit header>[Sharding Manager Contract], [Notary{bg:wheat}]downloads collation availability and votes-.->[Sharding Manager Contract]-->
![proposers](https://yuml.me/69cbd7da.png)
Collators add collations via a smart contract on the Ethereum mainchain. Collators subscribe to updates from proposers and pick a collation that offers them the highest payout, known as a **proposer bid**. Once collators are selected to add collations to the canonical chain, and do so successfully, they get paid by the deposit the proposer offered.
To recap, the role of a collator is to reach consensus through Proof of Stake on collations they receive in the period they are assigned to, as well as to determine the canonical shard chain via a process known as "windback". This consensus will involve validation and data availability proofs of collations proposed to them by proposer nodes, along with validating collations from the immediate past (See: [Windback](#enforced-windback)).
Notaries stake ETH into the contract and vote on collations submitted by proposers during a certain period. Notaries are in charge of checking for data availability of such collations and reach consensus on canonical shard chains.
So then, are proposers in charge of state execution? The short answer is that phase 1 will contain **no state execution**. Instead, proposers will simply package all types of transactions into collations and later down the line, agents known as executors will download, run, and validate state as they need to through possibly different types of execution engines (potentially TrueBit-style, interactive execution).
The proposal-collator interaction is akin to the current transaction fee open bidding market where miners accept the transactions that maximize their profits. This abstract separation of concerns between collators and proposers allows for more computational efficiency within the system, as collators will not have to do the heavy lifting of state execution and focus solely on consensus through fork-choice rules. In this scheme, it makes sense that eventually **proposers** will become **executors** in later phases of a sharding spec.
This separation of concerns between notaries and proposers allows for more computational efficiency within the system, as notaries will not have to do the heavy lifting of state execution and focus solely on consensus through fork-choice rules. In this scheme, it makes sense that eventually **proposers** will become **executors** in later phases of a sharding spec.
When deciding and signing a proposed, valid collation, collators have the responsibility of finding the **longest valid shard chain within the longest valid main chain**.
Notaries periodically get assigned to different shards, a period is defined as a certain interval of blocks.
Collators periodically get assigned to different shards, the moment between when collators get assigned to a shard and the moment they get reassigned is called a **period**.
Given that we are splitting up the global state of the Ethereum blockchain into shards, new types of attacks arise because fewer resources are required to completely dominate a shard. This is why a **source of randomness** and periods are critical components to ensuring the integrity of the system.
Given that we are splitting up the global state of the Ethereum blockchain into shards, new types of attacks arise because fewer hash power is required to completely dominate a shard. This is why a **source of randomness**, and periods are critical components to ensuring the integrity of the system.
The Ethereum Wikis [Sharding FAQ](https://github.com/ethereum/wiki/wiki/Sharding-FAQ) suggests pseudorandom sampling of notaries on each shard. The goal is so that these notaries will not know which shard they will get in advance. Otherwise, malicious actors could concentrate resources into a single shard and try to overtake it (See: [1% Attack](https://medium.com/@icebearhww/ethereum-sharding-and-finality-65248951f649)).
The Ethereum Wikis [Sharding FAQ](https://github.com/ethereum/wiki/wiki/Sharding-FAQ) suggests random sampling of collators on each shard. The goal is so that these collators will not know which shard they will get in advance. Every shard will get assigned a bunch of collators and the ones that will actually be collating transactions will be randomly sampled from that set. Otherwise, malicious actors could concentrate hash power into a single shard and try to overtake it (See: [1% Attack](https://medium.com/@icebearhww/ethereum-sharding-and-finality-65248951f649)).
Casper Proof of Stake (Casper [FFG](https://arxiv.org/abs/1710.09437) and [CBC](https://arxiv.org/abs/1710.09437)) makes this quite trivial because there is already a set of global validators that we can select notaries from. The source of randomness needs to be common to ensure that this sampling is entirely compulsory and cant be gamed by the notaries in question.
Casper Proof of Stake (Casper [FFG](https://arxiv.org/abs/1710.09437) and [CBC](https://arxiv.org/abs/1710.09437)) makes this quite trivial because there is already a set of global collators that we can select collator nodes from. The source of randomness needs to be common to ensure that this sampling is entirely compulsory and cant be gamed by the collators in question.
In practice, the first phase of sharding will not be a complete overhaul of the network, but rather an implementation through a smart contract on the main chain known as the **Sharding Manager Contract (SMC)**. Its responsibility is to manage submitted collation headers and manage notaries.
In practice, the first phase of sharding will not be a complete overhaul of the network, but rather an implementation through a smart contract on the main chain known as the **Sharding Manager Contract (SMC)**. Its responsibility is to manage proposers, their proposal bids, and the sampling of collators from a global collator set. As the SMC lives in the Ethereum main chain, it will take guarantee a canonical head for all shard states.
Among its basic responsibilities, the SMC is be responsible for reconciling collators across all shards. It is in charge of pseudorandomly sampling collators from a collator set of accounts that have staked ETH into the SMC. The SMC is also responsible for providing immediate collation header verification that records a valid collation header hash on the main chain, as well as managing proposer's bids. In essence, sharding revolves around being able to store proofs of shard states in the main chain through this smart contract.
Among its basic responsibilities, the SMC is be responsible for reconciling notaries across all shards. It is in charge of pseudorandomly sampling notaries from addresses that have staked ETH into the SMC. The SMC is also responsible for providing immediate collation header verification that records a valid collation header hash on the main chain. In essence, sharding revolves around being able to store collation headers and their associated votes in the main chain through this smart contract.
# Roadmap Phases
Prysmatic Labs will follow the updated Phase 1 Spec posted on [ETHResearch](https://ethresear.ch/t/sharding-phase-1-spec/1407) by the Foundation's research team to roll out a local version of qudratic sharding. In essence, the high-level sharding roadmap is as follows as outlined by Justin Drake:
Prysmatic Labs will follow the parts of the (now deprecated) Phase 1 Spec posted on [ETHResearch](https://ethresear.ch/t/sharding-phase-1-spec/1407) by the Foundation's research team to roll out a local version of qudratic sharding. In essence, the high-level sharding roadmap is as follows as outlined by Justin Drake:
- Phase 1: Basic sharding without EVM
- Blob shard without transactions
- Proposers
- Proposal commitments
- Collation availability challenges
- Notaries
- Phase 2: EVM state transition function
- Full nodes only
- Asynchronous cross-contract calls only
@ -138,12 +119,11 @@ To concretize these phases, we will be releasing our implementation of sharding
Our current work is focused on creating a localized version of phase 1, quadratic sharding that would include the following:
- A minimal, **collator client** system that will interact with a **Sharding Manager Contract** on a locally running geth node
- Ability to deposit ETH into the SMC through the command line and to be selected as a collator by the local **SMC** in addition to the ability to withdraw the ETH staked
- A **proposer client** and cryptoeconomic incentive system for proposer nodes to listen for pending txs, create collations, and submit them along with a deposit to collator nodes in the network
- Ability to inspect the shard states and visualize the working system locally through the command line
- A minimal, **sharding client** system that will interact with a **Sharding Manager Contract** on a locally running geth node
- Ability to deposit ETH into the SMC through the command line and to be selected as a notary by the local **SMC** in addition to the ability to withdraw the ETH staked
- A **proposer client** that listens for pending txs, creates collations, and submits them to the SMC
- Ability to inspect the shard states and visualize the working system locally
Proposers and collators will interact through a local file system, as peer to peer networking considerations for sharding are still under heavy research.
We will forego several security considerations that will be critical for testnet and mainnet release for the purposes of demonstration and local network testing as part of the Ruby Release (See: [Security Considerations Not Included in Ruby](#not-included-in-ruby-release)).
@ -151,7 +131,7 @@ ETA: To be determined
## The Sapphire Release: Ropsten Testnet
Part 1 of the **Sapphire Release** will focus around getting the **Ruby Release** polished enough to be live on an Ethereum testnet and manage a set of collators effectively processing collations through the **on-chain SMC**. This will require a lot more elaborate simulations around the safety of the randomness behind the collator assignments in the SMC. Futhermore we need to pass stress testing against DDoS and other sorts of byzantine attacks. Additionally, it will be the first release to have real users proposing collations concurrently along with collators that can accept these proposals and add their headers to the SMC.
Part 1 of the **Sapphire Release** will focus around getting the **Ruby Release** polished enough to be live on an Ethereum testnet and manage a set of notaries voting on collations through the **on-chain SMC**. This will require a lot more elaborate simulations around the safety of the randomness behind the notary assignments in the SMC. Futhermore we need to pass stress testing against DoS and other sorts of byzantine attacks. Additionally, it will be the first release to have real users proposing collations concurrently with notaries reaching consensus on these collations.
Part 2 of the **Sapphire Release** will focus on implementing state execution and defining the State Transition Function for sharding on a local testnet (as outlined in [Beyond Phase 1](#beyond-phase-1)) as an extenstion to the Ruby Release.
@ -167,239 +147,109 @@ ETA: To Be determined
# Go-Ethereum Sharding Alpha Implementation
Prysmatic Labs will begin by focusing its implementation entirely on the **Ruby Release** from our roadmap. We plan on being as pragmatic as possible to create something that can be locally run by any developer as soon as possible. Our initial deliverable will center around a command line tool that will serve as an entrypoint into a collator client that allows staking, a proposer client that allows for simple state execution and creation of collation proposals, and processing collations through on-chain verification via the Sharding Manager Contract.
Prysmatic Labs will begin by focusing its implementation entirely on the **Ruby Release** from our roadmap. We plan on being as pragmatic as possible to create something that can be locally run by any developer as soon as possible. Our initial deliverable will center around a command line tool that will serve as an entrypoint into a sharding client that allows staking to become a notary, a proposer client that allows for the creation of collation proposals, shard state local storage, and on-chain voting of collation headers via the Sharding Manager Contract.
Here is a full reference spec explaining how our initial system will function:
## System Architecture
Our implementation revolves around 4 core components:
Our implementation revolves around 5 core components:
- A **locally-running geth node** that spins up an instance of the Ethereum blockchain and mines on the Proof of Work chain
- A **Sharding Manager Contract (SMC)** that is deployed onto this blockchain instance
- A **collator client** that connects to the running geth node through JSON-RPC, provides bindings to the SMC, and listens for incoming collation proposals
- A **proposer client** that is tasked with processing pending tx's into collations that are then submitted to collators via a local filesystem for the purposes of simplified, phase 1 implementation. In phase 1, proposers _do not_ execute state, but rather just serialize pending tx data into possibly valid/invalid data blobs.
- A **sharding client** that connects to the running geth node through JSON-RPC, provides bindings to the SMC
- A **notary client** that allows users to stake ETH into the SMC and be selected as a notary in a certain period on a shard
- A **proposer client** that is tasked with processing pending tx's into collations that are then submitted to the SMC. In phase 1, proposers _do not_ execute state, but rather just serialize pending tx data into possibly valid/invalid data blobs.
Our initial implementation will function through simple command line arguments that will allow a user running the local geth node to deposit ETH into the SMC and join as a collator that is automatically assigned to a certain period. We will also launch a proposer client that will create collations and submit them to collators for them to add their headers to the SMC via a cryptoeconomic "game".
Our initial implementation will function through simple command line arguments that will allow a user running the local geth node to deposit ETH into the SMC and join as a notary that is randomly assigned to a shard in a certain period.
A basic, end-to-end example of the system is as follows:
1. _**User starts a collator client and deposits 100ETH into the SMC:**_ the sharding client connects to a locally running geth node and asks the user to confirm a deposit from his/her personal account.
1. _**User starts a notary client and deposits 1000ETH into the SMC:**_ the sharding client connects to a locally running geth node and asks the user to confirm a deposit from his/her personal account.
2. _**Collator client connects & listens to incoming headers from the geth node and assigns user as collators on a shard per period:**_ The collator is selected in CURRENT_PERIOD + LOOKEAD_PERIOD (which is around a 5 minutes notice) and must accept collations from proposer nodes that offer the best prices.
2. _**Client connects & listens to incoming headers from the geth node and assigns user as notary on a shard per period:**_ The notary is selected in CURRENT_PERIOD + LOOKEAD_PERIOD (which is around a 5 minutes notice) and must download data for collation headers submitted in that time period.
3. _**Concurrently, a proposer client processes pending transaction data into blobs:**_ the proposer client will create collation bodies and submit them to collators through an open bidding system. In Phase 1, it is important to note that we will _not_ have any state execution. Proposers will just serialize pending tx into fixed collation body sizes without executing them for validity.
3. _**Concurrently, a proposer client processes pending transaction data into blobs:**_ the proposer client will create collation bodies and submit their headers to the SMC. In Phase 1, it is important to note that we will _not_ have any state execution. Proposers will just serialize pending tx into fixed collation body sizes without executing them for state transition validity.
4. _**Collators broadcast a signed commitment to all top proposers:**_ collators create a signed list called a _commitment_ that specifies top collations he/she would accept based on a high payout. This eliminates some risk on behalf of proposers, who need some guarantee that they are not being led on by collators.
5. _**The set of notaries vote on collation headers as canonical unitl the period ends:**_ the headers that received >= 2/3 votes are accepted as canonical.
5. _**Top proposer in commitment broadcasts his/her collation body to the collator:** proposer sends the collation body to the collator, but does not have a way of ensuring the collator will not be lazy and not download the body.
4. _**Proposers can challenge collators on data availability:**_ to ease more proposer risk, we give proposers the ability to challenge the data availability of a collation downloaded by a collator. If collator cannot respond in a certain period of time, collator is slashed.
5. _**The collator adds collation headers through the SMC:**_ the collator client checks the previous 25 collations in the shard chain to check for validity, and then uses the fork choice rule to determine the canonical shard head the recently exchanged collation would be appended to via an `addHeader` SMC call.
6. _**User is selected as collator again on the SMC in a different period or can withdraw his/her stake from the collator's pool:**_ the user can keep staking and adding incoming collation headers and restart the process, or withdraw his/her stake and be removed from the SMC collator pool.
6. _**User is selected as notary again on the SMC in a different period or can withdraw his/her stake:**_ the user can keep staking and voting on incoming collation headers and restart the process, or withdraw his/her stake and be deregistered from the SMC.
Now, well explore our architecture and implementation in detail as part of the go-ethereum repository.
## System Start and User Entrypoint
Our Ruby Release requires users to start a local geth node running a localized, private blockchain to deploy the **SMC** into. Users can spin up a collator client as a command line entrypoint into geth while the node is running as follows:
Our Ruby Release requires users to start a local geth node running a localized, private blockchain to deploy the **SMC** into. Users can spin up a notary client as a command line entrypoint into geth while the node is running as follows:
```
$ geth sharding-collator --deposit --datadir /path/to/your/datadir --password /path/to/your/password.txt --networkid 12345
```
geth sharding-notary --deposit --datadir /path/to/your/datadir --password /path/to/your/password.txt --networkid 12345
If it is the first time the client runs, it deploys a new **SMC** into the local chain and establishes a JSON-RPC connection to interact with the node directly. The `--deposit` flag tells the sharding client to automatically unlock the users keystore and begin depositing ETH into the SMC to become a collator.
This will extract 1000ETH from the user's account balance and insert him/her into the SMC's notaries. Then, the program will listen for incoming block headers and notify the user when he/she has been selected as to vote on collations for a certain shard in a given period. Once you are selected, the sharding client will download collation information to check for data availability on vote on proposals that have been submitted via the `addHeader` function on the SMC.
Concurrently, a user needs to launch a **proposer client** that starts processing transactions into collations that can then be offered to collators by including an ETH deposit in their unsigned collation headers. The proposer client can also be initialized as follows in a separate process:
Users can also run a proposer client that is tasked with processing transactions into collations and submitting them to the SMC via the `addHeader` function.
```
geth sharding-proposer --datadir /path/to/your/datadir --password /path/to/your/password.txt --networkid 12345
```
geth sharding-proposer --datadir /path/to/your/datadir --password /path/to/your/password.txt --networkid 12345
Back to the collators, the collator client begins to work by its main loop, which involves the following steps:
This client is tasked with processing pending transactions into blobs within collations by serializing data into collation bodies. It is responsible for submitting proposals (collation headers) to the SMC via the `addHeader` function.
The sharding client begins to work by its main loop, which involves the following steps:
1. _**Subscribe to incoming block headers:**_ the client will begin by issuing a subscription over JSON-RPC for block headers from the running geth node.
2. _**Check shards for eligible collator within LOOKEAD_PERIOD:**_ on incoming headers, the client will interact with the SMC to check if the current collator is an eligible collator for an upcoming period (only a few minutes notice)
2. _**Check shards for notary selection within LOOKEAD_PERIOD:**_ on incoming headers, the client will interact with the SMC to check if the current user is an eligible notary for an upcoming period (only a few minutes notice)
3. _**If the collator is selected, fetch proposals from proposal nodes and add collation headers to SMC:**_ once a collator is selected, he/she only has a small timeframe to add collation headers through the SMC, so he/she looks for proposals from proposer nodes and accepts those that offer the highest payouts. The collator then broadcasts a commitment that includes a cryptographic guarantee of the specific proposals such collator would accept if proposer publishes a corresponding collation body. Then, the top proposer that acts accordingly gets his/her proposal accepted, and the collator appends it to the SMC through a simplified PoS consensus.
3. _**If the notary is selected, check data availability for submitted collation headers:**_ once a notary is selected, he/she has to download subimtted collation headers for the shard in a certain period and check for their data availability
5. _**If user withdraws, remove from collator set:**_ a user can choose to stop being a collator and then his/her ETH is withdrawn from the collator set after a certain lockup period is met.
5. _**The notary issues a vote:**_ the notary votes on the available collation header that came first in the submissions.
6. _**Otherwise, collating client keeps subscribing to block headers:**_ If the user chooses to keep going, it will be the proposer clients responsibility to listen to any new pending transactions and interact with collators that have staked their ETH into the SMC through an open bidding system for collation proposals.
6. _**Other notaries vote, period ends, and header is selected as canonical shard chain header:**_ Once notaries vote, headers that received >=2/3 votes are selected as canonical
<!--[Transaction Generator]generate test txs->[Shard TXPool],[Geth Node]-deploys>[Sharding Manager Contract{bg:wheat}], [Shard TXPool]<fetch pending txs-.->[Proposer Client], [Proposer Client]-propose collation>[Collator Client],[Collator Client]add collation header->[Sharding Manager Contract{bg:wheat}]-->
![system functioning](https://yuml.me/4a7c8c5b.png)
<!--[Transaction Generator]generate test txs->[Shard TXPool],[Geth Node]-deploys>[Sharding Manager Contract{bg:wheat}], [Shard TXPool]<fetch pending txs-.->[Proposer Client], [Proposer Client]-propose collation>[Sharding Manager Contract],[Notary Client]download availability and vote->[Sharding Manager Contract{bg:wheat}]-->
![system functioning](https://yuml.me/6c2f90a5.png)
## The Sharding Manager Contract
Our solidity implementation of the Collator Manager Contract follows the reference spec outlined in ETHResearch's Updated Phase 1 Spec [here](https://ethresear.ch/t/sharding-phase-1-spec/1407).
Our solidity implementation of the Sharding Manager Contract follows the reference spec outlined in ETHResearch's [minimal sharding protocol](https://ethresear.ch/t/a-minimal-sharding-protocol-that-may-be-worthwhile-as-a-development-target-now/1650)
### Necessary Functionality
In our Solidity implementation, we begin with the following sensible defaults:
```javascript
// Constant values
uint constant periodLength = 5;
int constant public shardCount = 100;
// The exact deposit size which you have to deposit to become a collator
uint constant depositSize = 100 ether;
// Number of periods ahead of current period, which the contract
// is able to return the collator of that period
uint constant lookAheadPeriods = 4;
```
The contract will store the following data structures (full credit to the Phase 1 Spec)
- Collator pool
- `collator_pool`: `address[int128]`—array of active collator addresses
- `collator_pool_len`: `int128`—size of the collator pool
- `empty_slots_stack`: `int128[int128]`—stack of empty collator slot indices
- `empty_slots_stack_top`: `int128`—top index of the stack
- Collator registry
- `collator_registry`: `{deregistered: int128, pool_index: int128}[address]`—collator registry (deregistered is 0 for not yet deregistered collators)
- Proposer registry
- `proposer_registry`: `{deregistered: int128, balances: wei_value[uint256]}[address]`—proposer registry
- Collation trees
- `collation_trees`: `bytes32[bytes32][uint256]`—collation trees (the collation tree of a shard maps collation hashes to previous collation hashes truncated to 24 bytes packed into a bytes32 with the collation height in the last 8 bytes)
- `last_update_periods`: `int128[uint256]`—period of last update for each shard
- Availability challenges
- `availability_challenges`:TBD—availability challenges
- `availability_challenges_len`: `int128`—availability challenges counter
Then, the minimal functions required by the SMC are as follows:
#### Registering Collators and Proposers
```javascript
function registerCollator() public payable returns(bool) {
require(!isCollatorDeposited[msg.sender]);
require(msg.value == depositSize);
...
}
```
Locks a collator into the contract and updates the collator pool accordingly.
```javascript
function registerProposer() public payable returns(bool) {
require(!isCollatorDeposited[msg.sender]);
require(msg.value == depositSize);
...
}
```
Same as above but does **not** update the collator pool.
```javascript
function proposerAddBalance(uint256 shard_id) returns(bool) {
...
}
```
Adds `msg.value` to the balance of the proposer on `shard_id`, and returns `True` on success. Checks:
Shard: shard_id against NETWORK_ID and SHARD_COUNT
Authentication: `proposer_registry[msg.sender]` exists
#### Determining an Eligible Collator for a Period on a Shard
```javascript
function getEligibleCollator(int _shardId, int _period) public view returns(address) {
require(_period >= lookAheadPeriod);
require((_period - lookAheadPeriods) * periodLength < block.number);
...
}
```
The `getEligibleCollator` function uses a block hash as a seed to pseudorandomly select a signer from the collator set. The chance of being selected should be proportional to the collator's deposit. The function should be able to return a value for the current period or any future up to `LOOKAHEAD_PERIODS` periods ahead.
#### Processing and Verifying a Collation Header
```javascript
function addHeader(int _shardId, uint _expectedPeriodNumber, bytes32 _periodStartPrevHash,
bytes32 _parentHash, bytes32 _transactionRoot,
address _coinbase, bytes32 _stateRoot, bytes32 _receiptRoot,
int _number) public returns(bool) {
HeaderVars memory headerVars;
// Check if the header is valid
require((_shardId >= 0) && (_shardId < shardCount));
require(block.number >= periodLength);
require(_expectedPeriodNumber == block.number / periodLength);
require(_periodStartPrevHash == block.blockhash(_expectedPeriodNumber * periodLength - 1));
}
```
The `addHeader` function is the most important function in the SMC as it provides on-chain verification of collation headers, and maintains a canonical ordering of processed collation headers.
<!-- removed old solidity code cause it will be bound to change -->
Our current [solidity implementation](https://github.com/prysmaticlabs/geth-sharding/blob/master/sharding/contracts/sharding_manager.sol) includes all of these functions along with other utilities important for the our Ruby Release sharding scheme.
For more details on these methods, please refer to the Phase 1 spec as it details all important requirements and additional functions to be included in the production-ready SMC.
### Collator Sampling
### Notary Sampling
The probability of being selected as a collator on a particular shard should be completely dependent on the stake of the collator and not on other factors. This is a key distinction. As specified in the [Sharding FAQ](https://github.com/ethereum/wiki/wiki/Sharding-FAQ) by Vitalik, “if validators [collators] could choose, then attackers with small total stake could concentrate their stake onto one shard and attack it, thereby eliminating the systems security.”
The probability of being selected as a notary on a particular shard is being heavily researched in the latest ETHResearch discussions. As specified in the [Sharding FAQ](https://github.com/ethereum/wiki/wiki/Sharding-FAQ) by Vitalik, “if validators [collators] could choose, then attackers with small total stake could concentrate their stake onto one shard and attack it, thereby eliminating the systems security.”
The idea is that collators should not be able to figure out which shard they will become a collator of and during which period they will be assigned with anything more than a few minutes notice. To accomplish this, random sampling would require collators to redownload entire large parts of new shard states they get assigned to as part of the collation process in a naive approach. However, our approach separates the consensus and state execution/collation proposal mechanisms, which allows collators to not have to download shard states save for specific situations.
The idea is that notaries should not be able to figure out which shard they will become a notary of and during which period they will be assigned with anything more than a few minutes notice.
Ideally, we want collators to shuffle across shards very rapidly and through a trustworthy source of randomness built in-protocol.
Ideally, we want notaries to shuffle across shards very rapidly and through a source of pseudorandomness built in-protocol.
Although this separation of consensus and state execution is an attractive way to fix the overhead of having to redownload shard states, random sampling does not help in a bribing, coordinated attack model. In Vitaliks own words:
Despite its benefits, random sampling does not help in a bribing, coordinated attack model. In Vitaliks own words:
_"Either the attacker can bribe the great majority of the sample to do as the attacker pleases, or the attacker controls a majority of the sample directly and can direct the sample to perform arbitrary actions at low cost (O(c) cost, to be precise).
At that point, the attacker has the ability to conduct 51% attacks against that sample. The threat is further magnified because there is a risk of cross-shard contagion: if the attacker corrupts the state of a shard, the attacker can then start to send unlimited quantities of funds out to other shards and perform other cross-shard mischief. All in all, security in the bribing attacker or coordinated choice model is not much better than that of simply creating O(c) altcoins.”_
However, this problem transcends the sharding scheme itself and goes into the broader problem of fraud detection, which we have yet to comprehensively address.
### Collation Header Approval
## The Notary Client
Explains the on-chain verification of a collation header.
One of the main running threads of our implementation is the notary client, which serves as a bridge between users staking their ETH to become notaries and the **Sharding Manager Contract** that verifies collation headers on the canonical chain.
Work in progress.
### Event Logs
Explain how CollationAdded logs will later on be used.
Work in progress.
## The Collator Client
The main running thread of our implementation is the collator client, which serves as a bridge between users staking their ETH, proposers offering collations to these collators, and the **Sharding Manager Contract** that verifies collation headers on the canonical chain.
When we launch the client with
```
geth sharding-collator --deposit --datadir /path/to/your/datadir --password /path/to/your/password.txt --networkid 12345
```
The instance connects to a running geth node via JSON-RPC and calls the deposit function on a deployed, Sharding Manager Contract to insert the user into a collator set. Then, we subscribe for updates on incoming block headers and call `getEligibleCollator` on receiving each header. Once we are selected within a LOOKAHEAD_PERIOD, our client fetches proposed, unsigned collations created by proposer nodes. The collator client accepts a collation that offer the highest payout, countersigns it, and adds it to the SMC.
When we launch the client, The instance connects to a running geth node via JSON-RPC and calls the deposit function on a deployed, Sharding Manager Contract to insert the user into a notary pool. Then, we subscribe for updates on incoming block headers and determine if the user is a notary on receiving each header. Once we are selected within a LOOKAHEAD_PERIOD, our client fetches data associated with submitted collation headers to that shard. The notary votes on the SMC, and if other notaries reach consensus, the collation is accepted as canonical.
### Local Shard Storage
Local shard information is done through the same LevelDB, key-value store used to store the mainchain information in the local data directory specified by the running geth node. Adding a collation to a shard will effectively modify this key-value store.
Local shard information is done through a key-value store used to store the mainchain information in the local data directory specified by the running geth node. Adding a collation to a shard will effectively modify this key-value store.
Work in progress.
## The Proposer Client
In addition to launching a collator client, our system requires a user to concurrently launch a proposer client that is tasked with fetching pending txs from the network and creating collations that can be sent to collators.
In addition to launching a notary client, our system requires a user to concurrently launch a proposer client that is tasked with fetching pending txs from the network and creating collations that can be sent to the SMC.
Users launch a proposal client as another geth entrypoint as follows:
This client connects via JSON-RPC to give the client the ability to call required functions on the SMC. The proposer is tasked with packaging pending transaction data into _blobs_ and **not** executing these transactions. This is very important, we will not consider state execution until later phases of a sharding roadmap.
```
geth sharding-collator --datadir /path/to/your/datadir --password /path/to/your/password.txt --networkid 12345
```
Launching this command connects via JSON-RPC to give the client the ability to call required functions on the SMC. The proposer is tasked with packaging pending transaction data into _blobs_ and **not** executing these transactions. This is very important, we will not consider state execution until later phases of a sharding roadmap. The proposer broadcasts the unsigned header **(note: the body is not broadcasted)** to a proposals folder in the local, shared filesystem along with a guaranteed ETH deposit that is extracted from the proposers account upfront. Then, the current collator assigned for the period will find proposals for him/her assigned shard and sign the one with the highest payout.
Then, the collator node calls the addHeader function on the SMC by submitting this collation header. Well explore the structure of collation headers in this next section.
Then, the proposer node calls the `addHeader` function on the SMC by submitting this collation header. Well explore the structure of collation headers in this next section.
### Collation Headers
@ -445,29 +295,23 @@ Work in progress.
## Sharding In-Practice
### Fork Choice Rule
In the sharding consensus mechanism, it is important to consider that we now have two layers of longest chain rules when adding a collation. When we are reaching consensus on the best shard chain, we not only have to check for the longest canonical main chain, but also the longest shard chain **within** this longest main chain. Vlad Zamfir has elaborated on this fork-choice rule in a [tweet](https://twitter.com/VladZamfir/status/945358660187893761) that is important for our sharding scheme.
### Use-Case Stories: Proposers
The primary purpose of proposers is to package transaction data into collations that can then be put on an open market for collators to take. Upon offering a proposal, proposers will deposit part of their ETH as a payout to the collator that adds its collation header to the SMC, __even if the collation gets orphaned__. By forcing proposers to take on this risk, we prevent a certain degree of collation proposal spamming, albeit not without a few other security problems: (See: [Active Research](#active-questions-and-research)).
The primary purpose of proposers is to package transaction data into collations that can then be submitted to the SMC.
The primary incentive for proposers to generate these collations is to receive a payout to their coinbase address along with transactions fees from the ones they process once added to a block in the canonical chain. This process, however, cannot occur until we have state execution in our protocol, so proposers will be running at a loss for our Phase 1 implementation.
The primary incentive for proposers to generate these collations is to receive a payout to their coinbase address from transactions fees once these collations are added to a block in the canonical chain. This process, however, cannot occur until we have state execution in our protocol, so proposers will be running at a loss for our Phase 1 implementation.
### Use-Case Stories: Collators
### Use-Case Stories: Notaries
The primary purpose of collators is to use Proof of Stake and reach **consensus** on valid shard chains based on the collations they process and add to the Sharding Manager Contract. They have two primary options they can choose to do:
The primary purpose of notaries is to use Proof of Stake and reach **consensus** on valid shard chains based on the collations they process and add to the Sharding Manager Contract. They have three primary things to do:
- They can deposit ETH into the SMC and become a collator. They then have to wait to be selected by the SMC on a particular period to add a collation header to the SMC.
- They can accept a collation proposal from the collation pool during their eligible period.
- They can withdraw their stake and stop being a part of the collator pool
The primary incentive for collators is to earn the payouts from the proposers offering them collations within their period.
- They can deposit ETH into the SMC and become a notary. They then have to wait to be selected by the SMC on a particular period to vote on collation headers in the SMC.
- They download availability of collation headers submitted to their assigned shard in the period.
- They vote on available collation headers
## Current Status
Currently, Prysmatic Labs is focusing its initial implementation around the logic of the collator and proposer clients. We have built the command line entrypoints as well as the minimum, required functions of the Sharding Manager Contract that is deployed to a local Ethereum blockchain instance. Our collator client is able to subscribe for block headers from the running Geth node and determine when we are selected as an eligible collator in a given period if we have deposited ETH into the contract.
Currently, Prysmatic Labs is focusing its initial implementation around the logic of the notary and proposer clients, as well as shard state local storage and p2p networking. We have built the command line entrypoints as well as the minimum, required functions of the Sharding Manager Contract that is deployed to a local Ethereum blockchain instance. Our notary client is able to subscribe for block headers from the running Geth node and determine when we are selected as an eligible notary in a given period if we have deposited ETH into the contract.
You can track our progress, open issues, and projects in our repository [here](https://github.com/prysmaticlabs/geth-sharding).
@ -475,8 +319,6 @@ You can track our progress, open issues, and projects in our repository [here](h
## Not Included in Ruby Release
Under the uncoordinated majority model, in order to prevent a single shard takeover, random sampling is utilized. Each shard is assigned a certain number of collators. Collators that approve the collations on that shard are sampled randomly. However for the ruby release we will not be implementing any random sampling of the collators of the shard, as the primary objective of this release is to launch an archival sharding client which deploys the Sharding Management Contract to a locally running geth node.
We will not be considering data availability proofs (part of the stateless client model) as part of the ruby release we will not be implementing them as it just yet as they are an area of active research.
## Bribing, Coordinated Attack Models
@ -485,26 +327,13 @@ Work in progress.
## Enforced Windback
When collators are extending collator chains by adding headers to the SMC, it is critical that they are able to verify some of the collation headers in the immediate past for security purposes. There have already been instances where mining blindly has led to invalid transactions that forced Bitcoin to undergo a fork (See: [BIP66 Incident](https://bitcoin.stackexchange.com/questions/38437/what-is-spv-mining-and-how-did-it-inadvertently-cause-the-fork-after-bip66-wa)).
When notaries are extending shard chains, it is critical that they are able to verify some of the collation headers in the immediate past for security purposes. There have already been instances where mining blindly has led to invalid transactions that forced Bitcoin to undergo a fork (See: [BIP66 Incident](https://bitcoin.stackexchange.com/questions/38437/what-is-spv-mining-and-how-did-it-inadvertently-cause-the-fork-after-bip66-wa)).
As part of the sharding process, we want to ensure collators do two things when they look at the immediate past:
This process of checking previous blocks is known as **“windback”**. In a [post](https://ethresear.ch/t/enforcing-windback-validity-and-availability-and-a-proof-of-custody/949) by Justin Drake on ETHResearch, he outlines that this is necessary for security, but is counterintuitive to the end-goal of scalability as this obviously imposes more computational and network constraints on nodes.
Validity of collations through checking the integrity of transactions within their body.
Checking for availability of the data within past collation bodies.
One way to enforce **validity** during the windback process is for nodes to produce zero-knowedge proofs of validity that can then be stored in collation headers for quick verification.
This checking process is known as **“windback”**. In a [post](https://ethresear.ch/t/enforcing-windback-validity-and-availability-and-a-proof-of-custody/949) by Justin Drake on ETHResearch, he outlines that this is necessary for security, but is counterintuitive to the end-goal of scalability as this obviously imposes more computational and network constraints on collator nodes.
One way to enforce **validity** during the windback process is for collators to produce zero-knowedge proofs of validity that can then be stored in collation headers for quick verification.
On the other hand, to enforce **availability** for the windback process, a possible approach is for collators to produce “proofs of custody” in collation headers that prove the collator was in possession of the full data of a collation when produced. Drake proposes a constant time, non-interactive zkSNARK method for collators to check these proofs of custody. In his construction, he mentions splitting up a collation body into “chunks” that are then mixed with the collator's private key through a hashing scheme. The security in this relies in the idea that a collator would not leak his/her private key without compromising him or herself, so it provides a succinct way of checking if the full data was available when a collator processed the collation body and proof was created.
### Explicit Finality for Stateless Clients
Vitalik has [mentioned](https://ethresear.ch/t/detailed-analysis-of-stateless-client-witness-size-and-gains-from-batching-and-multi-state-roots/862/5?u=rauljordan) that the average amount of windback, or how many immediate periods in the past a collator has to check before adding a collation header, is around 25. In a [medium post](https://medium.com/@icebearhww/ethereum-sharding-and-finality-65248951f649) on the value of explicit finality for sharding, Hsiao-Wei Wang mentions how the finality that Casper FFG provides would mean stateless clients would be entirely confident of blocks ahead to prevent complete reshuffling and faster collation processing. In her own words:
_“Casper FFG will provide explicit finality threshold after about 2.5 “epoch times”, i.e., 125 block times [1][7]. If validators [collators] can verify more than 125 / PERIOD_LENGTH = 25 collations during reshuffling, the shard system can benefit from explicit finality and be more confident of the 25 ahead collations from now are all finalized.”_
Casper allows us to forego some of these windback considerations and reduces the number of constraints on scalability from a collation header verification standpoint.
On the other hand, to enforce **availability** for the windback process, a possible approach is for nodes to produce “proofs of custody” in collation headers that prove the notary was in possession of the full data of a collation when produced. Drake proposes a constant time, non-interactive zkSNARK method for notaries to check these proofs of custody. In his construction, he mentions splitting up a collation body into “chunks” that are then mixed with the node's private key through a hashing scheme. The security in this relies in the idea that a node would not leak his/her private key without compromising him or herself, so it provides a succinct way of checking if the full data was available when a node processed the collation body and proof was created.
## The Data Availability Problem
@ -560,38 +389,11 @@ Work in progress.
# Active Questions and Research
## Separation of Proposals and Consensus
## Selecting Notaries Via Random Beacon Chains
In a recent [blog post](https://ethresear.ch/t/separating-proposing-and-confirmation-of-collations/1000/7), Vitalik has outlined a novel system to the sharding mechanism through better separation of concerns. In the current reference documentation for a sharding systems, collators are responsible for proposing transactions into collations and reaching consensus on these proposals. That is, this process happens _all at once_, as proposing a collation happens in tandem with consensus.
In our current implementation for the Ruby Release, we are selecting notaries through a pseudorandom method built into the SMC directly. Inspired by dfinity's Random Beacon chains, the Ethereum Research team has been proposing better solutions that have faster finality guarantees.
This leads to significant computational burdens on collators that need to keep track of the state of a particular shard in the proposal process as part of the transaction packaging process. The potentially better approach outlined above is the idea of separating the transaction packaging process and the consensus mechanism into two separate nodes with different responsibilities. **Our model will be based on this separation and we will be releasing a proposer client alongside a collator client in our Ruby release**.
The two nodes would interact through a cryptoeconomic incentive system where proposers would package transactions and send unsigned collation headers (with obfuscated collation bodies) over to collators with a signed message including a deposit. If a collator chooses to accept the proposal, he/she would be rewarded by the amount specified in the deposit.
Along the same lines, it will make it easier for collators to constantly jump across shards to validate collations, as they no longer have the need for resyncing an entire state tree because they can simply receive collation proposals from proposer nodes. This is very important, as collator reshuffling is a crucial security consideration to prevent shard hostile takeovers.
A key question asked in the post is whether _“this makes it easy for a proposer to censor a transaction by paying a high pass-through fee for collations without a certain transaction?”_ and the answer to that is that yes, this could happen but in the current system a proposer could censor transactions by simply not including them (see: [The Data Availability Problem](#the-data-availability-problem)).
It is important to note a possible attack vector in this case: which is that a an attacker could spam proposals on a particular shard and take on the price of excluding certain transactions as a censorship mechanism while the collators would have no idea this is happening. However, given a competitive enough proposal economy, this would be very similar to the current problem of transaction spam in traditional blockchains.
In this system, collators would get paid the proposals deposit __even if the collation does not get appended to the shard__. Proposers would have to take on this risk to mitigate the possibilities of malicious intent to make an obfuscated-collation-body proposal system work. Only collations that are double signed and have an available body can be included in the main chain, fee F goes to the collator regardless whether collation gets into the main chain, but fee T only goes to the proposer if the collation gets included to the main chain.
In practice, we would end up with a system of 3 types of nodes to ensure the functioning of a sharded blockchain
1. Proposer nodes that are tasked with the creation of unsigned collation headers, obfuscated collation bodies, and an ETH deposit to be relayed to collators.
2. Collator nodes that accept proposals through an open auction system similar to way transactions fees currently work. These nodes then sign these collations and pass them through the SMC for inclusion into a shard through PoS.
## Selecting Eligible Collators Off-Chain
In our current implementation for the Ruby Release, we are selecting collators on-chain by calling the `getEligibleCollator` function from the SMC directly. Justin Drake proposes an alternative scheme that could potentially open up new collator selection mechanisms off-chain through a fork-choice rule in [this](https://ethresear.ch/t/fork-choice-rule-for-collation-proposal-mechanisms/922) post on ETHResearch.
In his own words, this scheme “saves gas when calling addHeader and unlocks the possibility for fancier proposer eligibility functions”. A potential way to do so would be through private collator sampling which is elaborated on below:
_“We now look at the problem of private sampling. That is, can we find a proposal mechanism which selects a single validator [collator] per period and provides “private lookahead”, i.e. it does not reveal to others which validators will be selected next?
There are various possible private sampling strategies (based on MPCs, SNARKs/STARKs, cryptoeconomic signalling, or fancy crypto) but finding a workable scheme is hard. Below we present our best attempt based on one-time ring signatures. The scheme has several nice properties:
Perfect privacy: private lookahead and private lookbehind (i.e. the scheme never matches eligible collators with specific validators)
Full lookahead: the lookahead extends to the end of the epoch (epochs are defined below, and have roughly the same size as the validator set)
Perfect fairness: within an epoch validators are selected proportionally according to deposit size, with zero variance”_ - Justin Drake
<https://ethresear.ch/t/posw-random-beacon/1814/6>
# Community Updates and Contributions

View File

@ -1,6 +1,6 @@
// Package client provides an interface for interacting with a running ethereum full node.
// As part of the initial phases of sharding, actors will need access to the sharding management
// contract on the main PoW chain
// contract on the main PoW chain.
package client
import (
@ -33,17 +33,17 @@ const (
clientIdentifier = "geth" // Used to determine the ipc name.
)
// General client for Notary/Proposer - Communicates to Geth node via JSON RPC
// General client for Notary/Proposer - Communicates to Geth node via JSON RPC.
type shardingClient struct {
endpoint string // Endpoint to JSON RPC
endpoint string // Endpoint to JSON RPC.
client *ethclient.Client // Ethereum RPC client.
keystore *keystore.KeyStore // Keystore containing the single signer
ctx *cli.Context // Command line context
smc *contracts.SMC // The deployed sharding management contract
rpcClient *rpc.Client // The RPC client connection to the main geth node
keystore *keystore.KeyStore // Keystore containing the single signer.
ctx *cli.Context // Command line context.
smc *contracts.SMC // The deployed sharding management contract.
rpcClient *rpc.Client // The RPC client connection to the main geth node.
}
// Client methods that must be implemented to run a sharded system
// Client methods that must be implemented to run a sharded system.
type Client interface {
Start() error
Close()
@ -54,7 +54,7 @@ type Client interface {
SMCTransactor() *contracts.SMCTransactor
}
// NewClient forms a new struct instance
// NewClient forms a new struct instance.
func NewClient(ctx *cli.Context) Client {
path := node.DefaultDataDir()
if ctx.GlobalIsSet(utils.DataDirFlag.Name) {
@ -75,7 +75,7 @@ func NewClient(ctx *cli.Context) Client {
scryptN, scryptP, keydir, err := config.AccountConfig()
if err != nil {
panic(err) // TODO(prestonvanloon): handle this
panic(err) // TODO(prestonvanloon): handle this.
}
ks := keystore.NewKeyStore(keydir, scryptN, scryptP)
@ -86,9 +86,9 @@ func NewClient(ctx *cli.Context) Client {
}
}
// Start the sharding client
// Connects to Geth node
// Verifies or deploys the sharding manager contract
// Start the sharding client.
// Connects to Geth node.
// Verifies or deploys the sharding manager contract.
func (c *shardingClient) Start() error {
rpcClient, err := dialRPC(c.endpoint)
if err != nil {
@ -97,7 +97,7 @@ func (c *shardingClient) Start() error {
c.rpcClient = rpcClient
c.client = ethclient.NewClient(rpcClient)
// Check account existence and unlock account before starting notary client
// Check account existence and unlock account before starting notary client.
accounts := c.keystore.Accounts()
if len(accounts) == 0 {
return fmt.Errorf("no accounts found")
@ -116,7 +116,7 @@ func (c *shardingClient) Start() error {
return nil
}
// Close the RPC client connection
// Close the RPC client connection.
func (c *shardingClient) Close() {
c.rpcClient.Close()
}
@ -185,7 +185,7 @@ func (c *shardingClient) SMCCaller() *contracts.SMCCaller {
return &c.smc.SMCCaller
}
// SMCTransactor allows us to send tx's to the SMC programmatically
// SMCTransactor allows us to send tx's to the SMC programmatically.
func (c *shardingClient) SMCTransactor() *contracts.SMCTransactor {
return &c.smc.SMCTransactor
}

View File

@ -7,41 +7,41 @@ import (
"github.com/ethereum/go-ethereum/core/types"
)
// Collation base struct
// Collation base struct.
type Collation struct {
header *CollationHeader
transactions []*types.Transaction
}
// CollationHeader base struct
// CollationHeader base struct.
type CollationHeader struct {
shardID *big.Int //the shard ID of the shard
chunkRoot *common.Hash //the root of the chunk tree which identifies collation body
period *big.Int //the period number in which collation to be included
proposerAddress *common.Address //address of the collation proposer
proposerSignature []byte //the proposer's signature for calculating collation hash
shardID *big.Int //the shard ID of the shard.
chunkRoot *common.Hash //the root of the chunk tree which identifies collation body.
period *big.Int //the period number in which collation to be included.
proposerAddress *common.Address //address of the collation proposer.
proposerSignature []byte //the proposer's signature for calculating collation hash.
}
// Header returns the collation's header
// Header returns the collation's header.
func (c *Collation) Header() *CollationHeader { return c.header }
// Transactions returns an array of tx's in the collation
// Transactions returns an array of tx's in the collation.
func (c *Collation) Transactions() []*types.Transaction { return c.transactions }
// ShardID is the identifier for a shard
// ShardID is the identifier for a shard.
func (c *Collation) ShardID() *big.Int { return c.header.shardID }
// Period the collation corresponds to
// Period the collation corresponds to.
func (c *Collation) Period() *big.Int { return c.header.period }
// ProposerAddress is the coinbase addr of the creator for the collation
// ProposerAddress is the coinbase addr of the creator for the collation.
func (c *Collation) ProposerAddress() *common.Address { return c.header.proposerAddress }
// SetHeader updates the collation's header
// SetHeader updates the collation's header.
func (c *Collation) SetHeader(h *CollationHeader) { c.header = h }
// AddTransaction adds to the collation's body of tx blobs
// AddTransaction adds to the collation's body of tx blobs.
func (c *Collation) AddTransaction(tx *types.Transaction) {
// TODO: Include blob serialization instead
// TODO: Include blob serialization instead.
c.transactions = append(c.transactions, tx)
}

View File

@ -1,14 +1,15 @@
package sharding
import (
"math"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
// TODO: this test needs to change as we will be serializing tx's into blobs
// within the collation body instead.
func TestCollation_AddTransactions(t *testing.T) {
tests := []struct {
transactions []*types.Transaction
@ -42,40 +43,6 @@ func TestCollation_AddTransactions(t *testing.T) {
}
}
func TestCollation_GasUsed(t *testing.T) {
tests := []struct {
transactions []*types.Transaction
gasUsed *big.Int
}{
{
transactions: []*types.Transaction{
makeTxWithGasLimit(100),
makeTxWithGasLimit(100000),
makeTxWithGasLimit(899900),
},
gasUsed: big.NewInt(1000000),
}, {
transactions: []*types.Transaction{},
gasUsed: big.NewInt(0),
},
{
transactions: []*types.Transaction{
makeTxWithGasLimit(math.MaxUint64),
makeTxWithGasLimit(9001),
makeTxWithGasLimit(math.MaxUint64),
},
gasUsed: big.NewInt(0).SetUint64(math.MaxUint64),
},
}
for _, tt := range tests {
got := (&Collation{transactions: tt.transactions}).GasUsed()
if tt.gasUsed.Cmp(got) != 0 {
t.Errorf("Returned unexpected gasUsed. Got=%v, wanted=%v", got, tt.gasUsed)
}
}
}
func makeTxWithGasLimit(gl uint64) *types.Transaction {
return types.NewTransaction(0 /*nonce*/, common.HexToAddress("0x0") /*to*/, nil /*amount*/, gl, nil /*gasPrice*/, nil /*data*/)
}

View File

@ -7,33 +7,33 @@ import (
)
var (
// ShardCount is the number of network shards
// ShardCount is the number of network shards.
ShardCount = int64(100)
// ShardingManagerAddress is the address of the sharding manager contract
// ShardingManagerAddress is the address of the sharding manager contract.
ShardingManagerAddress = common.HexToAddress("0x0") // TODO
// SigGasLimit for verifying signatures
// SigGasLimit for verifying signatures.
SigGasLimit = 40000
// PeriodLength is num of blocks in period
// PeriodLength is num of blocks in period.
PeriodLength = int64(5)
// LookaheadPeriods is the umber of periods ahead of current period
// which the contract is able to return the notary of that period
// LookaheadPeriods is the number of periods ahead of current period
// which the contract is able to return the notary of that period.
LookaheadPeriods = 4
// NotaryDeposit is a required deposit size in wei
// NotaryDeposit is a required deposit size in wei.
NotaryDeposit = new(big.Int).Exp(big.NewInt(10), big.NewInt(21), nil) // 1000 ETH
// ProposerDeposit is a required deposit size in wei
// ProposerDeposit is a required deposit size in wei.
ProposerDeposit = new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil) // 1 ETH
// MinProposerBalance of proposer where bids are deducted
// MinProposerBalance of proposer where bids are deducted.
MinProposerBalance = new(big.Int).Exp(big.NewInt(10), big.NewInt(17), nil) // 0.1 ETH
// ContractGasLimit to create contract
ContractGasLimit = uint64(4700000) // Max is 4712388
// NotaryLockupLength to lockup notary deposit from time of deregistration
// ContractGasLimit to create contract.
ContractGasLimit = uint64(4700000) // Max is 4712388.
// NotaryLockupLength to lockup notary deposit from time of deregistration.
NotaryLockupLength = int64(16128)
// ProposerLockupLength to lockup proposer deposit from time of deregistration
// ProposerLockupLength to lockup proposer deposit from time of deregistration.
ProposerLockupLength = int64(48)
// NotarySubsidy is ETH awarded to notary after collation included in canonical chain
NotarySubsidy = new(big.Int).Exp(big.NewInt(10), big.NewInt(15), nil) // 0.001 ETH
// NotaryCommitSize sampled per block from the notaries pool per period per shard
// NotarySubsidy is ETH awarded to notary after collation included in canonical chain.
NotarySubsidy = new(big.Int).Exp(big.NewInt(10), big.NewInt(15), nil) // 0.001 ETH.
// NotaryCommitSize sampled per block from the notaries pool per period per shard.
NotaryCommitSize = int64(135)
// NotaryQuorumSize votes the collation needs to get accepted to the canonical chain
// NotaryQuorumSize votes the collation needs to get accepted to the canonical chain.
NotaryQuorumSize = int64(90)
)

View File

@ -40,7 +40,7 @@ func deploySMCContract(backend *backends.SimulatedBackend, key *ecdsa.PrivateKey
return DeploySMC(transactOpts, backend)
}
// Test creating the SMC contract
// Test creating the SMC contract.
func TestContractCreation(t *testing.T) {
addr := crypto.PubkeyToAddress(mainKey.PublicKey)
backend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: accountBalance2000Eth}})
@ -58,7 +58,7 @@ func TestNotaryRegister(t *testing.T) {
var txOpts [notaryCount]*bind.TransactOpts
genesis := make(core.GenesisAlloc)
// initializes back end with 3 accounts and each with 2000 eth balances
// initializes back end with 3 accounts and each with 2000 eth balances.
for i := 0; i < notaryCount; i++ {
key, _ := crypto.GenerateKey()
notaryPoolPrivKeys[i] = key
@ -73,7 +73,7 @@ func TestNotaryRegister(t *testing.T) {
backend := backends.NewSimulatedBackend(genesis)
_, _, smc, _ := deploySMCContract(backend, notaryPoolPrivKeys[0])
// Notary 0 has not registered
// Notary 0 has not registered.
notary, err := smc.NotaryRegistry(&bind.CallOpts{}, notaryPoolAddr[0])
if err != nil {
t.Fatalf("Can't get notary registry info: %v", err)
@ -83,7 +83,7 @@ func TestNotaryRegister(t *testing.T) {
t.Errorf("Notary has not registered. Got deposited flag: %v", notary.Deposited)
}
// Test notary 0 has registered
// Test notary 0 has registered.
if _, err := smc.RegisterNotary(txOpts[0]); err != nil {
t.Fatalf("Registering notary has failed: %v", err)
}
@ -98,7 +98,7 @@ func TestNotaryRegister(t *testing.T) {
"Got - deposited:%v, index:%v, period:%v ", notary.Deposited, notary.PoolIndex, notary.DeregisteredPeriod)
}
// Test notary 1 and 2 have registered
// Test notary 1 and 2 have registered.
if _, err := smc.RegisterNotary(txOpts[1]); err != nil {
t.Fatalf("Registering notary has failed: %v", err)
}
@ -127,7 +127,7 @@ func TestNotaryRegister(t *testing.T) {
"Got - deposited:%v, index:%v, period:%v ", notary.Deposited, notary.PoolIndex, notary.DeregisteredPeriod)
}
// Check total numbers of notaries in pool
// Check total numbers of notaries in pool.
numNotaries, err := smc.NotaryPoolLength(&bind.CallOpts{})
if err != nil {
t.Fatalf("Failed to get notary pool length: %v", err)
@ -169,7 +169,7 @@ func TestNotaryDoubleRegisters(t *testing.T) {
txOpts.Value = notaryDeposit
_, _, smc, _ := deploySMCContract(backend, mainKey)
// Notary 0 registers
// Notary 0 registers.
smc.RegisterNotary(txOpts)
backend.Commit()
@ -184,7 +184,7 @@ func TestNotaryDoubleRegisters(t *testing.T) {
t.Errorf("Incorrect count from notary pool. Want: 1, Got: %v", numNotaries)
}
// Notary 0 registers again
// Notary 0 registers again.
_, err := smc.RegisterNotary(txOpts)
if err == nil {
t.Errorf("Notary register should have failed with double registers")
@ -202,7 +202,7 @@ func TestNotaryDeregister(t *testing.T) {
txOpts.Value = notaryDeposit
_, _, smc, _ := deploySMCContract(backend, mainKey)
// Notary 0 registers
// Notary 0 registers.
smc.RegisterNotary(txOpts)
backend.Commit()
@ -217,12 +217,12 @@ func TestNotaryDeregister(t *testing.T) {
t.Errorf("Incorrect count from notary pool. Want: 1, Got: %v", numNotaries)
}
// Fast forward 100 blocks to check notary's deregistered period field is set correctly
// Fast forward 100 blocks to check notary's deregistered period field is set correctly.
for i := 0; i < FastForward100Blocks; i++ {
backend.Commit()
}
// Notary 0 deregisters
// Notary 0 deregisters.
txOpts = bind.NewKeyedTransactor(mainKey)
_, err := smc.DeregisterNotary(txOpts)
if err != nil {
@ -230,7 +230,7 @@ func TestNotaryDeregister(t *testing.T) {
}
backend.Commit()
// Verify notary has saved the deregistered period as: current block number / period length
// Verify notary has saved the deregistered period as: current block number / period length.
notary, _ = smc.NotaryRegistry(&bind.CallOpts{}, addr)
currentPeriod := big.NewInt(int64(FastForward100Blocks) / sharding.PeriodLength)
if currentPeriod.Cmp(notary.DeregisteredPeriod) != 0 {
@ -250,7 +250,7 @@ func TestNotaryDeregisterThenRegister(t *testing.T) {
txOpts.Value = notaryDeposit
_, _, smc, _ := deploySMCContract(backend, mainKey)
// Notary 0 registers
// Notary 0 registers.
smc.RegisterNotary(txOpts)
backend.Commit()
@ -265,7 +265,7 @@ func TestNotaryDeregisterThenRegister(t *testing.T) {
t.Errorf("Incorrect count from notary pool. Want: 1, Got: %v", numNotaries)
}
// Notary 0 deregisters
// Notary 0 deregisters.
txOpts = bind.NewKeyedTransactor(mainKey)
_, err := smc.DeregisterNotary(txOpts)
if err != nil {
@ -278,7 +278,7 @@ func TestNotaryDeregisterThenRegister(t *testing.T) {
t.Errorf("Incorrect count from notary pool. Want: 0, Got: %v", numNotaries)
}
// Notary 0 re-registers again
// Notary 0 re-registers again.
smc.RegisterNotary(txOpts)
backend.Commit()
@ -295,7 +295,7 @@ func TestNotaryRelease(t *testing.T) {
txOpts.Value = notaryDeposit
_, _, smc, _ := deploySMCContract(backend, mainKey)
// Notary 0 registers
// Notary 0 registers.
smc.RegisterNotary(txOpts)
backend.Commit()
@ -310,12 +310,12 @@ func TestNotaryRelease(t *testing.T) {
t.Errorf("Incorrect count from notary pool. Want: 1, Got: %v", numNotaries)
}
// Fast forward to the next period to deregister
// Fast forward to the next period to deregister.
for i := 0; i < int(sharding.PeriodLength); i++ {
backend.Commit()
}
// Notary 0 deregisters
// Notary 0 deregisters.
txOpts = bind.NewKeyedTransactor(mainKey)
_, err := smc.DeregisterNotary(txOpts)
if err != nil {
@ -328,12 +328,12 @@ func TestNotaryRelease(t *testing.T) {
t.Errorf("Incorrect count from notary pool. Want: 0, Got: %v", numNotaries)
}
// Fast forward until lockup ends
// Fast forward until lockup ends.
for i := 0; i < int(sharding.NotaryLockupLength*sharding.PeriodLength+1); i++ {
backend.Commit()
}
// Notary 0 releases
// Notary 0 releases.
_, err = smc.ReleaseNotary(txOpts)
if err != nil {
t.Fatalf("Failed to release notary: %v", err)
@ -366,7 +366,7 @@ func TestNotaryInstantRelease(t *testing.T) {
txOpts.Value = notaryDeposit
_, _, smc, _ := deploySMCContract(backend, mainKey)
// Notary 0 registers
// Notary 0 registers.
smc.RegisterNotary(txOpts)
backend.Commit()
@ -381,12 +381,12 @@ func TestNotaryInstantRelease(t *testing.T) {
t.Errorf("Incorrect count from notary pool. Want: 1, Got: %v", numNotaries)
}
// Fast forward to the next period to deregister
// Fast forward to the next period to deregister.
for i := 0; i < int(sharding.PeriodLength); i++ {
backend.Commit()
}
// Notary 0 deregisters
// Notary 0 deregisters.
txOpts = bind.NewKeyedTransactor(mainKey)
_, err := smc.DeregisterNotary(txOpts)
if err != nil {
@ -399,7 +399,7 @@ func TestNotaryInstantRelease(t *testing.T) {
t.Fatalf("Incorrect count from notary pool. Want: 0, Got: %v", numNotaries)
}
// Notary 0 tries to release before lockup ends
// Notary 0 tries to release before lockup ends.
_, err = smc.ReleaseNotary(txOpts)
backend.Commit()
@ -426,7 +426,7 @@ func TestCommitteeListsAreDifferent(t *testing.T) {
var txOpts [notaryCount]*bind.TransactOpts
genesis := make(core.GenesisAlloc)
// initializes back end with 10000 accounts and each with 2000 eth balances
// initializes back end with 10000 accounts and each with 2000 eth balances.
for i := 0; i < notaryCount; i++ {
key, _ := crypto.GenerateKey()
notaryPoolPrivKeys[i] = key
@ -471,7 +471,7 @@ func TestGetCommitteeWithNonMember(t *testing.T) {
var txOpts [notaryCount]*bind.TransactOpts
genesis := make(core.GenesisAlloc)
// initialize back end with 11 accounts and each with 2000 eth balances
// initialize back end with 11 accounts and each with 2000 eth balances.
for i := 0; i < notaryCount; i++ {
key, _ := crypto.GenerateKey()
notaryPoolPrivKeys[i] = key
@ -486,7 +486,7 @@ func TestGetCommitteeWithNonMember(t *testing.T) {
backend := backends.NewSimulatedBackend(genesis)
_, _, smc, _ := deploySMCContract(backend, notaryPoolPrivKeys[0])
// register 10 notaries to SMC, leave 1 address free
// register 10 notaries to SMC, leave 1 address free.
for i := 0; i < 10; i++ {
smc.RegisterNotary(txOpts[i])
backend.Commit()
@ -497,7 +497,7 @@ func TestGetCommitteeWithNonMember(t *testing.T) {
t.Fatalf("Incorrect count from notary pool. Want: 135, Got: %v", numNotaries)
}
// verify the unregistered account is not in the notary pool list
// verify the unregistered account is not in the notary pool list.
for i := 0; i < 10; i++ {
addr, _ := smc.GetNotaryInCommittee(&bind.CallOpts{}, big.NewInt(0), big.NewInt(int64(i)))
if notaryPoolAddr[10].String() == addr.String() {
@ -514,7 +514,7 @@ func TestGetCommitteeAfterDeregisters(t *testing.T) {
var txOpts [notaryCount]*bind.TransactOpts
genesis := make(core.GenesisAlloc)
// initialize back end with 10 accounts and each with 2000 eth balances
// initialize back end with 10 accounts and each with 2000 eth balances.
for i := 0; i < notaryCount; i++ {
key, _ := crypto.GenerateKey()
notaryPoolPrivKeys[i] = key
@ -529,7 +529,7 @@ func TestGetCommitteeAfterDeregisters(t *testing.T) {
backend := backends.NewSimulatedBackend(genesis)
_, _, smc, _ := deploySMCContract(backend, notaryPoolPrivKeys[0])
// register 10 notaries to SMC
// register 10 notaries to SMC.
for i := 0; i < 10; i++ {
smc.RegisterNotary(txOpts[i])
backend.Commit()
@ -540,7 +540,7 @@ func TestGetCommitteeAfterDeregisters(t *testing.T) {
t.Errorf("Incorrect count from notary pool. Want: 10, Got: %v", numNotaries)
}
// deregister notary 0 from SMC
// deregister notary 0 from SMC.
txOpts[0].Value = big.NewInt(0)
smc.DeregisterNotary(txOpts[0])
backend.Commit()
@ -550,7 +550,7 @@ func TestGetCommitteeAfterDeregisters(t *testing.T) {
t.Errorf("Incorrect count from notary pool. Want: 9, Got: %v", numNotaries)
}
// verify degistered notary 0 is not in the notary pool list
// verify degistered notary 0 is not in the notary pool list.
for i := 0; i < 10; i++ {
addr, _ := smc.GetNotaryInCommittee(&bind.CallOpts{}, big.NewInt(0), big.NewInt(int64(i)))
if notaryPoolAddr[0].String() == addr.String() {

View File

@ -17,7 +17,7 @@ import (
// we are an eligible notary for collations. Then, it finds the pending tx's
// from the running geth node and sorts them by descending order of gas price,
// eliminates those that ask for too much gas, and routes them over
// to the SMC to create a collation
// to the SMC to create a collation.
func subscribeBlockHeaders(c client.Client) error {
headerChan := make(chan *types.Header, 16)
@ -29,12 +29,12 @@ func subscribeBlockHeaders(c client.Client) error {
log.Info("Listening for new headers...")
for {
// TODO: Error handling for getting disconnected from the client
// TODO: Error handling for getting disconnected from the client.
head := <-headerChan
// Query the current state to see if we are an eligible notary
// Query the current state to see if we are an eligible notary.
log.Info(fmt.Sprintf("Received new header: %v", head.Number.String()))
// Check if we are in the notary pool before checking if we are an eligible notary
// Check if we are in the notary pool before checking if we are an eligible notary.
v, err := isAccountInNotaryPool(c)
if err != nil {
return fmt.Errorf("unable to verify client in notary pool. %v", err)
@ -53,19 +53,19 @@ func subscribeBlockHeaders(c client.Client) error {
// checkSMCForNotary checks if we are an eligible notary for
// collation for the available shards in the SMC. The function calls
// getEligibleNotary from the SMC and notary a collation if
// conditions are met
// conditions are met.
func checkSMCForNotary(c client.Client, head *types.Header) error {
log.Info("Checking if we are an eligible collation notary for a shard...")
period := big.NewInt(0).Div(head.Number, big.NewInt(sharding.PeriodLength))
for s := int64(0); s < sharding.ShardCount; s++ {
// Checks if we are an eligible notary according to the SMC
// Checks if we are an eligible notary according to the SMC.
addr, err := c.SMCCaller().GetNotaryInCommittee(&bind.CallOpts{}, big.NewInt(s), period)
if err != nil {
return err
}
// If output is non-empty and the addr == coinbase
// If output is non-empty and the addr == coinbase.
if addr == c.Account().Address {
log.Info(fmt.Sprintf("Selected as notary on shard: %d", s))
err := submitCollation(s)
@ -73,7 +73,7 @@ func checkSMCForNotary(c client.Client, head *types.Header) error {
return err
}
// If the account is selected as collator, submit collation
// If the account is selected as collator, submit collation.
if addr == c.Account().Address {
log.Info(fmt.Sprintf("Selected as collator on shard: %d", s))
err := submitCollation(s)
@ -90,10 +90,10 @@ func checkSMCForNotary(c client.Client, head *types.Header) error {
// isAccountInNotaryPool checks if the client is in the notary pool because
// we can't guarantee our tx for deposit will be in the next block header we receive.
// The function calls IsNotaryDeposited from the SMC and returns true if
// the client is in the notary pool
// the client is in the notary pool.
func isAccountInNotaryPool(c client.Client) (bool, error) {
account := c.Account()
// Checks if our deposit has gone through according to the SMC
// Checks if our deposit has gone through according to the SMC.
n, err := c.SMCCaller().NotaryRegistry(&bind.CallOpts{}, account.Address)
if !n.Deposited && err != nil {
log.Warn(fmt.Sprintf("Account %s not in notary pool.", account.Address.String()))
@ -101,7 +101,7 @@ func isAccountInNotaryPool(c client.Client) (bool, error) {
return n.Deposited, err
}
// submitCollation interacts with the SMC directly to add a collation header
// submitCollation interacts with the SMC directly to add a collation header.
func submitCollation(shardID int64) error {
// TODO: Adds a collation header to the SMC with the following fields:
// [

View File

@ -6,9 +6,9 @@ import (
cli "gopkg.in/urfave/cli.v1"
)
// Notary runnable client
// Notary runnable client.
type Notary interface {
// Start the main routine for a notary
// Start the main routine for a notary.
Start() error
}
@ -16,14 +16,14 @@ type notary struct {
client client.Client
}
// NewNotary creates a new notary instance
// NewNotary creates a new notary instance.
func NewNotary(ctx *cli.Context) Notary {
return &notary{
client: client.NewClient(ctx),
}
}
// Start the main routine for a notary
// Start the main routine for a notary.
func (c *notary) Start() error {
log.Info("Starting notary client")
err := c.client.Start()

View File

@ -49,7 +49,7 @@ func (m *mockClient) CreateTXOpts(value *big.Int) (*bind.TransactOpts, error) {
return txOpts, nil
}
// Unused mockClient methods
// Unused mockClient methods.
func (m *mockClient) Start() error {
m.t.Fatal("Start called")
return nil
@ -58,7 +58,7 @@ func (m *mockClient) Close() {
m.t.Fatal("Close called")
}
// Helper/setup methods
// Helper/setup methods.
// TODO: consider moving these to common sharding testing package as the notary and smc tests
// use them.
func transactOpts() *bind.TransactOpts {
@ -75,7 +75,7 @@ func TestIsAccountInNotaryPool(t *testing.T) {
backend, smc := setup()
client := &mockClient{smc: smc, t: t}
// address should not be in pool initially
// address should not be in pool initially.
b, err := isAccountInNotaryPool(client)
if err != nil {
t.Fatal(err)
@ -85,7 +85,7 @@ func TestIsAccountInNotaryPool(t *testing.T) {
}
txOpts := transactOpts()
// deposit in notary pool, then it should return true
// deposit in notary pool, then it should return true.
txOpts.Value = sharding.NotaryDeposit
if _, err := smc.RegisterNotary(txOpts); err != nil {
t.Fatalf("Failed to deposit: %v", err)
@ -104,7 +104,7 @@ func TestJoinNotaryPool(t *testing.T) {
backend, smc := setup()
client := &mockClient{smc, t}
// There should be no notary initially
// There should be no notary initially.
numNotaries, err := smc.NotaryPoolLength(&bind.CallOpts{})
if err != nil {
t.Fatal(err)
@ -119,7 +119,7 @@ func TestJoinNotaryPool(t *testing.T) {
}
backend.Commit()
// Now there should be one notary
// Now there should be one notary.
numNotaries, err = smc.NotaryPoolLength(&bind.CallOpts{})
if err != nil {
t.Fatal(err)

View File

@ -7,7 +7,7 @@ import (
)
// Proposer holds functionality required to run a collation proposer
// in a sharded system
// in a sharded system.
type Proposer interface {
Start() error
}
@ -16,14 +16,14 @@ type proposer struct {
client client.Client
}
// NewProposer creates a struct instance
// NewProposer creates a struct instance.
func NewProposer(ctx *cli.Context) Proposer {
return &proposer{
client: client.NewClient(ctx),
}
}
// Start the main entry point for proposing collations
// Start the main entry point for proposing collations.
func (p *proposer) Start() error {
log.Info("Starting proposer client")
err := p.client.Start()
@ -32,7 +32,7 @@ func (p *proposer) Start() error {
}
defer p.client.Close()
// TODO: Propose collations
// TODO: Propose collations.
return nil
}