Skip to content

Commit 6c23a8b

Browse files
committed
Free the blobs
fix the types rejig the new constants in params add comment for cleanup update reqresp fix api package rename blobs repo commit the wip modifications further appropriate renaming further references update further reference updates continue refac fix reqresp build further refac further refac fix api fix db interface fix beacondb alloc build fix api improve blob verificaion correct validation call fixes fix the produce block/blobs flow reduce diff blob gossip validation update validations cleanup block vali reduce diff handle gossip of block and blob fix test for timebeing modify publishing flow fix import flow onsidecarbyrange fix and some type fixes fix sidecars by root prune blockinput cache fix kzg interface small renaming interface rename fix fetch blockmaybeblobs by range test fix build lint issues for now c-kzg version fix FullOrBlindedBlobSidecar changes fix tests complete the blob publishing flow fix test get the single node run functional get the gossip blob flow working fix peer syncing using req/resp fix sidecar by root check refactor blobsidecars hotdb and remove archive add blob gossip validation flow fix topic fix the validation condition add blob validation and test various sync modes fix tests rebase fixes enable deneb spec tests make blobsbyroot multi block fixes cleanup defunt builder endpoint archive blobs post finalization uptill the blob window serve finalized blobs within the blob prune window fix test fix test lookup in archive as well cleanup and improvements rebase fx Add 4844 sim test and override the field elements per blob update image add blob test add test run in package start unknown sync and range sync finalize the sims change the signing flow fix test types fix tests fix test lint update tx type and corresponding ethereumjs image update c-kzg and use blobs bundle proof fix test fix test merge getblobsbundle into getpayloadv3 update images fix genesis config rebase fixes fix test update images fix tests lint fix the sidecar request count limit fix test lint rebase fixes cleanup fix unit tests update kzg to big endian devnet 6 integration fix passing setup arg update path fix tests fix blobs sidecar by range response cleanup blob import vals reduce diff
1 parent 268c9be commit 6c23a8b

52 files changed

Lines changed: 973 additions & 546 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ env:
1717
NETHERMIND_IMAGE: nethermind/nethermind:1.14.3
1818
MERGEMOCK_IMAGE: g11tech/mergemock:latest
1919
GETH_WITHDRAWALS_IMAGE: g11tech/geth:withdrawalsfeb8
20-
ETHEREUMJS_WITHDRAWALS_IMAGE: g11tech/ethereumjs:feb8
20+
ETHEREUMJS_WITHDRAWALS_IMAGE: g11tech/ethereumjs:blobs-b6b63
2121
NETHERMIND_WITHDRAWALS_IMAGE: nethermindeth/nethermind:withdrawals_yolo
22+
ETHEREUMJS_BLOBS_IMAGE: g11tech/ethereumjs:blobs-b6b63
2223

2324
jobs:
2425
sim-merge-tests:
@@ -128,6 +129,17 @@ jobs:
128129
# EL_BINARY_DIR: ${{ env.NETHERMIND_WITHDRAWALS_IMAGE }}
129130
# EL_SCRIPT_DIR: netherminddocker
130131

132+
- name: Pull ethereumjs blobs
133+
run: docker pull $ETHEREUMJS_BLOBS_IMAGE
134+
135+
- name: Test Lodestar <> ethereumjs blobs
136+
run: yarn test:sim:blobs
137+
working-directory: packages/beacon-node
138+
env:
139+
EL_BINARY_DIR: ${{ env.ETHEREUMJS_BLOBS_IMAGE }}
140+
EL_SCRIPT_DIR: ethereumjsdocker
141+
DEV_RUN: true
142+
131143
- name: Upload debug log test files
132144
if: ${{ always() }}
133145
uses: actions/upload-artifact@v2

