@lodestar/beacon-node 1.30.0 → 1.31.0-dev.065cb80811
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api/impl/beacon/blocks/index.d.ts +1 -1
- package/lib/api/impl/beacon/blocks/index.js +4 -2
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/config/index.d.ts +1 -1
- package/lib/api/impl/config/index.js +5 -0
- package/lib/api/impl/config/index.js.map +1 -1
- package/lib/api/impl/validator/index.js +11 -33
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/historicalState/types.d.ts +2 -2
- package/lib/chain/archiveStore/utils/archiveBlocks.js +1 -2
- package/lib/chain/archiveStore/utils/archiveBlocks.js.map +1 -1
- package/lib/chain/blocks/blockInput/blockInput.d.ts +166 -0
- package/lib/chain/blocks/blockInput/blockInput.js +538 -0
- package/lib/chain/blocks/blockInput/blockInput.js.map +1 -0
- package/lib/chain/blocks/blockInput/errors.d.ts +38 -0
- package/lib/chain/blocks/blockInput/errors.js +17 -0
- package/lib/chain/blocks/blockInput/errors.js.map +1 -0
- package/lib/chain/blocks/blockInput/index.d.ts +5 -0
- package/lib/chain/blocks/blockInput/index.js +5 -0
- package/lib/chain/blocks/blockInput/index.js.map +1 -0
- package/lib/chain/blocks/blockInput/types.d.ts +117 -0
- package/lib/chain/blocks/blockInput/types.js +19 -0
- package/lib/chain/blocks/blockInput/types.js.map +1 -0
- package/lib/chain/blocks/blockInput/utils.d.ts +9 -0
- package/lib/chain/blocks/blockInput/utils.js +35 -0
- package/lib/chain/blocks/blockInput/utils.js.map +1 -0
- package/lib/chain/blocks/importBlock.d.ts +5 -0
- package/lib/chain/blocks/importBlock.js +27 -2
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/chain.d.ts +2 -0
- package/lib/chain/chain.js +16 -10
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/initState.js +11 -0
- package/lib/chain/initState.js.map +1 -1
- package/lib/chain/interface.d.ts +2 -0
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/opPools/aggregatedAttestationPool.d.ts +20 -14
- package/lib/chain/opPools/aggregatedAttestationPool.js +41 -49
- package/lib/chain/opPools/aggregatedAttestationPool.js.map +1 -1
- package/lib/chain/opPools/syncContributionAndProofPool.d.ts +11 -4
- package/lib/chain/opPools/syncContributionAndProofPool.js +50 -8
- package/lib/chain/opPools/syncContributionAndProofPool.js.map +1 -1
- package/lib/chain/opPools/types.d.ts +1 -1
- package/lib/chain/opPools/types.js +1 -1
- package/lib/chain/opPools/types.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +2 -2
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/seenCache/seenAggregateAndProof.d.ts +3 -3
- package/lib/chain/seenCache/seenAggregateAndProof.js +22 -10
- package/lib/chain/seenCache/seenAggregateAndProof.js.map +1 -1
- package/lib/chain/seenCache/seenAttesters.js +20 -11
- package/lib/chain/seenCache/seenAttesters.js.map +1 -1
- package/lib/chain/seenCache/seenBlockInput.d.ts +84 -0
- package/lib/chain/seenCache/seenBlockInput.js +225 -0
- package/lib/chain/seenCache/seenBlockInput.js.map +1 -0
- package/lib/chain/validation/aggregateAndProof.js +10 -2
- package/lib/chain/validation/aggregateAndProof.js.map +1 -1
- package/lib/chain/validation/attestation.js +8 -0
- package/lib/chain/validation/attestation.js.map +1 -1
- package/lib/chain/validation/blobSidecar.js +2 -2
- package/lib/chain/validation/blobSidecar.js.map +1 -1
- package/lib/chain/validation/block.js +2 -2
- package/lib/chain/validation/block.js.map +1 -1
- package/lib/chain/validatorMonitor.d.ts +3 -3
- package/lib/chain/validatorMonitor.js +273 -56
- package/lib/chain/validatorMonitor.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +39 -70
- package/lib/metrics/metrics/lodestar.js +72 -215
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- package/lib/network/core/networkCore.d.ts +2 -1
- package/lib/network/core/networkCore.js.map +1 -1
- package/lib/network/core/networkCoreWorkerHandler.d.ts +2 -1
- package/lib/network/core/networkCoreWorkerHandler.js.map +1 -1
- package/lib/network/core/types.d.ts +3 -2
- package/lib/network/network.js +3 -3
- package/lib/network/network.js.map +1 -1
- package/lib/network/peers/score/interface.d.ts +1 -1
- package/lib/network/peers/score/interface.js.map +1 -1
- package/lib/network/peers/score/store.d.ts +2 -1
- package/lib/network/peers/score/store.js +1 -1
- package/lib/network/peers/score/store.js.map +1 -1
- package/lib/network/processor/gossipHandlers.js +5 -2
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/index.d.ts +1 -6
- package/lib/network/processor/index.js +0 -14
- package/lib/network/processor/index.js.map +1 -1
- package/lib/node/nodejs.js +1 -1
- package/lib/node/nodejs.js.map +1 -1
- package/lib/util/graffiti.d.ts +3 -2
- package/lib/util/graffiti.js +2 -2
- package/lib/util/graffiti.js.map +1 -1
- package/package.json +15 -15
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { ForkName, ForkPreDeneb } from "@lodestar/params";
|
|
2
|
+
import { BlobIndex, ColumnIndex, SignedBeaconBlock, Slot, deneb, fulu } from "@lodestar/types";
|
|
3
|
+
import { VersionedHashes } from "../../../execution/index.js";
|
|
4
|
+
import { AddBlob, AddBlock, AddColumn, BlobMeta, BlobWithSource, BlockInputInit, ColumnMeta, ColumnWithSource, CreateBlockInputMeta, DAData, DAType, IBlockInput, LogMetaBasic, LogMetaBlobs, LogMetaColumns, PromiseParts, SourceMeta } from "./types.js";
|
|
5
|
+
export type BlockInput = BlockInputPreData | BlockInputBlobs | BlockInputColumns;
|
|
6
|
+
export declare function isBlockInputPreDeneb(blockInput: IBlockInput): blockInput is BlockInputPreData;
|
|
7
|
+
export declare function isBlockInputBlobs(blockInput: IBlockInput): blockInput is BlockInputBlobs;
|
|
8
|
+
export declare function isBlockInputColumns(blockInput: IBlockInput): blockInput is BlockInputColumns;
|
|
9
|
+
type BlockInputState<F extends ForkName> = {
|
|
10
|
+
hasBlock: false;
|
|
11
|
+
hasAllData: false;
|
|
12
|
+
} | {
|
|
13
|
+
hasBlock: false;
|
|
14
|
+
hasAllData: true;
|
|
15
|
+
} | {
|
|
16
|
+
hasBlock: true;
|
|
17
|
+
hasAllData: false;
|
|
18
|
+
block: SignedBeaconBlock<F>;
|
|
19
|
+
source: SourceMeta;
|
|
20
|
+
} | {
|
|
21
|
+
hasBlock: true;
|
|
22
|
+
hasAllData: true;
|
|
23
|
+
block: SignedBeaconBlock<F>;
|
|
24
|
+
source: SourceMeta;
|
|
25
|
+
timeCompleteSec: number;
|
|
26
|
+
};
|
|
27
|
+
declare abstract class AbstractBlockInput<F extends ForkName = ForkName, TData extends DAData = DAData> implements IBlockInput<F, TData> {
|
|
28
|
+
abstract type: DAType;
|
|
29
|
+
daOutOfRange: boolean;
|
|
30
|
+
timeCreatedSec: number;
|
|
31
|
+
forkName: ForkName;
|
|
32
|
+
slot: Slot;
|
|
33
|
+
blockRootHex: string;
|
|
34
|
+
parentRootHex: string;
|
|
35
|
+
abstract state: BlockInputState<F>;
|
|
36
|
+
protected blockPromise: PromiseParts<SignedBeaconBlock<F>>;
|
|
37
|
+
protected dataPromise: PromiseParts<TData>;
|
|
38
|
+
constructor(init: BlockInputInit);
|
|
39
|
+
abstract addBlock(props: AddBlock<F>): void;
|
|
40
|
+
hasBlock(): boolean;
|
|
41
|
+
getBlock(): SignedBeaconBlock<F>;
|
|
42
|
+
getBlockSource(): SourceMeta;
|
|
43
|
+
hasAllData(): boolean;
|
|
44
|
+
hasBlockAndAllData(): boolean;
|
|
45
|
+
getLogMeta(): LogMetaBasic;
|
|
46
|
+
getTimeComplete(): number;
|
|
47
|
+
waitForBlock(timeout: number, signal?: AbortSignal): Promise<SignedBeaconBlock<F>>;
|
|
48
|
+
waitForAllData(timeout: number, signal?: AbortSignal): Promise<TData>;
|
|
49
|
+
waitForBlockAndAllData(timeout: number, signal?: AbortSignal): Promise<this>;
|
|
50
|
+
}
|
|
51
|
+
type BlockInputPreDataState = {
|
|
52
|
+
hasBlock: true;
|
|
53
|
+
hasAllData: true;
|
|
54
|
+
block: SignedBeaconBlock<ForkPreDeneb>;
|
|
55
|
+
source: SourceMeta;
|
|
56
|
+
timeCompleteSec: number;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Pre-DA, BlockInput only has a single state.
|
|
60
|
+
* - the block simply exists
|
|
61
|
+
*/
|
|
62
|
+
export declare class BlockInputPreData extends AbstractBlockInput<ForkPreDeneb, null> {
|
|
63
|
+
type: DAType.PreData;
|
|
64
|
+
state: BlockInputPreDataState;
|
|
65
|
+
private constructor();
|
|
66
|
+
static createFromBlock(props: AddBlock & CreateBlockInputMeta): BlockInputPreData;
|
|
67
|
+
addBlock(_: AddBlock): void;
|
|
68
|
+
}
|
|
69
|
+
export type ForkBlobsDA = ForkName.deneb | ForkName.electra;
|
|
70
|
+
type BlockInputBlobsState = {
|
|
71
|
+
hasBlock: true;
|
|
72
|
+
hasAllData: true;
|
|
73
|
+
versionedHashes: VersionedHashes;
|
|
74
|
+
block: SignedBeaconBlock<ForkBlobsDA>;
|
|
75
|
+
source: SourceMeta;
|
|
76
|
+
timeCompleteSec: number;
|
|
77
|
+
} | {
|
|
78
|
+
hasBlock: true;
|
|
79
|
+
hasAllData: false;
|
|
80
|
+
versionedHashes: VersionedHashes;
|
|
81
|
+
block: SignedBeaconBlock<ForkBlobsDA>;
|
|
82
|
+
source: SourceMeta;
|
|
83
|
+
} | {
|
|
84
|
+
hasBlock: false;
|
|
85
|
+
hasAllData: false;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* With blobs, BlockInput has several states:
|
|
89
|
+
* - The block is seen and all blobs are seen
|
|
90
|
+
* - The block is seen and all blobs are not yet seen
|
|
91
|
+
* - The block is yet not seen and its unknown if all blobs are seen
|
|
92
|
+
*/
|
|
93
|
+
export declare class BlockInputBlobs extends AbstractBlockInput<ForkBlobsDA, deneb.BlobSidecars> {
|
|
94
|
+
type: DAType.Blobs;
|
|
95
|
+
state: BlockInputBlobsState;
|
|
96
|
+
private blobsCache;
|
|
97
|
+
private constructor();
|
|
98
|
+
static createFromBlock(props: AddBlock<ForkBlobsDA> & CreateBlockInputMeta): BlockInputBlobs;
|
|
99
|
+
static createFromBlob(props: AddBlob & CreateBlockInputMeta): BlockInputBlobs;
|
|
100
|
+
getLogMeta(): LogMetaBlobs;
|
|
101
|
+
addBlock({ blockRootHex, block, source }: AddBlock<ForkBlobsDA>): void;
|
|
102
|
+
hasBlob(blobIndex: BlobIndex): boolean;
|
|
103
|
+
addBlob({ blockRootHex, blobSidecar, source, peerIdStr, seenTimestampSec }: AddBlob): void;
|
|
104
|
+
getVersionedHashes(): VersionedHashes;
|
|
105
|
+
getMissingBlobMeta(): BlobMeta[];
|
|
106
|
+
getAllBlobsWithSource(): BlobWithSource[];
|
|
107
|
+
getBlobs(): deneb.BlobSidecars;
|
|
108
|
+
}
|
|
109
|
+
export type ForkColumnsDA = ForkName.fulu;
|
|
110
|
+
type BlockInputColumnsState = {
|
|
111
|
+
hasBlock: true;
|
|
112
|
+
hasAllData: true;
|
|
113
|
+
versionedHashes: VersionedHashes;
|
|
114
|
+
block: SignedBeaconBlock<ForkColumnsDA>;
|
|
115
|
+
source: SourceMeta;
|
|
116
|
+
timeCompleteSec: number;
|
|
117
|
+
} | {
|
|
118
|
+
hasBlock: true;
|
|
119
|
+
hasAllData: false;
|
|
120
|
+
versionedHashes: VersionedHashes;
|
|
121
|
+
block: SignedBeaconBlock<ForkColumnsDA>;
|
|
122
|
+
source: SourceMeta;
|
|
123
|
+
} | {
|
|
124
|
+
hasBlock: false;
|
|
125
|
+
hasAllData: true;
|
|
126
|
+
versionedHashes: VersionedHashes;
|
|
127
|
+
} | {
|
|
128
|
+
hasBlock: false;
|
|
129
|
+
hasAllData: false;
|
|
130
|
+
versionedHashes: VersionedHashes;
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* With columns, BlockInput has several states:
|
|
134
|
+
* - The block is seen and all required sampled columns are seen
|
|
135
|
+
* - The block is seen and all required sampled columns are not yet seen
|
|
136
|
+
* - The block is not yet seen and all required sampled columns are seen
|
|
137
|
+
* - The block is not yet seen and all required sampled columns are not yet seen
|
|
138
|
+
*/
|
|
139
|
+
export declare class BlockInputColumns extends AbstractBlockInput<ForkColumnsDA, fulu.DataColumnSidecars> {
|
|
140
|
+
type: DAType.Columns;
|
|
141
|
+
state: BlockInputColumnsState;
|
|
142
|
+
private columnsCache;
|
|
143
|
+
private readonly sampledColumns;
|
|
144
|
+
private readonly custodyColumns;
|
|
145
|
+
private constructor();
|
|
146
|
+
static createFromBlock(props: AddBlock<ForkColumnsDA> & CreateBlockInputMeta & {
|
|
147
|
+
sampledColumns: ColumnIndex[];
|
|
148
|
+
custodyColumns: ColumnIndex[];
|
|
149
|
+
}): BlockInputColumns;
|
|
150
|
+
static createFromColumn(props: AddColumn & CreateBlockInputMeta & {
|
|
151
|
+
sampledColumns: ColumnIndex[];
|
|
152
|
+
custodyColumns: ColumnIndex[];
|
|
153
|
+
}): BlockInputColumns;
|
|
154
|
+
getLogMeta(): LogMetaColumns;
|
|
155
|
+
addBlock(props: AddBlock<ForkColumnsDA>): void;
|
|
156
|
+
addColumn({ blockRootHex, columnSidecar, source, seenTimestampSec, peerIdStr }: AddColumn): void;
|
|
157
|
+
hasColumn(columnIndex: number): boolean;
|
|
158
|
+
getVersionedHashes(): VersionedHashes;
|
|
159
|
+
getCustodyColumns(): fulu.DataColumnSidecars;
|
|
160
|
+
getSampledColumns(): fulu.DataColumnSidecars;
|
|
161
|
+
getAllColumnsWithSource(): ColumnWithSource[];
|
|
162
|
+
getAllColumns(): fulu.DataColumnSidecars;
|
|
163
|
+
getMissingSampledColumnMeta(): ColumnMeta[];
|
|
164
|
+
}
|
|
165
|
+
export {};
|
|
166
|
+
//# sourceMappingURL=blockInput.d.ts.map
|
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
import { fromHex, prettyBytes, toRootHex, withTimeout } from "@lodestar/utils";
|
|
2
|
+
import { kzgCommitmentToVersionedHash } from "../../../util/blobs.js";
|
|
3
|
+
import { BlockInputError, BlockInputErrorCode } from "./errors.js";
|
|
4
|
+
import { DAType, } from "./types.js";
|
|
5
|
+
export function isBlockInputPreDeneb(blockInput) {
|
|
6
|
+
return blockInput.type === DAType.PreData;
|
|
7
|
+
}
|
|
8
|
+
export function isBlockInputBlobs(blockInput) {
|
|
9
|
+
return blockInput.type === DAType.Blobs;
|
|
10
|
+
}
|
|
11
|
+
export function isBlockInputColumns(blockInput) {
|
|
12
|
+
return blockInput.type === DAType.Columns;
|
|
13
|
+
}
|
|
14
|
+
function createPromise() {
|
|
15
|
+
let resolve;
|
|
16
|
+
let reject;
|
|
17
|
+
const promise = new Promise((_resolve, _reject) => {
|
|
18
|
+
resolve = _resolve;
|
|
19
|
+
reject = _reject;
|
|
20
|
+
});
|
|
21
|
+
return {
|
|
22
|
+
promise,
|
|
23
|
+
resolve,
|
|
24
|
+
reject,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
class AbstractBlockInput {
|
|
28
|
+
constructor(init) {
|
|
29
|
+
this.blockPromise = createPromise();
|
|
30
|
+
this.dataPromise = createPromise();
|
|
31
|
+
this.daOutOfRange = init.daOutOfRange;
|
|
32
|
+
this.timeCreatedSec = init.timeCreated;
|
|
33
|
+
this.forkName = init.forkName;
|
|
34
|
+
this.slot = init.slot;
|
|
35
|
+
this.blockRootHex = init.blockRootHex;
|
|
36
|
+
this.parentRootHex = init.parentRootHex;
|
|
37
|
+
}
|
|
38
|
+
hasBlock() {
|
|
39
|
+
return this.state.hasBlock;
|
|
40
|
+
}
|
|
41
|
+
getBlock() {
|
|
42
|
+
if (!this.state.hasBlock) {
|
|
43
|
+
throw new BlockInputError({
|
|
44
|
+
code: BlockInputErrorCode.MISSING_BLOCK,
|
|
45
|
+
blockRoot: this.blockRootHex,
|
|
46
|
+
}, "Cannot getBlock from BlockInput without a block");
|
|
47
|
+
}
|
|
48
|
+
return this.state.block;
|
|
49
|
+
}
|
|
50
|
+
getBlockSource() {
|
|
51
|
+
if (!this.state.hasBlock) {
|
|
52
|
+
throw new BlockInputError({
|
|
53
|
+
code: BlockInputErrorCode.MISSING_BLOCK,
|
|
54
|
+
blockRoot: this.blockRootHex,
|
|
55
|
+
}, "Cannot getBlockSource from BlockInput without a block");
|
|
56
|
+
}
|
|
57
|
+
return this.state.source;
|
|
58
|
+
}
|
|
59
|
+
hasAllData() {
|
|
60
|
+
return this.state.hasAllData;
|
|
61
|
+
}
|
|
62
|
+
hasBlockAndAllData() {
|
|
63
|
+
return this.state.hasBlock && this.state.hasAllData;
|
|
64
|
+
}
|
|
65
|
+
getLogMeta() {
|
|
66
|
+
return {
|
|
67
|
+
blockRoot: prettyBytes(this.blockRootHex),
|
|
68
|
+
slot: this.slot,
|
|
69
|
+
timeCreatedSec: this.timeCreatedSec,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
getTimeComplete() {
|
|
73
|
+
if (!this.state.hasBlock || !this.state.hasAllData) {
|
|
74
|
+
throw new BlockInputError({
|
|
75
|
+
code: BlockInputErrorCode.MISSING_TIME_COMPLETE,
|
|
76
|
+
blockRoot: this.blockRootHex,
|
|
77
|
+
}, "Cannot getTimeComplete from BlockInput without a block and data");
|
|
78
|
+
}
|
|
79
|
+
return this.state.timeCompleteSec;
|
|
80
|
+
}
|
|
81
|
+
waitForBlock(timeout, signal) {
|
|
82
|
+
if (!this.state.hasBlock) {
|
|
83
|
+
return withTimeout(() => this.blockPromise.promise, timeout, signal);
|
|
84
|
+
}
|
|
85
|
+
return Promise.resolve(this.state.block);
|
|
86
|
+
}
|
|
87
|
+
waitForAllData(timeout, signal) {
|
|
88
|
+
return withTimeout(() => this.dataPromise.promise, timeout, signal);
|
|
89
|
+
}
|
|
90
|
+
async waitForBlockAndAllData(timeout, signal) {
|
|
91
|
+
if (!this.state.hasBlock || !this.state.hasAllData) {
|
|
92
|
+
await withTimeout(() => Promise.all([this.blockPromise.promise, this.dataPromise.promise]), timeout, signal);
|
|
93
|
+
}
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Pre-DA, BlockInput only has a single state.
|
|
99
|
+
* - the block simply exists
|
|
100
|
+
*/
|
|
101
|
+
export class BlockInputPreData extends AbstractBlockInput {
|
|
102
|
+
constructor(init, state) {
|
|
103
|
+
super(init);
|
|
104
|
+
this.type = DAType.PreData;
|
|
105
|
+
this.state = state;
|
|
106
|
+
}
|
|
107
|
+
static createFromBlock(props) {
|
|
108
|
+
const init = {
|
|
109
|
+
daOutOfRange: props.daOutOfRange,
|
|
110
|
+
timeCreated: props.source.seenTimestampSec,
|
|
111
|
+
forkName: props.forkName,
|
|
112
|
+
slot: props.block.message.slot,
|
|
113
|
+
blockRootHex: props.blockRootHex,
|
|
114
|
+
parentRootHex: toRootHex(props.block.message.parentRoot),
|
|
115
|
+
};
|
|
116
|
+
const state = {
|
|
117
|
+
hasBlock: true,
|
|
118
|
+
hasAllData: true,
|
|
119
|
+
block: props.block,
|
|
120
|
+
source: props.source,
|
|
121
|
+
timeCompleteSec: props.source.seenTimestampSec,
|
|
122
|
+
};
|
|
123
|
+
return new BlockInputPreData(init, state);
|
|
124
|
+
}
|
|
125
|
+
addBlock(_) {
|
|
126
|
+
throw new BlockInputError({
|
|
127
|
+
code: BlockInputErrorCode.INVALID_CONSTRUCTION,
|
|
128
|
+
blockRoot: this.blockRootHex,
|
|
129
|
+
}, "Cannot addBlock to BlockInputPreData");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* With blobs, BlockInput has several states:
|
|
134
|
+
* - The block is seen and all blobs are seen
|
|
135
|
+
* - The block is seen and all blobs are not yet seen
|
|
136
|
+
* - The block is yet not seen and its unknown if all blobs are seen
|
|
137
|
+
*/
|
|
138
|
+
export class BlockInputBlobs extends AbstractBlockInput {
|
|
139
|
+
constructor(init, state) {
|
|
140
|
+
super(init);
|
|
141
|
+
this.type = DAType.Blobs;
|
|
142
|
+
this.blobsCache = new Map();
|
|
143
|
+
this.state = state;
|
|
144
|
+
}
|
|
145
|
+
static createFromBlock(props) {
|
|
146
|
+
const hasAllData = props.daOutOfRange || props.block.message.body.blobKzgCommitments.length === 0;
|
|
147
|
+
const state = {
|
|
148
|
+
hasBlock: true,
|
|
149
|
+
hasAllData,
|
|
150
|
+
versionedHashes: props.block.message.body.blobKzgCommitments.map(kzgCommitmentToVersionedHash),
|
|
151
|
+
block: props.block,
|
|
152
|
+
source: props.source,
|
|
153
|
+
timeCompleteSec: hasAllData ? props.source.seenTimestampSec : undefined,
|
|
154
|
+
};
|
|
155
|
+
const init = {
|
|
156
|
+
daOutOfRange: props.daOutOfRange,
|
|
157
|
+
timeCreated: props.source.seenTimestampSec,
|
|
158
|
+
forkName: props.forkName,
|
|
159
|
+
slot: props.block.message.slot,
|
|
160
|
+
blockRootHex: props.blockRootHex,
|
|
161
|
+
parentRootHex: toRootHex(props.block.message.parentRoot),
|
|
162
|
+
};
|
|
163
|
+
const blockInput = new BlockInputBlobs(init, state);
|
|
164
|
+
blockInput.blockPromise.resolve(props.block);
|
|
165
|
+
if (hasAllData) {
|
|
166
|
+
blockInput.dataPromise.resolve([]);
|
|
167
|
+
}
|
|
168
|
+
return blockInput;
|
|
169
|
+
}
|
|
170
|
+
static createFromBlob(props) {
|
|
171
|
+
const state = {
|
|
172
|
+
hasBlock: false,
|
|
173
|
+
hasAllData: false,
|
|
174
|
+
};
|
|
175
|
+
const init = {
|
|
176
|
+
daOutOfRange: props.daOutOfRange,
|
|
177
|
+
timeCreated: props.seenTimestampSec,
|
|
178
|
+
forkName: props.forkName,
|
|
179
|
+
blockRootHex: props.blockRootHex,
|
|
180
|
+
parentRootHex: toRootHex(props.blobSidecar.signedBlockHeader.message.parentRoot),
|
|
181
|
+
slot: props.blobSidecar.signedBlockHeader.message.slot,
|
|
182
|
+
};
|
|
183
|
+
const blockInput = new BlockInputBlobs(init, state);
|
|
184
|
+
blockInput.blobsCache.set(props.blobSidecar.index, {
|
|
185
|
+
blobSidecar: props.blobSidecar,
|
|
186
|
+
source: props.source,
|
|
187
|
+
seenTimestampSec: props.seenTimestampSec,
|
|
188
|
+
peerIdStr: props.peerIdStr,
|
|
189
|
+
});
|
|
190
|
+
return blockInput;
|
|
191
|
+
}
|
|
192
|
+
getLogMeta() {
|
|
193
|
+
return {
|
|
194
|
+
blockRoot: prettyBytes(this.blockRootHex),
|
|
195
|
+
slot: this.slot,
|
|
196
|
+
timeCreatedSec: this.timeCreatedSec,
|
|
197
|
+
expectedBlobs: this.state.hasBlock ? this.state.block.message.body.blobKzgCommitments.length : "unknown",
|
|
198
|
+
receivedBlobs: this.blobsCache.size,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
addBlock({ blockRootHex, block, source }) {
|
|
202
|
+
if (this.state.hasBlock) {
|
|
203
|
+
throw new BlockInputError({
|
|
204
|
+
code: BlockInputErrorCode.INVALID_CONSTRUCTION,
|
|
205
|
+
blockRoot: this.blockRootHex,
|
|
206
|
+
}, "Cannot addBlock to BlockInputBlobs after it already has a block");
|
|
207
|
+
}
|
|
208
|
+
// this check suffices for checking slot, parentRoot, and forkName
|
|
209
|
+
if (blockRootHex !== this.blockRootHex) {
|
|
210
|
+
throw new BlockInputError({
|
|
211
|
+
code: BlockInputErrorCode.MISMATCHED_ROOT_HEX,
|
|
212
|
+
blockInputRoot: this.blockRootHex,
|
|
213
|
+
mismatchedRoot: blockRootHex,
|
|
214
|
+
source: source.source,
|
|
215
|
+
peerId: `${source.peerIdStr}`,
|
|
216
|
+
}, "addBlock blockRootHex does not match BlockInput.blockRootHex");
|
|
217
|
+
}
|
|
218
|
+
for (const { blobSidecar } of this.blobsCache.values()) {
|
|
219
|
+
if (!blockAndBlobArePaired(block, blobSidecar)) {
|
|
220
|
+
this.blobsCache.delete(blobSidecar.index);
|
|
221
|
+
// TODO: (@matthewkeil) spec says to ignore invalid blobs but should we downscore the peer maybe?
|
|
222
|
+
// this.logger?.error(`Removing blobIndex=${blobSidecar.index} from BlockInput`, {}, err);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const hasAllData = this.blobsCache.size === block.message.body.blobKzgCommitments.length;
|
|
226
|
+
this.state = {
|
|
227
|
+
...this.state,
|
|
228
|
+
hasBlock: true,
|
|
229
|
+
hasAllData,
|
|
230
|
+
block,
|
|
231
|
+
versionedHashes: block.message.body.blobKzgCommitments.map(kzgCommitmentToVersionedHash),
|
|
232
|
+
source,
|
|
233
|
+
timeCompleteSec: hasAllData ? source.seenTimestampSec : undefined,
|
|
234
|
+
};
|
|
235
|
+
this.blockPromise.resolve(block);
|
|
236
|
+
if (hasAllData) {
|
|
237
|
+
this.dataPromise.resolve(this.getBlobs());
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
hasBlob(blobIndex) {
|
|
241
|
+
return this.blobsCache.has(blobIndex);
|
|
242
|
+
}
|
|
243
|
+
addBlob({ blockRootHex, blobSidecar, source, peerIdStr, seenTimestampSec }) {
|
|
244
|
+
if (this.state.hasAllData) {
|
|
245
|
+
throw new BlockInputError({
|
|
246
|
+
code: BlockInputErrorCode.INVALID_CONSTRUCTION,
|
|
247
|
+
blockRoot: this.blockRootHex,
|
|
248
|
+
}, "Cannot addBlob to BlockInputBlobs after it already is complete");
|
|
249
|
+
}
|
|
250
|
+
if (this.blobsCache.has(blobSidecar.index)) {
|
|
251
|
+
throw new BlockInputError({
|
|
252
|
+
code: BlockInputErrorCode.INVALID_CONSTRUCTION,
|
|
253
|
+
blockRoot: this.blockRootHex,
|
|
254
|
+
}, "Cannot addBlob to BlockInputBlobs with duplicate blobIndex");
|
|
255
|
+
}
|
|
256
|
+
// this check suffices for checking slot, parentRoot, and forkName
|
|
257
|
+
if (blockRootHex !== this.blockRootHex) {
|
|
258
|
+
throw new BlockInputError({
|
|
259
|
+
code: BlockInputErrorCode.MISMATCHED_ROOT_HEX,
|
|
260
|
+
blockInputRoot: this.blockRootHex,
|
|
261
|
+
mismatchedRoot: blockRootHex,
|
|
262
|
+
source: source,
|
|
263
|
+
peerId: `${peerIdStr}`,
|
|
264
|
+
}, "Blob BeaconBlockHeader blockRootHex does not match BlockInput.blockRootHex");
|
|
265
|
+
}
|
|
266
|
+
if (this.state.hasBlock) {
|
|
267
|
+
assertBlockAndBlobArePaired(this.blockRootHex, this.state.block, blobSidecar);
|
|
268
|
+
}
|
|
269
|
+
this.blobsCache.set(blobSidecar.index, { blobSidecar, source, seenTimestampSec, peerIdStr });
|
|
270
|
+
if (this.state.hasBlock && this.blobsCache.size === this.state.block.message.body.blobKzgCommitments.length) {
|
|
271
|
+
this.state = {
|
|
272
|
+
...this.state,
|
|
273
|
+
hasAllData: true,
|
|
274
|
+
timeCompleteSec: seenTimestampSec,
|
|
275
|
+
};
|
|
276
|
+
this.dataPromise.resolve([...this.blobsCache.values()].map(({ blobSidecar }) => blobSidecar));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
getVersionedHashes() {
|
|
280
|
+
if (!this.state.hasBlock) {
|
|
281
|
+
throw new BlockInputError({
|
|
282
|
+
code: BlockInputErrorCode.INCOMPLETE_DATA,
|
|
283
|
+
...this.getLogMeta(),
|
|
284
|
+
}, "Cannot get versioned hashes. Block is unknown");
|
|
285
|
+
}
|
|
286
|
+
return this.state.versionedHashes;
|
|
287
|
+
}
|
|
288
|
+
getMissingBlobMeta() {
|
|
289
|
+
if (!this.state.hasBlock) {
|
|
290
|
+
throw new BlockInputError({
|
|
291
|
+
code: BlockInputErrorCode.INCOMPLETE_DATA,
|
|
292
|
+
...this.getLogMeta(),
|
|
293
|
+
}, "Cannot get missing blobs. Block is unknown");
|
|
294
|
+
}
|
|
295
|
+
if (this.state.hasAllData) {
|
|
296
|
+
return [];
|
|
297
|
+
}
|
|
298
|
+
const blobMeta = [];
|
|
299
|
+
const versionedHashes = this.state.versionedHashes;
|
|
300
|
+
for (let index = 0; index < versionedHashes.length; index++) {
|
|
301
|
+
if (!this.blobsCache.has(index)) {
|
|
302
|
+
blobMeta.push({
|
|
303
|
+
index,
|
|
304
|
+
blockRoot: fromHex(this.blockRootHex),
|
|
305
|
+
versionHash: versionedHashes[index],
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return blobMeta;
|
|
310
|
+
}
|
|
311
|
+
getAllBlobsWithSource() {
|
|
312
|
+
if (!this.state.hasAllData) {
|
|
313
|
+
throw new BlockInputError({
|
|
314
|
+
code: BlockInputErrorCode.INCOMPLETE_DATA,
|
|
315
|
+
...this.getLogMeta(),
|
|
316
|
+
}, "Cannot get all blobs. DA status is not complete");
|
|
317
|
+
}
|
|
318
|
+
return [...this.blobsCache.values()];
|
|
319
|
+
}
|
|
320
|
+
getBlobs() {
|
|
321
|
+
return this.getAllBlobsWithSource().map(({ blobSidecar }) => blobSidecar);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function blockAndBlobArePaired(block, blobSidecar) {
|
|
325
|
+
const blockCommitment = block.message.body.blobKzgCommitments[blobSidecar.index];
|
|
326
|
+
if (!blockCommitment || !blobSidecar.kzgCommitment) {
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
return Buffer.compare(blockCommitment, blobSidecar.kzgCommitment) === 0;
|
|
330
|
+
}
|
|
331
|
+
function assertBlockAndBlobArePaired(blockRootHex, block, blobSidecar) {
|
|
332
|
+
if (!blockAndBlobArePaired(block, blobSidecar)) {
|
|
333
|
+
// TODO: (@matthewkeil) should this eject the bad blob instead? No way to tell if the blob or the block
|
|
334
|
+
// has the invalid commitment. Guessing it would be the blob though because we match via block
|
|
335
|
+
// hashTreeRoot and we do not take a hashTreeRoot of the BlobSidecar
|
|
336
|
+
throw new BlockInputError({
|
|
337
|
+
code: BlockInputErrorCode.MISMATCHED_KZG_COMMITMENT,
|
|
338
|
+
blockRoot: blockRootHex,
|
|
339
|
+
slot: block.message.slot,
|
|
340
|
+
sidecarIndex: blobSidecar.index,
|
|
341
|
+
}, "BlobSidecar commitment does not match block commitment");
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* With columns, BlockInput has several states:
|
|
346
|
+
* - The block is seen and all required sampled columns are seen
|
|
347
|
+
* - The block is seen and all required sampled columns are not yet seen
|
|
348
|
+
* - The block is not yet seen and all required sampled columns are seen
|
|
349
|
+
* - The block is not yet seen and all required sampled columns are not yet seen
|
|
350
|
+
*/
|
|
351
|
+
export class BlockInputColumns extends AbstractBlockInput {
|
|
352
|
+
constructor(init, state, sampledColumns, custodyColumns) {
|
|
353
|
+
super(init);
|
|
354
|
+
this.type = DAType.Columns;
|
|
355
|
+
this.columnsCache = new Map();
|
|
356
|
+
this.state = state;
|
|
357
|
+
this.sampledColumns = sampledColumns;
|
|
358
|
+
this.custodyColumns = custodyColumns;
|
|
359
|
+
}
|
|
360
|
+
static createFromBlock(props) {
|
|
361
|
+
const hasAllData = props.daOutOfRange ||
|
|
362
|
+
props.block.message.body.blobKzgCommitments.length === 0 ||
|
|
363
|
+
props.sampledColumns.length === 0;
|
|
364
|
+
const state = {
|
|
365
|
+
hasBlock: true,
|
|
366
|
+
hasAllData,
|
|
367
|
+
versionedHashes: props.block.message.body.blobKzgCommitments.map(kzgCommitmentToVersionedHash),
|
|
368
|
+
block: props.block,
|
|
369
|
+
source: props.source,
|
|
370
|
+
timeCreated: props.source.seenTimestampSec,
|
|
371
|
+
timeCompleteSec: hasAllData ? props.source.seenTimestampSec : undefined,
|
|
372
|
+
};
|
|
373
|
+
const init = {
|
|
374
|
+
daOutOfRange: props.daOutOfRange,
|
|
375
|
+
timeCreated: props.source.seenTimestampSec,
|
|
376
|
+
forkName: props.forkName,
|
|
377
|
+
blockRootHex: props.blockRootHex,
|
|
378
|
+
parentRootHex: toRootHex(props.block.message.parentRoot),
|
|
379
|
+
slot: props.block.message.slot,
|
|
380
|
+
};
|
|
381
|
+
const blockInput = new BlockInputColumns(init, state, props.sampledColumns, props.custodyColumns);
|
|
382
|
+
blockInput.blockPromise.resolve(props.block);
|
|
383
|
+
if (hasAllData) {
|
|
384
|
+
blockInput.dataPromise.resolve([]);
|
|
385
|
+
}
|
|
386
|
+
return blockInput;
|
|
387
|
+
}
|
|
388
|
+
static createFromColumn(props) {
|
|
389
|
+
const hasAllData = props.sampledColumns.length === 0;
|
|
390
|
+
const state = {
|
|
391
|
+
hasBlock: false,
|
|
392
|
+
hasAllData,
|
|
393
|
+
versionedHashes: props.columnSidecar.kzgCommitments.map(kzgCommitmentToVersionedHash),
|
|
394
|
+
};
|
|
395
|
+
const init = {
|
|
396
|
+
daOutOfRange: false,
|
|
397
|
+
timeCreated: props.seenTimestampSec,
|
|
398
|
+
forkName: props.forkName,
|
|
399
|
+
blockRootHex: props.blockRootHex,
|
|
400
|
+
parentRootHex: toRootHex(props.columnSidecar.signedBlockHeader.message.parentRoot),
|
|
401
|
+
slot: props.columnSidecar.signedBlockHeader.message.slot,
|
|
402
|
+
};
|
|
403
|
+
const blockInput = new BlockInputColumns(init, state, props.sampledColumns, props.custodyColumns);
|
|
404
|
+
if (hasAllData) {
|
|
405
|
+
blockInput.dataPromise.resolve([]);
|
|
406
|
+
}
|
|
407
|
+
return blockInput;
|
|
408
|
+
}
|
|
409
|
+
getLogMeta() {
|
|
410
|
+
return {
|
|
411
|
+
blockRoot: prettyBytes(this.blockRootHex),
|
|
412
|
+
slot: this.slot,
|
|
413
|
+
timeCreatedSec: this.timeCreatedSec,
|
|
414
|
+
expectedColumns: this.state.hasBlock && this.state.block.message.body.blobKzgCommitments.length === 0
|
|
415
|
+
? 0
|
|
416
|
+
: this.sampledColumns.length,
|
|
417
|
+
receivedColumns: this.getSampledColumns().length,
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
addBlock(props) {
|
|
421
|
+
if (this.state.hasBlock) {
|
|
422
|
+
throw new BlockInputError({
|
|
423
|
+
code: BlockInputErrorCode.INVALID_CONSTRUCTION,
|
|
424
|
+
blockRoot: this.blockRootHex,
|
|
425
|
+
}, "Cannot addBlock to BlockInputColumns after it already has a block");
|
|
426
|
+
}
|
|
427
|
+
if (props.blockRootHex !== this.blockRootHex) {
|
|
428
|
+
throw new BlockInputError({
|
|
429
|
+
code: BlockInputErrorCode.MISMATCHED_ROOT_HEX,
|
|
430
|
+
blockInputRoot: this.blockRootHex,
|
|
431
|
+
mismatchedRoot: props.blockRootHex,
|
|
432
|
+
source: props.source.source,
|
|
433
|
+
peerId: `${props.source.peerIdStr}`,
|
|
434
|
+
}, "addBlock blockRootHex does not match BlockInput.blockRootHex");
|
|
435
|
+
}
|
|
436
|
+
for (const { columnSidecar } of this.columnsCache.values()) {
|
|
437
|
+
if (!blockAndColumnArePaired(props.block, columnSidecar)) {
|
|
438
|
+
this.columnsCache.delete(columnSidecar.index);
|
|
439
|
+
// this.logger?.error(`Removing columnIndex=${columnSidecar.index} from BlockInput`, {}, err);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
const hasAllData = props.block.message.body.blobKzgCommitments.length === 0 || this.state.hasAllData;
|
|
443
|
+
this.state = {
|
|
444
|
+
...this.state,
|
|
445
|
+
hasBlock: true,
|
|
446
|
+
hasAllData,
|
|
447
|
+
block: props.block,
|
|
448
|
+
source: props.source,
|
|
449
|
+
timeCompleteSec: hasAllData ? props.source.seenTimestampSec : undefined,
|
|
450
|
+
};
|
|
451
|
+
this.blockPromise.resolve(props.block);
|
|
452
|
+
}
|
|
453
|
+
addColumn({ blockRootHex, columnSidecar, source, seenTimestampSec, peerIdStr }) {
|
|
454
|
+
if (blockRootHex !== this.blockRootHex) {
|
|
455
|
+
throw new BlockInputError({
|
|
456
|
+
code: BlockInputErrorCode.MISMATCHED_ROOT_HEX,
|
|
457
|
+
blockInputRoot: this.blockRootHex,
|
|
458
|
+
mismatchedRoot: blockRootHex,
|
|
459
|
+
source: source,
|
|
460
|
+
peerId: `${peerIdStr}`,
|
|
461
|
+
}, "Column BeaconBlockHeader blockRootHex does not match BlockInput.blockRootHex");
|
|
462
|
+
}
|
|
463
|
+
if (this.state.hasBlock) {
|
|
464
|
+
assertBlockAndColumnArePaired(this.blockRootHex, this.state.block, columnSidecar);
|
|
465
|
+
}
|
|
466
|
+
this.columnsCache.set(columnSidecar.index, { columnSidecar, source, seenTimestampSec, peerIdStr });
|
|
467
|
+
const sampledColumns = this.getSampledColumns();
|
|
468
|
+
const hasAllData = this.state.hasAllData || sampledColumns.length === this.sampledColumns.length;
|
|
469
|
+
this.state = {
|
|
470
|
+
...this.state,
|
|
471
|
+
hasAllData: hasAllData || this.state.hasAllData,
|
|
472
|
+
timeCompleteSec: hasAllData ? seenTimestampSec : undefined,
|
|
473
|
+
};
|
|
474
|
+
if (hasAllData && sampledColumns !== null) {
|
|
475
|
+
this.dataPromise.resolve(sampledColumns);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
hasColumn(columnIndex) {
|
|
479
|
+
return this.columnsCache.has(columnIndex);
|
|
480
|
+
}
|
|
481
|
+
getVersionedHashes() {
|
|
482
|
+
return this.state.versionedHashes;
|
|
483
|
+
}
|
|
484
|
+
getCustodyColumns() {
|
|
485
|
+
const columns = [];
|
|
486
|
+
for (const index of this.custodyColumns) {
|
|
487
|
+
const column = this.columnsCache.get(index);
|
|
488
|
+
if (column) {
|
|
489
|
+
columns.push(column.columnSidecar);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return columns;
|
|
493
|
+
}
|
|
494
|
+
getSampledColumns() {
|
|
495
|
+
const columns = [];
|
|
496
|
+
for (const index of this.sampledColumns) {
|
|
497
|
+
const column = this.columnsCache.get(index);
|
|
498
|
+
if (column) {
|
|
499
|
+
columns.push(column.columnSidecar);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return columns;
|
|
503
|
+
}
|
|
504
|
+
getAllColumnsWithSource() {
|
|
505
|
+
return [...this.columnsCache.values()];
|
|
506
|
+
}
|
|
507
|
+
getAllColumns() {
|
|
508
|
+
return this.getAllColumnsWithSource().map(({ columnSidecar }) => columnSidecar);
|
|
509
|
+
}
|
|
510
|
+
getMissingSampledColumnMeta() {
|
|
511
|
+
if (this.state.hasAllData) {
|
|
512
|
+
return [];
|
|
513
|
+
}
|
|
514
|
+
const needed = [];
|
|
515
|
+
const blockRoot = fromHex(this.blockRootHex);
|
|
516
|
+
for (const index of this.sampledColumns) {
|
|
517
|
+
if (!this.columnsCache.has(index)) {
|
|
518
|
+
needed.push({ index, blockRoot });
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return needed;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
function blockAndColumnArePaired(block, columnSidecar) {
|
|
525
|
+
return (block.message.body.blobKzgCommitments.length === columnSidecar.kzgCommitments.length &&
|
|
526
|
+
block.message.body.blobKzgCommitments.every((commitment, index) => Buffer.compare(commitment, columnSidecar.kzgCommitments[index])));
|
|
527
|
+
}
|
|
528
|
+
function assertBlockAndColumnArePaired(blockRootHex, block, columnSidecar) {
|
|
529
|
+
if (!blockAndColumnArePaired(block, columnSidecar)) {
|
|
530
|
+
throw new BlockInputError({
|
|
531
|
+
code: BlockInputErrorCode.MISMATCHED_KZG_COMMITMENT,
|
|
532
|
+
blockRoot: blockRootHex,
|
|
533
|
+
slot: block.message.slot,
|
|
534
|
+
sidecarIndex: columnSidecar.index,
|
|
535
|
+
}, "DataColumnsSidecar kzgCommitment does not match block kzgCommitment");
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
//# sourceMappingURL=blockInput.js.map
|