Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PortalNetwork: Deal with rough edges in network startup and CLI Support for Portal Network Node #704

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f233778
add default bind address and bootnodes params
cjustinobi Jan 10, 2025
46269a2
add portal client script
cjustinobi Jan 11, 2025
049bcf9
update README and EXAMPLES file
cjustinobi Jan 12, 2025
f9dc1a0
correct broken API link
cjustinobi Jan 12, 2025
5f416da
update EXAMPLES.md
cjustinobi Jan 12, 2025
6bab600
Move portalClient to portalnetwork dir and add test
cjustinobi Jan 14, 2025
4d24ae6
Merge branch 'master' into improvement/ultralight-defaults
cjustinobi Jan 14, 2025
ce1d95a
Update portalClient test
cjustinobi Jan 15, 2025
5fac772
add default bind address and bootnodes params
cjustinobi Jan 10, 2025
5530947
add portal client script
cjustinobi Jan 11, 2025
8b8e1d0
update README and EXAMPLES file
cjustinobi Jan 12, 2025
a8f4f1e
correct broken API link
cjustinobi Jan 12, 2025
b245c03
update EXAMPLES.md
cjustinobi Jan 12, 2025
b5bdec4
Move portalClient to portalnetwork dir and add test
cjustinobi Jan 14, 2025
e065bf8
Update portalClient test
cjustinobi Jan 15, 2025
8e4cdbe
Update bootnodes list
cjustinobi Jan 16, 2025
cbea73f
Fix conflict after pulling
cjustinobi Jan 16, 2025
5051dcf
Merge remote-tracking branch 'origin/master' into pr/cjustinobi/704
acolytec3 Jan 18, 2025
08e884b
Remove test
cjustinobi Jan 19, 2025
6e32ac9
Remove duplicate scripts and tests
acolytec3 Jan 22, 2025
478c257
Merge remote-tracking branch 'origin/master' into pr/cjustinobi/704
acolytec3 Jan 23, 2025
b45996c
remove examples and portal client stuff
acolytec3 Jan 23, 2025
1125f2a
add tests for default settings
acolytec3 Jan 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/portalnetwork/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A Typescript library for interacting with the Portal Network

See [API](./docs/modules.md) for more details
See [API](./docs/modules.html) for more details

See [Architecture](./diagrams/ARCHITECTURE.md) for architectural concepts

Expand Down
8 changes: 7 additions & 1 deletion packages/portalnetwork/src/client/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { PortalNetworkOpts } from './types'
import { hexToBytes } from '@ethereumjs/util'

import { formatBlockResponse, formatResponse } from '../util/helpers.js'
import { DEFAULT_OPTS } from '../util/config.js'

