Skip to content

Commit

Permalink
Merge pull request #185 from ChainSafe/greg/validator/rpc
Browse files Browse the repository at this point in the history
Greg/validator/rpc
  • Loading branch information
mpetrunic authored May 5, 2019
2 parents 6c95988 + 15ee2f3 commit 4a3d6fc
Show file tree
Hide file tree
Showing 20 changed files with 459 additions and 312 deletions.
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"rules": {
"@typescript-eslint/indent": ["error", 2],
"@typescript-eslint/no-require-imports": "error",
"@typescript-eslint/interface-name-prefix": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-unused-vars": ["warn", {
"varsIgnorePattern": "^_"
Expand Down
4 changes: 3 additions & 1 deletion src/chain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import assert from "assert";
import {EventEmitter} from "events";
import {hashTreeRoot} from "@chainsafe/ssz";

import {BeaconBlock, BeaconState, Deposit, Eth1Data, number64} from "../types";
import {BeaconBlock, BeaconState, bytes48, Deposit, Epoch, Eth1Data, number64, Slot, ValidatorIndex} from "../types";
import {GENESIS_SLOT, SECONDS_PER_SLOT} from "../constants";

import {DB} from "../db";
Expand All @@ -20,6 +20,7 @@ import {getBlockRoot, getEpochStartSlot} from "./stateTransition/util";
*/
export class BeaconChain extends EventEmitter {
public chain: string;
public genesisTime: number64;
private db: DB;
private eth1: Eth1Notifier;
private _latestBlock: BeaconBlock;
Expand Down Expand Up @@ -57,6 +58,7 @@ export class BeaconChain extends EventEmitter {
const genesisState = getGenesisBeaconState(genesisDeposits, genesisTime, genesisEth1Data);
const genesisBlock = getEmptyBlock();
genesisBlock.stateRoot = hashTreeRoot(genesisState, BeaconState);
this.genesisTime = genesisTime;
await this.db.setBlock(genesisBlock);
await this.db.setChainHead(genesisState, genesisBlock);
await this.db.setJustifiedBlock(genesisBlock);
Expand Down
62 changes: 61 additions & 1 deletion src/chain/stateTransition/util/validator.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import assert from "assert";
import {
BeaconState,
Epoch,
Epoch, Slot,
Validator,
ValidatorIndex,
} from "../../../types";
import {getBeaconProposerIndex, getCrosslinkCommitteesAtSlot, getPreviousEpoch, slotToEpoch} from "./index";
import {CommitteeAssignment} from "../../../validator/types";
import {getCurrentEpoch, getEpochStartSlot} from "./epoch";
import {SLOTS_PER_EPOCH} from "../../../constants";


/**
Expand Down Expand Up @@ -44,3 +49,58 @@ export function getActiveValidatorIndices(state: BeaconState, epoch: Epoch): Val
return indices;
}, []);
}

/**
* Return the committee assignment in the ``epoch`` for ``validator_index`` and ``registry_change``.
* ``assignment`` returned is a tuple of the following form:
* ``assignment[0]`` is the list of validators in the committee
* ``assignment[1]`` is the shard to which the committee is assigned
* ``assignment[2]`` is the slot at which the committee is assigned
* a beacon block at the assigned slot.
* @param {BeaconState} state
* @param {Epoch} epoch
* @param {ValidatorIndex} validatorIndex
* @param {boolean} registryChange
* @returns {{validators: ValidatorIndex[]; shard: Shard; slot: number; isProposer: boolean}}
*/
export function getCommitteeAssignment(
state: BeaconState,
epoch: Epoch,
validatorIndex: ValidatorIndex): CommitteeAssignment {

const previousEpoch = getPreviousEpoch(state);
const nextEpoch = getCurrentEpoch(state) + 1;
assert(previousEpoch <= epoch && epoch <= nextEpoch);

const epochStartSlot = getEpochStartSlot(epoch);
const loopEnd = epochStartSlot + SLOTS_PER_EPOCH;

for (let slot = epochStartSlot; slot < loopEnd; slot++) {
const crosslinkCommittees = getCrosslinkCommitteesAtSlot(state, slot);
const selectedCommittees = crosslinkCommittees.map((committee) => committee[0].includes(validatorIndex));

if (selectedCommittees.length > 0) {
const validators = selectedCommittees[0][0];
const shard = selectedCommittees[0][1];
return {validators, shard, slot};
}
}
}

/**
* Checks if a validator is supposed to propose a block
* @param {BeaconState} state
* @param {Slot} slot
* @param {ValidatorIndex} validatorIndex
* @returns {Boolean}
*/
export function isProposerAtSlot(
state: BeaconState,
slot: Slot,
validatorIndex: ValidatorIndex): boolean {

const currentEpoch = getCurrentEpoch(state);
assert(slotToEpoch(slot) === currentEpoch);

return getBeaconProposerIndex(state) === validatorIndex;
}
4 changes: 2 additions & 2 deletions src/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {BeaconChain} from "../chain";
import {OpPool} from "../opPool";
import {JSONRPC} from "../rpc/protocol";
import {WSServer} from "../rpc/transport";
import {BeaconAPI} from "../rpc/api";
import {ValidatorApi} from "../rpc/api";

export interface Service {
start(): Promise<void>;
Expand Down Expand Up @@ -72,7 +72,7 @@ class BeaconNode {
});
this.rpc = new JSONRPC(this.conf.rpc, {
transport: new WSServer(this.conf.rpc),
api: new BeaconAPI(this.conf.rpc, {
api: new ValidatorApi(this.conf.rpc, {
chain: this.chain,
db: this.db,
opPool: this.opPool,
Expand Down
52 changes: 0 additions & 52 deletions src/rpc/api/api.ts

This file was deleted.

4 changes: 1 addition & 3 deletions src/rpc/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
export * from "./interface";
export * from "./mock";
export * from "./api";
export * from "./validator";
47 changes: 4 additions & 43 deletions src/rpc/api/interface.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,7 @@
import {Attestation, AttestationData, BeaconBlock, bytes32, Deposit, Shard, Slot, Eth1Data} from "../../types";

/**
* The API interface defines the calls that can be made externally
*/
export interface API {
/**
* Return the current chain head
*/
getChainHead(): Promise<BeaconBlock>;

/**
* Return a list of attestations ready for inclusion in the next block
*/
getPendingAttestations(): Promise<Attestation[]>;

/**
* Return a list of deposits ready for inclusion in the next block
*/
getPendingDeposits(): Promise<Deposit[]>;

export interface IApi {
/**
* Return the Eth1Data to be included in the next block
* Name space for API commands
*/
getEth1Data(): Promise<Eth1Data>;

/**
* Return the state root after the block has been run through the state transition
*/
computeStateRoot(block: BeaconBlock): Promise<bytes32>;

/**
* Return the attestation data for a slot and shard based on the current head
*/
getAttestationData(slot: Slot, shard: Shard): Promise<AttestationData>;

/**
* Submit an attestation for processing
*/
putAttestation(attestation: Attestation): Promise<void>;

/**
* Submit a block for processing
*/
putBlock(block: BeaconBlock): Promise<void>;
namespace: string;
}

55 changes: 0 additions & 55 deletions src/rpc/api/mock.ts

This file was deleted.

7 changes: 7 additions & 0 deletions src/rpc/api/validator/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {ValidatorApi} from "./validator";
import {IValidatorApi} from "./interface";

export {
ValidatorApi,
IValidatorApi
};
88 changes: 88 additions & 0 deletions src/rpc/api/validator/interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* The API interface defines the calls that can be made from a Validator
*/
import {
Attestation, AttestationData,
BeaconBlock, bytes, bytes32, bytes48, Epoch, Fork, IndexedAttestation, number64, Shard, Slot, SyncingStatus, uint64,
ValidatorDuty, ValidatorIndex
} from "../../../types/index";
import {IApi} from "../interface";
import {CommitteeAssignment} from "../../../validator/types";

export interface IValidatorApi extends IApi {
/**
* Requests that the BeaconNode identify information about its implementation in a format similar to a HTTP User-Agent field.
* @returns {Promise<bytes32>} An ASCII-encoded hex string which uniquely defines the implementation of the BeaconNode and its current software version.
*/
getClientVersion(): Promise<bytes32>;

/**
* Requests the BeaconNode to provide which fork version it is currently on.
* @returns {Promise<{fork: Fork; chainId: uint64}>}
*/
getFork(): Promise<Fork>;

/**
* Requests the genesis_time parameter from the BeaconNode, which should be consistent across all BeaconNodes that follow the same beacon chain.
* @returns {Promise<uint64>} The genesis_time, which is a fairly static configuration option for the BeaconNode.
*/
getGenesisTime(): Promise<number64>;

/**
* Requests the BeaconNode to describe if it's currently syncing or not, and if it is, what block it is up to. This is modelled after the Eth1.0 JSON-RPC eth_syncing call.
* @returns {Promise<boolean | SyncingStatus>} Either false if the node is not syncing, or a SyncingStatus object if it is.
*/
getSyncingStatus(): Promise<boolean | SyncingStatus>;

/**
* Requests the BeaconNode to provide a set of “duties”, which are actions that should be performed by ValidatorClients. This API call should be polled at every slot, to ensure that any chain reorganisations are catered for, and to ensure that the currently connected BeaconNode is properly synchronised.
* @param {bytes48[]} validatorPubkey
* @returns {Promise<{currentVersion: bytes4; validatorDuty: ValidatorDuty}>} A list of unique validator public keys, where each item is a 0x encoded hex string.
*/
getDuties(validatorPubkey: bytes48): Promise<{currentVersion: Fork; validatorDuty: ValidatorDuty}>;

/**
* Requests to check if a validator should propose for a given slot.
* @param {bytes48} validatorPubkey
* @param {Slot} slot
* @returns {Promise<{slot: Slot, proposer: boolean}}
*/
isProposer(index: ValidatorIndex, slot: Slot): Promise<boolean>;

/**
* Requests a validators committeeAssignment, can be used for past, current and one epoch in the future
* @param {ValidatorIndex} index
* @param {Epoch} epoch
*/
getCommitteeAssignment(index: ValidatorIndex, epoch: Epoch): Promise<CommitteeAssignment>;

/**
* Requests a BeaconNode to produce a valid block, which can then be signed by a ValidatorClient.
* @param {Slot} slot
* @param {bytes} randaoReveal
* @returns {Promise<BeaconBlock>} A proposed BeaconBlock object, but with the signature field left blank.
*/
produceBlock(slot: Slot, randaoReveal: bytes): Promise<BeaconBlock>;

/**
* Requests that the BeaconNode produce an IndexedAttestation, with a blank signature field, which the ValidatorClient will then sign.
* @param {Slot} slot
* @param {Shard} shard
* @returns {Promise<Attestation>}
*/
produceAttestation(slot: Slot, shard: Shard): Promise<AttestationData>;

/**
* Instructs the BeaconNode to publish a newly signed beacon block to the beacon network, to be included in the beacon chain.
* @param {BeaconBlock} beaconBlock
* @returns {Promise<void>}
*/
publishBlock(beaconBlock: BeaconBlock): Promise<void>;

/**
* Instructs the BeaconNode to publish a newly signed IndexedAttestation object, to be incorporated into the beacon chain.
* @param {Attestation} attestation
* @returns {Promise<void>}
*/
publishAttestation(attestation: Attestation): Promise<void>;
}
Loading

0 comments on commit 4a3d6fc

Please sign in to comment.