Skip to content

Commit d785597

Browse files
authored
Merge c36a0c9 into d101913
2 parents d101913 + c36a0c9 commit d785597

29 files changed

+645
-81
lines changed

.github/workflows/test-sim-merge.yml

+21-9
Original file line numberDiff line numberDiff line change
@@ -58,18 +58,30 @@ jobs:
5858
- name: Pull Nethermind
5959
run: docker pull $NETHERMIND_IMAGE
6060

61-
- name: Pull mergemock
62-
run: docker pull $MERGEMOCK_IMAGE
63-
64-
- name: Test Lodestar <> mergemock relay
65-
run: yarn test:sim:mergemock
61+
- name: Test Lodestar <> Nethermind interop
62+
run: yarn test:sim:merge-interop
6663
working-directory: packages/beacon-node
6764
env:
68-
EL_BINARY_DIR: ${{ env.MERGEMOCK_IMAGE }}
69-
EL_SCRIPT_DIR: mergemock
70-
LODESTAR_PRESET: mainnet
65+
EL_BINARY_DIR: ${{ env.NETHERMIND_IMAGE }}
66+
EL_SCRIPT_DIR: netherminddocker
7167
ENGINE_PORT: 8551
72-
ETH_PORT: 8661
68+
ETH_PORT: 8545
69+
70+
# This container is pre-shanghai and does not support enginer_getPayloadBodyV2
71+
# for blinding/unblinding. Re-enable when we have a newer build.
72+
#
73+
# - name: Pull mergemock
74+
# run: docker pull $MERGEMOCK_IMAGE
75+
76+
# - name: Test Lodestar <> mergemock relay
77+
# run: yarn test:sim:mergemock
78+
# working-directory: packages/beacon-node
79+
# env:
80+
# EL_BINARY_DIR: ${{ env.MERGEMOCK_IMAGE }}
81+
# EL_SCRIPT_DIR: mergemock
82+
# LODESTAR_PRESET: mainnet
83+
# ENGINE_PORT: 8551
84+
# ETH_PORT: 8661
7385

7486
- name: Upload debug log test files
7587
if: ${{ always() }}

