Skip to content

Commit 49ab90f

Browse files
authored
Merge 8fcb43d into 3a6702e
2 parents 3a6702e + 8fcb43d commit 49ab90f

24 files changed

+613
-70
lines changed

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, type} = blockInput;
17+
const {block, type} = 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

+51-3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {ProcessShutdownCallback} from "@lodestar/validator";
3131
import {Logger, isErrorAborted, pruneSetToMax, sleep, toHex} from "@lodestar/utils";
3232
import {ForkSeq, SLOTS_PER_EPOCH, MAX_BLOBS_PER_BLOCK} from "@lodestar/params";
3333

34+
import {toHexString} from "@lodestar/utils";
3435
import {GENESIS_EPOCH, ZERO_HASH} from "../constants/index.js";
3536
import {IBeaconDb} from "../db/index.js";
3637
import {Metrics} from "../metrics/index.js";
@@ -39,6 +40,14 @@ import {IExecutionEngine, IExecutionBuilder} from "../execution/index.js";
3940
import {Clock, ClockEvent, IClock} from "../util/clock.js";
4041
import {ensureDir, writeIfNotExist} from "../util/file.js";
4142
import {isOptimisticBlock} from "../util/forkChoice.js";
43+
import {
44+
blindedOrFullBlockToFull,
45+
deserializeFullOrBlindedSignedBeaconBlock,
46+
getEth1BlockHashFromSerializedBlock,
47+
serializeFullOrBlindedSignedBeaconBlock,
48+
} from "../util/fullOrBlindedBlock.js";
49+
import {ExecutionPayloadBody} from "../execution/engine/types.js";
50+
import {Eth1Error, Eth1ErrorCode} from "../eth1/errors.js";
4251
import {CheckpointStateCache, StateContextCache} from "./stateCache/index.js";
4352
import {BlockProcessor, ImportBlockOpts} from "./blocks/index.js";
4453
import {ChainEventEmitter, ChainEvent} from "./emitter.js";
@@ -433,7 +442,7 @@ export class BeaconChain implements IBeaconChain {
433442
if (block) {
434443
const data = await this.db.block.get(fromHexString(block.blockRoot));
435444
if (data) {
436-
return {block: data, executionOptimistic: isOptimisticBlock(block)};
445+
return {block: await this.blindedOrFullBlockToFull(data), executionOptimistic: isOptimisticBlock(block)};
437446
}
438447
}
439448
// A non-finalized slot expected to be found in the hot db, could be archived during
@@ -442,7 +451,7 @@ export class BeaconChain implements IBeaconChain {
442451
}
443452

444453
const data = await this.db.blockArchive.get(slot);
445-
return data && {block: data, executionOptimistic: false};
454+
return data && {block: await this.blindedOrFullBlockToFull(data), executionOptimistic: false};
446455
}
447456

