Skip to content

Commit

Permalink
Add tests for storing and retrieving beacon content
Browse files Browse the repository at this point in the history
  • Loading branch information
acolytec3 committed Aug 5, 2023
1 parent 1a6d78f commit f87f8f6
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 9 deletions.
4 changes: 4 additions & 0 deletions packages/portalnetwork/src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import LRU from 'lru-cache'
import { dirSize, MEGABYTE } from '../util/index.js'
import { DBManager } from './dbManager.js'
import { peerIdFromKeys } from '@libp2p/peer-id'
import { BeaconLightClientNetwork } from '../subprotocols/beacon/beacon.js'

export class PortalNetwork extends (EventEmitter as { new (): PortalNetworkEventEmitter }) {
discv5: Discv5
Expand Down Expand Up @@ -184,6 +185,9 @@ export class PortalNetwork extends (EventEmitter as { new (): PortalNetworkEvent
case ProtocolId.Rendezvous:
this.supportsRendezvous = true
break
case ProtocolId.BeaconLightClientNetwork:
this.protocols.set(protocol, new BeaconLightClientNetwork(this, opts.radius))
break
}
}

Expand Down
17 changes: 11 additions & 6 deletions packages/portalnetwork/src/subprotocols/beacon/beacon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ export class BeaconLightClientNetwork extends BaseProtocol {
}

public findContentLocally = async (contentKey: Uint8Array): Promise<Uint8Array | undefined> => {
// TODO: We need to add special handling for LightClientUpdatesByRange since these shouldn't be stored
// in the DB as a range but individually
if (contentKey[0] === BeaconLightClientNetworkContentType.LightClientUpdatesByRange) {
throw new Error('special handling for update ranges not supported yet')
}
const value = await this.retrieve(toHexString(contentKey))
return value ? fromHexString(value) : fromHexString('0x')
}
Expand Down Expand Up @@ -159,13 +164,13 @@ export class BeaconLightClientNetwork extends BaseProtocol {

public store = async (
contentType: BeaconLightClientNetworkContentType,
hashKey: string,
contentKey: string,
value: Uint8Array
): Promise<void> => {
await this.put(
this.protocolId,
getBeaconContentKey(contentType, fromHexString(hashKey)),
toHexString(value)
)
// TODO: Add special handling for the LightClientUpdate since we can't just dump a whole batch of them into the DB
if (contentType === BeaconLightClientNetworkContentType.LightClientUpdatesByRange) {
throw new Error('special handling for update ranges not supported yet')
}
await this.put(this.protocolId, contentKey, toHexString(value))
}
}
100 changes: 97 additions & 3 deletions packages/portalnetwork/test/subprotocols/beacon/beacon.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,25 @@ import { fromHexString, toHexString } from '@chainsafe/ssz'
import { ssz } from '@lodestar/types'
import { createBeaconConfig, defaultChainConfig } from '@lodestar/config'
import {
BeaconLightClientNetworkContentType,
LightClientBootstrapKey,
LightClientFinalityUpdateKey,
LightClientOptimisticUpdateKey,
LightClientUpdatesByRange,
LightClientUpdatesByRangeKey,
MainnetGenesisValidatorsRoot,
} from '../../../src/subprotocols/beacon/types.js'
import { createFromProtobuf } from '@libp2p/peer-id-factory'
import { SignableENR } from '@chainsafe/discv5'
import { multiaddr } from '@multiformats/multiaddr'
import { PortalNetwork, ProtocolId, TransportLayer } from '../../../src/index.js'
import type { BeaconLightClientNetwork } from '../../../src/subprotocols/beacon/index.js'

const specTestVectors = require('./specTestVectors.json')
const genesisRoot = fromHexString(MainnetGenesisValidatorsRoot) // Genesis Validators Root
const config = createBeaconConfig(defaultChainConfig, genesisRoot)

tape('portal network spec test vectors', (t) => {
const specTestVectors = require('./specTestVectors.json')
const genesisRoot = fromHexString(MainnetGenesisValidatorsRoot) // Genesis Validators Root
const config = createBeaconConfig(defaultChainConfig, genesisRoot)
const serializedOptimistincUpdate = fromHexString(
specTestVectors.optimisticUpdate['6718463'].content_value
)
Expand Down Expand Up @@ -90,3 +97,90 @@ tape('portal network spec test vectors', (t) => {
)
t.end()
})

tape.only('API tests', async (t) => {
const privateKeys = [
'0x0a2700250802122102273097673a2948af93317235d2f02ad9cf3b79a34eeb37720c5f19e09f11783c12250802122102273097673a2948af93317235d2f02ad9cf3b79a34eeb37720c5f19e09f11783c1a2408021220aae0fff4ac28fdcdf14ee8ecb591c7f1bc78651206d86afe16479a63d9cb73bd',
]
const id1 = await createFromProtobuf(fromHexString(privateKeys[0]))
const enr1 = SignableENR.createFromPeerId(id1)
const initMa: any = multiaddr(`/ip4/127.0.0.1/udp/3000`)
enr1.setLocationMultiaddr(initMa)

const node1 = await PortalNetwork.create({
transport: TransportLayer.NODE,
supportedProtocols: [ProtocolId.BeaconLightClientNetwork],
config: {
enr: enr1,
multiaddr: initMa,
peerId: id1,
},
})

const protocol = <BeaconLightClientNetwork>(
node1.protocols.get(ProtocolId.BeaconLightClientNetwork)
)

const bootstrap = specTestVectors.bootstrap['6718368']

await protocol.store(
BeaconLightClientNetworkContentType.LightClientBootstrap,
bootstrap.content_key,
fromHexString(bootstrap.content_value)
)
const retrievedBootstrap = await protocol.findContentLocally(fromHexString(bootstrap.content_key))
t.equal(
ssz.capella.LightClientBootstrap.deserialize(retrievedBootstrap!.slice(4)).header.beacon.slot,
ssz.capella.LightClientBootstrap.deserialize(fromHexString(bootstrap.content_value).slice(4))
.header.beacon.slot,
'successfully stored and retrieved bootstrap'
)

const finalityUpdate = specTestVectors.finalityUpdate['6718463']
await protocol.store(
BeaconLightClientNetworkContentType.LightClientFinalityUpdate,
finalityUpdate.content_key,
fromHexString(finalityUpdate.content_value)
)
const retrievedFinalityUpdate = await protocol.findContentLocally(
fromHexString(finalityUpdate.content_key)
)
t.equal(
ssz.capella.LightClientFinalityUpdate.deserialize(retrievedFinalityUpdate!.slice(4))
.attestedHeader.beacon.slot,
ssz.capella.LightClientFinalityUpdate.deserialize(
fromHexString(finalityUpdate.content_value).slice(4)
).attestedHeader.beacon.slot,
'successfully stored and retrieved finality update'
)
const optimisticUpdate = specTestVectors.optimisticUpdate['6718463']
await protocol.store(
BeaconLightClientNetworkContentType.LightClientFinalityUpdate,
optimisticUpdate.content_key,
fromHexString(optimisticUpdate.content_value)
)
const retrievedOptimisticUpdate = await protocol.findContentLocally(
fromHexString(optimisticUpdate.content_key)
)
t.equal(
ssz.capella.LightClientOptimisticUpdate.deserialize(retrievedOptimisticUpdate!.slice(4))
.attestedHeader.beacon.slot,
ssz.capella.LightClientOptimisticUpdate.deserialize(
fromHexString(optimisticUpdate.content_value).slice(4)
).attestedHeader.beacon.slot,
'successfully stored and retrieved optimistic update'
)
// TODO: Update this test once logic for handling light client updates is implemented
const updatesByRange = specTestVectors.updateByRange['6684738']
try {
await protocol.store(
BeaconLightClientNetworkContentType.LightClientUpdatesByRange,
updatesByRange.content_key,
fromHexString(optimisticUpdate.content_value)
)
t.fail('should throw')
} catch {
t.pass('throws when trying to store a batch of light client updates')
}
t.end()
})

0 comments on commit f87f8f6

Please sign in to comment.