Skip to content

Commit

Permalink
Greg/typescript conversion (#39)
Browse files Browse the repository at this point in the history
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
GregTheGreek authored and ansermino committed Dec 4, 2018
1 parent f52953e commit 0be2202
Show file tree
Hide file tree
Showing 33 changed files with 2,521 additions and 1,117 deletions.
8 changes: 8 additions & 0 deletions .travis.yml
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
22 changes: 12 additions & 10 deletions README.md
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.
22 changes: 22 additions & 0 deletions beaconChain/constants/constants.ts
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,
};
36 changes: 36 additions & 0 deletions beaconChain/constants/enums.ts
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,
};
192 changes: 192 additions & 0 deletions beaconChain/helpers/stateTransitionHelpers.ts
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;
}

84 changes: 84 additions & 0 deletions beaconChain/interfaces/blocks.ts
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;
}
Loading

0 comments on commit 0be2202

Please sign in to comment.