mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-01-22 18:44:31 +00:00
fix formatting
This commit is contained in:
parent
9b4c1abf35
commit
aa2ab89f73
11 changed files with 785 additions and 923 deletions
|
@ -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:io';
|
||||
|
||||
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();
|
||||
|
||||
|
@ -43,13 +43,11 @@ Map<Type, PbCreateFunc> pbClassCreators = {
|
|||
ServerMessage: () => ServerMessage(),
|
||||
CovertMessage: () => CovertMessage(),
|
||||
CovertResponse: () => CovertResponse()
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
Future<void> sendPb(Connection connection, Type pbClass, GeneratedMessage subMsg, {Duration? timeout}) async {
|
||||
|
||||
Future<void> sendPb(
|
||||
Connection connection, Type pbClass, GeneratedMessage subMsg,
|
||||
{Duration? timeout}) async {
|
||||
// Construct the outer message with the submessage.
|
||||
|
||||
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, {Duration? timeout}) async {
|
||||
|
||||
|
||||
Future<void> sendPb2(SocketWrapper socketwrapper, Connection connection,
|
||||
Type pbClass, GeneratedMessage subMsg,
|
||||
{Duration? timeout}) async {
|
||||
// Construct the outer message with the submessage.
|
||||
|
||||
if (pbClassCreators[pbClass] == null) {
|
||||
|
@ -85,7 +81,8 @@ Future<void> sendPb2(SocketWrapper socketwrapper, Connection connection, Type pb
|
|||
var pbMessage = pbClassCreators[pbClass]!()..mergeFromMessage(subMsg);
|
||||
final msgBytes = pbMessage.writeToBuffer();
|
||||
try {
|
||||
await connection.sendMessageWithSocketWrapper(socketwrapper, msgBytes, timeout: timeout);
|
||||
await connection.sendMessageWithSocketWrapper(socketwrapper, msgBytes,
|
||||
timeout: timeout);
|
||||
} on SocketException {
|
||||
throw FusionError('Connection closed by remote');
|
||||
} 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 {
|
||||
|
||||
List<int> blob = await connection.recv_message2(socketwrapper, timeout: timeout);
|
||||
List<int> blob =
|
||||
await connection.recv_message2(socketwrapper, timeout: timeout);
|
||||
|
||||
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) {
|
||||
// Handle different exceptions here
|
||||
if (e is SocketException) {
|
||||
|
@ -134,15 +130,16 @@ Future<Tuple<GeneratedMessage, String>> recvPb2(SocketWrapper socketwrapper, Con
|
|||
} else if (e is OSError && e.errorCode == 9) {
|
||||
throw FusionError('Connection closed by local');
|
||||
} else {
|
||||
throw FusionError('Communications error: ${e.runtimeType}: ${e.toString()}');
|
||||
throw FusionError(
|
||||
'Communications error: ${e.runtimeType}: ${e.toString()}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Future<Tuple<GeneratedMessage, String>> recvPb(Connection connection, Type pbClass, List<String> expectedFieldNames, {Duration? timeout}) async {
|
||||
Future<Tuple<GeneratedMessage, String>> recvPb(
|
||||
Connection connection, Type pbClass, List<String> expectedFieldNames,
|
||||
{Duration? timeout}) async {
|
||||
try {
|
||||
|
||||
List<int> blob = await connection.recv_message(timeout: timeout);
|
||||
|
||||
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) {
|
||||
// Handle different exceptions here
|
||||
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) {
|
||||
throw FusionError('Connection closed by local');
|
||||
} else {
|
||||
throw FusionError('Communications error: ${e.runtimeType}: ${e.toString()}');
|
||||
throw FusionError(
|
||||
'Communications error: ${e.runtimeType}: ${e.toString()}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import 'socketwrapper.dart';
|
||||
import 'dart:io';
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'package:convert/convert.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
|
||||
|
@ -22,14 +22,14 @@ class BadFrameError extends Error {
|
|||
String toString() => message;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Future<Connection> openConnection(String host, int port,
|
||||
{double connTimeout = 5.0,
|
||||
Future<Connection> openConnection(
|
||||
String host,
|
||||
int port, {
|
||||
double connTimeout = 5.0,
|
||||
double defaultTimeout = 5.0,
|
||||
bool ssl = false,
|
||||
dynamic socksOpts}) async {
|
||||
|
||||
dynamic socksOpts,
|
||||
}) async {
|
||||
try {
|
||||
// Dart's Socket class handles connection timeout internally.
|
||||
Socket socket = await Socket.connect(host, port);
|
||||
|
@ -38,25 +38,27 @@ Future<Connection> openConnection(String host, int port,
|
|||
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) {
|
||||
throw 'Failed to open connection: $e';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Connection {
|
||||
Duration timeout = Duration(seconds: 1);
|
||||
Socket? socket;
|
||||
|
||||
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);
|
||||
Connection({required this.socket, 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;
|
||||
print("DEBUG sendmessage msg sending ");
|
||||
print(msg);
|
||||
|
@ -76,10 +78,7 @@ class Connection {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Future<void> sendMessage(List<int> msg, {Duration? timeout}) async {
|
||||
|
||||
timeout ??= this.timeout;
|
||||
|
||||
final lengthBytes = Uint8List(4);
|
||||
|
@ -93,7 +92,6 @@ class Connection {
|
|||
..addAll(msg);
|
||||
|
||||
try {
|
||||
|
||||
StreamController<List<int>> controller = StreamController();
|
||||
|
||||
controller.stream.listen((data) {
|
||||
|
@ -109,19 +107,18 @@ class Connection {
|
|||
} finally {
|
||||
controller.close();
|
||||
}
|
||||
|
||||
} on SocketException catch (e) {
|
||||
throw TimeoutException('Socket write timed out', timeout);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void 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;
|
||||
|
||||
await for (var data in socketwrapper.socket!.cast<List<int>>()) {
|
||||
|
@ -139,7 +136,8 @@ class Connection {
|
|||
}
|
||||
|
||||
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) {
|
||||
print("DEBUG fillBuf2 3 - breaking loop, recvBuf is big enough");
|
||||
|
@ -163,8 +161,6 @@ class Connection {
|
|||
});
|
||||
return recvBuf;
|
||||
|
||||
|
||||
|
||||
StreamSubscription<List<int>>? subscription; // Declaration moved here
|
||||
subscription = socket!.listen(
|
||||
(List<int> data) {
|
||||
|
@ -180,7 +176,8 @@ class Connection {
|
|||
onDone: () {
|
||||
print("DEBUG ON DONE");
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
Future<List<int>> recv_message2(SocketWrapper socketwrapper, {Duration? timeout}) async {
|
||||
Future<List<int>> recv_message2(SocketWrapper socketwrapper,
|
||||
{Duration? timeout}) async {
|
||||
if (timeout == null) {
|
||||
timeout = this.timeout;
|
||||
}
|
||||
|
@ -237,14 +234,17 @@ class Connection {
|
|||
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);
|
||||
|
||||
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(recvBuf.length);
|
||||
|
@ -255,13 +255,15 @@ class Connection {
|
|||
if (recvBuf.length == bytesRead && bytesRead == 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");
|
||||
|
||||
return message;
|
||||
} else {
|
||||
// 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 [];
|
||||
}
|
||||
|
||||
|
||||
Future<List<int>> recv_message({Duration? timeout}) async {
|
||||
|
||||
|
||||
// DEPRECATED
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
} // END OF CLASS
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
import 'dart:math';
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
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: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 TIMEOUT_INACTIVE_CONNECTION = 120;
|
||||
|
||||
|
||||
class FusionError implements Exception {
|
||||
String cause;
|
||||
FusionError(this.cause);
|
||||
|
@ -21,15 +22,14 @@ class Unrecoverable extends FusionError {
|
|||
Unrecoverable(String cause) : super(cause);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Future<bool> isTorPort(String host, int port) async {
|
||||
if (port < 0 || port > 65535) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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");
|
||||
List<int> data = await sock.first;
|
||||
sock.destroy();
|
||||
|
@ -43,8 +43,6 @@ Future<bool> isTorPort(String host, int port) async {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
class TorLimiter {
|
||||
Queue<DateTime> deque = Queue<DateTime>();
|
||||
int lifetime;
|
||||
|
@ -65,7 +63,6 @@ class TorLimiter {
|
|||
|
||||
TorLimiter limiter = TorLimiter(TOR_COOLDOWN_TIME);
|
||||
|
||||
|
||||
double randTrap(Random rng) {
|
||||
final sixth = 1.0 / 6;
|
||||
final f = rng.nextDouble();
|
||||
|
@ -80,8 +77,6 @@ double randTrap(Random rng) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class CovertConnection {
|
||||
Connection? connection; // replace dynamic with the type of your connection
|
||||
int? slotNum;
|
||||
|
@ -124,7 +119,8 @@ class CovertSlot {
|
|||
int submitTimeout;
|
||||
pb.GeneratedMessage? subMsg; // The work to be 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;
|
||||
DateTime? t_submit;
|
||||
|
||||
|
@ -138,25 +134,21 @@ class CovertSlot {
|
|||
throw Unrecoverable('connection is null');
|
||||
}
|
||||
|
||||
await sendPb(connection, CovertMessage, subMsg!, timeout: Duration(seconds: submitTimeout));
|
||||
var result = await recvPb(connection, CovertResponse, ['ok', 'error'], timeout: Duration(seconds: submitTimeout));
|
||||
await sendPb(connection, CovertMessage, subMsg!,
|
||||
timeout: Duration(seconds: submitTimeout));
|
||||
var result = await recvPb(connection, CovertResponse, ['ok', 'error'],
|
||||
timeout: Duration(seconds: submitTimeout));
|
||||
|
||||
if (result.item1 == 'error') {
|
||||
throw Unrecoverable('error from server: ${result.item2}');
|
||||
}
|
||||
done = true;
|
||||
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 {
|
||||
// Declare properties here
|
||||
}
|
||||
|
@ -194,12 +186,11 @@ class CovertSubmitter extends PrintError {
|
|||
this.num_slots,
|
||||
double randSpan, // 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...
|
||||
}
|
||||
|
||||
|
||||
void wakeAll() {
|
||||
for (var s in slots) {
|
||||
if (s.covConn != null) {
|
||||
|
@ -218,7 +209,6 @@ class CovertSubmitter extends PrintError {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void stop([Exception? exception]) {
|
||||
if (this.stopping) {
|
||||
// already requested!
|
||||
|
@ -226,13 +216,16 @@ class CovertSubmitter extends PrintError {
|
|||
}
|
||||
this.failureException = exception?.toString();
|
||||
this.stopping = true;
|
||||
var timeRemaining = this.stopTStart?.difference(DateTime.now()).inSeconds ?? 0;
|
||||
print("Stopping; connections will close in approximately $timeRemaining seconds");
|
||||
var timeRemaining =
|
||||
this.stopTStart?.difference(DateTime.now()).inSeconds ?? 0;
|
||||
print(
|
||||
"Stopping; connections will close in approximately $timeRemaining seconds");
|
||||
this.wakeAll();
|
||||
}
|
||||
|
||||
// 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>[];
|
||||
|
||||
for (var sNum = 0; sNum < this.slots.length; sNum++) {
|
||||
|
@ -244,8 +237,6 @@ class CovertSubmitter extends PrintError {
|
|||
if (myCovConn != null) {
|
||||
newConns.add(myCovConn);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,16 +249,15 @@ class CovertSubmitter extends PrintError {
|
|||
for (var covConn in newConns) {
|
||||
covConn.connNumber = 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);
|
||||
|
||||
runConnection(covConn, connTime.millisecondsSinceEpoch, randDelay, connectTimeout);
|
||||
|
||||
runConnection(
|
||||
covConn, connTime.millisecondsSinceEpoch, randDelay, connectTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void scheduleSubmit(int slotNum, DateTime tStart, dynamic subMsg) {
|
||||
var slot = slots[slotNum];
|
||||
|
||||
|
@ -282,7 +272,6 @@ class CovertSubmitter extends PrintError {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void scheduleSubmissions(DateTime tStart, List<dynamic> slotMessages) {
|
||||
// Convert to list (Dart does not have tuples)
|
||||
slotMessages = List.from(slotMessages);
|
||||
|
@ -317,11 +306,11 @@ class CovertSubmitter extends PrintError {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
Future runConnection(
|
||||
CovertConnection covConn, int connTime, double randDelay, int connectTimeout) async {
|
||||
Future runConnection(CovertConnection covConn, int connTime, double randDelay,
|
||||
int connectTimeout) async {
|
||||
// Main loop for connection thread
|
||||
DateTime connDateTime = DateTime.fromMillisecondsSinceEpoch(connTime * 1000);
|
||||
DateTime connDateTime =
|
||||
DateTime.fromMillisecondsSinceEpoch(connTime * 1000);
|
||||
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 (this.stopping) {
|
||||
|
@ -350,11 +339,11 @@ class CovertSubmitter extends PrintError {
|
|||
try {
|
||||
final connection = await openConnection(
|
||||
this.destAddr!, this.destPort!,
|
||||
connTimeout: connectTimeout.toDouble(), ssl: this.ssl, socksOpts: proxyOpts);
|
||||
connTimeout: connectTimeout.toDouble(),
|
||||
ssl: this.ssl,
|
||||
socksOpts: proxyOpts);
|
||||
covConn.connection = connection;
|
||||
}
|
||||
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
this.countFailed++;
|
||||
|
||||
final tEnd = DateTime.now().millisecondsSinceEpoch;
|
||||
|
@ -364,7 +353,6 @@ class CovertSubmitter extends PrintError {
|
|||
rethrow;
|
||||
}
|
||||
|
||||
|
||||
this.countEstablished++;
|
||||
|
||||
final tEnd = DateTime.now().millisecondsSinceEpoch;
|
||||
|
@ -372,7 +360,6 @@ class CovertSubmitter extends PrintError {
|
|||
print(
|
||||
'[${covConn.connNumber}] connection established after ${((tEnd - tBegin) / 1000).toStringAsFixed(3)}s');
|
||||
|
||||
|
||||
covConn.delay = (randTrap(this.rng) ?? 0) * (this.randSpan ?? 0);
|
||||
|
||||
var lastActionTime = DateTime.now().millisecondsSinceEpoch;
|
||||
|
@ -396,7 +383,8 @@ class CovertSubmitter extends PrintError {
|
|||
}
|
||||
// Last preference: wait doing nothing
|
||||
if (nextTime == null) {
|
||||
nextTime = DateTime.now().add(Duration(seconds: TIMEOUT_INACTIVE_CONNECTION));
|
||||
nextTime = DateTime.now()
|
||||
.add(Duration(seconds: TIMEOUT_INACTIVE_CONNECTION));
|
||||
action = covConn.inactive;
|
||||
}
|
||||
|
||||
|
@ -423,7 +411,9 @@ class CovertSubmitter extends PrintError {
|
|||
|
||||
// STATE 3 - stopping
|
||||
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))) {
|
||||
break;
|
||||
|
@ -442,7 +432,8 @@ class CovertSubmitter extends PrintError {
|
|||
// Found a spare.
|
||||
this.slots[slotNum].covConn = spare;
|
||||
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;
|
||||
} catch (e) {
|
||||
|
@ -453,7 +444,6 @@ class CovertSubmitter extends PrintError {
|
|||
} else {
|
||||
// Handle the case where the exception is not an instance of Exception
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
|
@ -462,7 +452,6 @@ class CovertSubmitter extends PrintError {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void checkOk() {
|
||||
// Implement checkOk logic here
|
||||
var e = this.failure_exception;
|
||||
|
@ -474,26 +463,21 @@ class CovertSubmitter extends PrintError {
|
|||
void checkConnected() {
|
||||
// Implement checkConnected logic here
|
||||
this.checkOk();
|
||||
var numMissing = this.slots
|
||||
.where((s) => s.covConn?.connection == null)
|
||||
.length;
|
||||
var numMissing =
|
||||
this.slots.where((s) => s.covConn?.connection == null).length;
|
||||
if (numMissing > 0) {
|
||||
throw FusionError(
|
||||
"Covert connections were too slow ($numMissing incomplete out of ${this
|
||||
.slots.length}).");
|
||||
"Covert connections were too slow ($numMissing incomplete out of ${this.slots.length}).");
|
||||
}
|
||||
}
|
||||
|
||||
void checkDone() {
|
||||
// Implement checkDone logic here
|
||||
this.checkOk();
|
||||
var numMissing = this.slots
|
||||
.where((s) => !s.done)
|
||||
.length;
|
||||
var numMissing = this.slots.where((s) => !s.done).length;
|
||||
if (numMissing > 0) {
|
||||
throw FusionError(
|
||||
"Covert submissions were too slow ($numMissing incomplete out of ${this
|
||||
.slots.length}).");
|
||||
"Covert submissions were too slow ($numMissing incomplete out of ${this.slots.length}).");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +1,20 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
import 'package:pointycastle/pointycastle.dart' hide Mac;
|
||||
|
||||
import 'package:crypto/crypto.dart' as crypto;
|
||||
import 'package:cryptography/cryptography.dart';
|
||||
import 'package:pointycastle/pointycastle.dart' hide Mac;
|
||||
|
||||
import 'util.dart';
|
||||
|
||||
final ECDomainParameters params = ECDomainParameters('secp256k1');
|
||||
final BigInt order = params.n;
|
||||
|
||||
class EncryptionFailed 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;
|
||||
try {
|
||||
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;
|
||||
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)..setRange(4, 4 + message.length, message);
|
||||
var plaintext = Uint8List(4 + message.length)
|
||||
..buffer.asByteData().setUint32(0, message.length, Endian.big)
|
||||
..setRange(4, 4 + message.length, message);
|
||||
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) {
|
||||
throw ArgumentError('$padToLength not multiple of 16');
|
||||
}
|
||||
if (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);
|
||||
|
||||
|
@ -50,16 +58,20 @@ Future<Uint8List> encrypt(Uint8List message, ECPoint pubkey, {int? padToLength})
|
|||
|
||||
final cipher = AesCbc.with128bits(macAlgorithm: macAlgorithm);
|
||||
|
||||
|
||||
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;
|
||||
|
||||
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(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 {
|
||||
|
@ -75,7 +87,8 @@ Future<Uint8List> decryptWithSymmkey(Uint8List data, Uint8List key) async {
|
|||
final cipher = AesCbc.with128bits(macAlgorithm: Hmac.sha256());
|
||||
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);
|
||||
|
||||
if (plaintext.length < 4) {
|
||||
|
@ -86,15 +99,14 @@ Future<Uint8List> decryptWithSymmkey(Uint8List data, Uint8List key) async {
|
|||
ByteData byteData = ByteData.sublistView(uint8list);
|
||||
var msgLength = byteData.getUint32(0, Endian.big);
|
||||
|
||||
|
||||
if (msgLength + 4 > plaintext.length) {
|
||||
throw DecryptionFailed();
|
||||
}
|
||||
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) {
|
||||
throw DecryptionFailed();
|
||||
}
|
||||
|
@ -122,4 +134,3 @@ Future<Tuple<Uint8List, Uint8List>> decrypt(Uint8List data, ECPrivateKey privkey
|
|||
var decryptedData = await decryptWithSymmkey(data, Uint8List.fromList(key));
|
||||
return Tuple(decryptedData, Uint8List.fromList(key));
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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.
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import 'package:pointycastle/ecc/api.dart';
|
||||
import 'util.dart';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:pointycastle/ecc/api.dart';
|
||||
import 'package:stackwallet/services/cashfusion/util.dart';
|
||||
|
||||
ECDomainParameters getDefaultParams() {
|
||||
return ECDomainParameters("secp256k1");
|
||||
}
|
||||
|
@ -11,10 +11,10 @@ class NullPointError implements Exception {
|
|||
String errMsg() => 'NullPointError: Either Hpoint or HGpoint is null.';
|
||||
}
|
||||
|
||||
|
||||
class NonceRangeError implements Exception {
|
||||
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";
|
||||
}
|
||||
|
||||
|
@ -26,12 +26,12 @@ class ResultAtInfinity implements Exception {
|
|||
|
||||
class InsecureHPoint implements Exception {
|
||||
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";
|
||||
}
|
||||
|
||||
|
||||
|
||||
class PedersenSetup {
|
||||
late ECPoint _H;
|
||||
late ECPoint _HG;
|
||||
|
@ -53,7 +53,6 @@ class PedersenSetup {
|
|||
Commitment commit(BigInt amount, {BigInt? nonce, Uint8List? PUncompressed}) {
|
||||
return Commitment(this, amount, nonce: nonce, PUncompressed: PUncompressed);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Commitment {
|
||||
|
@ -62,8 +61,8 @@ class Commitment {
|
|||
late BigInt nonce;
|
||||
late Uint8List PUncompressed;
|
||||
|
||||
|
||||
Commitment(this.setup, BigInt amount, {BigInt? nonce, Uint8List? PUncompressed}) {
|
||||
Commitment(this.setup, BigInt amount,
|
||||
{BigInt? nonce, Uint8List? PUncompressed}) {
|
||||
this.nonce = nonce ?? Util.secureRandomBigInt(setup.params.n.bitLength);
|
||||
amountMod = amount % setup.params.n;
|
||||
|
||||
|
@ -84,13 +83,16 @@ class Commitment {
|
|||
ECPoint? HpointMultiplied = Hpoint * multiplier1;
|
||||
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) {
|
||||
throw ResultAtInfinity();
|
||||
}
|
||||
|
||||
this.PUncompressed = PUncompressed ?? Ppoint?.getEncoded(false) ?? Uint8List(0);
|
||||
this.PUncompressed =
|
||||
PUncompressed ?? Ppoint?.getEncoded(false) ?? Uint8List(0);
|
||||
}
|
||||
|
||||
void calcInitial(PedersenSetup setup, BigInt amount) {
|
||||
|
@ -114,7 +116,9 @@ class Commitment {
|
|||
ECPoint? HpointMultiplied = Hpoint * multiplier1;
|
||||
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) {
|
||||
throw ResultAtInfinity();
|
||||
|
@ -124,14 +128,17 @@ class Commitment {
|
|||
}
|
||||
|
||||
static Uint8List add_points(Iterable<Uint8List> pointsIterable) {
|
||||
ECDomainParameters params = getDefaultParams(); // Using helper function here
|
||||
var pointList = pointsIterable.map((pser) => Util.ser_to_point(pser, params)).toList();
|
||||
ECDomainParameters params =
|
||||
getDefaultParams(); // Using helper function here
|
||||
var pointList =
|
||||
pointsIterable.map((pser) => Util.ser_to_point(pser, params)).toList();
|
||||
|
||||
if (pointList.isEmpty) {
|
||||
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++) {
|
||||
pSum = (pSum + pointList[i])!;
|
||||
|
@ -144,7 +151,6 @@ class Commitment {
|
|||
return Util.point_to_ser(pSum, false);
|
||||
}
|
||||
|
||||
|
||||
Commitment addCommitments(Iterable<Commitment> commitmentIterable) {
|
||||
BigInt ktotal = 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
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
|
@ -182,6 +189,7 @@ class Commitment {
|
|||
} else {
|
||||
PUncompressed = null;
|
||||
}
|
||||
return Commitment(setup, atotal, nonce: ktotal, PUncompressed: PUncompressed);
|
||||
return Commitment(setup, atotal,
|
||||
nonce: ktotal, PUncompressed: PUncompressed);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
|
||||
|
||||
class Protocol {
|
||||
static const VERSION = 'alpha13';
|
||||
|
||||
static const FUSE_ID = 'FUZ\x00';
|
||||
|
||||
|
||||
// Safety limits to prevent loss of funds / limit fees:
|
||||
//(Note that if we enter multiply into the same fusion, our limits apply
|
||||
//separately for each "player".)
|
||||
|
@ -56,4 +53,3 @@ class Protocol {
|
|||
static const STANDARD_TIMEOUT = 3.0;
|
||||
static const BLAME_VERIFY_TIME = 5.0;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,16 +5,19 @@ class SocketWrapper {
|
|||
final String serverIP;
|
||||
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);
|
||||
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 {
|
||||
_socket = await Socket.connect(serverIP, serverPort);
|
||||
_receiveStream = _socket.asBroadcastStream(); // initialize the broadcast stream
|
||||
_receiveStream =
|
||||
_socket.asBroadcastStream(); // initialize the broadcast stream
|
||||
_socket.done.then((_) {
|
||||
print('......Socket has been closed');
|
||||
});
|
||||
|
@ -25,7 +28,8 @@ class SocketWrapper {
|
|||
|
||||
void status() {
|
||||
if (_socket != null) {
|
||||
print("Socket connected to ${_socket.remoteAddress.address}:${_socket.remotePort}");
|
||||
print(
|
||||
"Socket connected to ${_socket.remoteAddress.address}:${_socket.remotePort}");
|
||||
} else {
|
||||
print("Socket is not connected");
|
||||
}
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
|
||||
import 'package:stackwallet/services/cashfusion/fusion.dart';
|
||||
import 'package:pointycastle/ecc/api.dart';
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:crypto/crypto.dart' as crypto;
|
||||
import 'protocol.dart';
|
||||
import 'package:pointycastle/ecc/api.dart';
|
||||
|
||||
import 'fusion.pb.dart';
|
||||
import 'dart:convert';
|
||||
import 'protocol.dart';
|
||||
|
||||
class Address {
|
||||
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});
|
||||
|
||||
|
@ -32,7 +32,6 @@ class Address {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
class Tuple<T1, T2> {
|
||||
T1 item1;
|
||||
T2 item2;
|
||||
|
@ -49,8 +48,6 @@ class Tuple<T1, T2> {
|
|||
}
|
||||
|
||||
class Util {
|
||||
|
||||
|
||||
static Uint8List hexToBytes(String hex) {
|
||||
var result = new Uint8List(hex.length ~/ 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) {
|
||||
// counter to bytes
|
||||
var counterBytes = Uint8List(4);
|
||||
|
@ -77,7 +73,8 @@ class Util {
|
|||
|
||||
// take the first 8 bytes
|
||||
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
|
||||
return ((int64 * numPositions) >> 64).toInt();
|
||||
|
@ -93,7 +90,6 @@ class Util {
|
|||
return 500;
|
||||
}
|
||||
|
||||
|
||||
static Address getAddressFromOutputScript(Uint8List scriptpubkey) {
|
||||
// Dummy implementation...
|
||||
|
||||
|
@ -102,19 +98,18 @@ class Util {
|
|||
return Address.fromString('dummy_address');
|
||||
}
|
||||
|
||||
|
||||
static bool schnorrVerify(ECPoint pubkey, List<int> signature, Uint8List messageHash) {
|
||||
static bool schnorrVerify(
|
||||
ECPoint pubkey, List<int> signature, Uint8List messageHash) {
|
||||
// Implementation needed: actual Schnorr signature verification
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static String formatSatoshis(sats, {int numZeros = 8}) {
|
||||
// To implement
|
||||
return "";
|
||||
}
|
||||
static void updateWalletLabel(String txid, String label) {
|
||||
|
||||
static void updateWalletLabel(String txid, String label) {
|
||||
// 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]]);
|
||||
}
|
||||
|
||||
|
||||
static List<int> calcInitialHash(int tier, Uint8List covertDomainB, int covertPort, bool covertSsl, double beginTime) {
|
||||
static List<int> calcInitialHash(int tier, Uint8List covertDomainB,
|
||||
int covertPort, bool covertSsl, double beginTime) {
|
||||
// Converting int to bytes in BigEndian order
|
||||
var tierBytes = ByteData(8)..setInt64(0, tier, 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
|
||||
const version = Protocol.VERSION;
|
||||
|
@ -159,7 +155,12 @@ static List<List<T>> zip<T>(List<T> list1, List<T> list2) {
|
|||
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([
|
||||
utf8.encode('Cash Fusion Round'),
|
||||
lastHash,
|
||||
|
@ -179,12 +180,11 @@ static List<int> calcRoundHash(List<int> lastHash, List<int> roundPubkey, int ro
|
|||
bytes.addAll(x);
|
||||
}
|
||||
return crypto.sha256.convert(bytes).bytes;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static Uint8List get_current_genesis_hash() {
|
||||
var GENESIS = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f";
|
||||
var GENESIS =
|
||||
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f";
|
||||
var _lastGenesisHash = hexToBytes(GENESIS).reversed.toList();
|
||||
return Uint8List.fromList(_lastGenesisHash);
|
||||
}
|
||||
|
@ -194,7 +194,6 @@ static List<int> calcRoundHash(List<int> lastHash, List<int> roundPubkey, int ro
|
|||
return [];
|
||||
}
|
||||
|
||||
|
||||
static List<Address> reserve_change_addresses(int number_addresses) {
|
||||
//implement later based on wallet.
|
||||
return [];
|
||||
|
@ -206,7 +205,8 @@ static List<int> calcRoundHash(List<int> lastHash, List<int> roundPubkey, int ro
|
|||
}
|
||||
|
||||
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() {
|
||||
|
@ -224,8 +224,6 @@ static Tuple<Uint8List, Uint8List> genKeypair() {
|
|||
return Tuple(privKey, pubKey);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Generates a cryptographically secure private key
|
||||
static BigInt _generatePrivateKey(int bitLength) {
|
||||
final random = Random.secure();
|
||||
|
@ -236,7 +234,9 @@ static Tuple<Uint8List, Uint8List> genKeypair() {
|
|||
List<int> rnd = List<int>.generate(bytes, (_) => random.nextInt(256));
|
||||
var rndBit = random.nextInt(1 << remBit);
|
||||
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;
|
||||
}
|
||||
|
@ -251,15 +251,16 @@ static Tuple<Uint8List, Uint8List> genKeypair() {
|
|||
return BigInt.parse(hexString, radix: 16);
|
||||
}
|
||||
|
||||
|
||||
static Uint8List sha256(Uint8List bytes) {
|
||||
crypto.Digest digest = crypto.sha256.convert(bytes);
|
||||
return Uint8List.fromList(digest.bytes);
|
||||
}
|
||||
|
||||
static Uint8List tokenBytes([int nbytes = 32]) {
|
||||
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) {
|
||||
|
@ -269,8 +270,8 @@ static Tuple<Uint8List, Uint8List> genKeypair() {
|
|||
return ((size * feerate) + 999) ~/ 1000;
|
||||
}
|
||||
|
||||
|
||||
static ECPoint ser_to_point(Uint8List serializedPoint, ECDomainParameters params) {
|
||||
static ECPoint ser_to_point(
|
||||
Uint8List serializedPoint, ECDomainParameters params) {
|
||||
var point = params.curve.decodePoint(serializedPoint);
|
||||
if (point == null) {
|
||||
throw FormatException('Point decoding failed');
|
||||
|
@ -282,7 +283,6 @@ static Tuple<Uint8List, Uint8List> genKeypair() {
|
|||
return point.getEncoded(compress);
|
||||
}
|
||||
|
||||
|
||||
static BigInt secureRandomBigInt(int bitLength) {
|
||||
final random = Random.secure();
|
||||
final bytes = (bitLength + 7) ~/ 8; // ceil division
|
||||
|
@ -292,9 +292,12 @@ static Tuple<Uint8List, Uint8List> genKeypair() {
|
|||
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;
|
||||
}
|
||||
|
||||
static ECPoint combinePubKeys(List<ECPoint> pubKeys) {
|
||||
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
|
||||
return left == right;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // END OF CLASS
|
||||
|
||||
|
|
|
@ -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 'pedersen.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 {
|
||||
final String message;
|
||||
|
@ -31,13 +29,12 @@ int componentContrib(pb.Component component, int feerate) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void check(bool condition, String failMessage) {
|
||||
if (!condition) {
|
||||
throw ValidationError(failMessage);
|
||||
}
|
||||
}
|
||||
|
||||
dynamic protoStrictParse(dynamic msg, List<int> blob) {
|
||||
try {
|
||||
if (msg.mergeFromBuffer(blob) != blob.length) {
|
||||
|
@ -63,21 +60,22 @@ dynamic protoStrictParse(dynamic msg, List<int> blob) {
|
|||
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(
|
||||
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");
|
||||
|
||||
check(minExcessFee <= msg.excessFee.toInt() && msg.excessFee.toInt() <= maxExcessFee, "bad excess fee");
|
||||
check(
|
||||
minExcessFee <= msg.excessFee.toInt() &&
|
||||
msg.excessFee.toInt() <= maxExcessFee,
|
||||
"bad excess fee");
|
||||
|
||||
check(msg.randomNumberCommitment.length == 32, "bad random commit");
|
||||
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 = [];
|
||||
for (var cblob in msg.initialCommitments) {
|
||||
|
@ -85,11 +83,15 @@ List<pb.InitialCommitment> checkPlayerCommit(
|
|||
check(cmsg.saltedComponentHash.length == 32, "bad salted hash");
|
||||
var P = cmsg.amountCommitment;
|
||||
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);
|
||||
}
|
||||
|
||||
Uint8List HBytes = Uint8List.fromList([0x02] + 'CashFusion gives us fungibility.'.codeUnits);
|
||||
Uint8List HBytes =
|
||||
Uint8List.fromList([0x02] + 'CashFusion gives us fungibility.'.codeUnits);
|
||||
ECDomainParameters params = ECDomainParameters('secp256k1');
|
||||
ECPoint? HMaybe = params.curve.decodePoint(HBytes);
|
||||
if (HMaybe == null) {
|
||||
|
@ -102,26 +104,28 @@ List<pb.InitialCommitment> checkPlayerCommit(
|
|||
var pointsum;
|
||||
// Verify pedersen commitment
|
||||
try {
|
||||
pointsum = Commitment.add_points(commitMessages.map((m) => Uint8List.fromList(m.amountCommitment)).toList());
|
||||
claimedCommit = setup.commit(BigInt.from(msg.excessFee.toInt()), nonce: Util.bytesToBigInt(Uint8List.fromList(msg.pedersenTotalNonce)));
|
||||
pointsum = Commitment.add_points(commitMessages
|
||||
.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) {
|
||||
throw ValidationError("pedersen commitment verification error");
|
||||
}
|
||||
check(pointsum == claimedCommit.PUncompressed, "pedersen commitment mismatch");
|
||||
check(
|
||||
pointsum == claimedCommit.PUncompressed, "pedersen commitment mismatch");
|
||||
return commitMessages;
|
||||
}
|
||||
|
||||
|
||||
Tuple<String, int> checkCovertComponent(
|
||||
pb.CovertComponent msg, ECPoint roundPubkey, int componentFeerate) {
|
||||
var messageHash = Util.sha256(Uint8List.fromList(msg.component));
|
||||
|
||||
check(msg.signature.length == 64, "bad message signature");
|
||||
check(
|
||||
Util.schnorrVerify(
|
||||
roundPubkey, msg.signature, messageHash),
|
||||
check(Util.schnorrVerify(roundPubkey, msg.signature, messageHash),
|
||||
"bad message signature");
|
||||
|
||||
var cmsg = protoStrictParse(pb.Component(), msg.component);
|
||||
|
@ -133,7 +137,8 @@ Tuple<String, int> checkCovertComponent(
|
|||
var inp = cmsg.input;
|
||||
check(inp.prevTxid.length == 32, "bad txid");
|
||||
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),
|
||||
"bad pubkey");
|
||||
sortKey = 'i' +
|
||||
|
@ -146,8 +151,7 @@ Tuple<String, int> checkCovertComponent(
|
|||
// Basically just checks if its ok address. should throw error if not.
|
||||
addr = Util.getAddressFromOutputScript(out.scriptpubkey);
|
||||
|
||||
check(
|
||||
out.amount >= Util.dustLimit(out.scriptpubkey.length), "dust output");
|
||||
check(out.amount >= Util.dustLimit(out.scriptpubkey.length), "dust output");
|
||||
sortKey = 'o' +
|
||||
out.amount.toString() +
|
||||
String.fromCharCodes(out.scriptpubkey) +
|
||||
|
@ -168,8 +172,8 @@ pb.InputComponent? validateProofInternal(
|
|||
List<int> badComponents,
|
||||
int componentFeerate,
|
||||
) {
|
||||
|
||||
Uint8List HBytes = Uint8List.fromList([0x02] + 'CashFusion gives us fungibility.'.codeUnits);
|
||||
Uint8List HBytes =
|
||||
Uint8List.fromList([0x02] + 'CashFusion gives us fungibility.'.codeUnits);
|
||||
ECDomainParameters params = ECDomainParameters('secp256k1');
|
||||
ECPoint? HMaybe = params.curve.decodePoint(HBytes);
|
||||
if (HMaybe == null) {
|
||||
|
@ -246,8 +250,10 @@ Future<dynamic> validateBlame(
|
|||
if (decrypter == pb.Blames_BlameProof_Decrypter.privkey) {
|
||||
var privkey = Uint8List.fromList(blame.privkey);
|
||||
check(privkey.length == 32, 'bad blame privkey');
|
||||
var privkeyHexStr = Util.bytesToHex(privkey); // Convert bytes to hex string.
|
||||
var privkeyBigInt = BigInt.parse(privkeyHexStr, radix: 16); // Convert hex string to BigInt.
|
||||
var privkeyHexStr =
|
||||
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 pubkeys = Util.pubkeysFromPrivkey(privkeyHexStr);
|
||||
check(destCommit.communicationKey == pubkeys[1], 'bad blame privkey');
|
||||
|
@ -282,11 +288,13 @@ Future<dynamic> validateBlame(
|
|||
}
|
||||
|
||||
if (!blame.needLookupBlockchain) {
|
||||
throw ValidationError('blame indicated internal inconsistency, none found!');
|
||||
throw ValidationError(
|
||||
'blame indicated internal inconsistency, none found!');
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
Loading…
Reference in a new issue