Arseniy Klempner 16253026c6
feat: implement lp-v3 error codes with backwards compatibility (#2501)
* feat: implement LightPush v3 protocol support

Add comprehensive LightPush v3 protocol implementation with:

Core Features:
- LightPush v3 protocol codec and multicodec detection
- Status code-based error handling and validation
- Protocol version inference and compatibility layers
- Enhanced error types with detailed failure information

Protocol Support:
- Automatic v3/v2 protocol negotiation and fallback
- Status code mapping to LightPush error types
- Protocol version tracking in SDK results
- Mixed protocol environment support

Testing Infrastructure:
- Comprehensive v3 error code handling tests
- Mock functions for v3/v2 response scenarios
- Protocol version detection and validation tests
- Backward compatibility verification

Implementation Details:
- Clean separation between v2 and v3 response handling
- Type-safe status code validation with isSuccess helper
- Enhanced failure reporting with protocol version context
- Proper error propagation through SDK layers

This implementation maintains full backward compatibility with v2
while providing enhanced functionality for v3 protocol features.

* feat: handle both light push protocols

* fix: unsubscribe test

* feat: consolidate lpv2/v3 types

* feat(tests): bump nwaku to 0.36.0

* fix: remove extraneous exports

* fix: add delay to tests

* fix: remove protocol result types

* feat: consolidate light push codec branching

* fix: revert nwaku image

* fix: remove multicodec

* fix: remove protocolversion

* feat: simplify v2/v3 branching logic to use two stream managers

* fix: remove unused utils

* fix: remove comments

* fix: revert store test

* fix: cleanup lightpush sdk

* fix: remove unused util

* fix: remove unused exports

* fix: rename file from public to protocol_handler

* fix: use proper type for sdk result

* fix: update return types in filter

* fix: rebase against latest master

* fix: use both lightpush codecs when waiting for peer

* fix: handle both lp codecs

* fix: remove unused code

* feat: use array for multicodec fields

* fix: add timestamp if missing in v3 rpc

* fix: resolve on either lp codec when waiting for peer

* fix: remove unused util

* fix: remove unnecessary abstraction

* feat: accept nwaku docker image as arg, test lp backwards compat

* fix: revert filter error

* feat: add legacy flag to enable lightpushv2 only

* Revert "feat: accept nwaku docker image as arg, test lp backwards compat"

This reverts commit 857e12cbc73305e5c51abd057665bd34708b2737.

* fix: remove unused test

* feat: improve lp3 (#2597)

* improve light push core

* move back to singualar multicodec property, enable array prop only for light push

* implement v2/v3 interop e2e test, re-add useLegacy flag, ensure e2e runs for v2 and v3

* fix v2 v3 condition

* generate message package earlier

* add log, fix condition

---------

Co-authored-by: Sasha <118575614+weboko@users.noreply.github.com>
Co-authored-by: Sasha <oleksandr@status.im>
2025-09-05 00:52:37 +02:00

138 lines
3.5 KiB
TypeScript

import type { PeerId } from "@libp2p/interface";
import { LightPushCore } from "@waku/core";
import {
type IEncoder,
ILightPush,
type IMessage,
type ISendOptions,
type Libp2p,
LightPushCoreResult,
LightPushError,
LightPushFailure,
type LightPushProtocolOptions,
LightPushSDKResult,
Protocols
} from "@waku/interfaces";
import { Logger } from "@waku/utils";
import { PeerManager } from "../peer_manager/index.js";
import { RetryManager } from "./retry_manager.js";
const log = new Logger("sdk:light-push");
const DEFAULT_MAX_ATTEMPTS = 3;
const DEFAULT_SEND_OPTIONS: LightPushProtocolOptions = {
autoRetry: true,
retryIntervalMs: 1000,
maxAttempts: DEFAULT_MAX_ATTEMPTS,
numPeersToUse: 1
};
type LightPushConstructorParams = {
peerManager: PeerManager;
libp2p: Libp2p;
options?: Partial<LightPushProtocolOptions>;
};
export class LightPush implements ILightPush {
private readonly config: LightPushProtocolOptions;
private readonly retryManager: RetryManager;
private readonly peerManager: PeerManager;
private readonly protocol: LightPushCore;
public constructor(params: LightPushConstructorParams) {
this.config = {
...DEFAULT_SEND_OPTIONS,
...(params.options || {})
} as LightPushProtocolOptions;
this.peerManager = params.peerManager;
this.protocol = new LightPushCore(params.libp2p);
this.retryManager = new RetryManager({
peerManager: params.peerManager,
retryIntervalMs: this.config.retryIntervalMs
});
}
public get multicodec(): string[] {
return this.protocol.multicodec;
}
public start(): void {
this.retryManager.start();
}
public stop(): void {
this.retryManager.stop();
}
public async send(
encoder: IEncoder,
message: IMessage,
options: ISendOptions = {}
): Promise<LightPushSDKResult> {
options = {
useLegacy: false,
...this.config,
...options
};
const { pubsubTopic } = encoder;
log.info("send: attempting to send a message to pubsubTopic:", pubsubTopic);
const peerIds = await this.peerManager.getPeers({
protocol: options.useLegacy ? "light-push-v2" : Protocols.LightPush,
pubsubTopic: encoder.pubsubTopic
});
const coreResults =
peerIds?.length > 0
? await Promise.all(
peerIds.map((peerId) =>
this.protocol
.send(encoder, message, peerId, options.useLegacy)
.catch((_e) => ({
success: null,
failure: {
error: LightPushError.GENERIC_FAIL
}
}))
)
)
: [];
const results: LightPushSDKResult = coreResults.length
? {
successes: coreResults
.filter((v) => v.success)
.map((v) => v.success) as PeerId[],
failures: coreResults
.filter((v) => v.failure)
.map((v) => v.failure) as LightPushFailure[]
}
: {
successes: [],
failures: [
{
error: LightPushError.NO_PEER_AVAILABLE
}
]
};
if (options.autoRetry && results.successes.length === 0) {
const sendCallback = (peerId: PeerId): Promise<LightPushCoreResult> =>
this.protocol.send(encoder, message, peerId, options.useLegacy);
this.retryManager.push(
sendCallback.bind(this),
options.maxAttempts || DEFAULT_MAX_ATTEMPTS,
encoder.routingInfo
);
}
return results;
}
}