Skip to content

Commit a543709

Browse files
authored
Remove BlockHeaderProofType union type (#725)
* Remove union type * fix tests and utils * fix more tests * remove unused method * Some test updates * comment out invalid test * Fix broken test * fix moar test * Fix integration test * address feedback
1 parent a87670b commit a543709

20 files changed

+469
-923
lines changed

packages/cli/src/util.ts

Lines changed: 1 addition & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,3 @@
1-
import { BlockHeader } from '@ethereumjs/block'
2-
import { RLP } from '@ethereumjs/rlp'
3-
import { TransactionFactory } from '@ethereumjs/tx'
4-
import { bytesToHex } from '@ethereumjs/util'
5-
import {
6-
BlockBodyContentType,
7-
BlockHeaderWithProof,
8-
EpochAccumulator,
9-
sszReceiptType,
10-
sszUnclesType,
11-
} from 'portalnetwork'
121

132
import type { BaseNetwork, NetworkId } from 'portalnetwork'
143
import type { Enr } from './rpc/schema/types.js'
@@ -62,52 +51,4 @@ export const addBootNode = async (networkId: NetworkId, baseNetwork: BaseNetwork
6251
throw new Error(`Error adding bootnode ${enr} to network \
6352
${networkId}: ${error.message ?? error}`)
6453
}
65-
}
66-
67-
export const toJSON = (contentKey: Uint8Array, res: Uint8Array) => {
68-
const contentType = contentKey[0]
69-
let content = {}
70-
switch (contentType) {
71-
case 0: {
72-
const blockHeaderWithProof = BlockHeaderWithProof.deserialize(res)
73-
const header = BlockHeader.fromRLPSerializedHeader(blockHeaderWithProof.header, {
74-
setHardfork: true,
75-
}).toJSON()
76-
const proof =
77-
blockHeaderWithProof.proof.selector === 0
78-
? []
79-
: (blockHeaderWithProof.proof.value as Uint8Array[])?.map((p) => bytesToHex(p))
80-
content = { header, proof }
81-
break
82-
}
83-
case 1: {
84-
const blockBody = BlockBodyContentType.deserialize(res)
85-
const transactions = blockBody.allTransactions.map((tx) =>
86-
TransactionFactory.fromSerializedData(tx).toJSON(),
87-
)
88-
const unclesRlp = bytesToHex(sszUnclesType.deserialize(blockBody.sszUncles))
89-
content = {
90-
transactions,
91-
uncles: {
92-
rlp: unclesRlp,
93-
count: RLP.decode(unclesRlp).length.toString(),
94-
},
95-
}
96-
break
97-
}
98-
case 2: {
99-
const receipt = sszReceiptType.deserialize(res)
100-
content = receipt
101-
break
102-
}
103-
case 3: {
104-
const epochAccumulator = EpochAccumulator.deserialize(res)
105-
content = epochAccumulator
106-
break
107-
}
108-
default: {
109-
content = {}
110-
}
111-
}
112-
return JSON.stringify(content)
113-
}
54+
}

packages/portalnetwork/src/networks/history/history.ts

Lines changed: 62 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import { Block, BlockHeader } from '@ethereumjs/block'
22
import { bytesToHex, bytesToInt, equalsBytes, hexToBytes } from '@ethereumjs/util'
33
import debug from 'debug'
44

