fix formatting

This commit is contained in:
julian 2023-07-27 11:33:39 -06:00
parent 9b4c1abf35
commit aa2ab89f73
11 changed files with 785 additions and 923 deletions

View file

@ -1,12 +1,12 @@
import 'util.dart';
import 'connection.dart';
import 'fusion.dart';
import 'fusion.pb.dart';
import 'dart:io';
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:protobuf/protobuf.dart'; import 'package:protobuf/protobuf.dart';
import 'socketwrapper.dart'; import 'package:stackwallet/services/cashfusion/connection.dart';
import 'package:stackwallet/services/cashfusion/fusion.dart';
import 'package:stackwallet/services/cashfusion/fusion.pb.dart';
import 'package:stackwallet/services/cashfusion/socketwrapper.dart';
import 'package:stackwallet/services/cashfusion/util.dart';
typedef PbCreateFunc = GeneratedMessage Function(); typedef PbCreateFunc = GeneratedMessage Function();
@ -43,13 +43,11 @@ Map<Type, PbCreateFunc> pbClassCreators = {
ServerMessage: () => ServerMessage(), ServerMessage: () => ServerMessage(),
CovertMessage: () => CovertMessage(), CovertMessage: () => CovertMessage(),
CovertResponse: () => CovertResponse() CovertResponse: () => CovertResponse()
}; };
Future<void> sendPb(
Connection connection, Type pbClass, GeneratedMessage subMsg,
Future<void> sendPb(Connection connection, Type pbClass, GeneratedMessage subMsg, {Duration? timeout}) async { {Duration? timeout}) async {
// Construct the outer message with the submessage. // Construct the outer message with the submessage.
if (pbClassCreators[pbClass] == null) { if (pbClassCreators[pbClass] == null) {
@ -70,11 +68,9 @@ Future<void> sendPb(Connection connection, Type pbClass, GeneratedMessage subMsg
} }
} }
Future<void> sendPb2(SocketWrapper socketwrapper, Connection connection,
Type pbClass, GeneratedMessage subMsg,
Future<void> sendPb2(SocketWrapper socketwrapper, Connection connection, Type pbClass, GeneratedMessage subMsg, {Duration? timeout}) async { {Duration? timeout}) async {
// Construct the outer message with the submessage. // Construct the outer message with the submessage.
if (pbClassCreators[pbClass] == null) { if (pbClassCreators[pbClass] == null) {
@ -85,7 +81,8 @@ Future<void> sendPb2(SocketWrapper socketwrapper, Connection connection, Type pb
var pbMessage = pbClassCreators[pbClass]!()..mergeFromMessage(subMsg); var pbMessage = pbClassCreators[pbClass]!()..mergeFromMessage(subMsg);
final msgBytes = pbMessage.writeToBuffer(); final msgBytes = pbMessage.writeToBuffer();
try { try {
await connection.sendMessageWithSocketWrapper(socketwrapper, msgBytes, timeout: timeout); await connection.sendMessageWithSocketWrapper(socketwrapper, msgBytes,
timeout: timeout);
} on SocketException { } on SocketException {
throw FusionError('Connection closed by remote'); throw FusionError('Connection closed by remote');
} on TimeoutException { } on TimeoutException {
@ -95,13 +92,12 @@ Future<void> sendPb2(SocketWrapper socketwrapper, Connection connection, Type pb
} }
} }
Future<Tuple<GeneratedMessage, String>> recvPb2(SocketWrapper socketwrapper,
Connection connection, Type pbClass, List<String> expectedFieldNames,
{Duration? timeout}) async {
Future<Tuple<GeneratedMessage, String>> recvPb2(SocketWrapper socketwrapper, Connection connection, Type pbClass, List<String> expectedFieldNames, {Duration? timeout}) async {
try { try {
List<int> blob =
List<int> blob = await connection.recv_message2(socketwrapper, timeout: timeout); await connection.recv_message2(socketwrapper, timeout: timeout);
var pbMessage = pbClassCreators[pbClass]!()..mergeFromBuffer(blob); var pbMessage = pbClassCreators[pbClass]!()..mergeFromBuffer(blob);
@ -121,8 +117,8 @@ Future<Tuple<GeneratedMessage, String>> recvPb2(SocketWrapper socketwrapper, Con
} }
} }
throw FusionError('None of the expected fields found in the received message'); throw FusionError(
'None of the expected fields found in the received message');
} catch (e) { } catch (e) {
// Handle different exceptions here // Handle different exceptions here
if (e is SocketException) { if (e is SocketException) {
@ -134,15 +130,16 @@ Future<Tuple<GeneratedMessage, String>> recvPb2(SocketWrapper socketwrapper, Con
} else if (e is OSError && e.errorCode == 9) { } else if (e is OSError && e.errorCode == 9) {
throw FusionError('Connection closed by local'); throw FusionError('Connection closed by local');
} else { } else {
throw FusionError('Communications error: ${e.runtimeType}: ${e.toString()}'); throw FusionError(
'Communications error: ${e.runtimeType}: ${e.toString()}');
} }
} }
} }
Future<Tuple<GeneratedMessage, String>> recvPb(
Future<Tuple<GeneratedMessage, String>> recvPb(Connection connection, Type pbClass, List<String> expectedFieldNames, {Duration? timeout}) async { Connection connection, Type pbClass, List<String> expectedFieldNames,
{Duration? timeout}) async {
try { try {
List<int> blob = await connection.recv_message(timeout: timeout); List<int> blob = await connection.recv_message(timeout: timeout);
var pbMessage = pbClassCreators[pbClass]!()..mergeFromBuffer(blob); var pbMessage = pbClassCreators[pbClass]!()..mergeFromBuffer(blob);
@ -163,8 +160,8 @@ Future<Tuple<GeneratedMessage, String>> recvPb(Connection connection, Type pbCla
} }
} }
throw FusionError('None of the expected fields found in the received message'); throw FusionError(
'None of the expected fields found in the received message');
} catch (e) { } catch (e) {
// Handle different exceptions here // Handle different exceptions here
if (e is SocketException) { if (e is SocketException) {
@ -176,8 +173,8 @@ Future<Tuple<GeneratedMessage, String>> recvPb(Connection connection, Type pbCla
} else if (e is OSError && e.errorCode == 9) { } else if (e is OSError && e.errorCode == 9) {
throw FusionError('Connection closed by local'); throw FusionError('Connection closed by local');
} else { } else {
throw FusionError('Communications error: ${e.runtimeType}: ${e.toString()}'); throw FusionError(
'Communications error: ${e.runtimeType}: ${e.toString()}');
} }
} }
} }

View file

@ -1,10 +1,10 @@
import 'socketwrapper.dart';
import 'dart:io';
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:convert/convert.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:convert/convert.dart';
import 'package:stackwallet/services/cashfusion/socketwrapper.dart';
/* /*
This file might need some fixing up because each time we call fillBuf, we're trying to This file might need some fixing up because each time we call fillBuf, we're trying to
@ -22,14 +22,14 @@ class BadFrameError extends Error {
String toString() => message; String toString() => message;
} }
Future<Connection> openConnection(
String host,
Future<Connection> openConnection(String host, int port, int port, {
{double connTimeout = 5.0, double connTimeout = 5.0,
double defaultTimeout = 5.0, double defaultTimeout = 5.0,
bool ssl = false, bool ssl = false,
dynamic socksOpts}) async { dynamic socksOpts,
}) async {
try { try {
// Dart's Socket class handles connection timeout internally. // Dart's Socket class handles connection timeout internally.
Socket socket = await Socket.connect(host, port); Socket socket = await Socket.connect(host, port);
@ -38,25 +38,27 @@ Future<Connection> openConnection(String host, int port,
socket = await SecureSocket.secure(socket); socket = await SecureSocket.secure(socket);
} }
return Connection(socket: socket, timeout: Duration(seconds: defaultTimeout.toInt())); return Connection(
socket: socket, timeout: Duration(seconds: defaultTimeout.toInt()));
} catch (e) { } catch (e) {
throw 'Failed to open connection: $e'; throw 'Failed to open connection: $e';
} }
} }
class Connection { class Connection {
Duration timeout = Duration(seconds: 1); Duration timeout = Duration(seconds: 1);
Socket? socket; Socket? socket;
static const int MAX_MSG_LENGTH = 200 * 1024; static const int MAX_MSG_LENGTH = 200 * 1024;
static final Uint8List magic = Uint8List.fromList([0x76, 0x5b, 0xe8, 0xb4, 0xe4, 0x39, 0x6d, 0xcf]); static final Uint8List magic =
Uint8List.fromList([0x76, 0x5b, 0xe8, 0xb4, 0xe4, 0x39, 0x6d, 0xcf]);
final Uint8List recvbuf = Uint8List(0); final Uint8List recvbuf = Uint8List(0);
Connection({required this.socket, this.timeout = const Duration(seconds: 1)}); Connection({required this.socket, this.timeout = const Duration(seconds: 1)});
Connection.withoutSocket({this.timeout = const Duration(seconds: 1)}); Connection.withoutSocket({this.timeout = const Duration(seconds: 1)});
Future<void> sendMessageWithSocketWrapper(SocketWrapper socketwrapper, List<int> msg, {Duration? timeout}) async { Future<void> sendMessageWithSocketWrapper(
SocketWrapper socketwrapper, List<int> msg,
{Duration? timeout}) async {
timeout ??= this.timeout; timeout ??= this.timeout;
print("DEBUG sendmessage msg sending "); print("DEBUG sendmessage msg sending ");
print(msg); print(msg);
@ -76,10 +78,7 @@ class Connection {
} }
} }
Future<void> sendMessage(List<int> msg, {Duration? timeout}) async { Future<void> sendMessage(List<int> msg, {Duration? timeout}) async {
timeout ??= this.timeout; timeout ??= this.timeout;
final lengthBytes = Uint8List(4); final lengthBytes = Uint8List(4);
@ -93,7 +92,6 @@ class Connection {
..addAll(msg); ..addAll(msg);
try { try {
StreamController<List<int>> controller = StreamController(); StreamController<List<int>> controller = StreamController();
controller.stream.listen((data) { controller.stream.listen((data) {
@ -109,19 +107,18 @@ class Connection {
} finally { } finally {
controller.close(); controller.close();
} }
} on SocketException catch (e) { } on SocketException catch (e) {
throw TimeoutException('Socket write timed out', timeout); throw TimeoutException('Socket write timed out', timeout);
} }
} }
void close() { void close() {
socket?.close(); socket?.close();
} }
Future<List<int>> fillBuf2(SocketWrapper socketwrapper, List<int> recvBuf, int n, {Duration? timeout}) async { Future<List<int>> fillBuf2(
SocketWrapper socketwrapper, List<int> recvBuf, int n,
{Duration? timeout}) async {
final maxTime = timeout != null ? DateTime.now().add(timeout) : null; final maxTime = timeout != null ? DateTime.now().add(timeout) : null;
await for (var data in socketwrapper.socket!.cast<List<int>>()) { await for (var data in socketwrapper.socket!.cast<List<int>>()) {
@ -139,7 +136,8 @@ class Connection {
} }
recvBuf.addAll(data); recvBuf.addAll(data);
print("DEBUG fillBuf2 2 - data added to recvBuf, new length: ${recvBuf.length}"); print(
"DEBUG fillBuf2 2 - data added to recvBuf, new length: ${recvBuf.length}");
if (recvBuf.length >= n) { if (recvBuf.length >= n) {
print("DEBUG fillBuf2 3 - breaking loop, recvBuf is big enough"); print("DEBUG fillBuf2 3 - breaking loop, recvBuf is big enough");
@ -163,8 +161,6 @@ class Connection {
}); });
return recvBuf; return recvBuf;
StreamSubscription<List<int>>? subscription; // Declaration moved here StreamSubscription<List<int>>? subscription; // Declaration moved here
subscription = socket!.listen( subscription = socket!.listen(
(List<int> data) { (List<int> data) {
@ -180,7 +176,8 @@ class Connection {
onDone: () { onDone: () {
print("DEBUG ON DONE"); print("DEBUG ON DONE");
if (recvBuf.length < n) { if (recvBuf.length < n) {
throw SocketException('Connection closed before enough data was received'); throw SocketException(
'Connection closed before enough data was received');
} }
}, },
); );
@ -197,8 +194,8 @@ class Connection {
return recvBuf; return recvBuf;
} }
Future<List<int>> recv_message2(SocketWrapper socketwrapper,
Future<List<int>> recv_message2(SocketWrapper socketwrapper, {Duration? timeout}) async { {Duration? timeout}) async {
if (timeout == null) { if (timeout == null) {
timeout = this.timeout; timeout = this.timeout;
} }
@ -237,14 +234,17 @@ class Connection {
throw BadFrameError('Bad magic in frame: ${hex.encode(magic)}'); throw BadFrameError('Bad magic in frame: ${hex.encode(magic)}');
} }
final byteData = ByteData.view(Uint8List.fromList(recvBuf.sublist(8, 12)).buffer); final byteData =
ByteData.view(Uint8List.fromList(recvBuf.sublist(8, 12)).buffer);
final messageLength = byteData.getUint32(0, Endian.big); final messageLength = byteData.getUint32(0, Endian.big);
if (messageLength > MAX_MSG_LENGTH) { if (messageLength > MAX_MSG_LENGTH) {
throw BadFrameError('Got a frame with msg_length=$messageLength > $MAX_MSG_LENGTH (max)'); throw BadFrameError(
'Got a frame with msg_length=$messageLength > $MAX_MSG_LENGTH (max)');
} }
print("DEBUG recv_message2 3 - about to read the message body, messageLength: $messageLength"); print(
"DEBUG recv_message2 3 - about to read the message body, messageLength: $messageLength");
print("DEBUG recvfbuf len is "); print("DEBUG recvfbuf len is ");
print(recvBuf.length); print(recvBuf.length);
@ -255,13 +255,15 @@ class Connection {
if (recvBuf.length == bytesRead && bytesRead == 12 + messageLength) { if (recvBuf.length == bytesRead && bytesRead == 12 + messageLength) {
final message = recvBuf.sublist(12, 12 + messageLength); final message = recvBuf.sublist(12, 12 + messageLength);
print("DEBUG recv_message2 4 - message received, length: ${message.length}"); print(
"DEBUG recv_message2 4 - message received, length: ${message.length}");
print("DEBUG recv_message2 5 - message content: $message"); print("DEBUG recv_message2 5 - message content: $message");
return message; return message;
} else { } else {
// Throwing exception if the length doesn't match // Throwing exception if the length doesn't match
throw Exception('Message length mismatch: expected ${12 + messageLength} bytes, received ${recvBuf.length} bytes.'); throw Exception(
'Message length mismatch: expected ${12 + messageLength} bytes, received ${recvBuf.length} bytes.');
} }
} }
} }
@ -273,13 +275,8 @@ class Connection {
return []; return [];
} }
Future<List<int>> recv_message({Duration? timeout}) async { Future<List<int>> recv_message({Duration? timeout}) async {
// DEPRECATED // DEPRECATED
return []; return [];
} }
} // END OF CLASS } // END OF CLASS

View file

@ -1,17 +1,18 @@
import 'dart:math';
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'dart:collection'; import 'dart:collection';
import 'connection.dart';
import 'package:protobuf/protobuf.dart' as pb;
import 'comms.dart';
import 'fusion.pb.dart';
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:protobuf/protobuf.dart' as pb;
import 'comms.dart';
import 'connection.dart';
import 'fusion.pb.dart';
const int TOR_COOLDOWN_TIME = 660; const int TOR_COOLDOWN_TIME = 660;
const int TIMEOUT_INACTIVE_CONNECTION = 120; const int TIMEOUT_INACTIVE_CONNECTION = 120;
class FusionError implements Exception { class FusionError implements Exception {
String cause; String cause;
FusionError(this.cause); FusionError(this.cause);
@ -21,15 +22,14 @@ class Unrecoverable extends FusionError {
Unrecoverable(String cause) : super(cause); Unrecoverable(String cause) : super(cause);
} }
Future<bool> isTorPort(String host, int port) async { Future<bool> isTorPort(String host, int port) async {
if (port < 0 || port > 65535) { if (port < 0 || port > 65535) {
return false; return false;
} }
try { try {
Socket sock = await Socket.connect(host, port, timeout: Duration(milliseconds: 100)); Socket sock =
await Socket.connect(host, port, timeout: Duration(milliseconds: 100));
sock.write("GET\n"); sock.write("GET\n");
List<int> data = await sock.first; List<int> data = await sock.first;
sock.destroy(); sock.destroy();
@ -43,8 +43,6 @@ Future<bool> isTorPort(String host, int port) async {
return false; return false;
} }
class TorLimiter { class TorLimiter {
Queue<DateTime> deque = Queue<DateTime>(); Queue<DateTime> deque = Queue<DateTime>();
int lifetime; int lifetime;
@ -65,7 +63,6 @@ class TorLimiter {
TorLimiter limiter = TorLimiter(TOR_COOLDOWN_TIME); TorLimiter limiter = TorLimiter(TOR_COOLDOWN_TIME);
double randTrap(Random rng) { double randTrap(Random rng) {
final sixth = 1.0 / 6; final sixth = 1.0 / 6;
final f = rng.nextDouble(); final f = rng.nextDouble();
@ -80,8 +77,6 @@ double randTrap(Random rng) {
} }
} }
class CovertConnection { class CovertConnection {
Connection? connection; // replace dynamic with the type of your connection Connection? connection; // replace dynamic with the type of your connection
int? slotNum; int? slotNum;
@ -124,7 +119,8 @@ class CovertSlot {
int submitTimeout; int submitTimeout;
pb.GeneratedMessage? subMsg; // The work to be done. pb.GeneratedMessage? subMsg; // The work to be done.
bool done; // Whether last work requested is done. bool done; // Whether last work requested is done.
CovertConnection? covConn; // which CovertConnection is assigned to work on this slot CovertConnection?
covConn; // which CovertConnection is assigned to work on this slot
CovertSlot(this.submitTimeout) : done = true; CovertSlot(this.submitTimeout) : done = true;
DateTime? t_submit; DateTime? t_submit;
@ -138,25 +134,21 @@ class CovertSlot {
throw Unrecoverable('connection is null'); throw Unrecoverable('connection is null');
} }
await sendPb(connection, CovertMessage, subMsg!, timeout: Duration(seconds: submitTimeout)); await sendPb(connection, CovertMessage, subMsg!,
var result = await recvPb(connection, CovertResponse, ['ok', 'error'], timeout: Duration(seconds: submitTimeout)); timeout: Duration(seconds: submitTimeout));
var result = await recvPb(connection, CovertResponse, ['ok', 'error'],
timeout: Duration(seconds: submitTimeout));
if (result.item1 == 'error') { if (result.item1 == 'error') {
throw Unrecoverable('error from server: ${result.item2}'); throw Unrecoverable('error from server: ${result.item2}');
} }
done = true; done = true;
t_submit = DateTime.fromMillisecondsSinceEpoch(0); t_submit = DateTime.fromMillisecondsSinceEpoch(0);
covConn?.tPing = DateTime.fromMillisecondsSinceEpoch(0); // if a submission is done, no ping is needed. covConn?.tPing = DateTime.fromMillisecondsSinceEpoch(
0); // if a submission is done, no ping is needed.
} }
} }
class PrintError { class PrintError {
// Declare properties here // Declare properties here
} }
@ -194,12 +186,11 @@ class CovertSubmitter extends PrintError {
this.num_slots, this.num_slots,
double randSpan, // changed from int to double double randSpan, // changed from int to double
double submit_timeout) // changed from int to double double submit_timeout) // changed from int to double
: slots = List<CovertSlot>.generate(num_slots, (index) => CovertSlot(submit_timeout.toInt())) { : slots = List<CovertSlot>.generate(
num_slots, (index) => CovertSlot(submit_timeout.toInt())) {
// constructor body... // constructor body...
} }
void wakeAll() { void wakeAll() {
for (var s in slots) { for (var s in slots) {
if (s.covConn != null) { if (s.covConn != null) {
@ -218,7 +209,6 @@ class CovertSubmitter extends PrintError {
} }
} }
void stop([Exception? exception]) { void stop([Exception? exception]) {
if (this.stopping) { if (this.stopping) {
// already requested! // already requested!
@ -226,13 +216,16 @@ class CovertSubmitter extends PrintError {
} }
this.failureException = exception?.toString(); this.failureException = exception?.toString();
this.stopping = true; this.stopping = true;
var timeRemaining = this.stopTStart?.difference(DateTime.now()).inSeconds ?? 0; var timeRemaining =
print("Stopping; connections will close in approximately $timeRemaining seconds"); this.stopTStart?.difference(DateTime.now()).inSeconds ?? 0;
print(
"Stopping; connections will close in approximately $timeRemaining seconds");
this.wakeAll(); this.wakeAll();
} }
// PYTHON USES MULTITHREADING, WHICH ISNT IMPLEMENTED HERE YET // PYTHON USES MULTITHREADING, WHICH ISNT IMPLEMENTED HERE YET
void scheduleConnections(DateTime tStart, Duration tSpan, {int numSpares = 0, int connectTimeout = 10}) { void scheduleConnections(DateTime tStart, Duration tSpan,
{int numSpares = 0, int connectTimeout = 10}) {
var newConns = <CovertConnection>[]; var newConns = <CovertConnection>[];
for (var sNum = 0; sNum < this.slots.length; sNum++) { for (var sNum = 0; sNum < this.slots.length; sNum++) {
@ -244,8 +237,6 @@ class CovertSubmitter extends PrintError {
if (myCovConn != null) { if (myCovConn != null) {
newConns.add(myCovConn); newConns.add(myCovConn);
} }
} }
} }
@ -258,16 +249,15 @@ class CovertSubmitter extends PrintError {
for (var covConn in newConns) { for (var covConn in newConns) {
covConn.connNumber = this.countAttempted; covConn.connNumber = this.countAttempted;
this.countAttempted++; this.countAttempted++;
var connTime = tStart.add(Duration(seconds: (tSpan.inSeconds * randTrap(this.rng)).round())); var connTime = tStart.add(
Duration(seconds: (tSpan.inSeconds * randTrap(this.rng)).round()));
var randDelay = (this.randSpan ?? 0) * randTrap(this.rng); var randDelay = (this.randSpan ?? 0) * randTrap(this.rng);
runConnection(covConn, connTime.millisecondsSinceEpoch, randDelay, connectTimeout); runConnection(
covConn, connTime.millisecondsSinceEpoch, randDelay, connectTimeout);
} }
} }
void scheduleSubmit(int slotNum, DateTime tStart, dynamic subMsg) { void scheduleSubmit(int slotNum, DateTime tStart, dynamic subMsg) {
var slot = slots[slotNum]; var slot = slots[slotNum];
@ -282,7 +272,6 @@ class CovertSubmitter extends PrintError {
} }
} }
void scheduleSubmissions(DateTime tStart, List<dynamic> slotMessages) { void scheduleSubmissions(DateTime tStart, List<dynamic> slotMessages) {
// Convert to list (Dart does not have tuples) // Convert to list (Dart does not have tuples)
slotMessages = List.from(slotMessages); slotMessages = List.from(slotMessages);
@ -317,11 +306,11 @@ class CovertSubmitter extends PrintError {
} }
} }
Future runConnection(CovertConnection covConn, int connTime, double randDelay,
Future runConnection( int connectTimeout) async {
CovertConnection covConn, int connTime, double randDelay, int connectTimeout) async {
// Main loop for connection thread // Main loop for connection thread
DateTime connDateTime = DateTime.fromMillisecondsSinceEpoch(connTime * 1000); DateTime connDateTime =
DateTime.fromMillisecondsSinceEpoch(connTime * 1000);
while (await covConn.waitWakeupOrTime(connDateTime)) { while (await covConn.waitWakeupOrTime(connDateTime)) {
// if we are woken up before connection and stopping is happening, then just don't make a connection at all // if we are woken up before connection and stopping is happening, then just don't make a connection at all
if (this.stopping) { if (this.stopping) {
@ -350,11 +339,11 @@ class CovertSubmitter extends PrintError {
try { try {
final connection = await openConnection( final connection = await openConnection(
this.destAddr!, this.destPort!, this.destAddr!, this.destPort!,
connTimeout: connectTimeout.toDouble(), ssl: this.ssl, socksOpts: proxyOpts); connTimeout: connectTimeout.toDouble(),
ssl: this.ssl,
socksOpts: proxyOpts);
covConn.connection = connection; covConn.connection = connection;
} } catch (e) {
catch (e) {
this.countFailed++; this.countFailed++;
final tEnd = DateTime.now().millisecondsSinceEpoch; final tEnd = DateTime.now().millisecondsSinceEpoch;
@ -364,7 +353,6 @@ class CovertSubmitter extends PrintError {
rethrow; rethrow;
} }
this.countEstablished++; this.countEstablished++;
final tEnd = DateTime.now().millisecondsSinceEpoch; final tEnd = DateTime.now().millisecondsSinceEpoch;
@ -372,7 +360,6 @@ class CovertSubmitter extends PrintError {
print( print(
'[${covConn.connNumber}] connection established after ${((tEnd - tBegin) / 1000).toStringAsFixed(3)}s'); '[${covConn.connNumber}] connection established after ${((tEnd - tBegin) / 1000).toStringAsFixed(3)}s');
covConn.delay = (randTrap(this.rng) ?? 0) * (this.randSpan ?? 0); covConn.delay = (randTrap(this.rng) ?? 0) * (this.randSpan ?? 0);
var lastActionTime = DateTime.now().millisecondsSinceEpoch; var lastActionTime = DateTime.now().millisecondsSinceEpoch;
@ -396,7 +383,8 @@ class CovertSubmitter extends PrintError {
} }
// Last preference: wait doing nothing // Last preference: wait doing nothing
if (nextTime == null) { if (nextTime == null) {
nextTime = DateTime.now().add(Duration(seconds: TIMEOUT_INACTIVE_CONNECTION)); nextTime = DateTime.now()
.add(Duration(seconds: TIMEOUT_INACTIVE_CONNECTION));
action = covConn.inactive; action = covConn.inactive;
} }
@ -423,7 +411,9 @@ class CovertSubmitter extends PrintError {
// STATE 3 - stopping // STATE 3 - stopping
while (true) { while (true) {
final stopTime = this.stopTStart?.add(Duration(seconds: randDelay.toInt())) ?? DateTime.now(); final stopTime =
this.stopTStart?.add(Duration(seconds: randDelay.toInt())) ??
DateTime.now();
if (!(await covConn.waitWakeupOrTime(stopTime))) { if (!(await covConn.waitWakeupOrTime(stopTime))) {
break; break;
@ -442,7 +432,8 @@ class CovertSubmitter extends PrintError {
// Found a spare. // Found a spare.
this.slots[slotNum].covConn = spare; this.slots[slotNum].covConn = spare;
spare.slotNum = slotNum; spare.slotNum = slotNum;
spare.wakeup.complete(); // python code is using set, possibly dealing wiht multi thread...double check this is ok. spare.wakeup
.complete(); // python code is using set, possibly dealing wiht multi thread...double check this is ok.
covConn.slotNum = null; covConn.slotNum = null;
} catch (e) { } catch (e) {
@ -453,7 +444,6 @@ class CovertSubmitter extends PrintError {
} else { } else {
// Handle the case where the exception is not an instance of Exception // Handle the case where the exception is not an instance of Exception
} }
} }
} }
} finally { } finally {
@ -462,7 +452,6 @@ class CovertSubmitter extends PrintError {
} }
} }
void checkOk() { void checkOk() {
// Implement checkOk logic here // Implement checkOk logic here
var e = this.failure_exception; var e = this.failure_exception;
@ -474,26 +463,21 @@ class CovertSubmitter extends PrintError {
void checkConnected() { void checkConnected() {
// Implement checkConnected logic here // Implement checkConnected logic here
this.checkOk(); this.checkOk();
var numMissing = this.slots var numMissing =
.where((s) => s.covConn?.connection == null) this.slots.where((s) => s.covConn?.connection == null).length;
.length;
if (numMissing > 0) { if (numMissing > 0) {
throw FusionError( throw FusionError(
"Covert connections were too slow ($numMissing incomplete out of ${this "Covert connections were too slow ($numMissing incomplete out of ${this.slots.length}).");
.slots.length}).");
} }
} }
void checkDone() { void checkDone() {
// Implement checkDone logic here // Implement checkDone logic here
this.checkOk(); this.checkOk();
var numMissing = this.slots var numMissing = this.slots.where((s) => !s.done).length;
.where((s) => !s.done)
.length;
if (numMissing > 0) { if (numMissing > 0) {
throw FusionError( throw FusionError(
"Covert submissions were too slow ($numMissing incomplete out of ${this "Covert submissions were too slow ($numMissing incomplete out of ${this.slots.length}).");
.slots.length}).");
} }
} }
} }

View file

@ -1,17 +1,20 @@
import 'dart:convert';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:pointycastle/pointycastle.dart' hide Mac;
import 'package:crypto/crypto.dart' as crypto; import 'package:crypto/crypto.dart' as crypto;
import 'package:cryptography/cryptography.dart'; import 'package:cryptography/cryptography.dart';
import 'package:pointycastle/pointycastle.dart' hide Mac;
import 'util.dart'; import 'util.dart';
final ECDomainParameters params = ECDomainParameters('secp256k1'); final ECDomainParameters params = ECDomainParameters('secp256k1');
final BigInt order = params.n; final BigInt order = params.n;
class EncryptionFailed implements Exception {} class EncryptionFailed implements Exception {}
class DecryptionFailed implements Exception {} class DecryptionFailed implements Exception {}
Future<Uint8List> encrypt(Uint8List message, ECPoint pubkey, {int? padToLength}) async { Future<Uint8List> encrypt(Uint8List message, ECPoint pubkey,
{int? padToLength}) async {
ECPoint pubpoint; ECPoint pubpoint;
try { try {
pubpoint = Util.ser_to_point(pubkey.getEncoded(true), params); pubpoint = Util.ser_to_point(pubkey.getEncoded(true), params);
@ -27,22 +30,27 @@ Future<Uint8List> encrypt(Uint8List message, ECPoint pubkey, {int? padToLength})
var pubpoint_times_nonceSec = pubpoint * nonceSec; var pubpoint_times_nonceSec = pubpoint * nonceSec;
if (pubpoint_times_nonceSec == null) { if (pubpoint_times_nonceSec == null) {
throw Exception('Multiplication of pubpoint with nonceSec resulted in null'); throw Exception(
'Multiplication of pubpoint with nonceSec resulted in null');
} }
var key = crypto.sha256.convert(Util.point_to_ser(pubpoint_times_nonceSec, true)).bytes; var key = crypto.sha256
.convert(Util.point_to_ser(pubpoint_times_nonceSec, true))
.bytes;
var plaintext = Uint8List(4 + message.length)
..buffer.asByteData().setUint32(0, message.length, Endian.big)
var plaintext = Uint8List(4 + message.length)..buffer.asByteData().setUint32(0, message.length, Endian.big)..setRange(4, 4 + message.length, message); ..setRange(4, 4 + message.length, message);
if (padToLength == null) { if (padToLength == null) {
padToLength = ((plaintext.length + 15) ~/ 16) * 16; // round up to nearest 16 padToLength =
((plaintext.length + 15) ~/ 16) * 16; // round up to nearest 16
} else if (padToLength % 16 != 0) { } else if (padToLength % 16 != 0) {
throw ArgumentError('$padToLength not multiple of 16'); throw ArgumentError('$padToLength not multiple of 16');
} }
if (padToLength < plaintext.length) { if (padToLength < plaintext.length) {
throw ArgumentError('$padToLength < ${plaintext.length}'); throw ArgumentError('$padToLength < ${plaintext.length}');
} }
plaintext = Uint8List(padToLength)..setRange(0, message.length + 4, plaintext); plaintext = Uint8List(padToLength)
..setRange(0, message.length + 4, plaintext);
final secretKey = SecretKey(key); final secretKey = SecretKey(key);
@ -50,16 +58,20 @@ Future<Uint8List> encrypt(Uint8List message, ECPoint pubkey, {int? padToLength})
final cipher = AesCbc.with128bits(macAlgorithm: macAlgorithm); final cipher = AesCbc.with128bits(macAlgorithm: macAlgorithm);
final nonce = Uint8List(16); // Random nonce final nonce = Uint8List(16); // Random nonce
final secretBox = await cipher.encrypt(plaintext, secretKey: secretKey, nonce: nonce); final secretBox =
await cipher.encrypt(plaintext, secretKey: secretKey, nonce: nonce);
final ciphertext = secretBox.cipherText; final ciphertext = secretBox.cipherText;
return Uint8List(noncePub.length + ciphertext.length + secretBox.mac.bytes.length) return Uint8List(
noncePub.length + ciphertext.length + secretBox.mac.bytes.length)
..setRange(0, noncePub.length, noncePub) ..setRange(0, noncePub.length, noncePub)
..setRange(noncePub.length, noncePub.length + ciphertext.length, ciphertext) ..setRange(noncePub.length, noncePub.length + ciphertext.length, ciphertext)
..setRange(noncePub.length + ciphertext.length, noncePub.length + ciphertext.length + secretBox.mac.bytes.length, secretBox.mac.bytes); ..setRange(
noncePub.length + ciphertext.length,
noncePub.length + ciphertext.length + secretBox.mac.bytes.length,
secretBox.mac.bytes);
} }
Future<Uint8List> decryptWithSymmkey(Uint8List data, Uint8List key) async { Future<Uint8List> decryptWithSymmkey(Uint8List data, Uint8List key) async {
@ -75,7 +87,8 @@ Future<Uint8List> decryptWithSymmkey(Uint8List data, Uint8List key) async {
final cipher = AesCbc.with128bits(macAlgorithm: Hmac.sha256()); final cipher = AesCbc.with128bits(macAlgorithm: Hmac.sha256());
final nonce = Uint8List(16); // Random nonce final nonce = Uint8List(16); // Random nonce
final secretBox = SecretBox(ciphertext, mac: Mac(data.sublist(data.length - 16)), nonce: nonce); final secretBox = SecretBox(ciphertext,
mac: Mac(data.sublist(data.length - 16)), nonce: nonce);
final plaintext = await cipher.decrypt(secretBox, secretKey: secretKey); final plaintext = await cipher.decrypt(secretBox, secretKey: secretKey);
if (plaintext.length < 4) { if (plaintext.length < 4) {
@ -86,15 +99,14 @@ Future<Uint8List> decryptWithSymmkey(Uint8List data, Uint8List key) async {
ByteData byteData = ByteData.sublistView(uint8list); ByteData byteData = ByteData.sublistView(uint8list);
var msgLength = byteData.getUint32(0, Endian.big); var msgLength = byteData.getUint32(0, Endian.big);
if (msgLength + 4 > plaintext.length) { if (msgLength + 4 > plaintext.length) {
throw DecryptionFailed(); throw DecryptionFailed();
} }
return Uint8List.fromList(plaintext.sublist(4, 4 + msgLength)); return Uint8List.fromList(plaintext.sublist(4, 4 + msgLength));
} }
Future<Tuple<Uint8List, Uint8List>> decrypt(Uint8List data, ECPrivateKey privkey) async { Future<Tuple<Uint8List, Uint8List>> decrypt(
Uint8List data, ECPrivateKey privkey) async {
if (data.length < 33 + 16 + 16) { if (data.length < 33 + 16 + 16) {
throw DecryptionFailed(); throw DecryptionFailed();
} }
@ -122,4 +134,3 @@ Future<Tuple<Uint8List, Uint8List>> decrypt(Uint8List data, ECPrivateKey privkey
var decryptedData = await decryptWithSymmkey(data, Uint8List.fromList(key)); var decryptedData = await decryptWithSymmkey(data, Uint8List.fromList(key));
return Tuple(decryptedData, Uint8List.fromList(key)); return Tuple(decryptedData, Uint8List.fromList(key));
} }

File diff suppressed because it is too large Load diff

View file

@ -1,122 +0,0 @@
import 'package:flutter/material.dart';
import 'fusion.dart';
import 'pedersen.dart';
import 'encrypt.dart';
import 'validation.dart';
void main() {
Fusion.foo();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}

View file

@ -1,8 +1,8 @@
import 'package:pointycastle/ecc/api.dart';
import 'util.dart';
import 'dart:math';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:pointycastle/ecc/api.dart';
import 'package:stackwallet/services/cashfusion/util.dart';
ECDomainParameters getDefaultParams() { ECDomainParameters getDefaultParams() {
return ECDomainParameters("secp256k1"); return ECDomainParameters("secp256k1");
} }
@ -11,10 +11,10 @@ class NullPointError implements Exception {
String errMsg() => 'NullPointError: Either Hpoint or HGpoint is null.'; String errMsg() => 'NullPointError: Either Hpoint or HGpoint is null.';
} }
class NonceRangeError implements Exception { class NonceRangeError implements Exception {
final String message; final String message;
NonceRangeError([this.message = "Nonce value must be in the range 0 < nonce < order"]); NonceRangeError(
[this.message = "Nonce value must be in the range 0 < nonce < order"]);
String toString() => "NonceRangeError: $message"; String toString() => "NonceRangeError: $message";
} }
@ -26,12 +26,12 @@ class ResultAtInfinity implements Exception {
class InsecureHPoint implements Exception { class InsecureHPoint implements Exception {
final String message; final String message;
InsecureHPoint([this.message = "The H point has a known discrete logarithm, which means the commitment setup is broken"]); InsecureHPoint(
[this.message =
"The H point has a known discrete logarithm, which means the commitment setup is broken"]);
String toString() => "InsecureHPoint: $message"; String toString() => "InsecureHPoint: $message";
} }
class PedersenSetup { class PedersenSetup {
late ECPoint _H; late ECPoint _H;
late ECPoint _HG; late ECPoint _HG;
@ -53,7 +53,6 @@ class PedersenSetup {
Commitment commit(BigInt amount, {BigInt? nonce, Uint8List? PUncompressed}) { Commitment commit(BigInt amount, {BigInt? nonce, Uint8List? PUncompressed}) {
return Commitment(this, amount, nonce: nonce, PUncompressed: PUncompressed); return Commitment(this, amount, nonce: nonce, PUncompressed: PUncompressed);
} }
} }
class Commitment { class Commitment {
@ -62,8 +61,8 @@ class Commitment {
late BigInt nonce; late BigInt nonce;
late Uint8List PUncompressed; late Uint8List PUncompressed;
Commitment(this.setup, BigInt amount,
Commitment(this.setup, BigInt amount, {BigInt? nonce, Uint8List? PUncompressed}) { {BigInt? nonce, Uint8List? PUncompressed}) {
this.nonce = nonce ?? Util.secureRandomBigInt(setup.params.n.bitLength); this.nonce = nonce ?? Util.secureRandomBigInt(setup.params.n.bitLength);
amountMod = amount % setup.params.n; amountMod = amount % setup.params.n;
@ -84,13 +83,16 @@ class Commitment {
ECPoint? HpointMultiplied = Hpoint * multiplier1; ECPoint? HpointMultiplied = Hpoint * multiplier1;
ECPoint? HGpointMultiplied = HGpoint * multiplier2; ECPoint? HGpointMultiplied = HGpoint * multiplier2;
ECPoint? Ppoint = HpointMultiplied != null && HGpointMultiplied != null ? HpointMultiplied + HGpointMultiplied : null; ECPoint? Ppoint = HpointMultiplied != null && HGpointMultiplied != null
? HpointMultiplied + HGpointMultiplied
: null;
if (Ppoint == setup.params.curve.infinity) { if (Ppoint == setup.params.curve.infinity) {
throw ResultAtInfinity(); throw ResultAtInfinity();
} }
this.PUncompressed = PUncompressed ?? Ppoint?.getEncoded(false) ?? Uint8List(0); this.PUncompressed =
PUncompressed ?? Ppoint?.getEncoded(false) ?? Uint8List(0);
} }
void calcInitial(PedersenSetup setup, BigInt amount) { void calcInitial(PedersenSetup setup, BigInt amount) {
@ -114,7 +116,9 @@ class Commitment {
ECPoint? HpointMultiplied = Hpoint * multiplier1; ECPoint? HpointMultiplied = Hpoint * multiplier1;
ECPoint? HGpointMultiplied = HGpoint * multiplier2; ECPoint? HGpointMultiplied = HGpoint * multiplier2;
ECPoint? Ppoint = HpointMultiplied != null && HGpointMultiplied != null ? HpointMultiplied + HGpointMultiplied : null; ECPoint? Ppoint = HpointMultiplied != null && HGpointMultiplied != null
? HpointMultiplied + HGpointMultiplied
: null;
if (Ppoint == setup.params.curve.infinity) { if (Ppoint == setup.params.curve.infinity) {
throw ResultAtInfinity(); throw ResultAtInfinity();
@ -124,14 +128,17 @@ class Commitment {
} }
static Uint8List add_points(Iterable<Uint8List> pointsIterable) { static Uint8List add_points(Iterable<Uint8List> pointsIterable) {
ECDomainParameters params = getDefaultParams(); // Using helper function here ECDomainParameters params =
var pointList = pointsIterable.map((pser) => Util.ser_to_point(pser, params)).toList(); getDefaultParams(); // Using helper function here
var pointList =
pointsIterable.map((pser) => Util.ser_to_point(pser, params)).toList();
if (pointList.isEmpty) { if (pointList.isEmpty) {
throw ArgumentError('Empty list'); throw ArgumentError('Empty list');
} }
ECPoint pSum = pointList.first; // Initialize pSum with the first point in the list ECPoint pSum =
pointList.first; // Initialize pSum with the first point in the list
for (var i = 1; i < pointList.length; i++) { for (var i = 1; i < pointList.length; i++) {
pSum = (pSum + pointList[i])!; pSum = (pSum + pointList[i])!;
@ -144,7 +151,6 @@ class Commitment {
return Util.point_to_ser(pSum, false); return Util.point_to_ser(pSum, false);
} }
Commitment addCommitments(Iterable<Commitment> commitmentIterable) { Commitment addCommitments(Iterable<Commitment> commitmentIterable) {
BigInt ktotal = BigInt.zero; // Changed to BigInt from int BigInt ktotal = BigInt.zero; // Changed to BigInt from int
BigInt atotal = BigInt.zero; // Changed to BigInt from int BigInt atotal = BigInt.zero; // Changed to BigInt from int
@ -168,7 +174,8 @@ class Commitment {
ktotal = ktotal % setup.params.n; // Changed order to setup.params.n ktotal = ktotal % setup.params.n; // Changed order to setup.params.n
if (ktotal == BigInt.zero) { // Changed comparison from 0 to BigInt.zero if (ktotal == BigInt.zero) {
// Changed comparison from 0 to BigInt.zero
throw Exception('Nonce range error'); throw Exception('Nonce range error');
} }
@ -182,6 +189,7 @@ class Commitment {
} else { } else {
PUncompressed = null; PUncompressed = null;
} }
return Commitment(setup, atotal, nonce: ktotal, PUncompressed: PUncompressed); return Commitment(setup, atotal,
nonce: ktotal, PUncompressed: PUncompressed);
} }
} }

View file

@ -1,11 +1,8 @@
class Protocol { class Protocol {
static const VERSION = 'alpha13'; static const VERSION = 'alpha13';
static const FUSE_ID = 'FUZ\x00'; static const FUSE_ID = 'FUZ\x00';
// Safety limits to prevent loss of funds / limit fees: // Safety limits to prevent loss of funds / limit fees:
//(Note that if we enter multiply into the same fusion, our limits apply //(Note that if we enter multiply into the same fusion, our limits apply
//separately for each "player".) //separately for each "player".)
@ -56,4 +53,3 @@ class Protocol {
static const STANDARD_TIMEOUT = 3.0; static const STANDARD_TIMEOUT = 3.0;
static const BLAME_VERIFY_TIME = 5.0; static const BLAME_VERIFY_TIME = 5.0;
} }

View file

@ -5,16 +5,19 @@ class SocketWrapper {
final String serverIP; final String serverIP;
final int serverPort; final int serverPort;
late Stream<List<int>> _receiveStream; // create a field for the broadcast stream late Stream<List<int>>
_receiveStream; // create a field for the broadcast stream
SocketWrapper(this.serverIP, this.serverPort); SocketWrapper(this.serverIP, this.serverPort);
Socket get socket => _socket; Socket get socket => _socket;
Stream<List<int>> get receiveStream => _receiveStream; // expose the stream with a getter Stream<List<int>> get receiveStream =>
_receiveStream; // expose the stream with a getter
Future<void> connect() async { Future<void> connect() async {
_socket = await Socket.connect(serverIP, serverPort); _socket = await Socket.connect(serverIP, serverPort);
_receiveStream = _socket.asBroadcastStream(); // initialize the broadcast stream _receiveStream =
_socket.asBroadcastStream(); // initialize the broadcast stream
_socket.done.then((_) { _socket.done.then((_) {
print('......Socket has been closed'); print('......Socket has been closed');
}); });
@ -25,7 +28,8 @@ class SocketWrapper {
void status() { void status() {
if (_socket != null) { if (_socket != null) {
print("Socket connected to ${_socket.remoteAddress.address}:${_socket.remotePort}"); print(
"Socket connected to ${_socket.remoteAddress.address}:${_socket.remotePort}");
} else { } else {
print("Socket is not connected"); print("Socket is not connected");
} }

View file

@ -1,18 +1,18 @@
import 'dart:convert';
import 'package:stackwallet/services/cashfusion/fusion.dart';
import 'package:pointycastle/ecc/api.dart';
import 'dart:math'; import 'dart:math';
import 'dart:typed_data'; import 'dart:typed_data';
import 'dart:convert';
import 'package:crypto/crypto.dart' as crypto; import 'package:crypto/crypto.dart' as crypto;
import 'protocol.dart'; import 'package:pointycastle/ecc/api.dart';
import 'fusion.pb.dart'; import 'fusion.pb.dart';
import 'dart:convert'; import 'protocol.dart';
class Address { class Address {
String addr = ""; String addr = "";
Address({required this.addr}); // Constructor updated to accept addr as a named parameter Address(
{required this.addr}); // Constructor updated to accept addr as a named parameter
Address._create({required this.addr}); Address._create({required this.addr});
@ -32,7 +32,6 @@ class Address {
} }
} }
class Tuple<T1, T2> { class Tuple<T1, T2> {
T1 item1; T1 item1;
T2 item2; T2 item2;
@ -49,8 +48,6 @@ class Tuple<T1, T2> {
} }
class Util { class Util {
static Uint8List hexToBytes(String hex) { static Uint8List hexToBytes(String hex) {
var result = new Uint8List(hex.length ~/ 2); var result = new Uint8List(hex.length ~/ 2);
for (var i = 0; i < hex.length; i += 2) { for (var i = 0; i < hex.length; i += 2) {
@ -65,7 +62,6 @@ class Util {
// //
} }
static int randPosition(Uint8List seed, int numPositions, int counter) { static int randPosition(Uint8List seed, int numPositions, int counter) {
// counter to bytes // counter to bytes
var counterBytes = Uint8List(4); var counterBytes = Uint8List(4);
@ -77,7 +73,8 @@ class Util {
// take the first 8 bytes // take the first 8 bytes
var first8Bytes = digest.bytes.take(8).toList(); var first8Bytes = digest.bytes.take(8).toList();
var int64 = ByteData.sublistView(Uint8List.fromList(first8Bytes)).getUint64(0, Endian.big); var int64 = ByteData.sublistView(Uint8List.fromList(first8Bytes))
.getUint64(0, Endian.big);
// perform the modulo operation // perform the modulo operation
return ((int64 * numPositions) >> 64).toInt(); return ((int64 * numPositions) >> 64).toInt();
@ -93,7 +90,6 @@ class Util {
return 500; return 500;
} }
static Address getAddressFromOutputScript(Uint8List scriptpubkey) { static Address getAddressFromOutputScript(Uint8List scriptpubkey) {
// Dummy implementation... // Dummy implementation...
@ -102,19 +98,18 @@ class Util {
return Address.fromString('dummy_address'); return Address.fromString('dummy_address');
} }
static bool schnorrVerify(
static bool schnorrVerify(ECPoint pubkey, List<int> signature, Uint8List messageHash) { ECPoint pubkey, List<int> signature, Uint8List messageHash) {
// Implementation needed: actual Schnorr signature verification // Implementation needed: actual Schnorr signature verification
return true; return true;
} }
static String formatSatoshis(sats, {int numZeros = 8}) { static String formatSatoshis(sats, {int numZeros = 8}) {
// To implement // To implement
return ""; return "";
} }
static void updateWalletLabel(String txid, String label) {
static void updateWalletLabel(String txid, String label) {
// Call the wallet layer. // Call the wallet layer.
} }
@ -132,12 +127,13 @@ static List<List<T>> zip<T>(List<T> list1, List<T> list2) {
return List<List<T>>.generate(length, (i) => [list1[i], list2[i]]); return List<List<T>>.generate(length, (i) => [list1[i], list2[i]]);
} }
static List<int> calcInitialHash(int tier, Uint8List covertDomainB,
static List<int> calcInitialHash(int tier, Uint8List covertDomainB, int covertPort, bool covertSsl, double beginTime) { int covertPort, bool covertSsl, double beginTime) {
// Converting int to bytes in BigEndian order // Converting int to bytes in BigEndian order
var tierBytes = ByteData(8)..setInt64(0, tier, Endian.big); var tierBytes = ByteData(8)..setInt64(0, tier, Endian.big);
var covertPortBytes = ByteData(4)..setInt32(0, covertPort, Endian.big); var covertPortBytes = ByteData(4)..setInt32(0, covertPort, Endian.big);
var beginTimeBytes = ByteData(8)..setInt64(0, beginTime.toInt(), Endian.big); var beginTimeBytes = ByteData(8)
..setInt64(0, beginTime.toInt(), Endian.big);
// Define constants // Define constants
const version = Protocol.VERSION; const version = Protocol.VERSION;
@ -159,7 +155,12 @@ static List<List<T>> zip<T>(List<T> list1, List<T> list2) {
return digest.bytes; return digest.bytes;
} }
static List<int> calcRoundHash(List<int> lastHash, List<int> roundPubkey, int roundTime, List<List<int>> allCommitments, List<List<int>> allComponents) { static List<int> calcRoundHash(
List<int> lastHash,
List<int> roundPubkey,
int roundTime,
List<List<int>> allCommitments,
List<List<int>> allComponents) {
return listHash([ return listHash([
utf8.encode('Cash Fusion Round'), utf8.encode('Cash Fusion Round'),
lastHash, lastHash,
@ -179,12 +180,11 @@ static List<int> calcRoundHash(List<int> lastHash, List<int> roundPubkey, int ro
bytes.addAll(x); bytes.addAll(x);
} }
return crypto.sha256.convert(bytes).bytes; return crypto.sha256.convert(bytes).bytes;
} }
static Uint8List get_current_genesis_hash() { static Uint8List get_current_genesis_hash() {
var GENESIS = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"; var GENESIS =
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f";
var _lastGenesisHash = hexToBytes(GENESIS).reversed.toList(); var _lastGenesisHash = hexToBytes(GENESIS).reversed.toList();
return Uint8List.fromList(_lastGenesisHash); return Uint8List.fromList(_lastGenesisHash);
} }
@ -194,7 +194,6 @@ static List<int> calcRoundHash(List<int> lastHash, List<int> roundPubkey, int ro
return []; return [];
} }
static List<Address> reserve_change_addresses(int number_addresses) { static List<Address> reserve_change_addresses(int number_addresses) {
//implement later based on wallet. //implement later based on wallet.
return []; return [];
@ -206,7 +205,8 @@ static List<int> calcRoundHash(List<int> lastHash, List<int> roundPubkey, int ro
} }
static Uint8List bigIntToBytes(BigInt bigInt) { static Uint8List bigIntToBytes(BigInt bigInt) {
return Uint8List.fromList(bigInt.toRadixString(16).padLeft(32, '0').codeUnits); return Uint8List.fromList(
bigInt.toRadixString(16).padLeft(32, '0').codeUnits);
} }
static Tuple<Uint8List, Uint8List> genKeypair() { static Tuple<Uint8List, Uint8List> genKeypair() {
@ -224,8 +224,6 @@ static Tuple<Uint8List, Uint8List> genKeypair() {
return Tuple(privKey, pubKey); return Tuple(privKey, pubKey);
} }
// Generates a cryptographically secure private key // Generates a cryptographically secure private key
static BigInt _generatePrivateKey(int bitLength) { static BigInt _generatePrivateKey(int bitLength) {
final random = Random.secure(); final random = Random.secure();
@ -236,7 +234,9 @@ static Tuple<Uint8List, Uint8List> genKeypair() {
List<int> rnd = List<int>.generate(bytes, (_) => random.nextInt(256)); List<int> rnd = List<int>.generate(bytes, (_) => random.nextInt(256));
var rndBit = random.nextInt(1 << remBit); var rndBit = random.nextInt(1 << remBit);
rnd.add(rndBit); rnd.add(rndBit);
var privateKey = BigInt.parse(rnd.map((x) => x.toRadixString(16).padLeft(2, '0')).join(), radix: 16); var privateKey = BigInt.parse(
rnd.map((x) => x.toRadixString(16).padLeft(2, '0')).join(),
radix: 16);
return privateKey; return privateKey;
} }
@ -251,15 +251,16 @@ static Tuple<Uint8List, Uint8List> genKeypair() {
return BigInt.parse(hexString, radix: 16); return BigInt.parse(hexString, radix: 16);
} }
static Uint8List sha256(Uint8List bytes) { static Uint8List sha256(Uint8List bytes) {
crypto.Digest digest = crypto.sha256.convert(bytes); crypto.Digest digest = crypto.sha256.convert(bytes);
return Uint8List.fromList(digest.bytes); return Uint8List.fromList(digest.bytes);
} }
static Uint8List tokenBytes([int nbytes = 32]) { static Uint8List tokenBytes([int nbytes = 32]) {
final Random _random = Random.secure(); final Random _random = Random.secure();
return Uint8List.fromList(List<int>.generate(nbytes, (i) => _random.nextInt(256))); return Uint8List.fromList(
List<int>.generate(nbytes, (i) => _random.nextInt(256)));
} }
static int componentFee(int size, int feerate) { static int componentFee(int size, int feerate) {
@ -269,8 +270,8 @@ static Tuple<Uint8List, Uint8List> genKeypair() {
return ((size * feerate) + 999) ~/ 1000; return ((size * feerate) + 999) ~/ 1000;
} }
static ECPoint ser_to_point(
static ECPoint ser_to_point(Uint8List serializedPoint, ECDomainParameters params) { Uint8List serializedPoint, ECDomainParameters params) {
var point = params.curve.decodePoint(serializedPoint); var point = params.curve.decodePoint(serializedPoint);
if (point == null) { if (point == null) {
throw FormatException('Point decoding failed'); throw FormatException('Point decoding failed');
@ -282,7 +283,6 @@ static Tuple<Uint8List, Uint8List> genKeypair() {
return point.getEncoded(compress); return point.getEncoded(compress);
} }
static BigInt secureRandomBigInt(int bitLength) { static BigInt secureRandomBigInt(int bitLength) {
final random = Random.secure(); final random = Random.secure();
final bytes = (bitLength + 7) ~/ 8; // ceil division final bytes = (bitLength + 7) ~/ 8; // ceil division
@ -292,9 +292,12 @@ static Tuple<Uint8List, Uint8List> genKeypair() {
randomBytes[i] = random.nextInt(256); randomBytes[i] = random.nextInt(256);
} }
BigInt randomNumber = BigInt.parse(randomBytes.map((e) => e.toRadixString(16).padLeft(2, '0')).join(), radix: 16); BigInt randomNumber = BigInt.parse(
randomBytes.map((e) => e.toRadixString(16).padLeft(2, '0')).join(),
radix: 16);
return randomNumber; return randomNumber;
} }
static ECPoint combinePubKeys(List<ECPoint> pubKeys) { static ECPoint combinePubKeys(List<ECPoint> pubKeys) {
if (pubKeys.isEmpty) throw ArgumentError('pubKeys cannot be empty'); if (pubKeys.isEmpty) throw ArgumentError('pubKeys cannot be empty');
@ -323,8 +326,4 @@ static Tuple<Uint8List, Uint8List> genKeypair() {
// Check if the point is on the curve // Check if the point is on the curve
return left == right; return left == right;
} }
} // END OF CLASS } // END OF CLASS

View file

@ -1,14 +1,12 @@
import 'package:protobuf/protobuf.dart'; import 'dart:typed_data';
import 'package:pointycastle/export.dart';
import 'encrypt.dart' as Encrypt;
import 'fusion.dart';
import 'fusion.pb.dart' as pb; import 'fusion.pb.dart' as pb;
import 'pedersen.dart'; import 'pedersen.dart';
import 'util.dart'; import 'util.dart';
import 'encrypt.dart' as Encrypt;
import 'protocol.dart';
import 'fusion.dart';
import 'dart:typed_data';
import 'package:pointycastle/export.dart';
import 'package:convert/convert.dart';
import 'pedersen.dart';
class ValidationError implements Exception { class ValidationError implements Exception {
final String message; final String message;
@ -31,13 +29,12 @@ int componentContrib(pb.Component component, int feerate) {
} }
} }
void check(bool condition, String failMessage) { void check(bool condition, String failMessage) {
if (!condition) { if (!condition) {
throw ValidationError(failMessage); throw ValidationError(failMessage);
} }
} }
dynamic protoStrictParse(dynamic msg, List<int> blob) { dynamic protoStrictParse(dynamic msg, List<int> blob) {
try { try {
if (msg.mergeFromBuffer(blob) != blob.length) { if (msg.mergeFromBuffer(blob) != blob.length) {
@ -63,21 +60,22 @@ dynamic protoStrictParse(dynamic msg, List<int> blob) {
return msg; return msg;
} }
List<pb.InitialCommitment> checkPlayerCommit(pb.PlayerCommit msg,
int minExcessFee, int maxExcessFee, int numComponents) {
check(msg.initialCommitments.length == numComponents,
"wrong number of component commitments");
check(msg.blindSigRequests.length == numComponents,
"wrong number of blind sig requests");
List<pb.InitialCommitment> checkPlayerCommit( check(
pb.PlayerCommit msg, minExcessFee <= msg.excessFee.toInt() &&
int minExcessFee, msg.excessFee.toInt() <= maxExcessFee,
int maxExcessFee, "bad excess fee");
int numComponents
) {
check(msg.initialCommitments.length == numComponents, "wrong number of component commitments");
check(msg.blindSigRequests.length == numComponents, "wrong number of blind sig requests");
check(minExcessFee <= msg.excessFee.toInt() && msg.excessFee.toInt() <= maxExcessFee, "bad excess fee");
check(msg.randomNumberCommitment.length == 32, "bad random commit"); check(msg.randomNumberCommitment.length == 32, "bad random commit");
check(msg.pedersenTotalNonce.length == 32, "bad nonce"); check(msg.pedersenTotalNonce.length == 32, "bad nonce");
check(msg.blindSigRequests.every((r) => r.length == 32), "bad blind sig request"); check(msg.blindSigRequests.every((r) => r.length == 32),
"bad blind sig request");
List<pb.InitialCommitment> commitMessages = []; List<pb.InitialCommitment> commitMessages = [];
for (var cblob in msg.initialCommitments) { for (var cblob in msg.initialCommitments) {
@ -85,11 +83,15 @@ List<pb.InitialCommitment> checkPlayerCommit(
check(cmsg.saltedComponentHash.length == 32, "bad salted hash"); check(cmsg.saltedComponentHash.length == 32, "bad salted hash");
var P = cmsg.amountCommitment; var P = cmsg.amountCommitment;
check(P.length == 65 && P[0] == 4, "bad commitment point"); check(P.length == 65 && P[0] == 4, "bad commitment point");
check(cmsg.communicationKey.length == 33 && (cmsg.communicationKey[0] == 2 || cmsg.communicationKey[0] == 3), "bad communication key"); check(
cmsg.communicationKey.length == 33 &&
(cmsg.communicationKey[0] == 2 || cmsg.communicationKey[0] == 3),
"bad communication key");
commitMessages.add(cmsg); commitMessages.add(cmsg);
} }
Uint8List HBytes = Uint8List.fromList([0x02] + 'CashFusion gives us fungibility.'.codeUnits); Uint8List HBytes =
Uint8List.fromList([0x02] + 'CashFusion gives us fungibility.'.codeUnits);
ECDomainParameters params = ECDomainParameters('secp256k1'); ECDomainParameters params = ECDomainParameters('secp256k1');
ECPoint? HMaybe = params.curve.decodePoint(HBytes); ECPoint? HMaybe = params.curve.decodePoint(HBytes);
if (HMaybe == null) { if (HMaybe == null) {
@ -102,26 +104,28 @@ List<pb.InitialCommitment> checkPlayerCommit(
var pointsum; var pointsum;
// Verify pedersen commitment // Verify pedersen commitment
try { try {
pointsum = Commitment.add_points(commitMessages.map((m) => Uint8List.fromList(m.amountCommitment)).toList()); pointsum = Commitment.add_points(commitMessages
claimedCommit = setup.commit(BigInt.from(msg.excessFee.toInt()), nonce: Util.bytesToBigInt(Uint8List.fromList(msg.pedersenTotalNonce))); .map((m) => Uint8List.fromList(m.amountCommitment))
.toList());
claimedCommit = setup.commit(BigInt.from(msg.excessFee.toInt()),
nonce: Util.bytesToBigInt(Uint8List.fromList(msg.pedersenTotalNonce)));
check(pointsum == claimedCommit.PUncompressed, "pedersen commitment mismatch"); check(pointsum == claimedCommit.PUncompressed,
"pedersen commitment mismatch");
} catch (e) { } catch (e) {
throw ValidationError("pedersen commitment verification error"); throw ValidationError("pedersen commitment verification error");
} }
check(pointsum == claimedCommit.PUncompressed, "pedersen commitment mismatch"); check(
pointsum == claimedCommit.PUncompressed, "pedersen commitment mismatch");
return commitMessages; return commitMessages;
} }
Tuple<String, int> checkCovertComponent( Tuple<String, int> checkCovertComponent(
pb.CovertComponent msg, ECPoint roundPubkey, int componentFeerate) { pb.CovertComponent msg, ECPoint roundPubkey, int componentFeerate) {
var messageHash = Util.sha256(Uint8List.fromList(msg.component)); var messageHash = Util.sha256(Uint8List.fromList(msg.component));
check(msg.signature.length == 64, "bad message signature"); check(msg.signature.length == 64, "bad message signature");
check( check(Util.schnorrVerify(roundPubkey, msg.signature, messageHash),
Util.schnorrVerify(
roundPubkey, msg.signature, messageHash),
"bad message signature"); "bad message signature");
var cmsg = protoStrictParse(pb.Component(), msg.component); var cmsg = protoStrictParse(pb.Component(), msg.component);
@ -133,7 +137,8 @@ Tuple<String, int> checkCovertComponent(
var inp = cmsg.input; var inp = cmsg.input;
check(inp.prevTxid.length == 32, "bad txid"); check(inp.prevTxid.length == 32, "bad txid");
check( check(
(inp.pubkey.length == 33 && (inp.pubkey[0] == 2 || inp.pubkey[0] == 3)) || (inp.pubkey.length == 33 &&
(inp.pubkey[0] == 2 || inp.pubkey[0] == 3)) ||
(inp.pubkey.length == 65 && inp.pubkey[0] == 4), (inp.pubkey.length == 65 && inp.pubkey[0] == 4),
"bad pubkey"); "bad pubkey");
sortKey = 'i' + sortKey = 'i' +
@ -146,8 +151,7 @@ Tuple<String, int> checkCovertComponent(
// Basically just checks if its ok address. should throw error if not. // Basically just checks if its ok address. should throw error if not.
addr = Util.getAddressFromOutputScript(out.scriptpubkey); addr = Util.getAddressFromOutputScript(out.scriptpubkey);
check( check(out.amount >= Util.dustLimit(out.scriptpubkey.length), "dust output");
out.amount >= Util.dustLimit(out.scriptpubkey.length), "dust output");
sortKey = 'o' + sortKey = 'o' +
out.amount.toString() + out.amount.toString() +
String.fromCharCodes(out.scriptpubkey) + String.fromCharCodes(out.scriptpubkey) +
@ -168,8 +172,8 @@ pb.InputComponent? validateProofInternal(
List<int> badComponents, List<int> badComponents,
int componentFeerate, int componentFeerate,
) { ) {
Uint8List HBytes =
Uint8List HBytes = Uint8List.fromList([0x02] + 'CashFusion gives us fungibility.'.codeUnits); Uint8List.fromList([0x02] + 'CashFusion gives us fungibility.'.codeUnits);
ECDomainParameters params = ECDomainParameters('secp256k1'); ECDomainParameters params = ECDomainParameters('secp256k1');
ECPoint? HMaybe = params.curve.decodePoint(HBytes); ECPoint? HMaybe = params.curve.decodePoint(HBytes);
if (HMaybe == null) { if (HMaybe == null) {
@ -246,8 +250,10 @@ Future<dynamic> validateBlame(
if (decrypter == pb.Blames_BlameProof_Decrypter.privkey) { if (decrypter == pb.Blames_BlameProof_Decrypter.privkey) {
var privkey = Uint8List.fromList(blame.privkey); var privkey = Uint8List.fromList(blame.privkey);
check(privkey.length == 32, 'bad blame privkey'); check(privkey.length == 32, 'bad blame privkey');
var privkeyHexStr = Util.bytesToHex(privkey); // Convert bytes to hex string. var privkeyHexStr =
var privkeyBigInt = BigInt.parse(privkeyHexStr, radix: 16); // Convert hex string to BigInt. Util.bytesToHex(privkey); // Convert bytes to hex string.
var privkeyBigInt =
BigInt.parse(privkeyHexStr, radix: 16); // Convert hex string to BigInt.
var privateKey = ECPrivateKey(privkeyBigInt, params); // Create ECPrivateKey var privateKey = ECPrivateKey(privkeyBigInt, params); // Create ECPrivateKey
var pubkeys = Util.pubkeysFromPrivkey(privkeyHexStr); var pubkeys = Util.pubkeysFromPrivkey(privkeyHexStr);
check(destCommit.communicationKey == pubkeys[1], 'bad blame privkey'); check(destCommit.communicationKey == pubkeys[1], 'bad blame privkey');
@ -282,11 +288,13 @@ Future<dynamic> validateBlame(
} }
if (!blame.needLookupBlockchain) { if (!blame.needLookupBlockchain) {
throw ValidationError('blame indicated internal inconsistency, none found!'); throw ValidationError(
'blame indicated internal inconsistency, none found!');
} }
if (inpComp == null) { if (inpComp == null) {
throw ValidationError('blame indicated blockchain error on a non-input component'); throw ValidationError(
'blame indicated blockchain error on a non-input component');
} }
return inpComp; return inpComp;