448457
async getBlockByRoot(
@@ -452,7 +461,7 @@ export class BeaconChain implements IBeaconChain {
452461
if (block) {
453462
const data = await this.db.block.get(fromHexString(root));
454463
if (data) {
455-
return {block: data, executionOptimistic: isOptimisticBlock(block)};
464+
return {block: await this.blindedOrFullBlockToFull(data), executionOptimistic: isOptimisticBlock(block)};
456465
}
457466
// If block is not found in hot db, try cold db since there could be an archive cycle happening
458467
// TODO: Add a lock to the archiver to have determinstic behaviour on where are blocks
@@ -462,6 +471,28 @@ export class BeaconChain implements IBeaconChain {
462471
return data && {block: data, executionOptimistic: false};
463472
}
464473

474+
async blindedOrFullBlockToFull(block: allForks.FullOrBlindedSignedBeaconBlock): Promise<allForks.SignedBeaconBlock> {
475+
const info = this.config.getForkInfo(block.message.slot);
476+
return blindedOrFullBlockToFull(
477+
this.config,
478+
info.seq,
479+
block,
480+
await this.getTransactionsAndWithdrawals(info.seq, toHexString(block.message.body.eth1Data.blockHash))
481+
);
482+
}
483+
484+
async blindedOrFullBlockToFullBytes(forkSeq: ForkSeq, block: Uint8Array): Promise<Uint8Array> {
485+
return serializeFullOrBlindedSignedBeaconBlock(
486+
this.config,
487+
blindedOrFullBlockToFull(
488+
this.config,
489+
forkSeq,
490+
deserializeFullOrBlindedSignedBeaconBlock(this.config, block),
491+
await this.getTransactionsAndWithdrawals(forkSeq, toHexString(getEth1BlockHashFromSerializedBlock(block)))
492+
)
493+
);
494+
}
495+
465496
produceBlock(blockAttributes: BlockAttributes): Promise<{block: allForks.BeaconBlock; blockValue: Wei}> {
466497
return this.produceBlockWrapper<BlockType.Full>(BlockType.Full, blockAttributes);
467498
}
@@ -626,6 +657,23 @@ export class BeaconChain implements IBeaconChain {
626657
}
627658
}
628659

660+
private async getTransactionsAndWithdrawals(
661+
forkSeq: ForkSeq,
662+
blockHash: string
663+
): Promise<Partial<ExecutionPayloadBody>> {
664+
if (forkSeq < ForkSeq.bellatrix) {
665+
return {};
666+
}
667+
const [payload] = await this.executionEngine.getPayloadBodiesByHash([blockHash]);
668+
if (!payload) {
669+
throw new Eth1Error(
670+
{code: Eth1ErrorCode.INVALID_PAYLOAD_BODY, blockHash},
671+
"payload body not found by eth1 engine"
672+
);
673+
}
674+
return payload;
675+
}
676+
629677
/**
630678
* `ForkChoice.onBlock` must never throw for a block that is valid with respect to the network
631679
* `justifiedBalancesGetter()` must never throw and it should always return a state.

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

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

1212
import {IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
13+
import {ForkSeq} from "@lodestar/params";
1314
import {IEth1ForBlockProduction} from "../eth1/index.js";
1415
import {IExecutionEngine, IExecutionBuilder} from "../execution/index.js";
1516
import {Metrics} from "../metrics/metrics.js";
@@ -138,6 +139,9 @@ export interface IBeaconChain {
138139

139140
getBlobSidecars(beaconBlock: deneb.BeaconBlock): deneb.BlobSidecars;
140141

142+
blindedOrFullBlockToFull(block: allForks.FullOrBlindedSignedBeaconBlock): Promise<allForks.SignedBeaconBlock>;
143+
blindedOrFullBlockToFullBytes(forkSeq: ForkSeq, block: Uint8Array): Promise<Uint8Array>;
144+
141145
produceBlock(blockAttributes: BlockAttributes): Promise<{block: allForks.BeaconBlock; blockValue: Wei}>;
142146
produceBlindedBlock(blockAttributes: BlockAttributes): Promise<{block: allForks.BlindedBeaconBlock; blockValue: Wei}>;
143147

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
}

packages/beacon-node/src/db/repositories/blockArchive.ts

+23-19
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import {ChainForkConfig} from "@lodestar/config";
33
import {Db, Repository, KeyValue, FilterOptions} from "@lodestar/db";
44
import {Slot, Root, allForks, ssz} from "@lodestar/types";
55
import {bytesToInt} from "@lodestar/utils";
6-
import {getSignedBlockTypeFromBytes} from "../../util/multifork.js";
6+
import {blindedOrFullBlockHashTreeRoot} from "@lodestar/state-transition";
7+
import {
8+
deserializeFullOrBlindedSignedBeaconBlock,
9+
serializeFullOrBlindedSignedBeaconBlock,
10+
} from "../../util/fullOrBlindedBlock.js";
711
import {Bucket, getBucketNameByValue} from "../buckets.js";
812
import {getRootIndexKey, getParentRootIndexKey} from "./blockArchiveIndex.js";
913
import {deleteParentRootIndex, deleteRootIndex, storeParentRootIndex, storeRootIndex} from "./blockArchiveIndex.js";
@@ -21,7 +25,7 @@ export type BlockArchiveBatchPutBinaryItem = KeyValue<Slot, Uint8Array> & {
2125
/**
2226
* Stores finalized blocks. Block slot is identifier.
2327
*/
24-
export class BlockArchiveRepository extends Repository<Slot, allForks.SignedBeaconBlock> {
28+
export class BlockArchiveRepository extends Repository<Slot, allForks.FullOrBlindedSignedBeaconBlock> {
2529
constructor(config: ChainForkConfig, db: Db) {
2630
const bucket = Bucket.allForks_blockArchive;
2731
const type = ssz.phase0.SignedBeaconBlock; // Pick some type but won't be used
@@ -30,17 +34,17 @@ export class BlockArchiveRepository extends Repository<Slot, allForks.SignedBeac
3034

3135
// Overrides for multi-fork
3236

33-
encodeValue(value: allForks.SignedBeaconBlock): Uint8Array {
34-
return this.config.getForkTypes(value.message.slot).SignedBeaconBlock.serialize(value);
37+
encodeValue(value: allForks.FullOrBlindedSignedBeaconBlock): Uint8Array {
38+
return serializeFullOrBlindedSignedBeaconBlock(this.config, value);
3539
}
3640

37-
decodeValue(data: Uint8Array): allForks.SignedBeaconBlock {
38-
return getSignedBlockTypeFromBytes(this.config, data).deserialize(data);
41+
decodeValue(data: Uint8Array): allForks.FullOrBlindedSignedBeaconBlock {
42+
return deserializeFullOrBlindedSignedBeaconBlock(this.config, data);
3943
}
4044

4145
// Handle key as slot
4246

43-
getId(value: allForks.SignedBeaconBlock): Slot {
47+
getId(value: allForks.FullOrBlindedSignedBeaconBlock): Slot {
4448
return value.message.slot;
4549
}
4650

@@ -50,8 +54,8 @@ export class BlockArchiveRepository extends Repository<Slot, allForks.SignedBeac
5054

5155
// Overrides to index
5256

53-
async put(key: Slot, value: allForks.SignedBeaconBlock): Promise<void> {
54-
const blockRoot = this.config.getForkTypes(value.message.slot).BeaconBlock.hashTreeRoot(value.message);
57+
async put(key: Slot, value: allForks.FullOrBlindedSignedBeaconBlock): Promise<void> {
58+
const blockRoot = blindedOrFullBlockHashTreeRoot(this.config, value.message);
5559
const slot = value.message.slot;
5660
await Promise.all([
5761
super.put(key, value),
@@ -60,12 +64,12 @@ export class BlockArchiveRepository extends Repository<Slot, allForks.SignedBeac
6064
]);
6165
}
6266

63-
async batchPut(items: KeyValue<Slot, allForks.SignedBeaconBlock>[]): Promise<void> {
67+
async batchPut(items: KeyValue<Slot, allForks.FullOrBlindedSignedBeaconBlock>[]): Promise<void> {
6468
await Promise.all([
6569
super.batchPut(items),
6670
Array.from(items).map((item) => {
6771
const slot = item.value.message.slot;
68-
const blockRoot = this.config.getForkTypes(slot).BeaconBlock.hashTreeRoot(item.value.message);
72+
const blockRoot = blindedOrFullBlockHashTreeRoot(this.config, item.value.message);
6973
return storeRootIndex(this.db, slot, blockRoot);
7074
}),
7175
Array.from(items).map((item) => {
@@ -84,25 +88,25 @@ export class BlockArchiveRepository extends Repository<Slot, allForks.SignedBeac
8488
]);
8589
}
8690

87-
async remove(value: allForks.SignedBeaconBlock): Promise<void> {
91+
async remove(value: allForks.FullOrBlindedSignedBeaconBlock): Promise<void> {
8892
await Promise.all([
8993
super.remove(value),
90-
deleteRootIndex(this.db, this.config.getForkTypes(value.message.slot).SignedBeaconBlock, value),
94+
deleteRootIndex(this.db, this.config.getForkTypes(value.message.slot).BeaconBlock, value),
9195
deleteParentRootIndex(this.db, value),
9296
]);
9397
}
9498

95-
async batchRemove(values: allForks.SignedBeaconBlock[]): Promise<void> {
99+
async batchRemove(values: allForks.FullOrBlindedSignedBeaconBlock[]): Promise<void> {
96100
await Promise.all([
97101
super.batchRemove(values),
98102
Array.from(values).map((value) =>
99-
deleteRootIndex(this.db, this.config.getForkTypes(value.message.slot).SignedBeaconBlock, value)
103+
deleteRootIndex(this.db, this.config.getForkTypes(value.message.slot).BeaconBlock, value)
100104
),
101105
Array.from(values).map((value) => deleteParentRootIndex(this.db, value)),
102106
]);
103107
}
104108

105-
async *valuesStream(opts?: BlockFilterOptions): AsyncIterable<allForks.SignedBeaconBlock> {
109+
async *valuesStream(opts?: BlockFilterOptions): AsyncIterable<allForks.FullOrBlindedSignedBeaconBlock> {
106110
const firstSlot = this.getFirstSlot(opts);
107111
const valuesStream = super.valuesStream(opts);
108112
const step = (opts && opts.step) ?? 1;
@@ -114,13 +118,13 @@ export class BlockArchiveRepository extends Repository<Slot, allForks.SignedBeac
114118
}
115119
}
116120

117-
async values(opts?: BlockFilterOptions): Promise<allForks.SignedBeaconBlock[]> {
121+
async values(opts?: BlockFilterOptions): Promise<allForks.FullOrBlindedSignedBeaconBlock[]> {
118122
return all(this.valuesStream(opts));
119123
}
120124

121125
// INDEX
122126

123-
async getByRoot(root: Root): Promise<allForks.SignedBeaconBlock | null> {
127+
async getByRoot(root: Root): Promise<allForks.FullOrBlindedSignedBeaconBlock | null> {
124128
const slot = await this.getSlotByRoot(root);
125129
return slot !== null ? this.get(slot) : null;
126130
}
@@ -130,7 +134,7 @@ export class BlockArchiveRepository extends Repository<Slot, allForks.SignedBeac
130134
return slot !== null ? ({key: slot, value: await this.getBinary(slot)} as KeyValue<Slot, Buffer>) : null;
131135
}
132136

133-
async getByParentRoot(root: Root): Promise<allForks.SignedBeaconBlock | null> {
137+
async getByParentRoot(root: Root): Promise<allForks.FullOrBlindedSignedBeaconBlock | null> {
134138
const slot = await this.getSlotByParentRoot(root);
135139
return slot !== null ? this.get(slot) : null;
136140
}

packages/beacon-node/src/db/repositories/blockArchiveIndex.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {Db, encodeKey} from "@lodestar/db";
2-
import {Slot, Root, allForks, ssz} from "@lodestar/types";
2+
import {Slot, Root, allForks} from "@lodestar/types";
33
import {intToBytes} from "@lodestar/utils";
44
import {Bucket} from "../buckets.js";
55

@@ -13,14 +13,13 @@ export async function storeParentRootIndex(db: Db, slot: Slot, parentRoot: Root)
1313

1414
export async function deleteRootIndex(
1515
db: Db,
16-
signedBeaconBlockType: allForks.AllForksSSZTypes["SignedBeaconBlock"],
17-
block: allForks.SignedBeaconBlock
16+
beaconBlockType: allForks.AllForksSSZTypes["BeaconBlock"],
17+
block: allForks.FullOrBlindedSignedBeaconBlock
1818
): Promise<void> {
19-
const beaconBlockType = (signedBeaconBlockType as typeof ssz.phase0.SignedBeaconBlock).fields["message"];
2019
return db.delete(getRootIndexKey(beaconBlockType.hashTreeRoot(block.message)));
2120
}
2221

23-
export async function deleteParentRootIndex(db: Db, block: allForks.SignedBeaconBlock): Promise<void> {
22+
export async function deleteParentRootIndex(db: Db, block: allForks.FullOrBlindedSignedBeaconBlock): Promise<void> {
2423
return db.delete(getParentRootIndexKey(block.message.parentRoot));
2524
}
2625

0 commit comments

Comments
 (0)