5+
import type { BaseNetworkConfig, ContentLookupResponse, FindContentMessage } from '../../index.js'
56
import {
67
ContentMessageType,
78
FoundContent,
9+
HistoricalSummariesBlockProof,
810
MessageCodes,
911
PingPongPayloadExtensions,
1012
PortalWireMessageType,
@@ -19,19 +21,26 @@ import { BaseNetwork } from '../network.js'
1921
import { NetworkId } from '../types.js'
2022

2123
import {
24+
AccumulatorProofType,
2225
BlockHeaderWithProof,
2326
BlockNumberKey,
27+
HistoricalRootsBlockProof,
2428
HistoryNetworkContentType,
2529
MERGE_BLOCK,
2630
SHANGHAI_BLOCK,
2731
sszReceiptsListType,
2832
} from './types.js'
29-
import { getContentKey, verifyPostCapellaHeaderProof, verifyPreCapellaHeaderProof, verifyPreMergeHeaderProof } from './util.js'
33+
import {
34+
getContentKey,
35+
verifyPostCapellaHeaderProof,
36+
verifyPreCapellaHeaderProof,
37+
verifyPreMergeHeaderProof,
38+
} from './util.js'
3039

3140
import type { ENR } from '@chainsafe/enr'
32-
import type { Debugger } from 'debug'
33-
import type { BaseNetworkConfig, ContentLookupResponse, FindContentMessage } from '../../index.js'
41+
3442
import { RunStatusCode } from '@lodestar/light-client'
43+
import type { Debugger } from 'debug'
3544

3645
export class HistoryNetwork extends BaseNetwork {
3746
networkId: NetworkId.HistoryNetwork
@@ -95,7 +104,7 @@ export class HistoryNetwork extends BaseNetwork {
95104
/**
96105
* Retrieve a blockheader from the DB by hash
97106
* @param blockHash the hash of the blockheader sought
98-
* @param asBytes return the header as RLP encoded bytes or as an @ethereumjs/block BlockHeader
107+
* @param asBytes return the header as RLP encoded bytes or as an `@ethereumjs/block` BlockHeader
99108
* @returns the bytes or Blockheader if found or else undefined
100109
*/
101110
public getBlockHeaderFromDB = async (
@@ -109,6 +118,7 @@ export class HistoryNetwork extends BaseNetwork {
109118
const value = await this.findContentLocally(contentKey)
110119
if (value === undefined) return undefined
111120
const header = BlockHeaderWithProof.deserialize(value).header
121+
112122
return asBytes === true
113123
? header
114124
: BlockHeader.fromRLPSerializedHeader(header, { setHardfork: true })
@@ -162,57 +172,76 @@ export class HistoryNetwork extends BaseNetwork {
162172
const proof = headerProof.proof
163173

164174
if (header.number < MERGE_BLOCK) {
165-
if (proof.value === null) {
166-
throw new Error('Received pre-merge block header without proof')
175+
let deserializedProof: Uint8Array[]
176+
try {
177+
deserializedProof = AccumulatorProofType.deserialize(proof)
178+
} catch (err: any) {
179+
this.logger(`invalid proof for block ${bytesToHex(header.hash())}`)
180+
throw new Error(`invalid proof for block ${bytesToHex(header.hash())}`)
167181
}
168-
if (Array.isArray(proof.value)) {
169-
let validated = false
170-
if ('blockHash' in validation) {
171-
validated = verifyPreMergeHeaderProof(proof.value, validation.blockHash, header.number)
172-
} else {
173-
validated = verifyPreMergeHeaderProof(
174-
proof.value,
175-
bytesToHex(header.hash()),
176-
validation.blockNumber,
177-
)
178-
}
179-
if (!validated) {
180-
throw new Error('Unable to validate proof for pre-merge header')
181-
}
182+
let validated = false
183+
if ('blockHash' in validation) {
184+
validated = verifyPreMergeHeaderProof(
185+
deserializedProof,
186+
validation.blockHash,
187+
header.number,
188+
)
189+
} else {
190+
validated = verifyPreMergeHeaderProof(
191+
deserializedProof,
192+
bytesToHex(header.hash()),
193+
validation.blockNumber,
194+
)
195+
}
196+
if (!validated) {
197+
throw new Error('Unable to validate proof for pre-merge header')
182198
}
183199
} else if (header.number < SHANGHAI_BLOCK) {
184-
if (proof.value === null) {
185-
this.logger('Received post-merge block without proof')
186-
throw new Error('Received post-merge block header without proof')
200+
let deserializedProof: ReturnType<typeof HistoricalRootsBlockProof.deserialize>
201+
try {
202+
deserializedProof = HistoricalRootsBlockProof.deserialize(proof)
203+
} catch (err: any) {
204+
this.logger(`invalid proof for block ${bytesToHex(header.hash())}`)
205+
throw new Error(`invalid proof for block ${bytesToHex(header.hash())}`)
187206
}
188207
let validated = false
189208
try {
190-
validated = verifyPreCapellaHeaderProof(proof.value as any, header.hash())
209+
validated = verifyPreCapellaHeaderProof(deserializedProof, header.hash())
191210
} catch (err: any) {
192211
this.logger(`Unable to validate proof for post-merge header: ${err.message}`)
193212
}
194213
if (!validated) {
195214
throw new Error('Unable to validate proof for post-merge header')
196215
}
197-
}
198-
else {
216+
} else {
199217
// TODO: Check proof slot to ensure header is from previous sync period and handle ephemeral headers separately
200-
if (proof.value === null) {
201-
this.logger('Received post-merge block without proof')
218+
219+
let deserializedProof: ReturnType<typeof HistoricalSummariesBlockProof.deserialize>
220+
try {
221+
deserializedProof = HistoricalSummariesBlockProof.deserialize(proof)
222+
} catch (err: any) {
223+
this.logger(`invalid proof for block ${bytesToHex(header.hash())}`)
224+
throw new Error(`invalid proof for block ${bytesToHex(header.hash())}`)
202225
}
203226
const beacon = this.portal.network()['0x500c']
204227
if (beacon !== undefined && beacon.lightClient?.status === RunStatusCode.started) {
205228
try {
206-
verifyPostCapellaHeaderProof(proof.value as any, header.hash(), beacon.historicalSummaries, beacon.beaconConfig)
229+
verifyPostCapellaHeaderProof(
230+
deserializedProof,
231+
header.hash(),
232+
beacon.historicalSummaries,
233+
beacon.beaconConfig,
234+
)
207235
this.logger(`Successfully verified proof for block header ${header.number}`)
208236
} catch {
209237
this.logger('Received post-capella block header with invalid proof')
210238
// TODO: throw new Error('Received post-merge block header with invalid proof')
211239
}
212240
} else {
213-
this.logger('Received post-capella block but Beacon light client is not running so cannot verify proof')
241+
this.logger(
242+
'Received post-capella block but Beacon light client is not running so cannot verify proof',
243+
)
214244
}
215-
216245
}
217246
await this.indexBlockHash(header.number, bytesToHex(header.hash()))
218247
return header.hash()
@@ -348,7 +377,8 @@ export class HistoryNetwork extends BaseNetwork {
348377
this.gossipManager.add(contentKey)
349378
}
350379
this.logger(
351-
`${HistoryNetworkContentType[contentType]} added for ${keyOpt instanceof Uint8Array ? bytesToHex(keyOpt) : keyOpt
380+
`${HistoryNetworkContentType[contentType]} added for ${
381+
keyOpt instanceof Uint8Array ? bytesToHex(keyOpt) : keyOpt
352382
}`,
353383
)
354384
}

packages/portalnetwork/src/networks/history/types.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ import {
33
ByteVectorType,
44
ContainerType,
55
ListCompositeType,
6-
NoneType,
76
UintBigintType,
8-
UnionType,
97
VectorCompositeType,
108
} from '@chainsafe/ssz'
119
import { MAX_WITHDRAWALS_PER_PAYLOAD } from '@lodestar/params'
@@ -25,7 +23,7 @@ export const MAX_TRANSACTION_COUNT = 16384 // 2 ** 14
2523
export const MAX_RECEIPT_LENGTH = 134217728 // 2 ** 27
2624
export const MAX_HEADER_LENGTH = 8192 // 2 ** 13
2725
export const MAX_ENCODED_UNCLES_LENGTH = 131072 // MAX_HEADER_LENGTH * 2 ** 4
28-
26+
export const MAX_HEADER_PROOF_LENGTH = 1024
2927
export const MERGE_BLOCK = 15537393n
3028
export const SHANGHAI_BLOCK = 17034871n
3129

@@ -207,14 +205,7 @@ export const HistoricalSummariesBlockProof = new ContainerType({
207205
slot: SlotType,
208206
})
209207

210-
export const BlockHeaderProofType = new UnionType([
211-
new NoneType(),
212-
AccumulatorProofType,
213-
HistoricalRootsBlockProof,
214-
HistoricalSummariesBlockProof,
215-
])
216-
217208
export const BlockHeaderWithProof = new ContainerType({
218209
header: new ByteListType(MAX_HEADER_LENGTH),
219-
proof: BlockHeaderProofType,
210+
proof: new ByteListType(MAX_HEADER_PROOF_LENGTH),
220211
})

packages/portalnetwork/src/networks/history/util.ts

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { ssz } from '@lodestar/types'
88
import { historicalEpochs } from './data/epochHashes.js'
99
import { historicalRoots } from './data/historicalRoots.js'
1010
import {
11+
AccumulatorProofType,
1112
BlockBodyContentType,
1213
BlockHeaderWithProof,
1314
BlockNumberKey,
@@ -95,17 +96,17 @@ export const decodeHistoryNetworkContentKey = (
9596
contentKey: Uint8Array,
9697
):
9798
| {
98-
contentType:
99-
| HistoryNetworkContentType.BlockHeader
100-
| HistoryNetworkContentType.BlockBody
101-
| HistoryNetworkContentType.Receipt
102-
| HistoryNetworkContentType.HeaderProof
103-
keyOpt: Uint8Array
104-
}
99+
contentType:
100+
| HistoryNetworkContentType.BlockHeader
101+
| HistoryNetworkContentType.BlockBody
102+
| HistoryNetworkContentType.Receipt
103+
| HistoryNetworkContentType.HeaderProof
104+
keyOpt: Uint8Array
105+
}
105106
| {
106-
contentType: HistoryNetworkContentType.BlockHeaderByNumber
107-
keyOpt: bigint
108-
} => {
107+
contentType: HistoryNetworkContentType.BlockHeaderByNumber
108+
keyOpt: bigint
109+
} => {
109110
const contentType: HistoryNetworkContentType = contentKey[0]
110111
if (contentType === HistoryNetworkContentType.BlockHeaderByNumber) {
111112
const blockNumber = BlockNumberKey.deserialize(contentKey.slice(1)).blockNumber
@@ -208,39 +209,27 @@ export const reassembleBlock = (rawHeader: Uint8Array, rawBody?: Uint8Array) =>
208209
* @param rlpHex RLP encoded block as hex string
209210
* @param blockHash block hash as 0x prefixed hex string
210211
* @param network a running `PortalNetwork` client
212+
* @param proof the header proof anchoring the block to an accumulator
213+
* (i.e. pre-merge historical accumulator, historical_roots, or historical summaries)
211214
*/
212215
export const addRLPSerializedBlock = async (
213216
rlpHex: string,
214217
blockHash: string,
215218
network: HistoryNetwork,
216-
witnesses: Witnesses,
219+
proof: Uint8Array,
217220
) => {
218221
const block = Block.fromRLPSerializedBlock(hexToBytes(rlpHex), {
219222
setHardfork: true,
220223
})
221224
const header = block.header
222225
const headerKey = getContentKey(HistoryNetworkContentType.BlockHeader, hexToBytes(blockHash))
223-
if (header.number < MERGE_BLOCK) {
224-
const proof: Witnesses = witnesses
225-
const headerProof = BlockHeaderWithProof.serialize({
226-
header: header.serialize(),
227-
proof: { selector: 1, value: proof },
228-
})
229-
try {
230-
await network.validateHeader(headerProof, { blockHash })
231-
} catch {
232-
network.logger('Header proof failed validation while loading block from RLP')
233-
}
234-
await network.store(headerKey, headerProof)
235-
} else {
236-
const headerProof = BlockHeaderWithProof.serialize({
237-
header: header.serialize(),
238-
proof: { selector: 0, value: null },
239-
})
240-
await network.indexBlockHash(header.number, bytesToHex(header.hash()))
226+
const headerProof = BlockHeaderWithProof.serialize({
227+
header: header.serialize(),
228+
proof,
229+
})
241230

242-
await network.store(headerKey, headerProof)
243-
}
231+
await network.store(headerKey, headerProof)
232+
await network.indexBlockHash(header.number, bytesToHex(header.hash()))
244233
const sszBlock = sszEncodeBlockBody(block)
245234
await network.addBlockBody(sszBlock, header.hash(), header.serialize())
246235
}

0 commit comments

Comments
 (0)