From 4fb5f8d7b2136e56fe57d91bd50035347e51c49d Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Tue, 23 Apr 2024 00:12:05 -0700 Subject: [PATCH] working sign and verify messages for nano --- cw_bitcoin/lib/electrum_wallet.dart | 2 +- cw_core/lib/nano_block_info_response.dart | 37 +++++++ cw_nano/lib/nano_client.dart | 54 ++++++++- cw_nano/lib/nano_wallet.dart | 7 +- cw_nano/pubspec.lock | 104 ++++++++++++------ cw_nano/pubspec.yaml | 2 +- lib/src/screens/dashboard/sign_page.dart | 2 +- .../dashboard/dashboard_view_model.dart | 2 + .../wallet_address_list_view_model.dart | 9 ++ pubspec_base.yaml | 2 +- 10 files changed, 180 insertions(+), 41 deletions(-) create mode 100644 cw_core/lib/nano_block_info_response.dart diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 5bcfb40d8..a18ebb76a 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -1250,7 +1250,7 @@ abstract class ElectrumWalletBase if (sigDecodedBytes.length != 64 && sigDecodedBytes.length != 65) { throw ArgumentException( - "litecoin signature must be 64 bytes without recover-id or 65 bytes with recover-id"); + "signature must be 64 bytes without recover-id or 65 bytes with recover-id"); } String messagePrefix = '\x18Bitcoin Signed Message:\n'; diff --git a/cw_core/lib/nano_block_info_response.dart b/cw_core/lib/nano_block_info_response.dart new file mode 100644 index 000000000..d2f000b9d --- /dev/null +++ b/cw_core/lib/nano_block_info_response.dart @@ -0,0 +1,37 @@ +class BlockContentsResponse { + String type; + String account; + String previous; + String representative; + String balance; + String link; + String linkAsAccount; + String signature; + String work; + + BlockContentsResponse({ + required this.type, + required this.account, + required this.previous, + required this.representative, + required this.balance, + required this.link, + required this.linkAsAccount, + required this.signature, + required this.work, + }); + + factory BlockContentsResponse.fromJson(Map json) { + return BlockContentsResponse( + type: json['type'] as String, + account: json['account'] as String, + previous: json['previous'] as String, + representative: json['representative'] as String, + balance: json['balance'] as String, + link: json['link'] as String, + linkAsAccount: json['link_as_account'] as String, + signature: json['signature'] as String, + work: json['work'] as String, + ); + } +} diff --git a/cw_nano/lib/nano_client.dart b/cw_nano/lib/nano_client.dart index df3da62df..e36c7d4d3 100644 --- a/cw_nano/lib/nano_client.dart +++ b/cw_nano/lib/nano_client.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'package:cw_core/nano_account_info_response.dart'; +import 'package:cw_core/nano_block_info_response.dart'; import 'package:cw_core/n2_node.dart'; import 'package:cw_nano/nano_balance.dart'; import 'package:cw_nano/nano_transaction_model.dart'; @@ -98,6 +99,27 @@ class NanoClient { } } + Future getBlockContents(String block) async { + try { + final response = await http.post( + _node!.uri, + headers: CAKE_HEADERS, + body: jsonEncode( + { + "action": "block_info", + "json_block": "true", + "hash": block, + }, + ), + ); + final data = await jsonDecode(response.body); + return BlockContentsResponse.fromJson(data["contents"] as Map); + } catch (e) { + print("error while getting block info $e"); + return null; + } + } + Future changeRep({ required String privateKey, required String repAddress, @@ -297,6 +319,35 @@ class NanoClient { representative = infoData.representative; } + if (!openBlock) { + // get the block info of the frontier block: + BlockContentsResponse? frontierContents = await getBlockContents(frontier); + + if (frontierContents == null) { + throw Exception("error while getting frontier block info"); + } + + final String frontierHash = NanoSignatures.computeStateHash( + NanoBasedCurrency.NANO, + frontierContents.account, + frontierContents.previous, + frontierContents.representative, + BigInt.parse(frontierContents.balance), + frontierContents.link, + ); + + bool valid = await NanoSignatures.verify( + frontierHash, + frontierContents.signature, + destinationAddress, + ); + + if (!valid) { + throw Exception( + "Frontier block signature is invalid! Potentially malicious block detected!"); + } + } + // first get the account balance: final BigInt currentBalance = (await getBalance(destinationAddress)).currentBalance; final BigInt txAmount = BigInt.parse(amountRaw); @@ -305,7 +356,8 @@ class NanoClient { // link = send block hash: final String link = blockHash; // this "linkAsAccount" is meaningless: - final String linkAsAccount = NanoDerivations.publicKeyToAddress(blockHash, currency: NanoBasedCurrency.NANO); + final String linkAsAccount = + NanoDerivations.publicKeyToAddress(blockHash, currency: NanoBasedCurrency.NANO); // construct the receive block: Map receiveBlock = { diff --git a/cw_nano/lib/nano_wallet.dart b/cw_nano/lib/nano_wallet.dart index 111cccb15..8f194bb15 100644 --- a/cw_nano/lib/nano_wallet.dart +++ b/cw_nano/lib/nano_wallet.dart @@ -432,7 +432,7 @@ abstract class NanoWalletBase _representativeAddress = await _client.getRepFromPrefs(); throw Exception("Failed to get representative address $e"); } - + repScore = await _client.getRepScore(_representativeAddress!); } @@ -500,7 +500,7 @@ abstract class NanoWalletBase @override Future signMessage(String message, {String? address = null}) async { - return NanoSignatures.sign(message, privateKey!); + return NanoSignatures.signMessage(message, privateKey!); } @override @@ -508,7 +508,6 @@ abstract class NanoWalletBase if (address == null) { return false; } - String publicKey = NanoDerivations.addressToPublicKey(address); - return NanoSignatures.verify(message, signature, publicKey); + return await NanoSignatures.verifyMessage(message, signature, address); } } diff --git a/cw_nano/pubspec.lock b/cw_nano/pubspec.lock index 37db3df28..321924494 100644 --- a/cw_nano/pubspec.lock +++ b/cw_nano/pubspec.lock @@ -93,10 +93,10 @@ packages: dependency: transitive description: name: build_daemon - sha256: "757153e5d9cd88253cb13f28c2fb55a537dc31fefd98137549895b5beb7c6169" + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "4.0.1" build_resolvers: dependency: transitive description: @@ -109,10 +109,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: b0a8a7b8a76c493e85f1b84bffa0588859a06197863dba8c9036b15581fd9727 + sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.4.9" build_runner_core: dependency: transitive description: @@ -173,10 +173,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.18.0" convert: dependency: transitive description: @@ -193,6 +193,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.3" + cryptography: + dependency: transitive + description: + name: cryptography + sha256: d146b76d33d94548cf035233fbc2f4338c1242fa119013bead807d033fc4ae05 + url: "https://pub.dev" + source: hosted + version: "2.7.0" cw_core: dependency: "direct main" description: @@ -244,10 +252,10 @@ packages: dependency: transitive description: name: ffi - sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" url: "https://pub.dev" source: hosted - version: "2.0.2" + version: "2.1.2" file: dependency: transitive description: @@ -399,6 +407,30 @@ packages: url: "https://pub.dev" source: hosted version: "4.8.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.dev" + source: hosted + version: "2.0.1" libcrypto: dependency: "direct main" description: @@ -419,26 +451,26 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.11.0" mime: dependency: transitive description: @@ -475,11 +507,11 @@ packages: dependency: "direct main" description: path: "." - ref: dd63c20a3d8956bba26732a36e14fda24ccef284 - resolved-ref: dd63c20a3d8956bba26732a36e14fda24ccef284 + ref: c01a9c552917008d8fbc6b540db657031625b04f + resolved-ref: c01a9c552917008d8fbc6b540db657031625b04f url: "https://github.com/perishllc/nanoutil.git" source: git - version: "1.0.2" + version: "1.0.3" package_config: dependency: transitive description: @@ -492,10 +524,10 @@ packages: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" path_provider: dependency: transitive description: @@ -713,26 +745,26 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -761,10 +793,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.1" timing: dependency: transitive description: @@ -789,14 +821,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - watcher: + vm_service: dependency: transitive description: - name: watcher - sha256: "6a7f46926b01ce81bfc339da6a7f20afbe7733eff9846f6d6a5466aa4c6667c0" + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "13.0.0" + watcher: + dependency: "direct overridden" + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" web_socket_channel: dependency: transitive description: @@ -830,5 +870,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.3.0-279.1.beta <4.0.0" flutter: ">=3.7.0" diff --git a/cw_nano/pubspec.yaml b/cw_nano/pubspec.yaml index d353fe707..995dc3f75 100644 --- a/cw_nano/pubspec.yaml +++ b/cw_nano/pubspec.yaml @@ -24,7 +24,7 @@ dependencies: nanoutil: git: url: https://github.com/perishllc/nanoutil.git - ref: dd63c20a3d8956bba26732a36e14fda24ccef284 + ref: c01a9c552917008d8fbc6b540db657031625b04f cw_core: path: ../cw_core diff --git a/lib/src/screens/dashboard/sign_page.dart b/lib/src/screens/dashboard/sign_page.dart index 2cee0cda0..024ced9e1 100644 --- a/lib/src/screens/dashboard/sign_page.dart +++ b/lib/src/screens/dashboard/sign_page.dart @@ -37,7 +37,7 @@ class SignPage extends BasePage { @override Widget middle(BuildContext context) => Observer( builder: (_) => Text( - "Sign / Verify", + "T: Sign / Verify", style: TextStyle( fontSize: 18.0, fontWeight: FontWeight.bold, diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 5a8f67522..b2a27ec74 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -380,6 +380,8 @@ abstract class DashboardViewModelBase with Store { WalletType.ethereum, WalletType.polygon, WalletType.solana, + WalletType.nano, + WalletType.banano, ].contains(wallet.type); bool get showRepWarning { diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart index 20980f5f0..0810b515a 100644 --- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart @@ -6,6 +6,7 @@ import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/haven/haven.dart'; import 'package:cake_wallet/monero/monero.dart'; +import 'package:cake_wallet/nano/nano.dart'; import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/solana/solana.dart'; import 'package:cake_wallet/store/app_store.dart'; @@ -348,6 +349,14 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress)); } + if (wallet.type == WalletType.nano) { + addressList.add(WalletAddressListItem( + isPrimary: true, + name: null, + address: wallet.walletAddresses.address, + )); + } + if (searchText.isNotEmpty) { return ObservableList.of(addressList.where((item) { if (item is WalletAddressListItem) { diff --git a/pubspec_base.yaml b/pubspec_base.yaml index e1022864c..df53009f3 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -26,7 +26,7 @@ dependencies: path_provider: ^2.0.11 mobx: ^2.1.4 flutter_mobx: ^2.0.6+5 - flutter_slidable: ^2.0.0 + flutter_slidable: ^3.0.1 share_plus: ^4.0.10 # date_range_picker: ^1.0.6 #https://api.flutter.dev/flutter/material/showDateRangePicker.html