diff --git a/packages/cli/src/rpc/modules/portal.ts b/packages/cli/src/rpc/modules/portal.ts index 08d9c3cca..967ddfe88 100644 --- a/packages/cli/src/rpc/modules/portal.ts +++ b/packages/cli/src/rpc/modules/portal.ts @@ -1,4 +1,4 @@ -import { EntryStatus } from '@chainsafe/discv5' +import { EntryStatus, distance } from '@chainsafe/discv5' import { ENR } from '@chainsafe/enr' import { bigIntToHex, bytesToHex, hexToBytes, short } from '@ethereumjs/util' import { @@ -34,6 +34,7 @@ const methods = [ 'portal_statePing', 'portal_stateRoutingTableInfo', 'portal_stateStore', + 'portal_statePutContent', 'portal_stateLocalContent', 'portal_stateGossip', 'portal_stateFindContent', @@ -54,6 +55,7 @@ const methods = [ 'portal_historyGetContent', 'portal_historyTraceGetContent', 'portal_historyStore', + 'portal_historyPutContent', 'portal_historyLocalContent', 'portal_historyGossip', 'portal_historyAddEnr', @@ -65,6 +67,7 @@ const methods = [ 'portal_beaconGetContent', 'portal_beaconTraceGetContent', 'portal_beaconStore', + 'portal_beaconPutContent', 'portal_beaconLocalContent', 'portal_beaconAddEnr', 'portal_beaconGetEnr', @@ -187,6 +190,19 @@ export class portal { [validators.hex], [validators.hex], ]) + // portal_*PutContent + this.historyPutContent = middleware(this.historyPutContent.bind(this), 2, [ + [validators.contentKey], + [validators.hex], + ]) + this.statePutContent = middleware(this.statePutContent.bind(this), 2, [ + [validators.hex], + [validators.hex], + ]) + this.beaconPutContent = middleware(this.beaconPutContent.bind(this), 2, [ + [validators.hex], + [validators.hex], + ]) // portal_*FindContent this.historyFindContent = middleware(this.historyFindContent.bind(this), 2, [ @@ -763,6 +779,68 @@ export class portal { return false } } + // portal_*PutContent + async historyPutContent(params: [string, string]) { + const [contentKey, content] = params.map((param) => hexToBytes(param)) + const contentId = this._history.contentKeyToId(contentKey) + const d = distance(contentId, this._client.discv5.enr.nodeId) + let storedLocally = false + try { + if (d <= this._history.nodeRadius) { + await this._history.store(contentKey, content) + storedLocally = true + } + const peerCount = await this._history.gossipContent(contentKey, content) + return { + peerCount, + storedLocally, + } + } catch { + return { + peerCount: 0, + storedLocally, + } + } + } + async statePutContent(params: [string, string]) { + const [contentKey, content] = params.map((param) => hexToBytes(param)) + const contentId = this._history.contentKeyToId(contentKey) + const d = distance(contentId, this._client.discv5.enr.nodeId) + let storedLocally = false + try { + if (d <= this._history.nodeRadius) { + await this._history.store(contentKey, content) + storedLocally = true + } + const peerCount = await this._history.gossipContent(contentKey, content) + return { + peerCount, + storedLocally, + } + } catch { + return { + peerCount: 0, + storedLocally, + } + } + } + async beaconPutContent(params: [string, string]) { + const [contentKey, content] = params.map((param) => hexToBytes(param)) + const contentId = this._history.contentKeyToId(contentKey) + const d = distance(contentId, this._client.discv5.enr.nodeId) + let storedLocally = false + try { + if (d <= this._history.nodeRadius) { + await this._history.store(contentKey, content) + storedLocally = true + } + } catch { + return { + peerCount: 0, + storedLocally, + } + } + } // portal_*FindContent async historyFindContent(params: [string, string]) { diff --git a/packages/portalnetwork/src/client/routingTable.ts b/packages/portalnetwork/src/client/routingTable.ts index 283257f3b..fb3fd84f7 100644 --- a/packages/portalnetwork/src/client/routingTable.ts +++ b/packages/portalnetwork/src/client/routingTable.ts @@ -45,22 +45,21 @@ export class PortalNetworkRoutingTable extends KademliaRoutingTable { * @returns boolean indicating if node has previously been OFFERed `contentKey` already */ public contentKeyKnownToPeer = (nodeId: NodeId, contentKey: Uint8Array) => { + const gossipList = this.gossipMap.get(nodeId) + if (gossipList !== undefined) { + const alreadyKnownToPeer = gossipList.has(contentKey) + if (alreadyKnownToPeer) return true + } + return false + } + + public markContentKeyAsKnownToPeer = (nodeId: NodeId, contentKey: Uint8Array) => { let gossipList = this.gossipMap.get(nodeId) if (!gossipList) { - // If no gossipList exists, create new one for `nodeId` and add contentKey to it gossipList = new Set() - gossipList.add(contentKey) - this.gossipMap.set(nodeId, gossipList) - return false - } - const alreadyKnownToPeer = gossipList.has(contentKey) - if (alreadyKnownToPeer) return true - else { - // If contentKey has not been shared with peer, add contentKey to gossipList - gossipList.add(contentKey) this.gossipMap.set(nodeId, gossipList) - return false } + gossipList.add(contentKey) } /** diff --git a/packages/portalnetwork/src/networks/network.ts b/packages/portalnetwork/src/networks/network.ts index 254966511..2c69e5380 100644 --- a/packages/portalnetwork/src/networks/network.ts +++ b/packages/portalnetwork/src/networks/network.ts @@ -854,6 +854,7 @@ export abstract class BaseNetwork extends EventEmitter { `Offering ${bytesToHex(contentKey)} to ${shortId(peer.nodeId)}`, ) const res = await this.sendMessage(peer, payload, this.networkId) + this.routingTable.markContentKeyAsKnownToPeer(peer.nodeId, contentKey) return [peer, res] }), )