Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
270 changes: 270 additions & 0 deletions src/bee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import { FeedPayloadResult, createFeedManifest, fetchLatestFeedUpdate } from './modules/feed'
import * as grantee from './modules/grantee'
import * as gsoc from './modules/gsoc'
import * as mic from './modules/mic'
import * as moc from './modules/moc'
import * as pinning from './modules/pinning'
import * as pss from './modules/pss'
import { rchash } from './modules/rchash'
Expand Down Expand Up @@ -59,6 +61,10 @@
LastCashoutActionResponse,
LastChequesForPeerResponse,
LastChequesResponse,
MicMessageHandler,
MicSubscription,
MocMessageHandler,
MocSubscription,
NodeAddresses,
NodeInfo,
NumberString,
Expand Down Expand Up @@ -119,6 +125,8 @@
prepareDownloadOptions,
prepareFileUploadOptions,
prepareGsocMessageHandler,
prepareMicMessageHandler,
prepareMocMessageHandler,
preparePostageBatchOptions,
preparePssMessageHandler,
prepareRedundantUploadOptions,
Expand Down Expand Up @@ -1155,7 +1163,7 @@
for (let i = 0n; i < 0xffffn; i++) {
const signer = new PrivateKey(Binary.numberToUint256(start + i, 'BE'))
const socAddress = makeSOCAddress(identifier, signer.publicKey().address())
// TODO: test the significance of the hardcoded 256

Check warning on line 1166 in src/bee.ts

View workflow job for this annotation

GitHub Actions / check (16.x)

Unexpected 'todo' comment: 'TODO: test the significance of the...'
const actualProximity = 256 - Binary.proximity(socAddress.toUint8Array(), targetOverlay.toUint8Array())

if (actualProximity <= 256 - proximity) {
Expand Down Expand Up @@ -1287,6 +1295,268 @@
return subscription
}

/**
* Mines the signer (a private key) to be used to send MOC messages to the specific target overlay address.
*
* Use {@link mocSend} to send MOC messages with the mined signer.
*
* Use {@link mocSubscribe} to subscribe to MOC messages for the identifier.
*
* **Warning! Only full nodes can accept MOC messages.**
*
* @param targetOverlay
* @param identifier
* @param proximity
* @returns
*
* @example
* const identifier = NULL_IDENTIFIER
* const { overlay } = await bee.getNodeAddresses()
* const signer = bee.mocMine(overlay, identifier)
* const cac = makeContentAddressedChunk('Hello MOC!')
* const soc = cac.toSingleOwnerChunk(identifier, signer)
* await bee.mocSend(soc, postageBatchId)
*/
mocMine(
targetOverlay: PeerAddress | Uint8Array | string,
identifier: Identifier | Uint8Array | string,
proximity = 12,
): PrivateKey {
targetOverlay = new PeerAddress(targetOverlay)
identifier = new Identifier(identifier)
for (let i = 0; i < 0xffff; i++) {
const randomBytes = crypto.getRandomValues(new Uint8Array(32))
const signer = new PrivateKey(randomBytes)
const socAddress = makeSOCAddress(identifier, signer.publicKey().address())
const actualProximity = Binary.proximity(socAddress.toUint8Array(), targetOverlay.toUint8Array())

if (actualProximity >= proximity) {
return signer
}
}
throw Error('Could not mine a valid signer')
}

mocSend = moc.send

/**
* Subscribes to MOC (Mined Owner Chunk) messages for the specified identifier.
*
* The node delivers every incoming single-owner chunk whose identifier matches,
* regardless of its owner.
*
* Use {@link mocSend} to send MOC messages on the identifier.
*
* **Warning! Only full nodes can accept MOC messages.**
*
* @param identifier
* @param handler
* @returns
*
* @example
* const identifier = NULL_IDENTIFIER
* const subscription = bee.mocSubscribe(identifier, {
* onMessage(message) {
* // handle
* },
* onError(error) {
* // handle
* },
* onClose() {
* // handle
* }
* })
*/
mocSubscribe(identifier: Identifier | Uint8Array | string, handler: MocMessageHandler): MocSubscription {
identifier = new Identifier(identifier)
handler = prepareMocMessageHandler(handler)

const ws = moc.subscribe(this.url, identifier, this.requestOptions.headers)

let cancelled = false
const cancel = () => {
if (!cancelled) {
cancelled = true

if (ws.terminate) {
ws.terminate()
} else {
ws.close()
}
}
}

const subscription = {
identifier,
cancel,
}

ws.onmessage = async event => {
const data = await prepareWebsocketData(event.data)

if (data.length) {
handler.onMessage(new Bytes(data), subscription)
}
}
ws.onerror = event => {
if (!cancelled) {
handler.onError(new BeeError(event.message), subscription)
}
}
ws.onclose = () => {
handler.onClose(subscription)
}

return subscription
}

/**
* Mines an identifier to be used to send MIC messages to the specified target overlay address.
*
* Unlike {@link gsocMine} (which mines the owner for a fixed identifier), this mines the
* identifier for a fixed owner (the signer), so that the resulting single-owner chunk address
* lands in the target overlay's neighbourhood. This is the MIC (Mined ID Chunk) counterpart.
*
* Use {@link micSend} to send MIC messages with the mined identifier.
*
* Use {@link micSubscribe} to subscribe to MIC messages for the owner (of the signer).
*
* **Warning! Only full nodes can accept MIC messages.**
*
* @param targetOverlay
* @param signer The fixed publisher identity
* @param proximity
* @returns
*
* @example
* const signer = new PrivateKey('...')
* const { overlay } = await bee.getNodeAddresses()
* const identifier = bee.micMine(overlay, signer)
* const cac = makeContentAddressedChunk('MIC!')
* const soc = cac.toSingleOwnerChunk(identifier, signer)
* await bee.micSend(soc, postageBatchId)
*/
micMine(
targetOverlay: PeerAddress | Uint8Array | string,
signer: PrivateKey | Uint8Array | string,
proximity = 12,
): Identifier {
targetOverlay = new PeerAddress(targetOverlay)
signer = new PrivateKey(signer)
const owner = signer.publicKey().address()
for (let i = 0; i < 0xffff; i++) {
const identifier = new Identifier(crypto.getRandomValues(new Uint8Array(32)))
const socAddress = makeSOCAddress(identifier, owner)
const actualProximity = Binary.proximity(socAddress.toUint8Array(), targetOverlay.toUint8Array())

if (actualProximity >= proximity) {
return identifier
}
}
throw Error('Could not mine a valid identifier')
}

/**
* Sends a MIC (Mined ID Chunk) message with the specified signer and identifier.
*
* A MIC is matched on the receiving node by its owner, regardless of identifier. The
* owner is a fixed publisher identity (the signer); the identifier is mined so that the
* chunk address lands in the subscriber's neighbourhood. Use {@link micMine} to mine
* such an identifier for the subscriber's overlay address.
*
* Use {@link micSubscribe} to subscribe to MIC messages for the owner.
*
* **Warning! Only full nodes can accept MIC messages.**
*
* @param postageBatchId
* @param signer The fixed publisher identity
* @param identifier The mined identifier, typically from {@link micMine}
* @param data
* @param options
* @param requestOptions Options for making requests, such as timeouts, custom HTTP agents, headers, etc.
* @returns
*
* @example
* const signer = new PrivateKey('...')
* const { overlay } = await bee.getNodeAddresses()
* const identifier = bee.micMine(overlay, signer)
* const cac = makeContentAddressedChunk('MIC!')
* const soc = cac.toSingleOwnerChunk(identifier, signer)
* await bee.micSend(soc, postageBatchId)
*/
micSend = mic.send

/**
* Subscribes to MIC (Mined ID Chunk) messages for the specified owner ethereum address.
*
* The node delivers every incoming single-owner chunk whose owner matches,
* regardless of its identifier.
*
* Use {@link micSend} to send MIC messages for the owner.
*
* **Warning! Only full nodes can accept MIC messages.**
*
* @param address Owner ethereum address (of the publisher's signer)
* @param handler
* @returns
*
* @example
* const signer = new PrivateKey('...')
* const subscription = bee.micSubscribe(signer.publicKey().address(), {
* onMessage(message) {
* // handle
* },
* onError(error) {
* // handle
* },
* onClose() {
* // handle
* }
* })
*/
micSubscribe(address: EthAddress | Uint8Array | string, handler: MicMessageHandler): MicSubscription {
address = new EthAddress(address)
handler = prepareMicMessageHandler(handler)

const ws = mic.subscribe(this.url, address, this.requestOptions.headers)

let cancelled = false
const cancel = () => {
if (!cancelled) {
cancelled = true

if (ws.terminate) {
ws.terminate()
} else {
ws.close()
}
}
}

const subscription = {
owner: address,
cancel,
}

ws.onmessage = async event => {
const data = await prepareWebsocketData(event.data)

if (data.length) {
handler.onMessage(new Bytes(data), subscription)
}
}
ws.onerror = event => {
if (!cancelled) {
handler.onError(new BeeError(event.message), subscription)
}
}
ws.onclose = () => {
handler.onClose(subscription)
}

return subscription
}

/**
* Creates a feed manifest chunk and returns the reference to it.
*
Expand Down Expand Up @@ -1845,7 +2115,7 @@
gasPrice?: NumberString | string | bigint,
requestOptions?: BeeRequestOptions,
): Promise<TransactionId> {
// TODO: check BZZ in tests

Check warning on line 2118 in src/bee.ts

View workflow job for this annotation

GitHub Actions / check (16.x)

Unexpected 'todo' comment: 'TODO: check BZZ in tests'
const amountString =
amount instanceof BZZ ? amount.toPLURString() : asNumberString(amount, { min: 1n, name: 'amount' })

Expand Down Expand Up @@ -2520,7 +2790,7 @@
* @deprecated Use `getPostageBatches` instead
*/
async getAllPostageBatch(requestOptions?: BeeRequestOptions): Promise<PostageBatch[]> {
return stamps.getAllPostageBatches(this.getRequestOptionsForCall(requestOptions)) // TODO: remove in June 2025

Check warning on line 2793 in src/bee.ts

View workflow job for this annotation

GitHub Actions / check (16.x)

Unexpected 'todo' comment: 'TODO: remove in June 2025'
}

/**
Expand All @@ -2531,7 +2801,7 @@
* @deprecated Use `getGlobalPostageBatches` instead
*/
async getAllGlobalPostageBatch(requestOptions?: BeeRequestOptions): Promise<GlobalPostageBatch[]> {
return stamps.getGlobalPostageBatches(this.getRequestOptionsForCall(requestOptions)) // TODO: remove in June 2025

Check warning on line 2804 in src/bee.ts

View workflow job for this annotation

GitHub Actions / check (16.x)

Unexpected 'todo' comment: 'TODO: remove in June 2025'
}

/**
Expand Down
20 changes: 20 additions & 0 deletions src/modules/mic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { System } from 'cafe-utility'
import WebSocket from 'isomorphic-ws'
import { uploadSingleOwnerChunk } from '../chunk/soc'
import { EthAddress } from '../utils/typed-bytes'

const endpoint = 'mic'

export { uploadSingleOwnerChunk as send }

export function subscribe(url: string, owner: EthAddress, headers?: Record<string, string>) {
const wsUrl = url.replace(/^http/i, 'ws')

if (System.whereAmI() === 'browser') {
return new WebSocket(`${wsUrl}/${endpoint}/subscribe/${owner.toHex()}`)
}

return new WebSocket(`${wsUrl}/${endpoint}/subscribe/${owner.toHex()}`, {
headers,
})
}
20 changes: 20 additions & 0 deletions src/modules/moc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { System } from 'cafe-utility'
import WebSocket from 'isomorphic-ws'
import { uploadSingleOwnerChunk } from '../chunk/soc'
import { Identifier } from '../utils/typed-bytes'

const endpoint = 'moc'

export { uploadSingleOwnerChunk as send }

export function subscribe(url: string, identifier: Identifier, headers?: Record<string, string>) {
const wsUrl = url.replace(/^http/i, 'ws')

if (System.whereAmI() === 'browser') {
return new WebSocket(`${wsUrl}/${endpoint}/subscribe/${identifier.toHex()}`)
}

return new WebSocket(`${wsUrl}/${endpoint}/subscribe/${identifier.toHex()}`, {
headers,
})
}
22 changes: 22 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,28 @@ export interface GsocMessageHandler {
onClose: (subscription: GsocSubscription) => void
}

export interface MocSubscription {
readonly identifier: Identifier
cancel: () => void
}

export interface MocMessageHandler {
onMessage: (message: Bytes, subscription: MocSubscription) => void
onError: (error: BeeError, subscription: MocSubscription) => void
onClose: (subscription: MocSubscription) => void
}

export interface MicSubscription {
readonly owner: EthAddress
cancel: () => void
}

export interface MicMessageHandler {
onMessage: (message: Bytes, subscription: MicSubscription) => void
onError: (error: BeeError, subscription: MicSubscription) => void
onClose: (subscription: MicSubscription) => void
}

export interface ReferenceResponse {
reference: Reference
}
Expand Down
Loading
Loading