Skip to content

Commit

Permalink
Handle block forks
Browse files Browse the repository at this point in the history
  • Loading branch information
Tuyen committed Dec 13, 2019
1 parent dbfd93b commit b09cc25
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 31 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {assembleAttestation} from "../../../chain/factory/attestation";
import {Attestation, BeaconState, BLSPubkey, CommitteeIndex, Slot} from "@chainsafe/eth2.0-types";
import {Attestation, BLSPubkey, CommitteeIndex, Slot} from "@chainsafe/eth2.0-types";
import {IBeaconDb} from "../../../db/api";
import {IBeaconChain} from "../../../chain";
import {IBeaconConfig} from "@chainsafe/eth2.0-config";
import {clone} from "@chainsafe/ssz";
import { processSlots } from "@chainsafe/eth2.0-state-transition";

export async function produceAttestation(
{config, db, chain}: {config: IBeaconConfig; db: IBeaconDb; chain: IBeaconChain},
Expand All @@ -12,11 +12,12 @@ export async function produceAttestation(
slot: Slot
): Promise<Attestation|null> {
try {
const [headState, headBlock, validatorIndex] = await Promise.all([
clone(chain.latestState, config.types.BeaconState) as BeaconState,
const [headBlock, validatorIndex] = await Promise.all([
db.block.get(chain.forkChoice.head()),
db.getValidatorIndex(validatorPubKey)
]);
const headState = await db.state.get(headBlock.stateRoot);
await processSlots(config, headState, slot);
return await assembleAttestation({config, db}, headState, headBlock, validatorIndex, index, slot);
} catch (e) {
throw e;
Expand Down
42 changes: 16 additions & 26 deletions packages/lodestar/src/chain/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,18 +117,17 @@ export class BeaconChain extends (EventEmitter as { new(): ChainEventEmitter })
public async receiveAttestation(attestation: Attestation): Promise<void> {
const attestationHash = hashTreeRoot(attestation, this.config.types.Attestation);
this.logger.info(`Received attestation ${attestationHash.toString("hex")}`);
const latestState = this.latestState;
try {
const attestationSlot: Slot = attestation.data.slot;
if(attestationSlot + this.config.params.SLOTS_PER_EPOCH < latestState.slot) {
if(attestationSlot + this.config.params.SLOTS_PER_EPOCH < this.latestState.slot) {
this.logger.verbose(`Attestation ${attestationHash.toString("hex")} is too old. Ignored.`);
return;
}
} catch (e) {
return;
}
this.attestationProcessingQueue.push(async () => {
return this.processAttestation(latestState, attestation, attestationHash);
return this.processAttestation(attestation, attestationHash);
});
}

Expand All @@ -141,6 +140,8 @@ export class BeaconChain extends (EventEmitter as { new(): ChainEventEmitter })

if(!await this.db.block.has(block.parentRoot)) {
this.emit("unknownBlockRoot", block.parentRoot);
this.blockProcessingQueue.add(block);
return;
}

if(block.slot <= this.latestState.slot) {
Expand All @@ -151,25 +152,11 @@ export class BeaconChain extends (EventEmitter as { new(): ChainEventEmitter })
return;
}

if(block.slot > this.latestState.slot) {
//either block came too early or we are suppose to skip some slots
const latestBlock = await this.db.block.getChainHead();
if(!block.parentRoot.equals(signingRoot(latestBlock, this.config.types.BeaconBlock))){
//block processed too early
this.logger.warn(`Block ${blockHash.toString("hex")} tried to be processed too early. Requeue...`);
//wait a bit to give new block a chance
await sleep(500);
// add to priority queue
this.blockProcessingQueue.add(block);
return;
}
}

await this.processBlock(block, blockHash);
const nextBlockInQueue = this.blockProcessingQueue.peek();
while (nextBlockInQueue) {
const latestBlock = await this.db.block.getChainHead();
if (nextBlockInQueue.parentRoot.equals(signingRoot(latestBlock, this.config.types.BeaconBlock))) {
if (await this.db.block.has(nextBlockInQueue.parentRoot)) {
await this.processBlock(nextBlockInQueue, signingRoot(nextBlockInQueue, this.config.types.BeaconBlock));
this.blockProcessingQueue.poll();
} else{
Expand Down Expand Up @@ -242,16 +229,19 @@ export class BeaconChain extends (EventEmitter as { new(): ChainEventEmitter })
return Math.floor(Date.now() / 1000) >= stateSlotTime;
}

private processAttestation = async (latestState: BeaconState, attestation: Attestation, attestationHash: Hash) => {
const currentSlot = getCurrentSlot(this.config, latestState.genesisTime);
private processAttestation = async (attestation: Attestation, attestationHash: Hash) => {
const justifiedCheckpoint = this.forkChoice.getJustified();
const justifiedBlock = await this.db.block.get(justifiedCheckpoint.root);
const checkpointState = await this.db.state.get(justifiedBlock.stateRoot);
const currentSlot = getCurrentSlot(this.config, checkpointState.genesisTime);
const currentEpoch = computeEpochAtSlot(this.config, currentSlot);
const previousEpoch = currentEpoch > GENESIS_EPOCH ? currentEpoch - 1 : GENESIS_EPOCH;
const epoch = attestation.data.target.epoch;
assert([currentEpoch, previousEpoch].includes(epoch));

const validators = getAttestingIndices(
this.config, latestState, attestation.data, attestation.aggregationBits);
const balances = validators.map((index) => latestState.balances[index]);
this.config, checkpointState, attestation.data, attestation.aggregationBits);
const balances = validators.map((index) => checkpointState.balances[index]);
for (let i = 0; i < validators.length; i++) {
this.forkChoice.addAttestation(attestation.data.beaconBlockRoot, validators[i], balances[i]);
}
Expand All @@ -260,12 +250,12 @@ export class BeaconChain extends (EventEmitter as { new(): ChainEventEmitter })
};

private processBlock = async (block: BeaconBlock, blockHash: Hash) => {

const isValidBlock = await this.isValidBlock(this.latestState, block);
const parentBlock = await this.db.block.get(block.parentRoot);
const pre = await this.db.state.get(parentBlock.stateRoot);
const isValidBlock = await this.isValidBlock(pre, block);
assert(isValidBlock);
this.logger.info(`0x${blockHash.toString("hex")} is valid, running state transition...`);

const pre = this.latestState;
// process current slot
const post = await this.runStateTransition(block, pre);

Expand Down Expand Up @@ -337,7 +327,7 @@ export class BeaconChain extends (EventEmitter as { new(): ChainEventEmitter })
await this.db.setChainHeadRoots(blockRoot, block.stateRoot);
this.forkChoice.addBlock(block.slot, blockRoot, block.parentRoot, newState.currentJustifiedCheckpoint,
newState.finalizedCheckpoint);
// await this.applyForkChoiceRule();
await this.applyForkChoiceRule();
await this.updateDepositMerkleTree(newState);
// update metrics
this.metrics.currentSlot.set(block.slot);
Expand Down
1 change: 1 addition & 0 deletions packages/lodestar/src/chain/forkChoice/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export interface ILMDGHOST {
finalizedCheckpoint: Checkpoint): void;
addAttestation(blockRootBuf: Hash, attester: ValidatorIndex, weight: Gwei): void;
head(): Hash;
getJustified(): Checkpoint;
}
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,13 @@ export class StatefulDagLMDGHOST implements ILMDGHOST {
return true;
}

public getJustified(): Checkpoint {
if (!this.justified) {
return null;
}
return {root: Buffer.from(this.justified.node.blockRoot, "hex"), epoch: this.justified.epoch};
}

private setFinalized(checkpoint: Checkpoint): void {
this.synced = false;
const rootHex = checkpoint.root.toString("hex");
Expand Down
2 changes: 1 addition & 1 deletion packages/lodestar/src/chain/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export interface IBeaconChain extends ChainEventEmitter {
/**
* Add attestation to the fork-choice rule
*/
receiveAttestation(attestation: Attestation): Promise<void>;
receiveAttestation(attestation: Attestation, beaconState?: BeaconState): Promise<void>;

/**
* Pre-process and run the per slot state transition function
Expand Down

0 comments on commit b09cc25

Please sign in to comment.