Enhanced features for new rust libraries

This commit is contained in:
Chris Daßler
2025-09-18 15:22:12 +02:00
parent 739ec66898
commit 6077826815
2 changed files with 139 additions and 17 deletions

View File

@@ -3,6 +3,7 @@
*/
import { NativeEventEmitter, NativeModules } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { LIBP2P_CONFIG } from '../utils/constants';
import type { ConnectionStatusEvent, PeerDiscoveredEvent, PeerInfoEvent } from '../utils/types';
import type {
@@ -74,8 +75,13 @@ export class Libp2pComponent implements ILibp2pComponent {
private _peerId?: PeerId;
private _multiaddrs: Multiaddr[] = [];
private _started: boolean = false;
private _starting: boolean = false;
private _error: string | null = null;
private cachedConnections: Connection[] = [];
private options: Libp2pOptions;
private nodeStartedListener: any = null;
private nodeStoppedListener: any = null;
private errorListener: any = null;
constructor(options?: Libp2pOptions, nativeModules?: typeof NativeModules) {
// Allow dependency injection of NativeModules for testing
@@ -90,6 +96,7 @@ export class Libp2pComponent implements ILibp2pComponent {
this.setupNativeEventListeners();
this.setupProtocolHandlers();
this.setupStateListeners();
}
get peerId(): PeerId | null {
@@ -100,6 +107,14 @@ export class Libp2pComponent implements ILibp2pComponent {
return this._multiaddrs;
}
get isStarting(): boolean {
return this._starting;
}
get error(): string | null {
return this._error;
}
private setupNativeEventListeners(): void {
// Map native events to js-libp2p style events
@@ -195,6 +210,27 @@ export class Libp2pComponent implements ILibp2pComponent {
);
}
private setupStateListeners(): void {
// Listen for node started event
this.nodeStartedListener = this.eventEmitter.addListener('onNodeStarted', () => {
this._started = true;
this._starting = false;
this._error = null;
});
// Listen for node stopped event
this.nodeStoppedListener = this.eventEmitter.addListener('onNodeStopped', () => {
this._started = false;
this._starting = false;
});
// Listen for error events
this.errorListener = this.eventEmitter.addListener('onError', (event: any) => {
this._error = event.error || 'Unknown error';
this._starting = false;
});
}
private emit<K extends keyof Libp2pEvents>(event: K, detail: unknown): void {
const handlers = this.eventHandlers.get(event);
if (handlers) {
@@ -206,6 +242,14 @@ export class Libp2pComponent implements ILibp2pComponent {
}
async start(): Promise<void> {
if (this._started || this._starting) {
return; // Already started or starting
}
this._starting = true;
this._error = null;
try {
// Register protocols if any
if (this.options.protocols && this.nativeModule.registerProtocolHandler) {
for (const protocol of this.options.protocols) {
@@ -213,25 +257,65 @@ export class Libp2pComponent implements ILibp2pComponent {
}
}
// Load stored private key if available and not provided
let privateKeyBytes = this.options.keypair?.privateKey;
if (!privateKeyBytes) {
try {
const storedKey = await AsyncStorage.getItem('libp2p_private_key');
if (storedKey) {
privateKeyBytes = new Uint8Array(JSON.parse(storedKey));
}
} catch (err) {
// Failed to load stored key, will generate new one
}
}
// Pass configuration options to native module including keypair
const config = {
const config: any = {
tcpPort: this.options.config?.tcpPort,
wsPort: this.options.config?.wsPort,
// Convert Uint8Array to base64 for passing to native module
// Using Buffer.from for proper encoding of binary data
privateKey: this.options.keypair?.privateKey
? Buffer.from(this.options.keypair.privateKey).toString('base64')
: undefined,
};
await this.nativeModule.startLibp2p(config);
if (privateKeyBytes) {
// Convert Uint8Array to base64 for passing to native module
config.privateKey = Buffer.from(privateKeyBytes).toString('base64');
}
const result = await this.nativeModule.startLibp2p(config);
// Update internal state from result
if (result.peerId) {
this._peerId = new SimplePeerId(result.peerId);
}
if (result.multiaddrs) {
this._multiaddrs = result.multiaddrs.map((addr: string) => new SimpleMultiaddr(addr));
}
// Store the private key if we generated a new one
if (result.privateKey && !privateKeyBytes) {
try {
const keyBytes = Buffer.from(result.privateKey, 'base64');
await AsyncStorage.setItem('libp2p_private_key', JSON.stringify(Array.from(keyBytes)));
} catch (err) {
// Failed to store key, but continue
}
}
this._started = true;
this._starting = false;
} catch (error) {
this._starting = false;
this._error = error instanceof Error ? error.message : 'Failed to start';
throw error;
}
}
async stop(): Promise<void> {
try {
await this.nativeModule.stopLibp2p();
this._started = false;
this._peerId = undefined;
this._multiaddrs = [];
} catch (error) {
// If libp2p wasn't running, that's okay - just update our state
const errorMessage = error instanceof Error ? error.message : String(error);
@@ -245,6 +329,24 @@ export class Libp2pComponent implements ILibp2pComponent {
}
}
cleanup(): void {
// Remove state listeners
if (this.nodeStartedListener) {
this.nodeStartedListener.remove();
this.nodeStartedListener = null;
}
if (this.nodeStoppedListener) {
this.nodeStoppedListener.remove();
this.nodeStoppedListener = null;
}
if (this.errorListener) {
this.errorListener.remove();
this.errorListener = null;
}
// Clear event handlers
this.eventHandlers.clear();
}
async dial(multiaddr: string): Promise<Connection> {
await this.nativeModule.connectToPeer(multiaddr);
@@ -368,6 +470,20 @@ export class Libp2pComponent implements ILibp2pComponent {
throw new Error('Protocol sending not supported on this platform');
}
}
async acceptConnection(peerId: string): Promise<void> {
if (!this.nativeModule.acceptConnection) {
throw new Error('acceptConnection not supported by native module');
}
await this.nativeModule.acceptConnection(peerId);
}
async rejectConnection(peerId: string): Promise<void> {
if (!this.nativeModule.rejectConnection) {
throw new Error('rejectConnection not supported by native module');
}
await this.nativeModule.rejectConnection(peerId);
}
}
// Type definition for CustomEventInit

View File

@@ -43,6 +43,9 @@ export interface ProtocolHandler {
export interface ILibp2pComponent {
peerId: PeerId | null;
multiaddrs: Multiaddr[];
isStarted: boolean;
isStarting: boolean;
error: string | null;
start(): Promise<void>;
stop(): Promise<void>;
@@ -52,6 +55,9 @@ export interface ILibp2pComponent {
sendProtocolData(peerId: string, protocolId: string, data: Uint8Array): Promise<void>;
refreshDiscovery(): Promise<void>;
pingPeer(peerId: string): Promise<{ success: boolean; rtt?: number; peerId: string }>;
acceptConnection(peerId: string): Promise<void>;
rejectConnection(peerId: string): Promise<void>;
cleanup(): void;
addEventListener<K extends keyof Libp2pEvents>(
event: K,