-
-
Notifications
You must be signed in to change notification settings - Fork 273
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor typescript setup. Add test for split and clamp. Added TravisCI, linting and README updates. Closes #34, Closes #30, Closes #29, Closes #24 - Add alias for unsupported math types
- Loading branch information
1 parent
f52953e
commit 0be2202
Showing
33 changed files
with
2,521 additions
and
1,117 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
language: node_js | ||
node_js: | ||
- "8.4.0" | ||
before_script: | ||
- cd beaconChain && npm install | ||
script: | ||
- npm test | ||
- npm run lint |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,30 @@ | ||
[![Build Status](https://travis-ci.org/ChainSafeSystems/lodestar_chain.svg?branch=master)](https://travis-ci.org/ChainSafeSystems/lodestar_chain) | ||
|
||
# Overview | ||
The goal of this repository is to provide an implementation of the beacon chain | ||
that was recently announced by the Ethereum Core dev team. As even the Ethereum Core dev team don't know how the finalized beacon chain | ||
The goal of this repository is to provide an implementation of the beacon chain. As even the Ethereum Core dev team don't know how the finalized beacon chain | ||
will be implemented, this is our contribution to the effort to transitioning Ethereum from a PoW blockchain to a PoS blockchain. | ||
|
||
This is currently a work in progress and you can ask questions and contribute in our [gitter](https://gitter.im/chainsafe/lodestar-chain). | ||
|
||
Our current file structure is: | ||
``` | ||
loadestar_chain/ | ||
-- core/ # Non-solidity components | ||
-- solidity/ # Contracts, truffle project | ||
-- beaconChain/ # Non-solidity components | ||
-- solidity/ # Contracts, truffle project | ||
``` | ||
|
||
## What you need | ||
You will need to go over the [specification](https://notes.ethereum.org/SCIg8AH5SA-O4C1G1LYZHQ?view#). You will also need to have a [basic understanding of sharding](https://github.com/ethereum/wiki/wiki/Sharding-FAQs). Note that that the specification is an ongoing document and will get outdated. The reference implementation by the Ethereum development team is written in Python and can be found [here](https://github.com/ethereum/beacon_chain). | ||
You will need to go over the [specification](https://github.com/ethereum/eth2.0-specs). You will also need to have a [basic understanding of sharding](https://github.com/ethereum/wiki/wiki/Sharding-FAQs). Note that that the specification is an ongoing document and will get outdated. The reference implementation by the Ethereum development team is written in Python and can be found [here](https://github.com/ethereum/beacon_chain). | ||
|
||
In order to run the code in this repository, you will first need to run `npm install` to install all dependencies. Then, to run the tests, you will need to run `npm test` | ||
## Run | ||
1. `cd beaconChain` | ||
2. `npm install` | ||
3. `npm test` | ||
|
||
## Note about tests | ||
For `solidity/` you will need to ensure that there is a terminal window with ganache-cli running to execute the tests. Ensure the dependencies are installed then run `truffle test`. | ||
|
||
For `core/` you can run `mocha tests/` after installing dependencies. | ||
For `solidity/` you will need to ensure that there is a terminal window with ganache-cli running to execute the tests. Ensure the dependencies are installed then run `truffle test`. | ||
|
||
Note: There is a WIP that will auto boot a ganache terminal when you run `npm test` :) | ||
For `beaconChain/` you can run `npm test` after installing dependencies. | ||
|
||
## Contributors | ||
If you would like to contribute, please submit an issue or talk to us on our [gitter](https://gitter.im/chainsafe/lodestar-chain). |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#constants | ||
// TODO Update TBD | ||
// TODO Explain what each constant does | ||
|
||
export default { | ||
BASE_REWARD_QUOTIENT: 32768, | ||
CYCLE_LENGTH: 64, | ||
DEPOSIT_SIZE: 32, | ||
GENESIS_TIME: "TBD", | ||
GWEI_PER_ETH: 1000000000, | ||
INITIAL_FORK_VERSION: 0, | ||
LOGOUT_MESSAGE: "LOGOUT", | ||
MAX_VALIDATOR_CHURN_QUOTIENT: 32, | ||
MIN_COMMITTEE_SIZE: 128, | ||
MIN_ONLINE_DEPOSIT_SIZE: 16, | ||
MIN_VALIDATOR_SET_CHANGE_INTERVAL: 256, | ||
RANDAO_SLOTS_PER_LAYER: 4096, | ||
SHARD_COUNT: 1024, | ||
SLOT_DURATION: 16, | ||
SQRT_E_DROP_TIME: 65536, | ||
WITHDRAWAL_PERIOD: 524288, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#constants | ||
|
||
enum ValidatorStatusCodes { | ||
PENDING_ACTIVATION = 0, | ||
ACTIVE = 1, | ||
PENDING_EXIT = 2, | ||
PENDING_WITHDRAW = 3, | ||
WITHDRAWN = 4, | ||
PENALIZED = 127, | ||
} | ||
|
||
enum SpecialRecordTypes { | ||
LOGOUT = 0, | ||
CASPER_SLASHING = 1, | ||
PROPOSER_SLASHING = 2, | ||
DEPOSIT_PROOF = 3, | ||
} | ||
|
||
enum ValidatorSetDeltaFlags { | ||
ENTRY = 0, | ||
EXIT = 1, | ||
} | ||
|
||
enum BLSDomains { | ||
DOMAIN_DEPOSIT = 0, | ||
DOMAIN_ATTESTATION = 1, | ||
DOMAIN_PROPOSAL = 2, | ||
DOMAIN_LOGOUT = 3, | ||
} | ||
|
||
export { | ||
ValidatorStatusCodes, | ||
SpecialRecordTypes, | ||
ValidatorSetDeltaFlags, | ||
BLSDomains, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
// Helper functions related to state transition functions | ||
import constants from "../constants/constants"; | ||
import { ValidatorStatusCodes } from "../constants/enums"; | ||
import {AttestationSignedData, BeaconBlock} from "../interfaces/blocks"; | ||
import {BeaconState, ShardAndCommittee, ValidatorRecord} from "../interfaces/state"; | ||
|
||
type int = number; | ||
type bytes = number; | ||
type uint24 = number; | ||
type hash32 = number; | ||
|
||
/** | ||
* The following is a function that gets active validator indices from the validator list. | ||
* @param {ValidatorRecord[]} validators | ||
* @returns {int[]} | ||
*/ | ||
// TODO .map may cause mutation issues, probably want to look into fixing this later on... | ||
function getActiveValidatorIndices(validators: ValidatorRecord[]): int[] { | ||
const result = validators.map((validator: ValidatorRecord, index: int) => { | ||
if (validator.status === ValidatorStatusCodes.ACTIVE) { | ||
return index; | ||
} | ||
}); | ||
return result; | ||
} | ||
|
||
/** | ||
* The following is a function that shuffles any list; we primarily use it for the validator list. | ||
* @param {T[]} values | ||
* @param {hash32} seed | ||
* @returns {T[]} Returns the shuffled values with seed as entropy. | ||
*/ | ||
// TODO finish this | ||
function shuffle<T>(values: T[], seed: hash32): T[] { | ||
const valuesCount: int = values.length; | ||
// Entropy is consumed from the seed in 3-byte (24 bit) chunks. | ||
const randBytes = 3; | ||
// Highest possible result of the RNG | ||
const randMax = 2 ** (randBytes * 8) - 1; | ||
|
||
// The range of the RNG places an upper-bound on the size of the list that may be shuffled. | ||
// It is a logic error to supply an oversized list. | ||
if (valuesCount < randMax) { throw new Error("Oversized list supplied to shuffle()!"); } | ||
|
||
// Make a copy of the values | ||
const output: T[] = values.slice(); | ||
const source = seed; // REALLY?? | ||
const index = 0; // REALLY?? | ||
while (index < valuesCount - 1) { | ||
// Re-hash the `source` to obtain a new pattern of bytes. | ||
// TODO figure out what this hash function is in python -> JS | ||
// let source = hash(source) | ||
|
||
// Iterate through the `source` bytes in 3-byte chunks. | ||
|
||
} | ||
|
||
return []; | ||
} | ||
|
||
/** | ||
* Splits a list into split_count pieces. | ||
* @param {T[]} values | ||
* @param {int} splitCount | ||
* @returns {T[]} | ||
*/ | ||
export function split<T>(values: T[], splitCount: int): T[][] { | ||
// Returns the split ``seq`` in ``split_count`` pieces in protocol. | ||
const listLength: int = values.length; | ||
const array: T[][] = []; | ||
for (let i: int = 0; i < splitCount; i++) { | ||
array.push(values.slice( | ||
Math.floor((listLength * i) / splitCount), Math.floor((listLength * (i + 1)) / splitCount), | ||
)); | ||
} | ||
return array; | ||
} | ||
|
||
/** | ||
* Helper function for readability. | ||
* @param {int} minval | ||
* @param {int} maxval | ||
* @param {int} x | ||
* @returns {int} | ||
*/ | ||
export function clamp(minval: int, maxval: int, x: int): int { | ||
if (x <= minval) { | ||
return minval; | ||
} else if (x >= maxval) { | ||
return maxval; | ||
} | ||
return x; | ||
} | ||
|
||
/** | ||
* Determines the shards and committee for a given beacon block. | ||
* Should not change unless the validator set changes. | ||
* @param {BeaconState} state | ||
* @param {int} slot | ||
* @returns {ShardAndCommittee[] | Error} | ||
*/ | ||
function getShardsAndCommitteesForSlot(state: BeaconState, slot: int): ShardAndCommittee[] { | ||
const earliestSlotInArray: int = state.lastStateRecalculationSlot - constants.CYCLE_LENGTH; | ||
// TODO Figure out why this is an error | ||
// TODO fix error with `<` | ||
// if (earliestSlotInArray <= slot < earliestSlotInArray + constants.CYCLE_LENGTH * 2) throw new Error(); | ||
return state.shardAndCommitteeForSlots[slot - earliestSlotInArray]; | ||
} | ||
|
||
/** | ||
* Retrieves hash for a given beacon block. | ||
* It should always return the block hash in the beacon chain slot for `slot`. | ||
* @param {BeaconState} state | ||
* @param {BeaconBlock} currentBlock | ||
* @param {int} slot | ||
* @returns {hash32} | ||
*/ | ||
function getBlockHash(state: BeaconState, currentBlock: BeaconBlock, slot: int): hash32 { | ||
const earliestSlotInArray = currentBlock.slot - state.recentBlockHashes.length; | ||
// TODO Figure out why this is an error | ||
// TODO fix error with `<` | ||
// if (earliestSlotInArray <= slot < currentBlock.slot) throw new Error(); | ||
return state.recentBlockHashes[slot - earliestSlotInArray]; | ||
} | ||
|
||
/** | ||
* Determines the proposer of a beacon block. | ||
* @param {BeaconState} state | ||
* @param {int} slot | ||
* @returns {int} | ||
*/ | ||
function getBeaconProposerIndex(state: BeaconState, slot: int): int { | ||
const firstCommittee = getShardsAndCommitteesForSlot(state, slot)[0].committee; | ||
return firstCommittee[slot % firstCommittee.length]; | ||
} | ||
|
||
// TODO finish | ||
function getAttestationParticipants(state: BeaconState, attestationData: AttestationSignedData, participationBitfield: bytes): int[] { | ||
const sncsForSlot: ShardAndCommittee[] = getShardsAndCommitteesForSlot(state, attestationData.slot); | ||
const snc: ShardAndCommittee = sncsForSlot.filter((x: ShardAndCommittee) => { | ||
if (x.shard === attestationData.shard) { return x; } | ||
})[0]; | ||
|
||
// TODO Figure out why this is an error | ||
// TODO implement error based on python pseudo code | ||
// TODO what is ceil_div8() | ||
// assert len(participation_bitfield) == ceil_div8(len(snc.committee)) | ||
|
||
const participants: int[] = snc.committee.filter((validator: uint24, index: int) => { | ||
const bit: int = (participationBitfield[Math.floor(index / 8)] >> (7 - (index % 8))) % 2; | ||
if (bit === 1) { | ||
return index; | ||
} | ||
}); | ||
return participants; | ||
} | ||
|
||
/** | ||
* Determine the balance of a validator. | ||
* Used for determining punishments and calculating stake. | ||
* @param {ValidatorRecord} validator | ||
* @returns {int} | ||
*/ | ||
// TODO Math.min requires int, validator.record is a uint64 | ||
function getEffectiveBalance(validator: ValidatorRecord): int { | ||
return Math.min(validator.balance, constants.DEPOSIT_SIZE); | ||
} | ||
|
||
// TODO figure out what bytes1() does in python | ||
// function getNewValidatorSetDeltaHashChain(currentValidator: hash32, index: int, pubkey: int, flag: int): hash32 { | ||
// return newValidatorSetDeltaHashChain = hash( | ||
// currentValidator + bytes1(flag) + bytes3(index) + bytes32(pubkey) | ||
// ) | ||
// } | ||
|
||
/** | ||
* Calculate the largest integer k such that k**2 <= n. | ||
* Used in reward/penalty calculations | ||
* @param {int} n | ||
* @returns {int} | ||
*/ | ||
// TODO Can use built in JS function if available | ||
export function intSqrt(n: int): int { | ||
let x: int = n; | ||
let y: int = Math.floor((x + 1) / 2); | ||
while (y < x) { | ||
x = y; | ||
y = Math.floor((x + Math.floor(n / x)) / 2); | ||
} | ||
return x; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// TODO replace uint, hash32, bytes | ||
|
||
// These interfaces relate to the data structures for beacon chain blocks | ||
|
||
type bytes = number; | ||
type uint64 = number; | ||
type uint384 = number; | ||
type hash32 = number; | ||
|
||
export interface BeaconBlock { | ||
// Slot number | ||
slot: uint64; | ||
// Proposer RANDAO reveal | ||
randaoReveal: hash32; | ||
// Recent PoW receipt root | ||
candidatePowReceiptRoot: hash32; | ||
// Skip list of previous beacon block hashes | ||
// i'th item is the most recent ancestor whose slot is a multiple of 2**i for i = 0, ..., 31 | ||
ancestorHashes: hash32[]; | ||
// State root | ||
stateRoot: hash32; | ||
// Attestations | ||
attestations: AttestationRecord[]; | ||
// Specials (e.g. logouts, penalties) | ||
specials: SpecialRecord[]; | ||
// Proposer signature | ||
proposerSignature: uint384[]; | ||
} | ||
|
||
export interface AttestationRecord { | ||
// Slot number | ||
slot: uint64; | ||
// Shard number | ||
shard: uint64; | ||
// Beacon block hashes not part of the current chain, oldest to newest | ||
obliqueParentHashes: hash32[]; | ||
// Shard block hash being attested to | ||
shardBlockHash: hash32; | ||
// Last crosslink hash | ||
lastCrosslinkHash: hash32; | ||
// Root of data between last hash and this one | ||
shardBlockCombinedDataRoot: hash32; | ||
// Attester participation bitfield (1 bit per attester) | ||
attesterBitfield: bytes; | ||
// Slot of last justified beacon block | ||
justifiedSlot: uint64; | ||
// Hash of last justified beacon block | ||
justifiedBlockHash: hash32; | ||
// BLS aggregate signature | ||
aggregateSig: uint384[]; | ||
} | ||
|
||
export interface ProposalSignedData { | ||
// Slot number | ||
slot: uint64; | ||
// Shard number (or `2**64 - 1` for beacon chain) | ||
shard: uint64; | ||
// Block hash | ||
blockHash: hash32; | ||
} | ||
|
||
export interface AttestationSignedData { | ||
// Slot number | ||
slot: uint64; | ||
// Shard number | ||
shard: uint64; | ||
// CYCLE_LENGTH parent hashes | ||
parentHashes: hash32[]; | ||
// Shard block hash | ||
shardBlockHash: hash32; | ||
// Last crosslink hash | ||
lastCrosslinkHash: hash32; | ||
// Root of data between last hash and this one | ||
shardBlockCombinedDataRoot: hash32; | ||
// Slot of last justified beacon block referenced in the attestation | ||
justifiedSlot: uint64; | ||
} | ||
|
||
export interface SpecialRecord { | ||
// Kind | ||
kind: uint64; | ||
// Data | ||
data: bytes; | ||
} |
Oops, something went wrong.