From 237ce7e5c3f3509d34405549d0980e8243b6be2b Mon Sep 17 00:00:00 2001 From: LeoLox <58687994+leo-lox@users.noreply.github.com> Date: Tue, 21 Jan 2025 11:03:13 +0100 Subject: [PATCH 1/2] link to docs --- README.md | 2 +- packages/ndk/README.md | 247 +-------------------------------- packages/ndk/example/README.md | 103 +------------- 3 files changed, 5 insertions(+), 347 deletions(-) diff --git a/README.md b/README.md index da2f8da1..b82cdbd1 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ NDK (Nostr Development Kit) is a Dart library that enhances the Nostr developmen NDK supplies you with high-level usecases like lists or metadata while still allowing you to use low-level queries enhanced with inbox/outbox (gossip) by default.\ Our Target is to make it easy to build constrained Nostr clients, particularly for mobile devices. -# Get started here ➡️ [pub.dev/packages/ndk](https://pub.dev/packages/ndk) +# Get started here ➡️ [Documentation](https://dart-nostr.com/) ## Monorepo Packages diff --git a/packages/ndk/README.md b/packages/ndk/README.md index cb45734c..4242def5 100644 --- a/packages/ndk/README.md +++ b/packages/ndk/README.md @@ -9,126 +9,9 @@ NDK (Nostr Development Kit) is a Dart library that enhances the Nostr developmen NDK supplies you with high-level usecases like lists or metadata while still allowing you to use low-level queries enhanced with inbox/outbox (gossip) by default.\ Our Target is to make it easy to build constrained Nostr clients, particularly for mobile devices. -**Table of Contents:** +# ➡️ [Getting Started 🔗](https://dart-nostr.com/guides/getting-started/) -- [Dart Nostr Development Kit (NDK)](#dart-nostr-development-kit-ndk) -- [Getting started](#getting-started) - - [Prerequisites](#prerequisites) - - [Prerequisites `ndk_rust_verifier`](#prerequisites-ndk_rust_verifier) - - [Install](#install) - - [Import](#import) - - [Usage](#usage) -- [Features / what does NDK do?](#features--what-does-ndk-do) - - [not Included](#not-included) - - [NIPs](#nips) - - [Performance](#performance) - - [Gossip/outbox model of relay discovery and connectivity](#gossipoutbox-model-of-relay-discovery-and-connectivity) -- [Common terminology](#common-terminology) -- [Changelog 🔗](#changelog-) -- [Library development 🏗️](#library-development-️) - - [Setup](#setup) - - [Architecture](#architecture) - - [Folder Structure of `ndk`](#folder-structure-of-ndk) - - [Engines](#engines) - -$~~~~~~~~~~~$ - -# Getting started - -## Prerequisites - -- dart SDK - -### Prerequisites `ndk_rust_verifier` - -- android SDK (also for desktop builds) -- flutter SDK -- rust ( + toolchain for target) - -Rust toolchain android: - -```bash -rustup target add \ - aarch64-linux-android \ - armv7-linux-androideabi \ - x86_64-linux-android \ - i686-linux-android -``` - -Rust toolchain ios: - -```bash -# 64 bit targets (real device & simulator): -rustup target add aarch64-apple-ios x86_64-apple-ios -# New simulator target for Xcode 12 and later -rustup target add aarch64-apple-ios-sim -# 32 bit targets (you probably don't need these): -rustup target add armv7-apple-ios i386-apple-ios -``` - -## Install - -Ndk has a core package `ndk` and optional packages like `rust_verifier` and `amber`. - -```bash -flutter pub add ndk -``` - -Optional: - -```bash -flutter pub add ndk_rust_verifier -flutter pub add ndk_amber -``` - -## Import - -```dart -import 'package:ndk/ndk.dart'; -``` - -Optional: - -```dart -import 'package:ndk_rust_verifier/ndk_rust_verifier.dart'; -import 'package:ndk_amber/ndk_amber.dart'; -``` - -## Usage - -> **more [examples 🔗](https://pub.dev/packages/ndk/example)** - -```dart -import 'package:ndk/ndk.dart'; -import 'package:ndk_rust_verifier/ndk_rust_verifier.dart'; - -// init -final ndk = Ndk( - NdkConfig( - eventVerifier: RustEventVerifier(), - cache: MemCacheManager(), - ), -); - -// query -final response = ndk.requests.query( - filters: [ - Filter( - authors: ['hexPubkey'] - kinds: [Nip01Event.TEXT_NODE_KIND], - limit: 10, - ), - ], -); - -// result -await for (final event in response.stream) { - print(event); -} -``` - -> We strongly recommend using `RustEventVerifier()` for client applications. It uses a separate thread for signature verification and is therefore more performant. -> $~~~~~~~~~~~$ +# [Changelog 🔗](./CHANGELOG.md) --- @@ -173,132 +56,8 @@ await for (final event in response.stream) { - [x] Lists ([NIP-51](https://github.com/nostr-protocol/nips/blob/master/51.md)) - [x] Relay List Metadata ([NIP-65](https://github.com/nostr-protocol/nips/blob/master/65.md)) - [x] Wallet Connect API ([NIP-47](https://github.com/nostr-protocol/nips/blob/master/47.md)) -- [X] Zaps ([NIP-57](https://github.com/nostr-protocol/nips/blob/master/57.md)) +- [x] Zaps ([NIP-57](https://github.com/nostr-protocol/nips/blob/master/57.md)) - [x] Authentication of clients to relays ([NIP-42](https://github.com/nostr-protocol/nips/blob/master/42.md)) - [x] Encrypted Payloads (Versioned) ([NIP-44](https://github.com/nostr-protocol/nips/blob/master/44.md)) - [ ] Bech Encoding support ([NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md)) - [ ] Badges ([NIP-58](https://github.com/nostr-protocol/nips/blob/master/58.md)) - -## Performance - -There are two main constrains that we aim for: battery/compute and network bandwidth. - -**network**\ -Inbox/Outbox (gossip) is our main pillar to help avoid unnecessary nostr requests. We try to leverage the cache as much as possible. \ -Even splitting the users filters into smaller relay tailored filters if we know the relay has the information we need. - -**compute**\ -Right now the most compute intensive operation is verifying signatures. \ -We use the cache to determine if we have already seen the event and only if it is unknown signature verification is done. \ -To make the operation as optimized as possible we strongly recommend using `RustEventVerifier()` because it uses a separate thread for verification. - -$~~~~~~~~~~~$ - -### Gossip/outbox model of relay discovery and connectivity - -The simplest characterization of the gossip model is just this: _reading the posts of people you follow from the relays that they wrote them to._ - - - -more details on - -# Common terminology - -| term | explanation | simmilar to | -| ------------------- | ------------------------------------------------- | --------------------------- | -| **broadcastEvent** | push event to nostr network/relays | postEvent, publishEvent | -| **JIT** | Just In Time, e.g. as it happens | - | -| **query** | get data once and close the request | get request | -| **subscription** | stream of events as they come in | stream of data | -| **bootstrapRelays** | default relays to connect; Used to get Nip65 data | seed relays, initial relays | -| **engine** | optimized network resolver for nostr requests | - | - -$~~~~~~~~~~~$ - -# [Changelog 🔗](./CHANGELOG.md) - -$~~~~~~~~~~~$ - -# Library development 🏗️ - -## Setup - -Install [prerequisites](#prerequisites) - -run `melos bootstrap` to install all dependencies. - -If you work on rust code (`packages/rust_verifier/rust_builder/rust`) run `flutter_rust_bridge_codegen generate --watch` to generate the rust dart glue code. - -Run build runner: (e.g for generating mocks)\ -`dart run build_runner build` - -## Architecture - -The repo is setup as a monorepo and packages are split to enable user choice of what to include.\ -The main package is `ndk` which is the main entry point for the lib user. \ -Other packages like `rust_verifier` or `amber` are optional and can be included if needed. - -NDK uses Clean Architecture. Reasons for it being clear separation of concerns and therefore making it more accessible for future contributors.\ -You can read more about it [here](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html). - -For initialization we use `presentation_layer/init.dart` to assemble all dependencies, these are then exposed in `presentation_layer/ndk.dart` the main entry point for the lib user. - -Global state is realized via a simple [GlobalState] object created by `ndk.dart`. \ -The lib user is supposed to keep the [NDK] object in memory. - -Other state objects are created on demand, for example [RequestState] for each request. - -### Folder Structure of `ndk` - -```bash -lib/ -├── config/ -│ └── # Configuration files -├── shared/ -│ ├── nipX/ # NIP-specific code folders -│ └── # Internal code, no external dependencies -├── data_layer/ -│ ├── data_sources/ -│ │ └── # External APIs, WebSocket implementations, etc. -│ ├── models/ -│ │ └── # Data transformation (e.g., JSON to entity) -│ └── repositories/ -│ └── # Concrete repository implementations -├── domain_layer/ -│ ├── entities/ -│ │ └── # Core business objects -│ ├── repositories/ -│ │ └── # Repository contracts -│ └── usecases/ -│ └── # Business logic / use cases -├── presentation_layer/ -│ └── # API design (exposing use cases to the outside world) -└── ndk.dart # Entry point, directs to presentation layer - -``` - -## Engines - -NDK ships with two network Engines. An Engine is part of the code that resolves nostr requests over the network and handles the WebSocket connections.\ -Its used to handle the inbox/outbox (gossip) model efficiently. - -**Lists Engine:**\ -Precalculates the best possible relays based on nip65 data. During calculation relay connectivity is taken into account. This works by connecting and checking the health status of a relay before its added to the ranking pool.\ -This method gets close to the optimal connections given a certain pubkey coverage. - -**Just in Time (JIT) Engine:**\ -JIT Engine does the ranking on the fly only for the missing coverage/pubkey. Healthy relays are assumed during ranking and replaced later on if a relay fails to connect.\ -To Avoid rarely used relays and spawning a bunch of unessecary connections, already connected relays get a boost, and a usefulness score is considered for the ranking.\ -For more information [look here](./doc/jit_engine/README.md) - -**Custom Engine**\ -If you want to implement your own engine with custom behavior you need to touch the following things: - -1. implement `NetworkEngine` interface -2. write your response stream to `networkController` in the `RequestState` -3. if you need global state you can register your own data type in `global_state.dart` -4. initialize your engine in `init.dart` - -The current state solution is not ideal because it requires coordination between the engine authors and not enforceable by code. If you have ideas how to improve this system, please reach out. - -> The network engine is only concerned about network requests! Caching and avoiding concurrency is handled by separate usecases. Take a look at `requests.dart` usecase to learn more. diff --git a/packages/ndk/example/README.md b/packages/ndk/example/README.md index e610637b..96cf9ef8 100644 --- a/packages/ndk/example/README.md +++ b/packages/ndk/example/README.md @@ -42,105 +42,4 @@ await for (final event in response.stream) { } ``` -## breakdown - -### initialize - -#### 1️⃣ Initialize a global `ndk` object - -setup your preferred config/dependencies. - -```dart -final ndk = Ndk( - NdkConfig( - eventVerifier: RustEventVerifier(), - cache: MemCacheManager(), - - // optional: - logLevel: Logger.logLevels.debug, - bootstrapRelays: ["wss://myrelay.example", "wss://myrelay2.example"], - ignoreRelays: ['wss://bad.reputation'] - engine: NdkEngine.JIT, - defaultQueryTimeout: Duration(seconds: 10), - eventOutFilters: [MyEventFilter()], - ), -); -``` - -$~~~~~~~~~~~$ - -#### 2️⃣ Usecase - -Use a prebuilt ndk usecase (contact list in this case) - -```dart -final ContactList? response = await ndk.follows.getContactList('3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d'); -``` - -#### 3️⃣ read response - -depending on the usecase entity or a `NdkResponse` is returned - -```dart -// read entity -print(response) -// read NdkResponse -final response_stream = ndkResponse.stream; -final response_future = ndkResponse.future; -``` - -## imports - -`import 'package:ndk/ndk.dart';` \ - contains everything exposed by ndk.\ -If you want to reuse ndk entities you can use them like this: - -```dart -import 'package:ndk/entities.dart' as ndk_entities; - -final ndk_entities.Nip01Event myEvent = ndk_entities.Nip01Event(...); -``` - -other imports: - -```dart -import 'package:ndk_rust_verifier/ndk_rust_verifier.dart'; -import 'package:ndk_amber/ndk_amber.dart'; -``` - -## tip: how to keep the ndk obj global - -If you have a relatively simple app you can initialize ndk in your main method and pass down ndk on the widget tree.\ -For more more complex applications we recommend using [riverpod](<[test](https://pub.dev/packages/riverpod)>) or simmilar packages/methods (get_it, singletons etc). - -riverpod example: - -```dart -import 'package:ndk/ndk.dart'; -import 'package:riverpod/riverpod.dart'; - -final ndkProvider = Provider((ref) { - final EventSigner eventSigner = Bip340EventSigner("privateKey", "publicKey"); - final EventVerifier eventVerifier = RustEventVerifier(); - final CacheManager cache = MemCacheManager(); - - final NdkConfig ndkConfig = NdkConfig( - engine: NdkEngine.JIT, - cache: cache, - eventSigner: eventSigner, - eventVerifier: eventVerifier, - ); - - final ndk = Ndk(ndkConfig); - return ndk; -}); -``` - -## more examples - -- [Nostr wallet Connect](https://github.com/relaystr/ndk/tree/master/packages/ndk/example/nwc) -- [Zaps](https://github.com/relaystr/ndk/tree/master/packages/ndk/example/zaps) -- [example folder github](https://github.com/relaystr/ndk/tree/master/packages/ndk/example) -- [example app](https://github.com/relaystr/ndk/tree/master/packages/sample-app) -- [yana](https://github.com/frnandu/yana) -- [camelus](https://github.com/leo-lox/camelus) +# [DOCUMENTATION 🔗](https://dart-nostr.com/) From 88c0b1fcefc9560b72c3ed1e4bcc68d14e30c9da Mon Sep 17 00:00:00 2001 From: LeoLox <58687994+leo-lox@users.noreply.github.com> Date: Tue, 21 Jan 2025 11:06:53 +0100 Subject: [PATCH 2/2] move doc link up --- packages/ndk/example/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/ndk/example/README.md b/packages/ndk/example/README.md index 96cf9ef8..296c15f6 100644 --- a/packages/ndk/example/README.md +++ b/packages/ndk/example/README.md @@ -1,6 +1,8 @@ # NDK -## Basic example +## ➡️ [DOCUMENTATION 🔗](https://dart-nostr.com/guides/getting-started/) + +## Basic example - query This example uses a generic low level nostr request @@ -41,5 +43,3 @@ await for (final event in response.stream) { print(event); } ``` - -# [DOCUMENTATION 🔗](https://dart-nostr.com/)