packages/beacon-node/src/api/impl/beacon/blocks/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ export function getBeaconBlockApi({
269269

270270
return {
271271
async getBlockHeaders({slot, parentRoot}) {
272+
// TODO: (matthewkeil) Make this code BlindedOrFull block aware
272273
// TODO - SLOW CODE: This code seems like it could be improved
273274

274275
// If one block in the response contains an optimistic block, mark the entire response as optimistic

packages/beacon-node/src/api/impl/beacon/blocks/utils.ts

+19-4
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,38 @@
11
import {allForks} from "@lodestar/types";
22
import {routes} from "@lodestar/api";
3-
import {blockToHeader} from "@lodestar/state-transition";
43
import {ChainForkConfig} from "@lodestar/config";
54
import {GENESIS_SLOT} from "../../../../constants/index.js";
65
import {ApiError, ValidationError} from "../../errors.js";
76
import {IBeaconChain} from "../../../../chain/interface.js";
87
import {rootHexRegex} from "../../../../eth1/provider/utils.js";
8+
import {isBlinded} from "../../../../util/fullOrBlindedBlock.js";
99

1010
export function toBeaconHeaderResponse(
1111
config: ChainForkConfig,
12-
block: allForks.SignedBeaconBlock,
12+
block: allForks.FullOrBlindedSignedBeaconBlock,
1313
canonical = false
1414
): routes.beacon.BlockHeaderResponse {
15+
// need to have ts-ignore below to pull type here so it only happens once and
16+
// gets used twice
17+
const types = isBlinded(block)
18+
? config.getBlindedForkTypes(block.message.slot)
19+
: config.getForkTypes(block.message.slot);
1520
return {
16-
root: config.getForkTypes(block.message.slot).BeaconBlock.hashTreeRoot(block.message),
21+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
22+
// @ts-ignore
23+
root: types.BeaconBlock.hashTreeRoot(block.message),
1724
canonical,
1825
header: {
19-
message: blockToHeader(config, block.message),
2026
signature: block.signature,
27+
message: {
28+
stateRoot: block.message.stateRoot,
29+
proposerIndex: block.message.proposerIndex,
30+
slot: block.message.slot,
31+
parentRoot: block.message.parentRoot,
32+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
33+
// @ts-ignore
34+
bodyRoot: types.BeaconBlockBody.hashTreeRoot(block.message.body),
35+
},
2136
},
2237
};
2338
}

packages/beacon-node/src/chain/blocks/writeBlockInputToDb.ts

+4-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {toHex} from "@lodestar/utils";
22
import {BeaconChain} from "../chain.js";
3+
import {blindedOrFullBlockToBlinded} from "../../util/fullOrBlindedBlock.js";
34
import {BlockInput, BlockInputType} from "./types.js";
45

56
/**
@@ -13,17 +14,11 @@ export async function writeBlockInputToDb(this: BeaconChain, blocksInput: BlockI
1314
const fnPromises: Promise<void>[] = [];
1415

1516
for (const blockInput of blocksInput) {
16-
const {block, blockBytes} = blockInput;
17+
const {block} = blockInput;
1718
const blockRoot = this.config.getForkTypes(block.message.slot).BeaconBlock.hashTreeRoot(block.message);
1819
const blockRootHex = toHex(blockRoot);
19-
if (blockBytes) {
20-
// skip serializing data if we already have it
21-
this.metrics?.importBlock.persistBlockWithSerializedDataCount.inc();
22-
fnPromises.push(this.db.block.putBinary(this.db.block.getId(block), blockBytes));
23-
} else {
24-
this.metrics?.importBlock.persistBlockNoSerializedDataCount.inc();
25-
fnPromises.push(this.db.block.add(block));
26-
}
20+
this.metrics?.importBlock.persistBlockNoSerializedDataCount.inc();
21+
fnPromises.push(this.db.block.add(blindedOrFullBlockToBlinded(this.config, block)));
2722
this.logger.debug("Persist block to hot DB", {
2823
slot: block.message.slot,
2924
root: blockRootHex,

packages/beacon-node/src/chain/chain.ts

+58-3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ import {Clock, ClockEvent, IClock} from "../util/clock.js";
4343
import {ensureDir, writeIfNotExist} from "../util/file.js";
4444
import {isOptimisticBlock} from "../util/forkChoice.js";
4545
import {BufferPool} from "../util/bufferPool.js";
46+
import {
47+
blindedOrFullBlockToFull,
48+
deserializeFullOrBlindedSignedBeaconBlock,
49+
getEth1BlockHashFromSerializedBlock,
50+
serializeFullOrBlindedSignedBeaconBlock,
51+
} from "../util/fullOrBlindedBlock.js";
52+
import {ExecutionPayloadBody} from "../execution/engine/types.js";
53+
import {Eth1Error, Eth1ErrorCode} from "../eth1/errors.js";
4654
import {BlockProcessor, ImportBlockOpts} from "./blocks/index.js";
4755
import {ChainEventEmitter, ChainEvent} from "./emitter.js";
4856
import {
@@ -506,7 +514,11 @@ export class BeaconChain implements IBeaconChain {
506514
if (block) {
507515
const data = await this.db.block.get(fromHexString(block.blockRoot));
508516
if (data) {
509-
return {block: data, executionOptimistic: isOptimisticBlock(block), finalized: false};
517+
return {
518+
block: await this.blindedOrFullBlockToFull(data),
519+
executionOptimistic: isOptimisticBlock(block),
520+
finalized: false,
521+
};
510522
}
511523
}
512524
// A non-finalized slot expected to be found in the hot db, could be archived during
@@ -515,7 +527,7 @@ export class BeaconChain implements IBeaconChain {
515527
}
516528

517529
const data = await this.db.blockArchive.get(slot);
518-
return data && {block: data, executionOptimistic: false, finalized: true};
530+
return data && {block: await this.blindedOrFullBlockToFull(data), executionOptimistic: false, finalized: true};
519531
}
520532

521533
async getBlockByRoot(
@@ -525,7 +537,11 @@ export class BeaconChain implements IBeaconChain {
525537
if (block) {
526538
const data = await this.db.block.get(fromHexString(root));
527539
if (data) {
528-
return {block: data, executionOptimistic: isOptimisticBlock(block), finalized: false};
540+
return {
541+
block: await this.blindedOrFullBlockToFull(data),
542+
executionOptimistic: isOptimisticBlock(block),
543+
finalized: false,
544+
};
529545
}
530546
// If block is not found in hot db, try cold db since there could be an archive cycle happening
531547
// TODO: Add a lock to the archiver to have deterministic behavior on where are blocks
@@ -570,6 +586,28 @@ export class BeaconChain implements IBeaconChain {
570586
return this.produceBlockWrapper<BlockType.Blinded>(BlockType.Blinded, blockAttributes);
571587
}
572588

589+
async blindedOrFullBlockToFull(block: allForks.FullOrBlindedSignedBeaconBlock): Promise<allForks.SignedBeaconBlock> {
590+
const info = this.config.getForkInfo(block.message.slot);
591+
return blindedOrFullBlockToFull(
592+
this.config,
593+
info.seq,
594+
block,
595+
await this.getTransactionsAndWithdrawals(info.seq, toHexString(block.message.body.eth1Data.blockHash))
596+
);
597+
}
598+
599+
async blindedOrFullBlockToFullBytes(forkSeq: ForkSeq, block: Uint8Array): Promise<Uint8Array> {
600+
return serializeFullOrBlindedSignedBeaconBlock(
601+
this.config,
602+
blindedOrFullBlockToFull(
603+
this.config,
604+
forkSeq,
605+
deserializeFullOrBlindedSignedBeaconBlock(this.config, block),
606+
await this.getTransactionsAndWithdrawals(forkSeq, toHexString(getEth1BlockHashFromSerializedBlock(block)))
607+
)
608+
);
609+
}
610+
573611
async produceBlockWrapper<T extends BlockType>(
574612
blockType: T,
575613
{
@@ -799,6 +837,23 @@ export class BeaconChain implements IBeaconChain {
799837
}
800838
}
801839

840+
private async getTransactionsAndWithdrawals(
841+
forkSeq: ForkSeq,
842+
blockHash: string
843+
): Promise<Partial<ExecutionPayloadBody>> {
844+
if (forkSeq < ForkSeq.bellatrix) {
845+
return {};
846+
}
847+
const [payload] = await this.executionEngine.getPayloadBodiesByHash([blockHash]);
848+
if (!payload) {
849+
throw new Eth1Error(
850+
{code: Eth1ErrorCode.INVALID_PAYLOAD_BODY, blockHash},
851+
"payload body not found by eth1 engine"
852+
);
853+
}
854+
return payload;
855+
}
856+
802857
/**
803858
* Regenerate state for attestation verification, this does not happen with default chain option of maxSkipSlots = 32 .
804859
* However, need to handle just in case. Lodestar doesn't support multiple regen state requests for attestation verification

packages/beacon-node/src/chain/interface.ts

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {BeaconConfig} from "@lodestar/config";
2424
import {Logger} from "@lodestar/utils";
2525

2626
import {CheckpointWithHex, IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
27+
import {ForkSeq} from "@lodestar/params";
2728
import {IEth1ForBlockProduction} from "../eth1/index.js";
2829
import {IExecutionEngine, IExecutionBuilder} from "../execution/index.js";
2930
import {Metrics} from "../metrics/metrics.js";
@@ -185,6 +186,9 @@ export interface IBeaconChain {
185186
consensusBlockValue: Wei;
186187
}>;
187188

189+
blindedOrFullBlockToFull(block: allForks.FullOrBlindedSignedBeaconBlock): Promise<allForks.SignedBeaconBlock>;
190+
blindedOrFullBlockToFullBytes(forkSeq: ForkSeq, block: Uint8Array): Promise<Uint8Array>;
191+
188192
/** Process a block until complete */
189193
processBlock(block: BlockInput, opts?: ImportBlockOpts): Promise<void>;
190194
/** Process a chain of blocks until complete */
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import {ChainForkConfig} from "@lodestar/config";
22
import {Db, Repository} from "@lodestar/db";
33
import {allForks, ssz} from "@lodestar/types";
4-
import {getSignedBlockTypeFromBytes} from "../../util/multifork.js";
4+
import {blindedOrFullBlockHashTreeRoot} from "@lodestar/state-transition";
5+
import {
6+
deserializeFullOrBlindedSignedBeaconBlock,
7+
serializeFullOrBlindedSignedBeaconBlock,
8+
} from "../../util/fullOrBlindedBlock.js";
59
import {Bucket, getBucketNameByValue} from "../buckets.js";
610

711
/**
812
* Blocks by root
913
*
1014
* Used to store unfinalized blocks
1115
*/
12-
export class BlockRepository extends Repository<Uint8Array, allForks.SignedBeaconBlock> {
16+
export class BlockRepository extends Repository<Uint8Array, allForks.FullOrBlindedSignedBeaconBlock> {
1317
constructor(config: ChainForkConfig, db: Db) {
1418
const bucket = Bucket.allForks_block;
1519
const type = ssz.phase0.SignedBeaconBlock; // Pick some type but won't be used
@@ -19,15 +23,15 @@ export class BlockRepository extends Repository<Uint8Array, allForks.SignedBeaco
1923
/**
2024
* Id is hashTreeRoot of unsigned BeaconBlock
2125
*/
22-
getId(value: allForks.SignedBeaconBlock): Uint8Array {
23-
return this.config.getForkTypes(value.message.slot).BeaconBlock.hashTreeRoot(value.message);
26+
getId(value: allForks.FullOrBlindedSignedBeaconBlock): Uint8Array {
27+
return blindedOrFullBlockHashTreeRoot(this.config, value.message);
2428
}
2529

26-
encodeValue(value: allForks.SignedBeaconBlock): Buffer {
27-
return this.config.getForkTypes(value.message.slot).SignedBeaconBlock.serialize(value) as Buffer;
30+
encodeValue(value: allForks.FullOrBlindedSignedBeaconBlock): Buffer {
31+
return serializeFullOrBlindedSignedBeaconBlock(this.config, value) as Buffer;
2832
}
2933

30-
decodeValue(data: Buffer): allForks.SignedBeaconBlock {
31-
return getSignedBlockTypeFromBytes(this.config, data).deserialize(data);
34+
decodeValue(data: Buffer): allForks.FullOrBlindedSignedBeaconBlock {
35+
return deserializeFullOrBlindedSignedBeaconBlock(this.config, data);
3236
}
3337
}

0 commit comments

Comments
 (0)