@substrate-system/bittorrent-protocol
Version:
Simple, robust, BitTorrent peer wire protocol implementation
331 lines • 13.1 kB
TypeScript
import BitField from '@substrate-system/bitfield';
import { Duplex, type DuplexEvents, type StreamEvents } from 'streamx';
declare class HaveAllBitField {
buffer: Uint8Array;
constructor();
get(_index?: number): boolean;
set(_index?: number): void;
}
export type ProtocolEvents = StreamEvents & {
'upload': (data: any) => void;
'new-item': (item: string) => void;
'item-updated': (item: string, newValue: number) => void;
'finish': (item: any) => void;
'piping': (data: any) => any;
'handshake': (data: any) => any;
'unchoke': () => any;
'have-all': () => void;
'end': () => void;
};
export interface Ext {
onHandshake?(infoHash: string, peerId: string, extensions: {
[name: string]: boolean;
}): void;
onExtendedHandshake?(handshake: {
[key: string]: any;
}): void;
onMessage?(buf: Uint8Array): void;
name: string;
}
export declare class Wire extends Duplex<any> {
_debugId: string;
peerId: null | string;
peerIdBuffer: null | Uint8Array;
readonly type: 'webrtc' | 'tcpIncoming' | 'tcpOutgoing' | 'webSeed' | null;
peerChoking: boolean;
peerInterested: boolean;
readonly requests: any[];
readonly peerRequests: any[];
readonly extendedMapping: Record<number, string>;
readonly peerExtendedMapping: Record<string, number>;
amChoking: boolean;
amInterested: boolean;
_dh: any | null;
_myPubKey: null | string;
_setGenerators: boolean;
_decryptGenerator: any;
_encryptGenerator: any;
_encryptMethod: any;
_peerPubKey: any;
_sharedSecret: string | null;
_cryptoHandshakeDone: boolean;
_peerCryptoProvide: any;
peerPieces: InstanceType<typeof BitField> | InstanceType<typeof HaveAllBitField>;
extensions: Partial<Record<'extended' | 'dht' | 'fast', boolean>>;
peerExtensions: Record<string, any>;
peerExtendedHandshake: any;
extendedHandshake: {};
hasFast: boolean;
allowedFastSet: any;
peerAllowedFastSet: any;
_ext: Record<string, Ext>;
_nextExt: number;
uploaded: number;
downloaded: number;
uploadSpeed: (delta: number) => number;
downloadSpeed: (delta: number) => number;
_keepAliveInterval: any;
_timeout: any;
_timeoutMs: any;
_timeoutUnref?: boolean;
_timeoutExpiresAt: any;
_finished: boolean;
_parserSize: number;
_parser: ((buf: Uint8Array) => void) | null | undefined;
_buffer: any;
_bufferSize: number;
_peEnabled: boolean;
_waitMaxBytes: number | null;
_cryptoSyncPattern: any;
_encryptionMethod: null | 1 | 2;
_infoHash?: Uint8Array;
_handshakeSent?: boolean;
_extendedHandshakeSent?: boolean;
constructor(type: 'webrtc' | 'tcpIncoming' | 'tcpOutgoing' | 'webSeed' | null, retries?: number, peEnabled?: boolean);
/**
* Factory function b/c async.
*
* @returns { Wire } A new Wire instance.
*/
static create(type?: null, retries?: number, peEnabled?: boolean): Promise<Wire>;
once(event: 'bitfield', listener: (bitfield: any) => void): this;
once(event: ('keep-alive' | 'choke' | 'unchoke' | 'interested' | 'uninterested' | 'timeout' | 'have-all' | 'have-none' | 'end' | 'close' | 'finish' | 'pe1' | 'pe2' | 'pe3' | 'pe4'), listener: () => void): this;
once(event: 'error', listener: (err: Error) => void): this;
once(event: 'suggest', listener: (index: number) => void): this;
once(event: 'piece', listener: (index: number, offset: number, buffer: Buffer) => void): this;
once(event: 'cancel', listener: (index: number, offset: number, length: number) => void): this;
once(event: 'extended', listener: (ext: 'handshake' | string, buf: any) => void): void;
once(event: 'unknownmessage', listener: (buffer: Buffer) => void): this;
once(event: 'handshake', listener: (infoHash: string, peerId: string, extensions: {
extended: boolean;
fast: boolean;
}) => void): this;
on(event: 'suggest', listener: (index: number) => void): this;
on(event: 'bitfield', listener: (bitfield: any) => void): this;
on(event: ('keep-alive' | 'choke' | 'unchoke' | 'interested' | 'uninterested' | 'timeout' | 'have-all' | 'have-none'), listener: () => void): this;
on(event: 'upload' | 'have' | 'download' | 'port', listener: (length: number) => void): this;
on(event: 'handshake', listener: (infoHash: string, peerId: string, extensions: {
extended: boolean;
fast: boolean;
}) => void): this;
on(event: 'request', listener: (index: number, offset: number, length: number, respond: (err: Error | null, data?: any) => void) => void): this;
on(event: 'piece', listener: (index: number, offset: number, buffer: Buffer) => void): this;
on(event: 'cancel', listener: (index: number, offset: number, length: number) => void): this;
on(event: 'extended', listener: (ext: 'handshake' | string, buf: any) => void): void;
on(event: 'unknownmessage', listener: (buffer: Buffer) => void): this;
on(event: string, listener: (...args: any[]) => void): this;
emit<K extends keyof ProtocolEvents>(evName: K & 'readable', ...rest: any[]): boolean;
removeListener<TEvent extends keyof StreamEvents | 'piping' | 'readable' | 'data' | 'end' | 'pipe' | 'finish' | 'drain'>(event: TEvent, listener: TEvent extends keyof StreamEvents | 'piping' | 'readable' | 'data' | 'end' | 'pipe' | 'finish' | 'drain' ? DuplexEvents<any, any>[TEvent] : (...args: any[]) => void): this;
/**
* Set whether to send a "keep-alive" ping (sent every 55s)
* @param {boolean} enable
*/
setKeepAlive(enable: boolean): void;
/**
* Set the amount of time to wait before considering a request to be "timed out"
* @param {number} ms
* @param {boolean=} unref (should the timer be unref'd? default: false)
*/
setTimeout(ms: number, unref?: boolean): void;
destroy(): this | undefined;
end(data?: any): void;
/**
* Use the specified protocol extension.
*
* @param {function} Extension
*/
use(Extension: any): void;
/**
* Message "keep-alive": <len=0000>
*/
keepAlive(): void;
sendPe1(): void;
sendPe2(): void;
sendPe3(infoHash: string): Promise<void>;
sendPe4(infoHash: string): Promise<void>;
/**
* Message: "handshake" <pstrlen><pstr><reserved><info_hash><peer_id>
*
* @param {Uint8Array|string} infoHash (as Buffer or *hex* string)
* @param {Uint8Array|string} peerId
* @param {Object} extensions
*/
handshake(infoHash: Uint8Array | string, peerId: Uint8Array | string, extensions?: any): void;
_sendExtendedHandshake(): void;
/**
* Message "choke": <len=0001><id=0>
*/
choke(): void;
/**
* Message "unchoke": <len=0001><id=1>
*/
unchoke(): void;
/**
* Message "interested": <len=0001><id=2>
*/
interested(): void;
/**
* Message "uninterested": <len=0001><id=3>
*/
uninterested(): void;
/**
* Message "have": <len=0005><id=4><piece index>
* @param {number} index
*/
have(index: number): void;
/**
* Message "bitfield": <len=0001+X><id=5><bitfield>
* @param {BitField|Buffer} bitfield
*/
bitfield(bitfield: any): void;
/**
* Message "request": <len=0013><id=6><index><begin><length>
* @param {number} index
* @param {number} offset
* @param {number} length
* @param {function} cb
*/
request(index: any, offset: any, length: any, cb: any): any;
/**
* Message "piece": <len=0009+X><id=7><index><begin><block>
* @param {number} index
* @param {number} offset
* @param {Uint8Array} buffer
*/
piece(index: number, offset: number, buffer: Uint8Array): void;
/**
* Message "cancel": <len=0013><id=8><index><begin><length>
* @param {number} index
* @param {number} offset
* @param {number} length
*/
cancel(index: any, offset: any, length: any): void;
/**
* Message: "port" <len=0003><id=9><listen-port>
* @param {Number} port
*/
port(port: any): void;
/**
* Message: "suggest" <len=0x0005><id=0x0D><piece index> (BEP6)
* @param {number} index
*/
suggest(index: any): void;
/**
* Message: "have-all" <len=0x0001><id=0x0E> (BEP6)
*/
haveAll(): void;
/**
* Message: "have-none" <len=0x0001><id=0x0F> (BEP6)
*/
haveNone(): void;
/**
* Message "reject": <len=0x000D><id=0x10><index><offset><length> (BEP6)
* @param {number} index
* @param {number} offset
* @param {number} length
*/
reject(index: any, offset: any, length: any): void;
/**
* Message: "allowed-fast" <len=0x0005><id=0x11><piece index> (BEP6)
* @param {number} index
*/
allowedFast(index: any): void;
/**
* Message: "extended" <len=0005+X><id=20><ext-number><payload>
* @param {number|string} ext
* @param {Object} obj
*/
extended(ext: number | string, obj: Uint8Array | object): void;
/**
* Sets the encryption method for this wire, as per PSE/ME specification
*
* @param {string} sharedSecret: A hex-encoded string, which is the shared secret agreed
* upon from DH key exchange
* @param {string} infoHash: A hex-encoded info hash
* @returns boolean, true if encryption setting succeeds, false if it fails.
*/
setEncrypt(sharedSecret: any, infoHash: any): Promise<boolean>;
/**
* Send a message to the remote peer.
*/
_message(id: any, numbers: any, data: any): void;
_push(data: any): boolean | undefined;
_onKeepAlive(): void;
_onPe1(pubKeyBuffer: any): void;
_onPe2(pubKeyBuffer: any): void;
_onPe3(hashesXorBuffer: Uint8Array): Promise<void>;
_onPe3Encrypted(vcBuffer: any, peerProvideBuffer: any): void;
_onPe4(peerSelectBuffer: Uint8Array): void;
_onHandshake(infoHashBuffer: any, peerIdBuffer: any, extensions: any): void;
_onChoke(): void;
_onUnchoke(): void;
_onInterested(): void;
_onUninterested(): void;
_onHave(index: any): void;
_onBitField(buffer: any): void;
_onRequest(index: number, offset: number, length: number): void;
_onPiece(index: any, offset: any, buffer: any): void;
_onCancel(index: any, offset: any, length: any): void;
_onPort(port: any): void;
_onSuggest(index: any): void;
_onHaveAll(): void;
_onHaveNone(): void;
_onReject(index: any, offset: any, length: any): void;
_onAllowedFast(index: number): void;
_onExtended(ext: number | string, buf: Uint8Array): void;
_onTimeout(): void;
/**
* Duplex stream method. Called whenever the remote peer has data for us. Data that the
* remote peer sends gets buffered (i.e. not actually processed) until the right number
* of bytes have arrived, determined by the last call to `this._parse(number, callback)`.
* Once enough bytes have arrived to process the message, the callback function
* (i.e. `this._parser`) gets called with the full buffer of data.
* @param {Uint8Array} data
* @param {(null):void} cb Signal that we're ready for more data
*/
_write(data: Uint8Array, cb: any): void;
_callback(request: any, err: any, buffer: any): void;
_resetTimeout(setAgain: any): void;
/**
* Takes a number of bytes that the local peer is waiting to receive from
* the remote peer.
* In order to parse a complete message, add a callback function to be
* called once enough bytes have arrived.
* @param {number} size
* @param {function} parser
*/
_parse(size: number, parser?: (buf: Uint8Array) => void): void;
_parseUntil(pattern: any, maxBytes: any): void;
/**
* Handle the first 4 bytes of a message, to determine the length of bytes
* that must be waited for in order to have the whole message.
* @param {Uint8Array} buffer
*/
_onMessageLength(buffer: Uint8Array): void;
/**
* Handle a message from the remote peer.
* @param {Uint8Array} buffer
*/
_onMessage(buffer: Uint8Array): void;
_determineHandshakeType(): void;
_parsePe1(pubKeyPrefix: any): void;
_parsePe2(): void;
_parsePe3(): Promise<void>;
_parsePe3Encrypted(): void;
_parsePe4(): void;
/**
* Reads the handshake as specified by the bittorrent wire protocol.
*/
_parseHandshake(): void;
_onHandshakeBuffer(handshake: any): void;
_onFinish(): void;
_debug(...args: any[]): void;
_pull(requests: any, piece: any, offset: any, length: any): any;
_encryptHandshake(buf: any): Uint8Array<any>;
_encrypt(buf: Uint8Array): Uint8Array<ArrayBuffer>;
_decryptHandshake(buf: Uint8Array): Uint8Array<ArrayBuffer>;
_decrypt(buf: Uint8Array): Uint8Array<ArrayBuffer>;
_utfToHex(str: string): string;
}
export default Wire;
//# sourceMappingURL=index.d.ts.map