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

253 lines
7.7 KiB
TypeScript

import type { PeerId } from "@libp2p/interface";
import type { ConnectionManagerOptions } from "./connection_manager.js";
import type { DiscoveryOptions, PeerCache } from "./discovery.js";
import type { FilterProtocolOptions } from "./filter.js";
import type { CreateLibp2pOptions } from "./libp2p.js";
import type { LightPushProtocolOptions } from "./light_push.js";
import type { IDecodedMessage } from "./message.js";
import type { ThisAndThat, ThisOrThat } from "./misc.js";
import { NetworkConfig } from "./sharding.js";
import type { StoreProtocolOptions } from "./store.js";
export enum Protocols {
Relay = "relay",
Store = "store",
LightPush = "lightpush",
Filter = "filter"
}
export type CreateNodeOptions = {
/**
* Set the user agent string to be used in identification of the node.
*
* @default "js-waku"
*/
userAgent?: string;
/**
* Starts Waku node automatically upon creations.
* Calls {@link @waku/sdk!WakuNode.start} before returning {@link @waku/sdk!WakuNode}
*
* @default true
*/
autoStart?: boolean;
/**
* Configuration for determining the network in use.
* Network configuration refers to the shards and clusters used in the network.
*
* If using Static Sharding:
* Cluster ID and shards are specified in the format: clusterId: number, shards: number[]
* The default value is configured for The Waku Network => clusterId: 0, shards: [0, 1, 2, 3, 4, 5, 6, 7]
* To learn more about the sharding specification, see [Relay Sharding](https://rfc.vac.dev/spec/51/).
*
* If using Auto Sharding:
* Cluster ID and content topics are specified in the format: clusterId: number, contentTopics: string[]
* Content topics are used to determine the shards to be configured for the network.
* Cluster ID is optional, and defaults to The Waku Network's cluster ID => 0
* To specify content topics, see [Waku v2 Topic Usage Recommendations](https://github.com/vacp2p/rfc-index/blob/main/waku/informational/23/topics.md#content-topics) for details
*
* @default { clusterId: 1, shards: [0, 1, 2, 3, 4, 5, 6, 7] }
*/
networkConfig?: NetworkConfig;
/**
* You can pass options to the `Libp2p` instance used by {@link @waku/sdk!WakuNode} using the `libp2p` property.
* This property is the same type as the one passed to [`Libp2p.create`](https://github.com/libp2p/js-libp2p/blob/master/doc/API.md#create)
* apart that we made the `modules` property optional and partial,
* allowing its omission and letting Waku set good defaults.
* Notes that some values are overridden by {@link @waku/sdk!WakuNode} to ensure it implements the Waku protocol.
*/
libp2p?: Partial<CreateLibp2pOptions>;
/**
* Number of peers to connect to, for the usage of the protocol.
* This is used by Filter to retrieve messages.
*
* @default 2.
*/
numPeersToUse?: number;
/**
* Byte array used as key for the noise protocol used for connection encryption
* by [`Libp2p.create`](https://github.com/libp2p/js-libp2p/blob/master/doc/API.md#create)
* This is only used for test purposes to not run out of entropy during CI runs.
*/
staticNoiseKey?: Uint8Array;
/**
* Use recommended bootstrap method to discovery and connect to new nodes.
*/
defaultBootstrap?: boolean;
/**
* Enable or disable specific discovery methods.
*
* @default { peerExchange: true, dns: true, peerCache: true }
*/
discovery?: Partial<DiscoveryOptions>;
/**
* Peer cache to use for storing and retrieving peer information.
* If present, enables peer cache discovery.
*
* @default browser's localStorage
*/
peerCache?: PeerCache;
/**
* List of peers to use to bootstrap the node. Ignored if defaultBootstrap is set to true.
*/
bootstrapPeers?: string[];
/**
* Configuration for connection manager.
* If not specified - default values are applied.
*/
connectionManager?: Partial<ConnectionManagerOptions>;
/**
* Configuration for Filter protocol.
* If not specified - default values are applied.
*/
filter?: Partial<FilterProtocolOptions>;
/**
* Options for the Store protocol.
* If not specified - default values are applied.
*/
store?: Partial<StoreProtocolOptions>;
/**
* Options for the LightPush protocol.
* If not specified - default values are applied.
*/
lightPush?: Partial<LightPushProtocolOptions>;
};
export type Callback<T extends IDecodedMessage> = (
msg: T
) => void | Promise<void>;
export enum LightPushError {
GENERIC_FAIL = "Generic error",
DECODE_FAILED = "Failed to decode",
NO_PEER_AVAILABLE = "No peer available",
NO_STREAM_AVAILABLE = "No stream available",
NO_RESPONSE = "No response received",
STREAM_ABORTED = "Stream aborted",
ENCODE_FAILED = "Failed to encode",
EMPTY_PAYLOAD = "Payload is empty",
SIZE_TOO_BIG = "Size is too big",
TOPIC_NOT_CONFIGURED = "Topic not configured",
RLN_PROOF_GENERATION = "Proof generation failed",
REMOTE_PEER_REJECTED = "Remote peer rejected",
BAD_REQUEST = "Bad request format",
PAYLOAD_TOO_LARGE = "Message payload exceeds maximum size",
INVALID_MESSAGE = "Message validation failed",
UNSUPPORTED_TOPIC = "Unsupported pubsub topic",
TOO_MANY_REQUESTS = "Rate limit exceeded",
INTERNAL_ERROR = "Internal server error",
UNAVAILABLE = "Service temporarily unavailable",
NO_RLN_PROOF = "RLN proof generation failed",
NO_PEERS = "No relay peers available"
}
export enum FilterError {
// General errors
GENERIC_FAIL = "Generic error",
DECODE_FAILED = "Failed to decode",
NO_PEER_AVAILABLE = "No peer available",
NO_STREAM_AVAILABLE = "No stream available",
NO_RESPONSE = "No response received",
STREAM_ABORTED = "Stream aborted",
// Filter specific errors
REMOTE_PEER_REJECTED = "Remote peer rejected",
TOPIC_NOT_CONFIGURED = "Topic not configured",
SUBSCRIPTION_FAILED = "Subscription failed",
UNSUBSCRIBE_FAILED = "Unsubscribe failed",
PING_FAILED = "Ping failed",
TOPIC_DECODER_MISMATCH = "Topic decoder mismatch",
INVALID_DECODER_TOPICS = "Invalid decoder topics",
SUBSCRIPTION_LIMIT_EXCEEDED = "Subscription limit exceeded",
INVALID_CONTENT_TOPIC = "Invalid content topic",
PUSH_MESSAGE_FAILED = "Push message failed",
EMPTY_MESSAGE = "Empty message received",
MISSING_PUBSUB_TOPIC = "Pubsub topic missing from push message"
}
export interface LightPushFailure {
error: LightPushError;
peerId?: PeerId;
}
export interface FilterFailure {
error: FilterError;
peerId?: PeerId;
}
export type LightPushCoreResult = ThisOrThat<
"success",
PeerId,
"failure",
LightPushFailure
>;
export type FilterCoreResult = ThisOrThat<
"success",
PeerId,
"failure",
FilterFailure
>;
export type LightPushSDKResult = ThisAndThat<
"successes",
PeerId[],
"failures",
LightPushFailure[]
>;
export type FilterSDKResult = ThisAndThat<
"successes",
PeerId[],
"failures",
FilterFailure[]
>;
/**
* @deprecated replace usage by specific result types
*/
export type SDKProtocolResult = ThisAndThat<
"successes",
PeerId[],
"failures",
Array<{
error: ProtocolError;
peerId?: PeerId;
}>
>;
/**
* @deprecated replace usage by specific result types
*/
export enum ProtocolError {
GENERIC_FAIL = "Generic error",
REMOTE_PEER_REJECTED = "Remote peer rejected",
DECODE_FAILED = "Failed to decode",
NO_PEER_AVAILABLE = "No peer available",
NO_STREAM_AVAILABLE = "No stream available",
NO_RESPONSE = "No response received",
ENCODE_FAILED = "Failed to encode",
EMPTY_PAYLOAD = "Payload is empty",
SIZE_TOO_BIG = "Size is too big",
TOPIC_NOT_CONFIGURED = "Topic not configured",
STREAM_ABORTED = "Stream aborted",
RLN_PROOF_GENERATION = "Proof generation failed",
TOPIC_DECODER_MISMATCH = "Topic decoder mismatch",
INVALID_DECODER_TOPICS = "Invalid decoder topics"
}