packages/beacon-node/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
"test:sim:merge-interop": "mocha 'test/sim/merge-interop.test.ts'",
8686
"test:sim:mergemock": "mocha 'test/sim/mergemock.test.ts'",
8787
"test:sim:withdrawals": "mocha 'test/sim/withdrawal-interop.test.ts'",
88+
"test:sim:blobs": "mocha 'test/sim/4844-interop.test.ts'",
8889
"download-spec-tests": "node --loader=ts-node/esm test/spec/downloadTests.ts",
8990
"check-spec-tests": "mocha test/spec/checkCoverage.ts",
9091
"test:spec-bls-general": "mocha --config .mocharc.spec.cjs 'test/spec/bls/**/*.test.ts' 'test/spec/general/**/*.test.ts'",

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

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,7 @@ import {SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
44
import {sleep} from "@lodestar/utils";
55
import {allForks, deneb} from "@lodestar/types";
66
import {fromHexString, toHexString} from "@chainsafe/ssz";
7-
import {
8-
BlockSource,
9-
getBlockInput,
10-
ImportBlockOpts,
11-
BlockInput,
12-
blobSidecarsToBlobsSidecar,
13-
} from "../../../../chain/blocks/types.js";
7+
import {BlockSource, getBlockInput, ImportBlockOpts, BlockInput} from "../../../../chain/blocks/types.js";
148
import {promiseAllMaybeAsync} from "../../../../util/promises.js";
159
import {isOptimisticBlock} from "../../../../util/forkChoice.js";
1610
import {BlockError, BlockErrorCode} from "../../../../chain/errors/index.js";
@@ -213,12 +207,7 @@ export function getBeaconBlockApi({
213207
config,
214208
signedBlock,
215209
BlockSource.api,
216-
// The blobsSidecar will be replaced in the followup PRs with just blobs
217-
blobSidecarsToBlobsSidecar(
218-
config,
219-
signedBlock,
220-
signedBlobs.map((sblob) => sblob.message)
221-
),
210+
signedBlobs.map((sblob) => sblob.message),
222211
null
223212
);
224213
} else {

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

Lines changed: 87 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import {CachedBeaconStateAllForks, computeEpochAtSlot, DataAvailableStatus} from "@lodestar/state-transition";
22
import {MaybeValidExecutionStatus} from "@lodestar/fork-choice";
3-
import {allForks, deneb, Slot} from "@lodestar/types";
3+
import {allForks, deneb, Slot, RootHex} from "@lodestar/types";
44
import {ForkSeq, MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS} from "@lodestar/params";
55
import {ChainForkConfig} from "@lodestar/config";
6-
7-
import {ckzg} from "../../util/kzg.js";
6+
import {toHexString} from "@chainsafe/ssz";
7+
import {pruneSetToMax} from "@lodestar/utils";
88

99
export enum BlockInputType {
1010
preDeneb = "preDeneb",
@@ -21,7 +21,7 @@ export enum BlockSource {
2121

2222
export type BlockInput = {block: allForks.SignedBeaconBlock; source: BlockSource; blockBytes: Uint8Array | null} & (
2323
| {type: BlockInputType.preDeneb}
24-
| {type: BlockInputType.postDeneb; blobs: deneb.BlobsSidecar}
24+
| {type: BlockInputType.postDeneb; blobs: deneb.BlobSidecars}
2525
);
2626

2727
export function blockRequiresBlobs(config: ChainForkConfig, blockSlot: Slot, clockSlot: Slot): boolean {
@@ -32,26 +32,89 @@ export function blockRequiresBlobs(config: ChainForkConfig, blockSlot: Slot, clo
3232
);
3333
}
3434

35-
// TODO DENEB: a helper function to convert blobSidecars to blobsSidecar, to be cleanup on BlockInput
36-
// migration
37-
export function blobSidecarsToBlobsSidecar(
38-
config: ChainForkConfig,
39-
signedBlock: allForks.SignedBeaconBlock,
40-
blobSidecars: deneb.BlobSidecars
41-
): deneb.BlobsSidecar {
42-
const beaconBlockSlot = signedBlock.message.slot;
43-
const beaconBlockRoot = config.getForkTypes(beaconBlockSlot).BeaconBlock.hashTreeRoot(signedBlock.message);
44-
const blobs = blobSidecars.map(({blob}) => blob);
45-
const blobsSidecar = {
46-
beaconBlockRoot,
47-
beaconBlockSlot,
48-
blobs,
49-
kzgAggregatedProof: ckzg.computeAggregateKzgProof(blobs),
50-
};
51-
return blobsSidecar;
35+
export enum GossipedInputType {
36+
block = "block",
37+
blob = "blob",
5238
}
39+
type GossipedBlockInput = {serializedData: Uint8Array | null} & (
40+
| {type: GossipedInputType.block; signedBlock: allForks.SignedBeaconBlock}
41+
| {type: GossipedInputType.blob; signedBlob: deneb.SignedBlobSidecar}
42+
);
43+
type BlockInputCacheType = {block?: allForks.SignedBeaconBlock; blobs: Map<number, deneb.BlobSidecar>};
44+
45+
const MAX_GOSSIPINPUT_CACHE = 5;
5346

5447
export const getBlockInput = {
48+
blockInputCache: new Map<RootHex, BlockInputCacheType>(),
49+
50+
getFullBlockInput(
51+
config: ChainForkConfig,
52+
gossipedInput: GossipedBlockInput
53+
):
54+
| {blockInput: BlockInput; blockInputMeta: {pending: null; haveBlobs: number; expectedBlobs: number}}
55+
| {blockInput: null; blockInputMeta: {pending: GossipedInputType.block; haveBlobs: number; expectedBlobs: null}}
56+
| {blockInput: null; blockInputMeta: {pending: GossipedInputType.blob; haveBlobs: number; expectedBlobs: number}} {
57+
let blockHex;
58+
let blockCache;
59+
if (gossipedInput.type === GossipedInputType.block) {
60+
const {signedBlock} = gossipedInput;
61+
blockHex = toHexString(
62+
config.getForkTypes(signedBlock.message.slot).BeaconBlock.hashTreeRoot(signedBlock.message)
63+
);
64+
blockCache = this.blockInputCache.get(blockHex) ?? {blobs: new Map<number, deneb.BlobSidecar>()};
65+
blockCache.block = signedBlock;
66+
} else {
67+
const {signedBlob} = gossipedInput;
68+
blockHex = toHexString(signedBlob.message.blockRoot);
69+
blockCache = this.blockInputCache.get(blockHex);
70+
71+
// If a new entry is going to be inserted, prune out old ones
72+
if (blockCache === undefined) {
73+
pruneSetToMax(this.blockInputCache, MAX_GOSSIPINPUT_CACHE);
74+
blockCache = {blobs: new Map<number, deneb.BlobSidecar>()};
75+
}
76+
// TODO: freetheblobs check if its the same blob or a duplicate and throw/take actions
77+
blockCache.blobs.set(signedBlob.message.index, signedBlob.message);
78+
}
79+
this.blockInputCache.set(blockHex, blockCache);
80+
const {block: signedBlock} = blockCache;
81+
if (signedBlock !== undefined) {
82+
const {blobKzgCommitments} = (signedBlock as deneb.SignedBeaconBlock).message.body;
83+
if (blobKzgCommitments.length < blockCache.blobs.size) {
84+
throw Error(`Received more blobs=${blockCache.blobs.size} than commitments=${blobKzgCommitments.length}`);
85+
}
86+
if (blobKzgCommitments.length === blockCache.blobs.size) {
87+
const blobSidecars = [];
88+
for (let index = 0; index < blobKzgCommitments.length; index++) {
89+
const blobSidecar = blockCache.blobs.get(index);
90+
if (blobSidecar === undefined) {
91+
throw Error("Missing blobSidecar");
92+
}
93+
blobSidecars.push(blobSidecar);
94+
}
95+
return {
96+
// TODO freetheblobs: collate and add serialized data for the postDeneb blockinput
97+
blockInput: getBlockInput.postDeneb(config, signedBlock, BlockSource.gossip, blobSidecars, null),
98+
blockInputMeta: {pending: null, haveBlobs: blockCache.blobs.size, expectedBlobs: blobKzgCommitments.length},
99+
};
100+
} else {
101+
return {
102+
blockInput: null,
103+
blockInputMeta: {
104+
pending: GossipedInputType.blob,
105+
haveBlobs: blockCache.blobs.size,
106+
expectedBlobs: blobKzgCommitments.length,
107+
},
108+
};
109+
}
110+
} else {
111+
return {
112+
blockInput: null,
113+
blockInputMeta: {pending: GossipedInputType.block, haveBlobs: blockCache.blobs.size, expectedBlobs: null},
114+
};
115+
}
116+
},
117+
55118
preDeneb(
56119
config: ChainForkConfig,
57120
block: allForks.SignedBeaconBlock,
@@ -73,7 +136,7 @@ export const getBlockInput = {
73136
config: ChainForkConfig,
74137
block: allForks.SignedBeaconBlock,
75138
source: BlockSource,
76-
blobs: deneb.BlobsSidecar,
139+
blobs: deneb.BlobSidecars,
77140
blockBytes: Uint8Array | null
78141
): BlockInput {
79142
if (config.getForkSeq(block.message.slot) < ForkSeq.deneb) {
@@ -127,8 +190,8 @@ export type ImportBlockOpts = {
127190
* Metadata: `true` if all the signatures including the proposer signature have been verified
128191
*/
129192
validSignatures?: boolean;
130-
/** Set to true if already run `validateBlobsSidecar()` sucessfully on the blobs */
131-
validBlobsSidecar?: boolean;
193+
/** Set to true if already run `validateBlobSidecars()` sucessfully on the blobs */
194+
validBlobSidecars?: boolean;
132195
/** Seen timestamp seconds */
133196
seenTimestampSec?: number;
134197
/** Set to true if persist block right at verification time */

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {Slot, deneb} from "@lodestar/types";
55
import {toHexString} from "@lodestar/utils";
66
import {IClock} from "../../util/clock.js";
77
import {BlockError, BlockErrorCode} from "../errors/index.js";
8-
import {validateBlobsSidecar} from "../validation/blobsSidecar.js";
8+
import {validateBlobSidecars} from "../validation/blobSidecar.js";
99
import {BlockInput, BlockInputType, ImportBlockOpts} from "./types.js";
1010

1111
/**
@@ -126,7 +126,7 @@ function maybeValidateBlobs(
126126
// TODO Deneb: Make switch verify it's exhaustive
127127
switch (blockInput.type) {
128128
case BlockInputType.postDeneb: {
129-
if (opts.validBlobsSidecar) {
129+
if (opts.validBlobSidecars) {
130130
return DataAvailableStatus.available;
131131
}
132132

@@ -135,7 +135,7 @@ function maybeValidateBlobs(
135135
const {blobKzgCommitments} = (block as deneb.SignedBeaconBlock).message.body;
136136
const beaconBlockRoot = config.getForkTypes(blockSlot).BeaconBlock.hashTreeRoot(block.message);
137137
// TODO Deneb: This function throws un-typed errors
138-
validateBlobsSidecar(blockSlot, beaconBlockRoot, blobKzgCommitments, blobs);
138+
validateBlobSidecars(blockSlot, beaconBlockRoot, blobKzgCommitments, blobs);
139139

140140
return DataAvailableStatus.available;
141141
}

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

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import {allForks, deneb} from "@lodestar/types";
21
import {toHex} from "@lodestar/utils";
32
import {BeaconChain} from "../chain.js";
43
import {BlockInput, BlockInputType} from "./types.js";
@@ -31,13 +30,13 @@ export async function writeBlockInputToDb(this: BeaconChain, blocksInput: BlockI
3130
});
3231

3332
if (type === BlockInputType.postDeneb) {
34-
const {blobs} = blockInput;
33+
const {blobs: blobSidecars} = blockInput;
3534
// NOTE: Old blobs are pruned on archive
36-
fnPromises.push(this.db.blobsSidecar.add(blobs));
37-
this.logger.debug("Persist blobsSidecar to hot DB", {
38-
blobsLen: blobs.blobs.length,
39-
slot: blobs.beaconBlockSlot,
40-
root: toHex(blobs.beaconBlockRoot),
35+
fnPromises.push(this.db.blobSidecars.add({blockRoot, slot: block.message.slot, blobSidecars}));
36+
this.logger.debug("Persisted blobSidecars to hot DB", {
37+
blobsLen: blobSidecars.length,
38+
slot: block.message.slot,
39+
root: blockRootHex,
4140
});
4241
}
4342
}
@@ -49,27 +48,26 @@ export async function writeBlockInputToDb(this: BeaconChain, blocksInput: BlockI
4948
* Prunes eagerly persisted block inputs only if not known to the fork-choice
5049
*/
5150
export async function removeEagerlyPersistedBlockInputs(this: BeaconChain, blockInputs: BlockInput[]): Promise<void> {
52-
const blockToRemove: allForks.SignedBeaconBlock[] = [];
53-
const blobsToRemove: deneb.BlobsSidecar[] = [];
51+
const blockToRemove = [];
52+
const blobsToRemove = [];
5453

5554
for (const blockInput of blockInputs) {
5655
const {block, type} = blockInput;
57-
const blockRoot = toHex(this.config.getForkTypes(block.message.slot).BeaconBlock.hashTreeRoot(block.message));
58-
if (!this.forkChoice.hasBlockHex(blockRoot)) {
56+
const blockRoot = this.config.getForkTypes(block.message.slot).BeaconBlock.hashTreeRoot(block.message);
57+
const blockRootHex = toHex(blockRoot);
58+
if (!this.forkChoice.hasBlockHex(blockRootHex)) {
5959
blockToRemove.push(block);
6060

6161
if (type === BlockInputType.postDeneb) {
62-
blobsToRemove.push(blockInput.blobs);
63-
this.db.blobsSidecar.remove(blockInput.blobs).catch((e) => {
64-
this.logger.verbose("Error removing eagerly imported blobsSidecar", {blockRoot}, e);
65-
});
62+
const blobSidecars = blockInput.blobs;
63+
blobsToRemove.push({blockRoot, slot: block.message.slot, blobSidecars});
6664
}
6765
}
6866
}
6967

7068
await Promise.all([
7169
// TODO: Batch DB operations not with Promise.all but with level db ops
7270
this.db.block.batchRemove(blockToRemove),
73-
this.db.blobsSidecar.batchRemove(blobsToRemove),
71+
this.db.blobSidecars.batchRemove(blobsToRemove),
7472
]);
7573
}

packages/beacon-node/src/chain/errors/blobsSidecarError.ts renamed to packages/beacon-node/src/chain/errors/blobSidecarError.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import {Slot} from "@lodestar/types";
22
import {GossipActionError} from "./gossipValidation.js";
33

4-
export enum BlobsSidecarErrorCode {
4+
export enum BlobSidecarErrorCode {
5+
INVALID_INDEX = "BLOBS_SIDECAR_ERROR_INVALID_INDEX",
56
/** !bls.KeyValidate(block.body.blob_kzg_commitments[i]) */
67
INVALID_KZG = "BLOBS_SIDECAR_ERROR_INVALID_KZG",
78
/** !verify_kzg_commitments_against_transactions(block.body.execution_payload.transactions, block.body.blob_kzg_commitments) */
@@ -14,11 +15,12 @@ export enum BlobsSidecarErrorCode {
1415
INVALID_KZG_PROOF = "BLOBS_SIDECAR_ERROR_INVALID_KZG_PROOF",
1516
}
1617

17-
export type BlobsSidecarErrorType =
18-
| {code: BlobsSidecarErrorCode.INVALID_KZG; kzgIdx: number}
19-
| {code: BlobsSidecarErrorCode.INVALID_KZG_TXS}
20-
| {code: BlobsSidecarErrorCode.INCORRECT_SLOT; blockSlot: Slot; blobSlot: Slot}
21-
| {code: BlobsSidecarErrorCode.INVALID_BLOB; blobIdx: number}
22-
| {code: BlobsSidecarErrorCode.INVALID_KZG_PROOF};
18+
export type BlobSidecarErrorType =
19+
| {code: BlobSidecarErrorCode.INVALID_INDEX; blobIdx: number; gossipIndex: number}
20+
| {code: BlobSidecarErrorCode.INVALID_KZG; blobIdx: number}
21+
| {code: BlobSidecarErrorCode.INVALID_KZG_TXS}
22+
| {code: BlobSidecarErrorCode.INCORRECT_SLOT; blockSlot: Slot; blobSlot: Slot; blobIdx: number}
23+
| {code: BlobSidecarErrorCode.INVALID_BLOB; blobIdx: number}
24+
| {code: BlobSidecarErrorCode.INVALID_KZG_PROOF; blobIdx: number};
2325

24-
export class BlobsSidecarError extends GossipActionError<BlobsSidecarErrorType> {}
26+
export class BlobSidecarError extends GossipActionError<BlobSidecarErrorType> {}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export * from "./attestationError.js";
22
export * from "./attesterSlashingError.js";
3-
export * from "./blobsSidecarError.js";
3+
export * from "./blobSidecarError.js";
44
export * from "./blockError.js";
55
export * from "./gossipValidation.js";
66
export * from "./proposerSlashingError.js";

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export enum RegenCaller {
99
processBlock = "processBlock",
1010
produceBlock = "produceBlock",
1111
validateGossipBlock = "validateGossipBlock",
12+
validateGossipBlob = "validateGossipBlob",
1213
precomputeEpoch = "precomputeEpoch",
1314
produceAttestationData = "produceAttestationData",
1415
processBlocksInEpoch = "processBlocksInEpoch",

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import {
33
CachedBeaconStateAllForks,
44
computeEpochAtSlot,
55
computeStartSlotAtEpoch,
6-
DataAvailableStatus,
76
ExecutionPayloadStatus,
7+
DataAvailableStatus,
88
processSlots,
99
stateTransition,
1010
} from "@lodestar/state-transition";

0 commit comments

Comments
 (0)