Skip to content

Commit

Permalink
Merge pull request #634 from ethereumjs/fix-beacon-hive-tests
Browse files Browse the repository at this point in the history
Hive fixes
  • Loading branch information
acolytec3 authored Sep 29, 2024
2 parents 39ca8be + 648f722 commit f5d0ecb
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 58 deletions.
21 changes: 8 additions & 13 deletions packages/cli/scripts/devnet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,20 +72,15 @@ const args: any = yargs(hideBin(process.argv))
const main = async () => {
console.log(`starting ${args.numNodes} nodes`)

// const networks =
// args.networks !== false
// ? (args.networks as Array<string>).map((network) => `--networks=${network}`)
// : []
const cmd = 'hostname -I'
const pubIp = execSync(cmd).toString().split(' ')
const ip = '0.0.0.0'
const ip = '127.0.0.1'
const children: ChildProcessByStdio<any, any, null>[] = []
const file = require.resolve(process.cwd() + '/dist/index.js')

const file = require.resolve('../src/index.ts')
if (args.pks !== undefined) {
const pks = fs.readFileSync(args.pks, { encoding: 'utf8' }).split('\n')
for (let idx = 0; idx < pks.length; idx++) {
const child = spawn(
process.execPath,
'tsx',
[
file,
`--rpc`,
Expand All @@ -104,7 +99,7 @@ const main = async () => {
} else if (args.numNodes !== undefined) {
for (let x = 0; x < args.numNodes; x++) {
const child = spawn(
process.execPath,
'tsx',
[
file,
`--rpcAddr=${ip}`,
Expand Down Expand Up @@ -142,7 +137,7 @@ const main = async () => {
}

// Connect nodes to other nodes in the network via `addBootNode`
if (args.connectNodes !== undefined) {
if (args.connectNodes !== false) {
console.log('connecting nodes')
const ultralights: jayson.HttpClient[] = []
for (let x = 0; x < 10; x++) {
Expand All @@ -159,8 +154,8 @@ const main = async () => {
}
}
}
if (args.connectBootNodes !== undefined) {
console.log('connecting to bootnodes')
if (args.connectBootNodes !== false) {
console.log('connecting to bootnodes')
for (let x = 0; x < args.numNodes; x++) {
const ultralight = Client.http({ host: ip, port: 8545 + x })
for (const bootnode of bootnodes) {
Expand Down
41 changes: 41 additions & 0 deletions packages/cli/scripts/findContent.ts

Large diffs are not rendered by default.

138 changes: 99 additions & 39 deletions packages/cli/src/rpc/modules/portal.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { EntryStatus } from '@chainsafe/discv5'
import { ENR } from '@chainsafe/enr'
import { BitArray } from '@chainsafe/ssz'
import { bytesToHex } from '@ethereumjs/util'
import { bytesToHex, short } from '@ethereumjs/util'
import {
ContentLookup,
ContentMessageType,
Expand Down Expand Up @@ -77,6 +77,7 @@ const methods = [
'portal_historyLookupEnr',
// beacon
'portal_beaconSendFindContent',
'portal_beaconFindContent',
'portal_beaconStore',
'portal_beaconLocalContent',
'portal_beaconAddEnr',
Expand Down Expand Up @@ -218,7 +219,10 @@ export class portal {
[validators.dstId],
[validators.hex],
])

this.beaconFindContent = middleware(this.beaconFindContent.bind(this), 2, [
[validators.enr],
[validators.hex],
])
this.beaconStore = middleware(this.beaconStore.bind(this), 2, [
[validators.hex],
[validators.hex],
Expand Down Expand Up @@ -616,7 +620,10 @@ export class portal {
`${res !== undefined ? toHexString(res) : 'content not found'}`,
)
if (res === undefined) {
throw new Error('No content found')
throw {
code: -32009,
message: 'no content found',
}
}
return toHexString(res)
}
Expand All @@ -630,7 +637,10 @@ export class portal {
`${res !== undefined ? toHexString(res) : 'content not found'}`,
)
if (res === undefined) {
throw new Error('No content found')
throw {
code: -32009,
message: 'no content found',
}
}
return toHexString(res)
}
Expand All @@ -650,24 +660,19 @@ export class portal {
if (!res) {
return { enrs: [] }
}
const content: Uint8Array | Uint8Array[] =
res.selector === FoundContent.ENRS
? (res.value as Uint8Array[])
: res.selector === FoundContent.CONTENT
? (res.value as Uint8Array)
: await new Promise((resolve) => {
const timeout = setTimeout(() => {
resolve(Uint8Array.from([]))
}, 10000)
this._history.on('ContentAdded', (_contentKey: Uint8Array, value: Uint8Array) => {
if (bytesToHex(_contentKey) === contentKey) {
clearTimeout(timeout)
resolve(value)
}
})
})
let content: Uint8Array | Uint8Array[] = []
switch (res.selector) {
case FoundContent.ENRS:
content = res.value as Uint8Array[]
break
case FoundContent.CONTENT:
case FoundContent.UTP:
content = res.value as Uint8Array
break
}

this.logger.extend('findContent')(`request returned ${content.length} bytes`)
res.selector === FoundContent.UTP && this.logger.extend('findContent')('utp')

const returnVal =
res.selector === FoundContent.ENRS
? { enrs: (<Uint8Array[]>content).map((v) => ENR.decode(v).encodeTxt()) }
Expand All @@ -681,6 +686,7 @@ export class portal {
})
return returnVal
}

async stateFindContent(params: [string, string]) {
const [enr, contentKey] = params
const nodeId = ENR.decodeTxt(enr).nodeId
Expand All @@ -697,24 +703,19 @@ export class portal {
if (!res) {
return { enrs: [] }
}
const content: Uint8Array | Uint8Array[] =
res.selector === FoundContent.ENRS
? (res.value as Uint8Array[])
: res.selector === FoundContent.CONTENT
? (res.value as Uint8Array)
: await new Promise((resolve) => {
const timeout = setTimeout(() => {
resolve(Uint8Array.from([]))
}, 2000)
this._state.on('ContentAdded', (_contentKey: Uint8Array, value: Uint8Array) => {
if (bytesToHex(_contentKey) === contentKey) {
clearTimeout(timeout)
resolve(value)
}
})
})
let content: Uint8Array | Uint8Array[] = []
switch (res.selector) {
case FoundContent.ENRS:
content = res.value as Uint8Array[]
break
case FoundContent.CONTENT:
case FoundContent.UTP:
content = res.value as Uint8Array
break
}

this.logger.extend('findContent')(`request returned ${content.length} bytes`)
res.selector === FoundContent.UTP && this.logger.extend('findContent')('utp')

this.logger.extend('findContent')(content)
return res.selector === FoundContent.ENRS
? { enrs: content }
Expand Down Expand Up @@ -920,6 +921,54 @@ export class portal {
return '0x'
}

async beaconFindContent(params: [string, string]) {
const [enr, contentKey] = params
const nodeId = ENR.decodeTxt(enr).nodeId
this.logger.extend('findContent')(
`received request to send request to ${shortId(nodeId)} for contentKey ${contentKey}`,
)
if (!this._beacon.routingTable.getWithPending(nodeId)?.value) {
const pong = await this._beacon.sendPing(enr)
if (!pong) {
return ''
}
}

const res = await this._beacon.sendFindContent(nodeId, fromHexString(contentKey))
this.logger.extend('findContent')(
`request returned type: ${res ? FoundContent[res.selector] : res}`,
)

if (!res) {
return { enrs: [] }
}
let content: Uint8Array | Uint8Array[] = []
switch (res.selector) {
case FoundContent.ENRS:
content = res.value as Uint8Array[]
break
case FoundContent.CONTENT:
case FoundContent.UTP:
content = res.value as Uint8Array
break
}

this.logger.extend('findContent')(`request returned ${content.length} bytes`)

const returnVal =
res.selector === FoundContent.ENRS
? { enrs: (<Uint8Array[]>content).map((v) => ENR.decode(v).encodeTxt()) }
: {
content: content.length > 0 ? toHexString(content as Uint8Array) : '0x',
utpTransfer: res.selector === FoundContent.UTP,
}
this.logger.extend('findContent')({
selector: FoundContent[res.selector],
value: returnVal,
})
return returnVal
}

async beaconStore(params: [string, string]) {
const [contentKey, content] = params.map((param) => fromHexString(param))
try {
Expand All @@ -933,9 +982,20 @@ export class portal {

async beaconLocalContent(params: [string]) {
const [contentKey] = params
this.logger.extend(`beaconLocalContent`)(`Received request for ${contentKey}`)

const content = await this._beacon.findContentLocally(fromHexString(contentKey))
this.logger.extend(`beaconLocalContent`)(
`request returned ${content !== undefined ? content.length : 'null'} bytes`,
)
this.logger.extend(`beaconLocalContent`)(
`retrieved content: ${content !== undefined ? short(toHexString(content)) : 'content not found'}`,
)
if (content !== undefined) return toHexString(content)
else return '0x'
throw {
code: -32009,
message: 'no content found',
}
}

async beaconAddBootNode(params: [string]): Promise<boolean> {
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/test/rpc/portal/beaconLocalContent.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe(`${method} tests`, () => {
getBeaconContentKey(BeaconLightClientNetworkContentType.LightClientOptimisticUpdate, key),
),
])
assert.equal(res.result, '0x')
assert.equal(res.error.code, -32009)
ultralight.kill(9)
}, 10000)
})
11 changes: 7 additions & 4 deletions packages/portalnetwork/src/networks/beacon/beacon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
hexToBytes,
intToHex,
padToEven,
short,
} from '@ethereumjs/util'
import { createBeaconConfig, defaultChainConfig } from '@lodestar/config'
import { genesisData } from '@lodestar/config/networks'
Expand Down Expand Up @@ -315,14 +316,14 @@ export class BeaconLightClientNetwork extends BaseNetwork {
public findContentLocally = async (contentKey: Uint8Array): Promise<Uint8Array | undefined> => {
let value
let key

switch (contentKey[0]) {
case BeaconLightClientNetworkContentType.LightClientUpdatesByRange:
try {
value = await this.constructLightClientRange(contentKey.slice(1))
} catch {
// We catch here in case we don't have all of the updates requested by the range
// in which case we shouldn't return any content
value = new Uint8Array()
}
break
case BeaconLightClientNetworkContentType.LightClientOptimisticUpdate:
Expand All @@ -348,7 +349,7 @@ export class BeaconLightClientNetwork extends BaseNetwork {
)
this.logger.extend('FINDLOCALLY')(
`light client is not running, retrieving whatever we have - ${
value ?? 'nothing found'
value !== undefined ? short(value) : 'nothing found'
}`,
)
} else {
Expand Down Expand Up @@ -413,7 +414,7 @@ export class BeaconLightClientNetwork extends BaseNetwork {
value = await this.retrieve(contentKey)
}

return value instanceof Uint8Array ? value : hexToBytes(value ?? '0x')
return value instanceof Uint8Array ? value : value !== undefined ? hexToBytes(value) : undefined
}

public sendFindContent = async (
Expand Down Expand Up @@ -449,6 +450,7 @@ export class BeaconLightClientNetwork extends BaseNetwork {
// TODO: Figure out how to clear this listener
this.on('ContentAdded', (contentKey: Uint8Array, value) => {
if (equalsBytes(contentKey, key)) {
this.logger.extend('FOUNDCONTENT')(`received content for uTP Connection ID ${id}`)
resolve({ selector: 0, value })
}
})
Expand Down Expand Up @@ -689,7 +691,7 @@ export class BeaconLightClientNetwork extends BaseNetwork {
}

this.logger(
`storing ${BeaconLightClientNetworkContentType[contentType]} content corresponding to ${contentKey}`,
`storing ${BeaconLightClientNetworkContentType[contentType]} content corresponding to ${bytesToHex(contentKey)}`,
)
this.emit('ContentAdded', contentKey, value)
}
Expand Down Expand Up @@ -749,6 +751,7 @@ export class BeaconLightClientNetwork extends BaseNetwork {
const count = Number(rangeKey.count)
const start = Number(rangeKey.startPeriod)
const range = []

for (let x = start; x < start + count; x++) {
const update = await this.retrieve(this.computeLightClientUpdateKey(x))
if (update === undefined) {
Expand Down
1 change: 0 additions & 1 deletion packages/portalnetwork/test/networks/beacon/beacon.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ describe('API tests', async () => {
LightClientOptimisticUpdateKey.serialize({ signatureSlot: 6718464n }),
),
)
console.log({ retrievedOptimisticUpdate })

assert.equal(
ssz.capella.LightClientOptimisticUpdate.deserialize(retrievedOptimisticUpdate!.slice(4))
Expand Down

0 comments on commit f5d0ecb

Please sign in to comment.