const ERROR_CODES = {
UNSUPPORTED_METHOD: 4200,
Expand Down Expand Up @@ -34,7 +35,12 @@ export class UltralightProvider {
}

static async create(opts: Partial<PortalNetworkOpts>): Promise<UltralightProvider> {
const portal = await PortalNetwork.create(opts)
const finalOpts = {
...DEFAULT_OPTS,
...opts,
}

const portal = await PortalNetwork.create(finalOpts)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ScottyPoi what do you think about adding these defaults to PortalNetwork.create as well? I'm trying to think of a reason we wouldn't want the bootnodes list to be an optional default in the create method for the base client and not just the provider here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think PortalNetwork.create should still default to an empty bootnodes list so the client doesn't automatically ping all of the bootnodes every time we run a test

return new UltralightProvider(portal)
}

Expand Down
20 changes: 20 additions & 0 deletions packages/portalnetwork/src/util/bootnodes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

export const DEFAULT_BOOTNODES = {
mainnet: [
'enr:-I24QO4X4ECNw19M51l3UYjQPq91dwy7FzEdOb43xEjvGnJMOU2cqD-KQ0FNZbpuzRWyQRiqLinAWw2qsgnRQ2guLt0EY4d1IDAuMC4xgmlkgnY0gmlwhKRc9_OJc2VjcDI1NmsxoQOdan7kE4_KU8yM1SNzw9OIrd-oQOlDBnz01fA2fz_1yoN1ZHCCE44',
'enr:-I24QFm1w_fuMnMf4DsUr_PDVzn_Kn_PY6zQYsoWkJIk4evHUxO8OBacbdo4-7bAyvrXsYgCmOVgOQulvA_9ompMfc8EY4d1IDAuMC4xgmlkgnY0gmlwhKRc9_OJc2VjcDI1NmsxoQPeFHF3dY24vc0QgrRIM1vz3ZFnbmddmKLjhP34pxaD5YN1ZHCCE40',
'enr:-I24QEkyh8nyn2PLMokMXzc_zpuiYxN2VHKrGfU7YI60K9_5YoGZsq-kSngZqLHeOWP3La-Pt5zaojutlsbbsbZ30dYEY4d1IDAuMC4xgmlkgnY0gmlwhKRc9_OJc2VjcDI1NmsxoQM-ccaM0TOFvYqC_RY_KhZNhEmWx8zdf6AQALhKyMVyboN1ZHCCE4w',
'enr:-I24QDoMcfNTC3xoH_TSmALXS4WMybTM5SQrysabBxR1DG_UaXHVRHtpQdiGNhxqjHvfSONhnPETB8HorZYplIluDS0EY4d1IDAuMC4xgmlkgnY0gmlwhKRc9_OJc2VjcDI1NmsxoQOImp2idIf2UoY-GoY49pOeJAtqeeDLfb5VDxj94h_I44N1ZHCCE48',
'enr:-I24QHZRM9Sd3UgUOdB443q3nX6NOUsg0VMyarcfD69z8M3SB1vW2hkqiPFczPpyY6wSUCcUeXTig75sC5fT4YnsL7MEY4d1IDAuMC4xgmlkgnY0gmlwhKRc9_OJc2VjcDI1NmsxoQMGuOLosx85PYtBn7rULoHY9EAtLmGTn7XWoIvFqvq4qIN1ZHCCE5A',
'enr:-I24QGMQnf1FhP_-tjr7AdT3aJbowJeowuAktBOmoTaxu3WsNPlB1MaD704orcQO8kncLKhEQPOCTv1LSkU27AUldyoEY4d1IDAuMC4xgmlkgnY0gmlwhKRc9-KJc2VjcDI1NmsxoQLJhXByb3LmxHQaqgLDtIGUmpANXaBbFw3ybZWzGqb9-IN1ZHCCE4k',
'enr:-I24QNw9C_xJvljho0dO27ug7-wZg7KCN1Mmqefdvqwxxqw3X-SLzBO3-KvzCbGFFJJMDn1be6Hd-Bf_TR3afjrwZ7UEY4d1IDAuMC4xgmlkgnY0gmlwhKRc9-KJc2VjcDI1NmsxoQJMpHmGj1xSP1O-Mffk_jYIHVcg6tY5_CjmWVg1gJEsPIN1ZHCCE4o',
'enr:-I24QOz_tsZ8kOSU_zxXh2HOAxLyAIOeqHZP3Olzgsu73uMRTh8ul7sigT4Q1LaiT12Me2BFm5a4Izi6PCR0_Xe9AHUEY4d1IDAuMC4xgmlkgnY0gmlwhKRc9_OJc2VjcDI1NmsxoQIdyr0pquxuEW1mHQC0_j0mjB1fIfWZEZLlr7nfaKQXLYN1ZHCCE5E',
'enr:-I24QD_1X6GriBdbJzOb5bgKqwrZyKHmemXo6OD5h6rmajHhcx0nTEMhqza6BaCA5DNXOi58wszHenV2pIXSTkvGaEsEY4d1IDAuMC4xgmlkgnY0gmlwhKRc9_OJc2VjcDI1NmsxoQNliw-242ySvi8lxyNOfrkfkC071-aS8iMAYd82EZ1SLYN1ZHCCE5I',
'enr:-I24QMeElaS4lKvAtYQYmqBkvUc516OLykrLq0DNrw2kuB00EZVXAgFNGlvNz2U1gqVIMzgNg73RPK2j7UT6388HbdcEY4d1IDAuMC4xgmlkgnY0gmlwhKRc9-KJc2VjcDI1NmsxoQKb1jKQ-3sdzLAIL-a-KM4zTVnmgGIKLuKlh61UGoU8jYN1ZHCCE40',
'enr:-I24QKRKw-asojN9E1YCyJnsyzERVqhnwWFXBobI7E91-LAqFx9IqouzXszzuuh_Q0WzbqFkR32pgCSmPezXcAPeFI0EY4d1IDAuMC4xgmlkgnY0gmlwhKRc9-KJc2VjcDI1NmsxoQMzvDQGNzKQSw3uGSZE86LqS5Xm5KYByI56NOZzTwWiRoN1ZHCCE4w',
'enr:-I24QK_aSBXvKCAdMsrRioJDSPlJEl79fO5VX2JTrZEks2gbcrarbdfkWMMyEoS_2879w9bnJ14iC9hA6UWexjQ25IYEY4d1IDAuMC4xgmlkgnY0gmlwhKRc9_OJc2VjcDI1NmsxoQJxPJGDYLZ_QTU310eORFp6-NEs6ThGXpNULnAXPyiKy4N1ZHCCE4o',
'enr:-I24QDT851x-fW12txAIkCOhq5guf9iMkY7qasRkxfECFsVGS9GnGf_xhy40rAB2aFV8M1kbAo0UMGs-vlDx1JJ1lxQEY4d1IDAuMC4xgmlkgnY0gmlwhKRc9-KJc2VjcDI1NmsxoQKJUamKYO0FWvhv_-H4p1nLdyAqXZWGEzkb9Lk7NtvrR4N1ZHCCE4s',
'enr:-I24QKa9-vJDAoEiZ4Eio0_z1_fH5OoCAY0mqIuBJ9iJOt9QXie9sAZbrrouToPwTu9hK1CukT7H-qBfdlzMVG2ryy8EY4d1IDAuMC4xgmlkgnY0gmlwhKRc9_OJc2VjcDI1NmsxoQKHPt5CQ0D66ueTtSUqwGjfhscU_LiwS28QvJ0GgJFd-YN1ZHCCE4k',
'enr:-I24QDs2O04xIlgNMLYzChw-YEcsOsVvkuAYVosX4CoDrFGlMbJQHfrodqYH7TvjZ8v1sNUaiG_7mD8LqFsMGhYf80UEY4d1IDAuMC4xgmlkgnY0gmlwhKRc9_OJc2VjcDI1NmsxoQO7DZE841adtMdh8qsDYCDyTjGLud1HZJg-P-OAbTDVz4N1ZHCCE4s',
]
}
6 changes: 6 additions & 0 deletions packages/portalnetwork/src/util/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { NetworkId } from '../networks/types.js'
import { setupMetrics } from './metrics.js'

import type { NetworkConfig, PortalNetworkOpts } from '../client'
import { DEFAULT_BOOTNODES } from './bootnodes.js'

export type AsyncReturnType<T extends (...args: any) => Promise<any>> = T extends (
...args: any
Expand Down Expand Up @@ -120,3 +121,8 @@ export const cliConfig = async (args: PortalClientOpts) => {
}
return clientConfig
}

export const DEFAULT_OPTS = {
bindAddress: '0.0.0.0',
bootnodes: DEFAULT_BOOTNODES.mainnet,
}
218 changes: 112 additions & 106 deletions packages/portalnetwork/test/client/provider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,123 +2,129 @@ import { SignableENR } from '@chainsafe/enr'
import { Block, BlockHeader } from '@ethereumjs/block'
import { keys } from '@libp2p/crypto'
import { multiaddr } from '@multiformats/multiaddr'
import { expect, it } from 'vitest'
import { assert, describe, expect, it } from 'vitest'

import { UltralightProvider } from '../../src/client/provider.js'
import { TransportLayer } from '../../src/index.js'
import { NetworkId } from '../../src/networks/types.js'

it('Test provider functionality', async () => {
const ma = multiaddr('/ip4/0.0.0.0/udp/1500')
const privateKey = await keys.generateKeyPair('secp256k1')
const enr = SignableENR.createFromPrivateKey(privateKey)
enr.setLocationMultiaddr(ma)
const provider = await UltralightProvider.create({
bindAddress: '0.0.0.0.0',
transport: TransportLayer.NODE,
config: {
bindAddrs: {
ip4: ma,
describe('Test provider functionality', () => {
it('should test provider API', async () => {
const ma = multiaddr('/ip4/0.0.0.0/udp/1500')
const privateKey = await keys.generateKeyPair('secp256k1')
const enr = SignableENR.createFromPrivateKey(privateKey)
enr.setLocationMultiaddr(ma)
const provider = await UltralightProvider.create({
bindAddress: '0.0.0.0.0',
transport: TransportLayer.NODE,
config: {
bindAddrs: {
ip4: ma,
},
enr,
privateKey,
},
enr,
privateKey,
},
supportedNetworks: [{ networkId: NetworkId.HistoryNetwork }, { networkId: NetworkId.StateNetwork }],
})
supportedNetworks: [{ networkId: NetworkId.HistoryNetwork }, { networkId: NetworkId.StateNetwork }], bootnodes: [],
})


// Stub getBlockByHash for unit testing
provider.portal.ETH.getBlockByHash = async (_hash: Uint8Array) => {
return Block.fromBlockData({ header: BlockHeader.fromHeaderData({ number: 2n }) })
}
// Stub getBlockByHash for unit testing
provider.portal.ETH.getBlockByHash = async (_hash: Uint8Array) => {
return Block.fromBlockData({ header: BlockHeader.fromHeaderData({ number: 2n }) })
}

provider.portal.ETH.getBlockByNumber = async (blockNumber: number | bigint | "latest" | "finalized") => {
return Block.fromBlockData({
header: BlockHeader.fromHeaderData({
number: typeof blockNumber === 'string' ? 0n : blockNumber,
provider.portal.ETH.getBlockByNumber = async (blockNumber: number | bigint | "latest" | "finalized") => {
return Block.fromBlockData({
header: BlockHeader.fromHeaderData({
number: typeof blockNumber === 'string' ? 0n : blockNumber,

})
})
}

provider.portal.ETH.getTransactionCount = async (_address: Uint8Array) => {
return BigInt('0x5')
}

provider.portal.ETH.getCode = async (_address: Uint8Array) => {
return new Uint8Array(Buffer.from('60806040', 'hex'))
}

provider.portal.ETH.getBalance = async (_address: Uint8Array) => {
return 1000000000000000000n
}

provider.portal.ETH.getStorageAt = async (_address: Uint8Array, _position: Uint8Array) => {
const result = new Uint8Array(32)
result[30] = 0x00
result[31] = 0x01
return Buffer.from(result).toString('hex')
}

provider.portal.ETH.call = async (_txObject: any) => {
return Buffer.from([0x00, 0x01]).toString('hex')
}

const blockByHash = await provider.request({
method: 'eth_getBlockByHash',
params: ['0x123', false]
}) as { result: { number: string } }
expect(blockByHash.result.number).toBe('0x2')

const blockByNumber = await provider.request({
method: 'eth_getBlockByNumber',
params: [100, false]
}) as { result: { number: string } }
expect(blockByNumber.result.number).toBe('0x64')

const balance = await provider.request({
method: 'eth_getBalance',
params: ['0x3DC00AaD844393c110b61aED5849b7c82104e748', '0x0']
}) as { result: string }
expect(balance.result).toBe('0xde0b6b3a7640000')


const storage = await provider.request({
method: 'eth_getStorageAt',
params: [
'0x1234567890123456789012345678901234567890',
'0x0000000000000000000000000000000000000000000000000000000000000000',
'0x64'
]
}) as { result: string }
expect(storage.result).toBe('0x' + '00'.repeat(30) + '0001')

const call = await provider.request({
method: 'eth_call',
params: [{
to: '0x1234567890123456789012345678901234567890',
data: '0x70a08231000000000000000000000000'
}, '0x64']
}) as { result: string }
expect(call.result).toBe('0x0001')

await expect(provider.request({
method: 'eth_unsupportedMethod',
params: []
})).rejects.toThrow()

await expect(provider.request({
method: 'eth_getBlockByHash',
params: ['0x123']
})).resolves.toEqual({
error: {
code: -32602,
message: 'Invalid params for eth_getBlockByHash'
},
id: null,
jsonrpc: '2.0',
})
}

provider.portal.ETH.getTransactionCount = async (_address: Uint8Array) => {
return BigInt('0x5')
}

provider.portal.ETH.getCode = async (_address: Uint8Array) => {
return new Uint8Array(Buffer.from('60806040', 'hex'))
}

provider.portal.ETH.getBalance = async (_address: Uint8Array) => {
return 1000000000000000000n
}

provider.portal.ETH.getStorageAt = async (_address: Uint8Array, _position: Uint8Array) => {
const result = new Uint8Array(32)
result[30] = 0x00
result[31] = 0x01
return Buffer.from(result).toString('hex')
}

provider.portal.ETH.call = async (_txObject: any) => {
return Buffer.from([0x00, 0x01]).toString('hex')
}

const blockByHash = await provider.request({
method: 'eth_getBlockByHash',
params: ['0x123', false]
}) as { result: { number: string } }
expect(blockByHash.result.number).toBe('0x2')

const blockByNumber = await provider.request({
method: 'eth_getBlockByNumber',
params: [100, false]
}) as { result: { number: string } }
expect(blockByNumber.result.number).toBe('0x64')

const balance = await provider.request({
method: 'eth_getBalance',
params: ['0x3DC00AaD844393c110b61aED5849b7c82104e748', '0x0']
}) as { result: string }
expect(balance.result).toBe('0xde0b6b3a7640000')


const storage = await provider.request({
method: 'eth_getStorageAt',
params: [
'0x1234567890123456789012345678901234567890',
'0x0000000000000000000000000000000000000000000000000000000000000000',
'0x64'
]
}) as { result: string }
expect(storage.result).toBe('0x' + '00'.repeat(30) + '0001')

const call = await provider.request({
method: 'eth_call',
params: [{
to: '0x1234567890123456789012345678901234567890',
data: '0x70a08231000000000000000000000000'
}, '0x64']
}) as { result: string }
expect(call.result).toBe('0x0001')

await expect(provider.request({
method: 'eth_unsupportedMethod',
params: []
})).rejects.toThrow()

await expect(provider.request({
method: 'eth_getBlockByHash',
params: ['0x123']
})).resolves.toEqual({
error: {
code: -32602,
message: 'Invalid params for eth_getBlockByHash'
},
id: null,
jsonrpc: '2.0',
})

await provider.portal.stop()

await provider.portal.stop()
})
it('should instantiate provider with default network settings', async () => {
const provider = await UltralightProvider.create({})
assert.ok(provider.portal.bootnodes.length > 0)
assert.ok(provider.portal.discv5.bindAddrs[0].toString().includes('0.0.0.0'))
})
})
Loading