Skip to content
Snippets Groups Projects
Commit fad55409 authored by poka's avatar poka
Browse files

WIP: payment almost done: Have to well configure change document management

parent d4d05a84
Branches
Tags
No related merge requests found
......@@ -16,8 +16,11 @@ Future<void> main(List<String> arguments) async {
payCommand
..addFlag('useMempool', abbr: 'u')
..addOption('recipient', abbr: 'r')
..addOption('issuer', abbr: 'i')
..addOption('amount', abbr: 'a');
..addOption('dewif', abbr: 'd', help: 'DEWIF base64 format.')
..addOption('password', abbr: 'p')
..addOption('derivation', abbr: 'n')
..addOption('amount', abbr: 'a')
..addOption('comment', abbr: 'c');
// parser.commands.update(
// 'pay',
......@@ -43,7 +46,9 @@ ${parser.usage}''');
print('Please enter a valid command.');
exit(1);
}
var node = Gva(node: 'https://duniter-g1.p2p.legal/gva');
// var node = Gva(node: 'https://duniter-g1.p2p.legal/gva');
var node = Gva(node: 'https://g1.librelois.fr/gva');
final command = argResults.command!;
switch (command.name) {
......@@ -72,14 +77,29 @@ ${parser.usage}''');
case 'pay':
{
String? recipient = argResults.command?['recipient'];
String? issuer = argResults.command?['issuer'];
double? amount = double.parse(argResults.command?['amount'] ?? '0');
if (recipient == null || issuer == null) {
print('Please enter a recipient and the amount of the transaction.');
String? dewif = argResults.command?['dewif'];
String? password = argResults.command?['password'];
int derivation = int.parse(argResults.command?['derivation'] ?? '-2');
String comment = argResults.command?['comment'] ?? '';
bool useMempool = argResults.command?['useMempool'] ?? false;
double amount = double.parse(argResults.command?['amount'] ?? '0');
if (recipient == null ||
dewif == null ||
amount == 0 ||
password == null ||
derivation == -2) {
print(
'Please enter a recipient, the amount of the transaction, the dewif base64 string and it password, and the derivation number');
exit(1);
} else {
bool isTransactionOK = await node.pay(
recipient: recipient, issuer: issuer, amount: amount);
recipient: recipient,
dewif: dewif,
password: password,
amount: amount,
comment: comment,
derivation: derivation,
useMempool: useMempool);
print(isTransactionOK);
}
}
......
......@@ -70,21 +70,25 @@ class Gva {
Future<bool> pay(
{required String recipient,
required double amount,
required String issuer,
required String dewif,
required String password,
int? derivation,
String comment = '',
bool useMempool = false}) async {
final transaction = Transaction(
Transaction transaction;
transaction = Transaction(
recipient: recipient,
amount: (amount.toPrecision(2) * 100).toInt(),
issuer: issuer,
dewif: dewif,
password: password,
derivation: derivation ?? -1,
comment: comment,
useMempool: useMempool,
client: _client);
// Execute transaction
transaction.process();
return false;
return await transaction.process();
}
}
......
......@@ -105,3 +105,15 @@ query ($recipient: PkOrScriptGva!, $issuer: PubKeyGva!, $amount: Int!, $comment:
)
}
''';
const String sendTransactionDocQuery = r'''
mutation ($signedDoc: String!){
tx(
rawTx: $signedDoc
) {
version
issuers
outputs
}
}
''';
import 'dart:convert';
import 'package:durt/durt.dart';
import 'package:durt/src/gva/queries.dart';
import 'package:graphql/client.dart';
import 'package:collection/collection.dart';
class Transaction {
final String recipient;
final String issuer;
final HdWallet wallet;
final int derivation;
final String issuerPubkey;
final int amount;
final String comment;
final bool useMempool;
final GraphQLClient client;
bool useMempool;
bool isChange = false;
Transaction({
Transaction._({
required this.recipient,
required this.amount,
required this.issuer,
required this.wallet,
required this.derivation,
required this.issuerPubkey,
required this.client,
this.comment = '',
this.useMempool = false,
});
// Main construtor transform dewif + password to hdwallet
factory Transaction({
required String recipient,
required int amount,
required String dewif,
required String password,
required GraphQLClient client,
required int derivation,
String comment = '',
bool useMempool = false,
}) {
HdWallet wallet = HdWallet.fromDewif(dewif, password);
return Transaction._(
recipient: recipient,
amount: amount,
wallet: wallet,
derivation: derivation,
issuerPubkey: wallet.getPubkey(derivation),
client: client);
}
Future<List> _generateTransactionDocument() async {
final QueryOptions options = QueryOptions(
document: gql(genTransDocQuery),
variables: <String, dynamic>{
'recipient': recipient,
'amount': amount,
'issuer': issuer,
'issuer': issuerPubkey,
'comment': comment,
'useMempool': useMempool
},
);
final QueryResult result = await client.query(options);
print('tata');
if (result.hasException) {
throw GraphQLException('GraphQL error: ${result.exception.toString()}');
} else {
......@@ -40,31 +69,99 @@ class Transaction {
}
bool _checkTransactionDocument(List transDoc) {
for (var doc in transDoc) {
print(doc);
List<String> issuersList = [];
List<String> recipientsList = [];
// List outputsList = [];
List<int> amountsList = [];
List<String> commentsList = [];
// Parse received transaction document
for (String doc in transDoc) {
List<String> docList = doc.split('\n');
docList.asMap().forEach((i, line) {
if (line == 'Issuers:') {
issuersList.add(docList[i + 1]);
} else if (line == 'Outputs:') {
List<String> outputList = docList[i + 1].split(':');
amountsList.add(int.parse(outputList[0]));
recipientsList
.add(outputList[2].split('SIG(')[1].replaceFirst(')', ''));
} else if (line.startsWith('Comment: ')) {
commentsList.add(line.split(': ')[1]);
}
});
}
// Check if it's a change transaction document
if (recipientsList.every((element) => element == recipient)) {
print("The document contain a change transaction");
isChange = useMempool = true;
return true;
} else if (issuersList.every((element) => element != issuerPubkey) ||
amountsList.sum != amount ||
recipientsList.every((element) =>
element != recipient ||
commentsList.every((element) => element != comment))) {
// Check validity of transaction document
print('The generated document is corrupted');
return false;
} else {
isChange = false;
return true;
}
}
List _signDocument(List documents) {
List<String> signedDocuments = [];
for (String doc in documents) {
var signature = base64Encode(
wallet.sign(doc, derivation: derivation).buffer.asUint8List());
signedDocuments.add(doc + signature);
// print(signedDocuments);
}
return signedDocuments;
}
Future<bool> _sendSignedDocuments(List signedDocs) async {
print(signedDocs);
for (String doc in signedDocs) {
final QueryOptions options = QueryOptions(
document: gql(sendTransactionDocQuery),
variables: <String, String>{
'signedDoc': doc,
},
);
final QueryResult result = await client.query(options);
if (result.hasException) {
throw GraphQLException('GraphQL error: ${result.exception.toString()}');
} else if (isChange) {
print('WE DO IT AGAIN...');
await process();
return true;
} else {
print('Transaction complete !');
return true;
}
}
return false;
}
Future<bool> process() async {
try {
final List transDoc = await _generateTransactionDocument();
if (_checkTransactionDocument(transDoc)) {
// final List signedTransDoc = _signTransactionDocument(transDoc);
// return await _sendSignedTransactionDocument(signedTransDoc);
final List transDocs = await _generateTransactionDocument();
if (_checkTransactionDocument(transDocs)) {
final List signedDocs = _signDocument(transDocs);
final bool result = await _sendSignedDocuments(signedDocs);
return result;
} else {
print('Transaction document is not valid.');
return false;
}
} on GraphQLException catch (e) {
print(e);
return false;
} catch (e) {
print('Unexpected error: $e');
print('GraphQL error: ' + e.cause.split('message: ')[1].split(',')[0]);
return false;
}
return true;
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment