add buildin relay support, fix remote signer some bugs

This commit is contained in:
DASHU
2024-11-30 01:33:56 +08:00
parent 87c9ac7351
commit d1f79f92b9
10 changed files with 268 additions and 19 deletions

3
.gitmodules vendored
View File

@@ -1,3 +1,6 @@
[submodule "packages/nostr_sdk"]
path = packages/nostr_sdk
url = git@github.com:haorendashu/nostr_sdk.git
[submodule "packages/relay_sdk"]
path = packages/relay_sdk
url = git@github.com:haorendashu/relay_sdk.git

View File

@@ -3,6 +3,7 @@ import 'dart:developer';
import 'package:bot_toast/bot_toast.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:google_fonts/google_fonts.dart';
@@ -11,6 +12,7 @@ import 'package:nostr_sdk/utils/string_util.dart';
import 'package:nowser/data/db.dart';
import 'package:nowser/provider/android_signer_mixin.dart';
import 'package:nowser/provider/app_provider.dart';
import 'package:nowser/provider/build_in_relay_provider.dart';
import 'package:nowser/provider/key_provider.dart';
import 'package:nowser/provider/permission_check_mixin.dart';
import 'package:nowser/provider/web_provider.dart';
@@ -52,8 +54,13 @@ late RemoteSigningProvider remoteSigningProvider;
late Map<String, WidgetBuilder> routes;
late RootIsolateToken rootIsolateToken;
late BuildInRelayProvider buildInRelayProvider;
Future<void> main() async {
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
rootIsolateToken = RootIsolateToken.instance!;
if (!PlatformUtil.isWeb() && PlatformUtil.isPC()) {
await windowManager.ensureInitialized();
@@ -87,6 +94,7 @@ Future<void> main() async {
Future<void> doInit() async {
keyProvider = KeyProvider();
appProvider = AppProvider();
buildInRelayProvider = BuildInRelayProvider();
var dataUtilTask = DataUtil.getInstance();
var keyTask = keyProvider.init();
@@ -156,6 +164,9 @@ class _MyApp extends State<MyApp> {
ListenableProvider<RemoteSigningProvider>.value(
value: remoteSigningProvider,
),
ListenableProvider<BuildInRelayProvider>.value(
value: buildInRelayProvider,
),
],
child: MaterialApp(
builder: BotToastInit(),

View File

@@ -0,0 +1,128 @@
import 'package:bot_toast/bot_toast.dart';
import 'package:flutter/material.dart';
import 'package:nostr_sdk/relay/relay_info.dart';
import 'package:nowser/const/base.dart';
import 'package:relay_sdk/network/connection.dart';
import 'package:relay_sdk/network/memory/mem_relay_client.dart';
import 'package:relay_sdk/relay_manager.dart';
import '../main.dart';
import '../util/ip_util.dart';
class BuildInRelayProvider extends ChangeNotifier {
String ip = "127.0.0.1";
static int port = 4870;
RelayInfo relayInfo = RelayInfo(
"${Base.APP_NAME}'s build-in relay",
"This is a build-in relay for Nowser. It don't save any message and it's designed for NWC.",
"29320975df855fe34a7b45ada2421e2c741c37c0136901fe477133a91eb18b07",
"29320975df855fe34a7b45ada2421e2c741c37c0136901fe477133a91eb18b07",
["47"],
Base.APP_NAME,
Base.VERSION_NAME);
RelayManager? _relayManager;
bool isRunning() {
if (_relayManager != null) {
return true;
}
return false;
}
Future<void> start() async {
if (isRunning()) {
return;
}
String? localIp = await IpUtil.getIp();
if (localIp != null) {
ip = localIp;
}
try {
var rm = _getRelayManager();
await rm.start(relayInfo, port);
await Future.delayed(const Duration(seconds: 10));
if (_penddingMemRelayClients.isNotEmpty) {
for (var memRelayClient in _penddingMemRelayClients) {
rm.addMemClient(memRelayClient);
try {
memRelayClient.onConnected();
} catch (e) {}
}
}
_penddingMemRelayClients.clear();
} catch (e) {
print(e);
BotToast.showText(text: "Start server fail.");
if (_relayManager != null) {
try {
_relayManager!.stop();
} catch (e) {}
}
_relayManager = null;
}
notifyListeners();
}
void stop() {
if (_relayManager != null) {
_relayManager!.stop();
}
_relayManager = null;
notifyListeners();
}
RelayManager _getRelayManager() {
if (_relayManager == null) {
_relayManager = RelayManager(rootIsolateToken);
_relayManager!.openFilterCheck = true;
_relayManager!.openDB = false;
// _relayManager!.trafficCounter = trafficCounterProvider;
// _relayManager!.networkLogsManager = networkLogProvider;
_relayManager!.rootIsolateToken = rootIsolateToken;
_relayManager!.connectionListener = connectionListener;
}
return _relayManager!;
}
int connectionNum() {
if (_relayManager != null) {
return _relayManager!.getConnections().length;
}
return 0;
}
List<Connection> getConnections() {
if (_relayManager != null) {
return _relayManager!.getConnections();
}
return [];
}
void connectionListener() {
notifyListeners();
}
List<MemRelayClient> _penddingMemRelayClients = [];
void addMemClient(MemRelayClient memRelayClient) {
if (isRunning()) {
_relayManager!.addMemClient(memRelayClient);
} else {
_penddingMemRelayClients.add(memRelayClient);
}
}
}

View File

@@ -18,11 +18,13 @@ import 'package:nowser/const/app_type.dart';
import 'package:nowser/const/auth_type.dart';
import 'package:nowser/data/app.dart';
import 'package:nowser/provider/permission_check_mixin.dart';
import 'package:relay_sdk/network/memory/mem_relay_client.dart';
import '../component/auth_dialog/auth_app_connect_dialog.dart';
import '../data/remote_signing_info.dart';
import '../data/remote_signing_info_db.dart';
import '../main.dart';
import 'build_in_relay_provider.dart';
class RemoteSigningProvider extends ChangeNotifier with PermissionCheckMixin {
BuildContext? context;
@@ -77,8 +79,18 @@ class RemoteSigningProvider extends ChangeNotifier with PermissionCheckMixin {
List<Relay> relays = [];
for (var relayAddr in relayAddrs) {
// use pubkey relace with
var relay = RelayIsolate(relayAddr, RelayStatus(remoteSignerPubkey));
bool isLocalRelay = false;
Relay? relay;
var relayStatus = RelayStatus(remoteSignerPubkey);
if (relayAddr == "ws://localhost:${BuildInRelayProvider.port}" ||
relayAddr == "ws://127.0.0.1:${BuildInRelayProvider.port}") {
// use pubkey relace with
relay = MemRelayClient(relayAddr, relayStatus);
isLocalRelay = true;
} else {
// use pubkey relace with
relay = RelayIsolate(relayAddr, relayStatus);
}
var filter = Filter(
p: [remoteSignerPubkey],
@@ -89,7 +101,11 @@ class RemoteSigningProvider extends ChangeNotifier with PermissionCheckMixin {
.add(["REQ", StringUtil.rndNameStr(10), filter.toJson()]);
relay.onMessage = _onEvent;
relay.connect();
if (isLocalRelay) {
buildInRelayProvider.addMemClient(relay as MemRelayClient);
} else {
relay.connect();
}
relays.add(relay);
}
@@ -122,7 +138,7 @@ class RemoteSigningProvider extends ChangeNotifier with PermissionCheckMixin {
if (request.params.length <= 1) {
response = NostrRemoteResponse(request.id, "", error: "params error");
} else {
if (request.params[0] == remoteSigningInfo.remotePubkey &&
if (request.params[0] == remoteSignerPubkey &&
request.params[1] == remoteSigningInfo.secret) {
// check pass, init app
var newApp = await getApp(appType, code);
@@ -182,6 +198,7 @@ class RemoteSigningProvider extends ChangeNotifier with PermissionCheckMixin {
int? eventKind;
String? authDetail;
String? thirdPartyPubkey;
int authType = AuthType.GET_PUBLIC_KEY;
dynamic eventObj;
@@ -197,15 +214,19 @@ class RemoteSigningProvider extends ChangeNotifier with PermissionCheckMixin {
authType = AuthType.GET_PUBLIC_KEY;
} else if (request.method == "nip04_encrypt") {
authType = AuthType.NIP04_ENCRYPT;
thirdPartyPubkey = request.params[0];
authDetail = request.params[1];
} else if (request.method == "nip04_decrypt") {
authType = AuthType.NIP04_DECRYPT;
thirdPartyPubkey = request.params[0];
authDetail = request.params[1];
} else if (request.method == "nip44_encrypt") {
authType = AuthType.NIP44_ENCRYPT;
thirdPartyPubkey = request.params[0];
authDetail = request.params[1];
} else if (request.method == "nip44_decrypt") {
authType = AuthType.NIP44_DECRYPT;
thirdPartyPubkey = request.params[0];
authDetail = request.params[1];
}
@@ -235,16 +256,16 @@ class RemoteSigningProvider extends ChangeNotifier with PermissionCheckMixin {
var pubkey = await signer.getPublicKey();
response = NostrRemoteResponse(request.id, pubkey!);
} else if (request.method == "nip04_encrypt") {
var text = await signer.encrypt(localPubkey, authDetail);
var text = await signer.encrypt(thirdPartyPubkey, authDetail);
response = NostrRemoteResponse(request.id, text!);
} else if (request.method == "nip04_decrypt") {
var text = await signer.decrypt(localPubkey, authDetail);
var text = await signer.decrypt(thirdPartyPubkey, authDetail);
response = NostrRemoteResponse(request.id, text!);
} else if (request.method == "nip44_encrypt") {
var text = await signer.nip44Encrypt(localPubkey, authDetail);
var text = await signer.nip44Encrypt(thirdPartyPubkey, authDetail);
response = NostrRemoteResponse(request.id, text!);
} else if (request.method == "nip44_decrypt") {
var text = await signer.nip44Decrypt(localPubkey, authDetail);
var text = await signer.nip44Decrypt(thirdPartyPubkey, authDetail);
response = NostrRemoteResponse(request.id, text!);
}
@@ -257,6 +278,8 @@ class RemoteSigningProvider extends ChangeNotifier with PermissionCheckMixin {
Future<void> sendResponse(List<Relay> relays, NostrRemoteResponse? response,
NostrSigner signer, String localPubkey, String remoteSignerPubkey) async {
if (response != null) {
// print("response:");
// print(response.toString());
var result = await response.encrypt(signer, localPubkey);
Event? event = Event(
remoteSignerPubkey,
@@ -267,10 +290,7 @@ class RemoteSigningProvider extends ChangeNotifier with PermissionCheckMixin {
result!);
event = await signer.signEvent(event);
var signerPubkey = await signer.getPublicKey();
// print("response:");
if (event != null) {
// print(event.toJson());
for (var relay in relays) {
relay.send(["EVENT", event.toJson()]);
}
@@ -290,7 +310,7 @@ class RemoteSigningProvider extends ChangeNotifier with PermissionCheckMixin {
print("remoteSigningInfo is null");
return;
}
var nostrSigner = LocalNostrSigner(remoteSigningInfo.remoteSignerKey!);
var remoteSigner = LocalNostrSigner(remoteSigningInfo.remoteSignerKey!);
var signer = keyProvider.getSigner(remoteSigningInfo.remotePubkey!);
if (signer == null) {
return;
@@ -309,7 +329,7 @@ class RemoteSigningProvider extends ChangeNotifier with PermissionCheckMixin {
if (event.kind == EventKind.NOSTR_REMOTE_SIGNING) {
if (handledIds[event.id] == null) {
var request = await NostrRemoteRequest.decrypt(
event.content, signer, event.pubkey);
event.content, remoteSigner, event.pubkey);
var relays = relayMap[remoteSignerPubkey];
if (relays == null || relays.isEmpty) {
relays = [relay];
@@ -341,7 +361,7 @@ class RemoteSigningProvider extends ChangeNotifier with PermissionCheckMixin {
];
Event? event =
Event(remoteSignerPubkey, EventKind.AUTHENTICATION, tags, "");
event = await nostrSigner.signEvent(event);
event = await remoteSigner.signEvent(event);
if (event != null) {
relay.send(["AUTH", event.toJson()], forceSend: true);

View File

@@ -5,11 +5,14 @@ import 'package:nostr_sdk/client_utils/keys.dart';
import 'package:nostr_sdk/nip19/nip19.dart';
import 'package:nostr_sdk/nip46/nostr_remote_signer_info.dart';
import 'package:nostr_sdk/utils/string_util.dart';
import 'package:nowser/component/cust_state.dart';
import 'package:nowser/component/qrscanner.dart';
import 'package:nowser/component/simple_qrcode_dialog.dart';
import 'package:nowser/data/remote_signing_info.dart';
import 'package:nowser/data/remote_signing_info_db.dart';
import 'package:nowser/main.dart';
import 'package:nowser/provider/build_in_relay_provider.dart';
import 'package:nowser/util/ip_util.dart';
import 'package:nowser/util/router_util.dart';
import 'package:provider/provider.dart';
@@ -25,15 +28,19 @@ class AddRemoteAppRouter extends StatefulWidget {
}
}
class _AddRemoteAppRouter extends State<AddRemoteAppRouter> {
class _AddRemoteAppRouter extends CustState<AddRemoteAppRouter> {
TextEditingController nostrconnectConn = TextEditingController();
TextEditingController bunkerConn = TextEditingController();
bool editBunker = false;
bool localRelay = false;
String? pubkey;
String? localIp;
@override
void initState() {
super.initState();
@@ -47,7 +54,12 @@ class _AddRemoteAppRouter extends State<AddRemoteAppRouter> {
}
@override
Widget build(BuildContext context) {
Future<void> onReady(BuildContext context) async {
localIp = await IpUtil.getIp();
}
@override
Widget doBuild(BuildContext context) {
var themeData = Theme.of(context);
var textColor = themeData.textTheme.bodyMedium!.color;
var mainColor = themeData.primaryColor;
@@ -135,10 +147,28 @@ class _AddRemoteAppRouter extends State<AddRemoteAppRouter> {
bunkerWidget = Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
child: Row(
children: [
Container(
margin: EdgeInsets.only(
right: Base.BASE_PADDING_HALF,
),
child: Text("Local Relay:"),
),
Checkbox(
value: localRelay,
onChanged: onLocalRelayChange,
),
Expanded(child: Container()),
],
),
),
Container(
child: TextField(
decoration: InputDecoration(
labelText: "Relay",
enabled: !localRelay,
),
controller: relayAddrController,
),
@@ -328,12 +358,17 @@ class _AddRemoteAppRouter extends State<AddRemoteAppRouter> {
TextEditingController relayAddrController = TextEditingController();
static const String DEFAULT_RELAY = "wss://relay.nsec.app";
TextEditingController secretController = TextEditingController();
void refreshBunkerUrl() {
remoteSignerKey = generatePrivateKey();
secretController.text = StringUtil.rndNameStr(20);
relayAddrController.text = "wss://relay.nsec.app";
if (StringUtil.isBlank(localIp) ||
!relayAddrController.text.contains(localIp!)) {
relayAddrController.text = DEFAULT_RELAY;
}
reloadBunker();
}
@@ -354,7 +389,12 @@ class _AddRemoteAppRouter extends State<AddRemoteAppRouter> {
return;
}
var relays = [relayAddrController.text];
List<String> relays = [];
if (localRelay) {
relays.add("ws://127.0.0.1:${BuildInRelayProvider.port}");
} else {
relays.add(relayAddrController.text);
}
var remoteSigningInfo = RemoteSigningInfo(
remotePubkey: pubkey,
@@ -377,4 +417,19 @@ class _AddRemoteAppRouter extends State<AddRemoteAppRouter> {
);
bunkerConn.text = nostrRemoteSignerInfo.toString();
}
void onLocalRelayChange(bool? v) {
if (v == true) {
relayAddrController.text = "ws://${localIp}:${BuildInRelayProvider.port}";
} else if (v == false) {
relayAddrController.text = DEFAULT_RELAY;
}
if (v != null) {
setState(() {
localRelay = v;
});
reloadBunker();
}
}
}

View File

@@ -37,6 +37,9 @@ class _IndexRouter extends CustState<IndexRouter>
Future<void> onReady(BuildContext context) async {
await remoteSigningProvider.reload();
await remoteSigningProvider.reloadPenddingRemoteApps();
// start build-in
buildInRelayProvider.start();
}
@override

19
lib/util/ip_util.dart Normal file
View File

@@ -0,0 +1,19 @@
import 'dart:io';
class IpUtil {
static Future<String?> getIp() async {
var ips = await NetworkInterface.list();
for (var interface in ips) {
print('== Interface: ${interface.name} ==');
if (interface.name == "WLAN") {
for (var addr in interface.addresses) {
print(
'${addr.address} ${addr.host} ${addr.isLoopback} ${addr.rawAddress} ${addr.type.name}');
return addr.address;
}
}
}
return ips.first.addresses.first.address;
}
}

1
packages/relay_sdk Submodule

Submodule packages/relay_sdk added at bef0b65fec

View File

@@ -764,6 +764,13 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.2.5"
relay_sdk:
dependency: "direct main"
description:
path: "packages/relay_sdk"
relative: true
source: path
version: "0.0.1"
rxdart:
dependency: transitive
description:
@@ -1090,5 +1097,5 @@ packages:
source: hosted
version: "3.1.2"
sdks:
dart: ">=3.5.0 <4.0.0"
dart: ">=3.5.3 <4.0.0"
flutter: ">=3.24.0"

View File

@@ -35,6 +35,8 @@ dependencies:
cupertino_icons: ^1.0.6
nostr_sdk:
path: packages/nostr_sdk
relay_sdk:
path: packages/relay_sdk
receive_intent: ^0.2.5
provider: ^6.1.2