Skip to content

Commit

Permalink
Merge pull request #648 from ChainSafe/mpetrunic/aggregate-attestations
Browse files Browse the repository at this point in the history
Attestation collection
  • Loading branch information
mpetrunic committed Feb 24, 2020
2 parents f0adb47 + 1a09fad commit b812afa
Show file tree
Hide file tree
Showing 43 changed files with 787 additions and 178 deletions.
11 changes: 11 additions & 0 deletions packages/eth2.0-params/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.0] - unreleased
### Added
- `RANDOM_SUBNETS_PER_VALIDATOR` and `EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION` constant params
- TODO: check what else is added
2 changes: 2 additions & 0 deletions packages/eth2.0-params/src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export interface IBeaconParams {
MIN_GENESIS_TIME: number;
MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: number;
TARGET_AGGREGATORS_PER_COMMITTEE: number;
RANDOM_SUBNETS_PER_VALIDATOR: number;
EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION: number;

// Deposit contract
DEPOSIT_CONTRACT_ADDRESS: number;
Expand Down
3 changes: 3 additions & 0 deletions packages/eth2.0-params/src/presets/mainnet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ export const SHUFFLE_ROUND_COUNT = 90;
export const MIN_GENESIS_ACTIVE_VALIDATOR_COUNT = 2 ** 14;
export const MIN_GENESIS_TIME = 1578009600;
export const TARGET_AGGREGATORS_PER_COMMITTEE = 16;
export const RANDOM_SUBNETS_PER_VALIDATOR = 1;
export const EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION = 256;


// Deposit contract
export const DEPOSIT_CONTRACT_ADDRESS = 0;
Expand Down
2 changes: 2 additions & 0 deletions packages/eth2.0-params/src/presets/minimal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export const SHUFFLE_ROUND_COUNT = 10; // CUSTOMIZED
export const MIN_GENESIS_TIME = 1578009600;
export const MIN_GENESIS_ACTIVE_VALIDATOR_COUNT = 64;
export const TARGET_AGGREGATORS_PER_COMMITTEE = 16;
export const RANDOM_SUBNETS_PER_VALIDATOR = 1;
export const EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION = 256;

// Deposit contract
export const DEPOSIT_CONTRACT_ADDRESS = 0;
Expand Down
11 changes: 11 additions & 0 deletions packages/eth2.0-types/src/ssz/generators/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {IBeaconSSZTypes} from "../interface";
import {ContainerType} from "@chainsafe/ssz";

export const SubscribeToCommitteeSubnetPayload = (ssz: IBeaconSSZTypes): ContainerType => new ContainerType({
fields: {
slot: ssz.Slot,
slotSignature: ssz.BLSSignature,
committeeIndex: ssz.CommitteeIndex,
aggregatorPubkey: ssz.BLSPubkey
},
});
2 changes: 2 additions & 0 deletions packages/eth2.0-types/src/ssz/generators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import * as block from "./block";
import * as state from "./state";
import * as validator from "./validator";
import * as wire from "./wire";
import * as api from "./api";

import {IBeaconSSZTypes, typeNames} from "../interface";

Expand All @@ -20,6 +21,7 @@ const allGenerators = {
...state,
...validator,
...wire,
...api,
};

export function createIBeaconSSZTypes(params: IBeaconParams): IBeaconSSZTypes {
Expand Down
2 changes: 2 additions & 0 deletions packages/eth2.0-types/src/ssz/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ export interface IBeaconSSZTypes {
BeaconBlocksByRangeResponse: ContainerType<t.BeaconBlocksByRangeResponse>;
BeaconBlocksByRootRequest: ContainerType<t.BeaconBlocksByRootRequest>;
BeaconBlocksByRootResponse: ContainerType<t.BeaconBlocksByRootResponse>;
//api
SubscribeToCommitteeSubnetPayload: ContainerType<t.SubscribeToCommitteeSubnetPayload>;
}

export const typeNames: (keyof IBeaconSSZTypes)[] = [
Expand Down
9 changes: 9 additions & 0 deletions packages/eth2.0-types/src/types/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/* eslint-disable @typescript-eslint/interface-name-prefix */
import {BLSPubkey, BLSSignature, CommitteeIndex, Slot} from "./primitive";

export interface SubscribeToCommitteeSubnetPayload {
slot: Slot;
slotSignature: BLSSignature;
committeeIndex: CommitteeIndex;
aggregatorPubkey: BLSPubkey;
}
1 change: 1 addition & 0 deletions packages/eth2.0-types/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export * from "./block";
export * from "./state";
export * from "./validator";
export * from "./wire";
export * from "./api";
15 changes: 15 additions & 0 deletions packages/eth2.0-utils/src/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,18 @@ export function bigIntSqrt(n: bigint): bigint {
}
return x;
}

/**
* Regenerates a random integer between min (included) and max (excluded).
*/
export function randBetween(min: number, max: number): number {
return Math.floor(Math.random() * (max - min)) + min;
}

/**
* Wraps randBetween and returns a bigNumber.
* @returns {bigint}
*/
export function randBetweenBigInt(min: number, max: number): bigint {
return BigInt(randBetween(min, max));
}
2 changes: 1 addition & 1 deletion packages/lodestar-cli/test/e2e/commands/dev.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ describe("e2e interop simulation", function() {
const libp2p = await createNodeJsLibp2p(peerId, {});
node = new BeaconNode(conf, {config: minimalConfig, logger, eth1: new InteropEth1Notifier(), libp2p});

const genesisTime = Math.round(Date.now()/1000);
const genesisTime = Math.floor(Date.now()/1000);
const depositDataRootList = minimalConfig.types.DepositDataRootList.tree.defaultValue();
const state = quickStartState(minimalConfig, depositDataRootList, genesisTime, VALIDATOR_COUNT);
await node.chain.initializeBeaconChain(state, depositDataRootList);
Expand Down
2 changes: 1 addition & 1 deletion packages/lodestar-validator/src/api/abstract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export abstract class AbstractApiClient
private async startSlotCounting(): Promise<void> {
this.running = true;
const genesisTime = await this.beacon.getGenesisTime();
const diffInSeconds = (Date.now() / 1000) - genesisTime;
const diffInSeconds = (Math.floor(Date.now() / 1000)) - genesisTime;
this.currentSlot = getCurrentSlot(this.config, genesisTime);
//update slot after remaining seconds until next slot
const diffTillNextSlot =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class RestValidatorApi implements IValidatorApi {
const hexPubKeys = validatorPubKeys.map(toHexString);
const url = `/duties/${epoch.toString()}/attester?validator_pubkeys=${JSON.stringify(hexPubKeys)}`;
const responseData = await this.client.get<Json[]>(url);
return responseData.map(value =>this.config.types.ValidatorDuty.fromJson(value));
return responseData.map(value => this.config.types.ValidatorDuty.fromJson(value));
}

public async publishAggregatedAttestation(
Expand Down Expand Up @@ -90,4 +90,21 @@ export class RestValidatorApi implements IValidatorApi {
`/duties/${slot}/aggregator?committee_index=${committeeIndex}&slot_signature=${toHexString(slotSignature)}`
);
}

public async subscribeCommitteeSubnet(
slot: Slot,
slotSignature: BLSSignature,
committeeIndex: CommitteeIndex,
aggregatorPubkey: BLSPubkey
): Promise<void> {
return this.client.post(
"/beacon_committee_subscription",
this.config.types.SubscribeToCommitteeSubnetPayload.toJson({
slot,
slotSignature,
committeeIndex,
aggregatorPubkey
})
);
}
}
4 changes: 4 additions & 0 deletions packages/lodestar-validator/src/api/interface/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,8 @@ export interface IValidatorApi {
): Promise<void>;

getWireAttestations(epoch: Epoch, committeeIndex: CommitteeIndex): Promise<Attestation[]>;

subscribeCommitteeSubnet(
slot: Slot, slotSignature: BLSSignature, committeeIndex: CommitteeIndex, aggregatorPubkey: BLSPubkey
): Promise<void>;
}
10 changes: 8 additions & 2 deletions packages/lodestar-validator/src/services/attestation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,11 @@ export class AttestationService {
) {
const duty = attesterDuties[0];
const fork = (await this.provider.beacon.getFork()).fork;
const slotSignature = this.getSlotSignature(duty.attestationSlot, fork);
const isAggregator = await this.provider.validator.isAggregator(
duty.attestationSlot,
duty.committeeIndex,
this.getSlotSignature(duty.attestationSlot, fork)
slotSignature
);
this.nextAttesterDuties.set(
duty.attestationSlot,
Expand All @@ -77,7 +78,12 @@ export class AttestationService {
isAggregator
});
if(isAggregator) {
//TODO: subscribe to committee subnet
await this.provider.validator.subscribeCommitteeSubnet(
duty.attestationSlot,
slotSignature,
duty.committeeIndex,
this.publicKey
);
}
}
};
Expand Down
6 changes: 6 additions & 0 deletions packages/lodestar-validator/test/utils/mocks/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,10 @@ export class MockValidatorApi implements IValidatorApi {
publishBlock(beaconBlock: SignedBeaconBlock): Promise<void> {
return undefined;
}

subscribeCommitteeSubnet(
slot: number, slotSignature: Buffer, committeeIndex: number, aggregatorPubkey: Buffer
): Promise<void> {
return undefined;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export async function getEpochProposers(
): Promise<Map<Slot, BLSPubkey>> {
const block = await db.block.get(chain.forkChoice.head());
const state = await db.state.get(block.message.stateRoot.valueOf() as Uint8Array);
assert(epoch <= computeEpochAtSlot(config, state.slot) + 2);
assert(epoch >= 0 && epoch <= computeEpochAtSlot(config, state.slot) + 2);
const startSlot = computeStartSlotAtEpoch(config, epoch);
if(state.slot < startSlot) {
processSlots(config, state, startSlot);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {IFastifyServer} from "../../index";
import fastify, {DefaultBody, DefaultHeaders, DefaultParams, DefaultQuery} from "fastify";
import {IApiModules} from "../../../interface";
import {verify} from "@chainsafe/bls";
import {hexToBuffer} from "../../../../util/hex";
import {computeEpochAtSlot, getDomain} from "@chainsafe/eth2.0-state-transition";
import {DomainType} from "../../../../constants";


const opts: fastify.RouteShorthandOptions = {
schema: {
body: {
type: "object",
requiredKeys: ["committee_index", "slot", "slot_signature", "aggregator_pubkey"],
"committee_index": {
type: "string"
},
"slot": {
type: "string"
},
"slot_signature": {
type: "string"
},
"aggregator_pubkey": {
type: "string"
},
},
}
};

interface IBody extends DefaultBody {
committee_index: string;
slot: string;
slot_signature: string;
aggregator_pubkey: string;
}

export const registerSubscribeToCommitteeSubnet = (fastify: IFastifyServer, modules: IApiModules): void => {
fastify.post<DefaultQuery, DefaultParams, DefaultHeaders, IBody>(
"/beacon_committee_subscription",
opts,
async (request, reply) => {
const slot = Number(request.body.slot);
const valid = verify(
hexToBuffer(request.body.aggregator_pubkey),
modules.config.types.Slot.hashTreeRoot(slot),
hexToBuffer(request.body.slot_signature),
getDomain(
modules.config,
modules.chain.latestState,
DomainType.BEACON_ATTESTER,
computeEpochAtSlot(modules.config, slot))
);
if(!valid) {
reply
.code(403)
.send();
return;
}
modules.sync.regularSync.collectAttestations(
slot,
Number(request.body.committee_index)
);
reply
.code(200)
.type("application/json")
.send();
}
);
};
5 changes: 5 additions & 0 deletions packages/lodestar/src/api/rpc/api/validator/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,9 @@ export interface IValidatorApi extends IApi {
): Promise<void>;

getWireAttestations(epoch: Epoch, committeeIndex: CommitteeIndex): Promise<Attestation[]>;

subscribeCommitteeSubnet(
slot: Slot, slotSignature: BLSSignature, committeeIndex: CommitteeIndex, aggregatorPubkey: BLSPubkey
): Promise<void>;

}
38 changes: 33 additions & 5 deletions packages/lodestar/src/api/rpc/api/validator/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import {
Bytes96,
CommitteeIndex,
Epoch,
SignedBeaconBlock,
Slot,
ValidatorDuty,
SignedBeaconBlock
ValidatorDuty
} from "@chainsafe/eth2.0-types";
import {IBeaconConfig} from "@chainsafe/eth2.0-config";

Expand All @@ -26,9 +26,13 @@ import {IEth1Notifier} from "../../../../eth1";
import {getAttesterDuties, getEpochProposers, produceAttestation, publishAttestation} from "../../../impl/validator";
import {ApiNamespace, IApiModules} from "../../../index";
import {IApiOptions} from "../../../options";
import {ILogger} from "@chainsafe/eth2.0-utils/lib/logger";
import {ILogger} from "@chainsafe/eth2.0-utils/lib/logger";
import {INetwork} from "../../../../network";
import {isAggregator} from "@chainsafe/eth2.0-state-transition";
import {getDomain, isAggregator} from "@chainsafe/eth2.0-state-transition";
import {verify} from "@chainsafe/bls";
import {Sync} from "../../../../sync";
import {DomainType} from "../../../../constants";
import {computeEpochAtSlot} from "@chainsafe/eth2.0-state-transition/lib";

export class ValidatorApi implements IValidatorApi {

Expand All @@ -38,19 +42,21 @@ export class ValidatorApi implements IValidatorApi {
private chain: IBeaconChain;
private db: IBeaconDb;
private network: INetwork;
private sync: Sync;
private opPool: OpPool;
private eth1: IEth1Notifier;
private logger: ILogger;

public constructor(
opts: Partial<IApiOptions>,
modules: Pick<IApiModules, "config"|"chain"|"db"|"opPool"|"eth1"|"network"|"logger">
modules: Pick<IApiModules, "config"|"chain"|"db"|"opPool"|"eth1"|"sync"|"network"|"logger">
) {
this.namespace = ApiNamespace.VALIDATOR;
this.config = modules.config;
this.chain = modules.chain;
this.db = modules.db;
this.network = modules.network;
this.sync = modules.sync;
this.logger = modules.logger;
this.opPool = modules.opPool;
this.eth1 = modules.eth1;
Expand Down Expand Up @@ -118,4 +124,26 @@ export class ValidatorApi implements IValidatorApi {
return isAggregator(this.config, await this.db.state.getLatest(), slot, committeeIndex, slotSignature);
}

public async subscribeCommitteeSubnet(
slot: Slot, slotSignature: BLSSignature, committeeIndex: CommitteeIndex, aggregatorPubkey: BLSPubkey
): Promise<void> {
const valid = verify(
aggregatorPubkey.valueOf() as Uint8Array,
this.config.types.Slot.hashTreeRoot(slot),
slotSignature.valueOf() as Uint8Array,
getDomain(
this.config,
this.chain.latestState,
DomainType.BEACON_ATTESTER,
computeEpochAtSlot(this.config, slot))
);
if(!valid) {
throw new Error("Ivalid slot signature");
}
this.sync.regularSync.collectAttestations(
slot,
committeeIndex
);
}

}
Loading

0 comments on commit b812afa

Please sign in to comment.