mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-03-12 09:27:01 +00:00
no logging within computes and use bip32 for derivations as it is more permissive (than coinlib) which lelantus requires
This commit is contained in:
parent
56b9e1f851
commit
e78af049ee
1 changed files with 109 additions and 119 deletions
|
@ -1,11 +1,12 @@
|
||||||
|
import 'package:bip32/bip32.dart';
|
||||||
import 'package:bitcoindart/bitcoindart.dart' as bitcoindart;
|
import 'package:bitcoindart/bitcoindart.dart' as bitcoindart;
|
||||||
import 'package:coinlib_flutter/coinlib_flutter.dart' as coinlib;
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:lelantus/lelantus.dart' as lelantus;
|
import 'package:lelantus/lelantus.dart' as lelantus;
|
||||||
import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models;
|
import 'package:stackwallet/models/isar/models/isar_models.dart' as isar_models;
|
||||||
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
||||||
import 'package:stackwallet/models/lelantus_fee_data.dart';
|
import 'package:stackwallet/models/lelantus_fee_data.dart';
|
||||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||||
|
import 'package:stackwallet/utilities/extensions/impl/string.dart';
|
||||||
import 'package:stackwallet/utilities/extensions/impl/uint8_list.dart';
|
import 'package:stackwallet/utilities/extensions/impl/uint8_list.dart';
|
||||||
import 'package:stackwallet/utilities/format.dart';
|
import 'package:stackwallet/utilities/format.dart';
|
||||||
import 'package:stackwallet/utilities/logger.dart';
|
import 'package:stackwallet/utilities/logger.dart';
|
||||||
|
@ -44,8 +45,15 @@ abstract final class LelantusFfiWrapper {
|
||||||
walletId: walletId,
|
walletId: walletId,
|
||||||
partialDerivationPath: partialDerivationPath,
|
partialDerivationPath: partialDerivationPath,
|
||||||
);
|
);
|
||||||
|
try {
|
||||||
return await compute(_restore, args);
|
return await compute(_restore, args);
|
||||||
|
} catch (e, s) {
|
||||||
|
Logging.instance.log(
|
||||||
|
"Exception rethrown from _restore(): $e\n$s",
|
||||||
|
level: LogLevel.Info,
|
||||||
|
);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// partialDerivationPath should be something like "m/$purpose'/$coinType'/$account'/"
|
// partialDerivationPath should be something like "m/$purpose'/$coinType'/$account'/"
|
||||||
|
@ -68,56 +76,96 @@ abstract final class LelantusFfiWrapper {
|
||||||
int lastFoundIndex = 0;
|
int lastFoundIndex = 0;
|
||||||
int currentIndex = 0;
|
int currentIndex = 0;
|
||||||
|
|
||||||
try {
|
Set<String> usedSerialNumbersSet = args.usedSerialNumbers.toSet();
|
||||||
Set<String> usedSerialNumbersSet = args.usedSerialNumbers.toSet();
|
|
||||||
|
|
||||||
final root = coinlib.HDPrivateKey.fromKeyAndChainCode(
|
final root = BIP32.fromPrivateKey(
|
||||||
coinlib.ECPrivateKey.fromHex(args.hexRootPrivateKey),
|
args.hexRootPrivateKey.toUint8ListFromHex,
|
||||||
args.chaincode,
|
args.chaincode,
|
||||||
|
);
|
||||||
|
|
||||||
|
while (currentIndex < lastFoundIndex + 50) {
|
||||||
|
final _derivePath =
|
||||||
|
"${args.partialDerivationPath}$MINT_INDEX/$currentIndex";
|
||||||
|
|
||||||
|
final mintKeyPair = root.derivePath(_derivePath);
|
||||||
|
|
||||||
|
final String mintTag = lelantus.CreateTag(
|
||||||
|
mintKeyPair.privateKey!.toHex,
|
||||||
|
currentIndex,
|
||||||
|
Format.uint8listToString(mintKeyPair.identifier),
|
||||||
|
isTestnet: args.cryptoCurrency.network == CryptoCurrencyNetwork.test,
|
||||||
);
|
);
|
||||||
|
|
||||||
while (currentIndex < lastFoundIndex + 50) {
|
for (int setId = 1; setId <= args.latestSetId; setId++) {
|
||||||
final _derivePath =
|
final setData = args.setDataMap[setId] as Map;
|
||||||
"${args.partialDerivationPath}$MINT_INDEX/$currentIndex";
|
final foundCoin = (setData["coins"] as List).firstWhere(
|
||||||
|
(e) => e[1] == mintTag,
|
||||||
final mintKeyPair = root.derivePath(_derivePath);
|
orElse: () => <Object>[],
|
||||||
|
|
||||||
final String mintTag = lelantus.CreateTag(
|
|
||||||
mintKeyPair.privateKey.data.toHex,
|
|
||||||
// Format.uint8listToString(mintKeyPair.privateKey!),
|
|
||||||
currentIndex,
|
|
||||||
Format.uint8listToString(mintKeyPair.identifier),
|
|
||||||
isTestnet: args.cryptoCurrency.network == CryptoCurrencyNetwork.test,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
for (int setId = 1; setId <= args.latestSetId; setId++) {
|
if (foundCoin.length == 4) {
|
||||||
final setData = args.setDataMap[setId] as Map;
|
lastFoundIndex = currentIndex;
|
||||||
final foundCoin = (setData["coins"] as List).firstWhere(
|
|
||||||
(e) => e[1] == mintTag,
|
|
||||||
orElse: () => <Object>[],
|
|
||||||
);
|
|
||||||
|
|
||||||
if (foundCoin.length == 4) {
|
final String publicCoin = foundCoin[0] as String;
|
||||||
lastFoundIndex = currentIndex;
|
final String txId = foundCoin[3] as String;
|
||||||
|
|
||||||
final String publicCoin = foundCoin[0] as String;
|
// this value will either be an int or a String
|
||||||
final String txId = foundCoin[3] as String;
|
final dynamic thirdValue = foundCoin[2];
|
||||||
|
|
||||||
// this value will either be an int or a String
|
if (thirdValue is int) {
|
||||||
final dynamic thirdValue = foundCoin[2];
|
final int amount = thirdValue;
|
||||||
|
final String serialNumber = lelantus.GetSerialNumber(
|
||||||
|
amount,
|
||||||
|
mintKeyPair.privateKey!.toHex,
|
||||||
|
currentIndex,
|
||||||
|
isTestnet:
|
||||||
|
args.cryptoCurrency.network == CryptoCurrencyNetwork.test,
|
||||||
|
);
|
||||||
|
final bool isUsed = usedSerialNumbersSet.contains(serialNumber);
|
||||||
|
|
||||||
|
lelantusCoins.removeWhere((e) =>
|
||||||
|
e.txid == txId &&
|
||||||
|
e.mintIndex == currentIndex &&
|
||||||
|
e.anonymitySetId != setId);
|
||||||
|
|
||||||
|
lelantusCoins.add(
|
||||||
|
isar_models.LelantusCoin(
|
||||||
|
walletId: args.walletId,
|
||||||
|
mintIndex: currentIndex,
|
||||||
|
value: amount.toString(),
|
||||||
|
txid: txId,
|
||||||
|
anonymitySetId: setId,
|
||||||
|
isUsed: isUsed,
|
||||||
|
isJMint: false,
|
||||||
|
otherData:
|
||||||
|
publicCoin, // not really needed but saved just in case
|
||||||
|
),
|
||||||
|
);
|
||||||
|
debugPrint("amount $amount used $isUsed");
|
||||||
|
} else if (thirdValue is String) {
|
||||||
|
final int keyPath = lelantus.GetAesKeyPath(publicCoin);
|
||||||
|
|
||||||
|
final derivePath =
|
||||||
|
"${args.partialDerivationPath}$JMINT_INDEX/$keyPath";
|
||||||
|
|
||||||
|
final aesKeyPair = root.derivePath(derivePath);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final String aesPrivateKey = aesKeyPair.privateKey!.toHex;
|
||||||
|
|
||||||
|
final int amount = lelantus.decryptMintAmount(
|
||||||
|
aesPrivateKey,
|
||||||
|
thirdValue,
|
||||||
|
);
|
||||||
|
|
||||||
if (thirdValue is int) {
|
|
||||||
final int amount = thirdValue;
|
|
||||||
final String serialNumber = lelantus.GetSerialNumber(
|
final String serialNumber = lelantus.GetSerialNumber(
|
||||||
amount,
|
amount,
|
||||||
mintKeyPair.privateKey.data.toHex,
|
aesPrivateKey,
|
||||||
// Format.uint8listToString(mintKeyPair.privateKey!),
|
|
||||||
currentIndex,
|
currentIndex,
|
||||||
isTestnet:
|
isTestnet:
|
||||||
args.cryptoCurrency.network == CryptoCurrencyNetwork.test,
|
args.cryptoCurrency.network == CryptoCurrencyNetwork.test,
|
||||||
);
|
);
|
||||||
final bool isUsed = usedSerialNumbersSet.contains(serialNumber);
|
bool isUsed = usedSerialNumbersSet.contains(serialNumber);
|
||||||
|
|
||||||
lelantusCoins.removeWhere((e) =>
|
lelantusCoins.removeWhere((e) =>
|
||||||
e.txid == txId &&
|
e.txid == txId &&
|
||||||
e.mintIndex == currentIndex &&
|
e.mintIndex == currentIndex &&
|
||||||
|
@ -131,81 +179,25 @@ abstract final class LelantusFfiWrapper {
|
||||||
txid: txId,
|
txid: txId,
|
||||||
anonymitySetId: setId,
|
anonymitySetId: setId,
|
||||||
isUsed: isUsed,
|
isUsed: isUsed,
|
||||||
isJMint: false,
|
isJMint: true,
|
||||||
otherData:
|
otherData:
|
||||||
publicCoin, // not really needed but saved just in case
|
publicCoin, // not really needed but saved just in case
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
Logging.instance.log(
|
jindexes.add(currentIndex);
|
||||||
"amount $amount used $isUsed",
|
|
||||||
level: LogLevel.Info,
|
|
||||||
);
|
|
||||||
} else if (thirdValue is String) {
|
|
||||||
final int keyPath = lelantus.GetAesKeyPath(publicCoin);
|
|
||||||
|
|
||||||
final derivePath =
|
spendTxIds.add(txId);
|
||||||
"${args.partialDerivationPath}$JMINT_INDEX/$keyPath";
|
} catch (_) {
|
||||||
|
debugPrint(
|
||||||
final aesKeyPair = root.derivePath(derivePath);
|
"AES keypair derivation issue for derive path: $derivePath");
|
||||||
|
|
||||||
try {
|
|
||||||
final String aesPrivateKey = aesKeyPair.privateKey.data.toHex;
|
|
||||||
|
|
||||||
final int amount = lelantus.decryptMintAmount(
|
|
||||||
aesPrivateKey,
|
|
||||||
thirdValue,
|
|
||||||
);
|
|
||||||
|
|
||||||
final String serialNumber = lelantus.GetSerialNumber(
|
|
||||||
amount,
|
|
||||||
aesKeyPair.privateKey.data.toHex,
|
|
||||||
currentIndex,
|
|
||||||
isTestnet:
|
|
||||||
args.cryptoCurrency.network == CryptoCurrencyNetwork.test,
|
|
||||||
);
|
|
||||||
bool isUsed = usedSerialNumbersSet.contains(serialNumber);
|
|
||||||
lelantusCoins.removeWhere((e) =>
|
|
||||||
e.txid == txId &&
|
|
||||||
e.mintIndex == currentIndex &&
|
|
||||||
e.anonymitySetId != setId);
|
|
||||||
|
|
||||||
lelantusCoins.add(
|
|
||||||
isar_models.LelantusCoin(
|
|
||||||
walletId: args.walletId,
|
|
||||||
mintIndex: currentIndex,
|
|
||||||
value: amount.toString(),
|
|
||||||
txid: txId,
|
|
||||||
anonymitySetId: setId,
|
|
||||||
isUsed: isUsed,
|
|
||||||
isJMint: true,
|
|
||||||
otherData:
|
|
||||||
publicCoin, // not really needed but saved just in case
|
|
||||||
),
|
|
||||||
);
|
|
||||||
jindexes.add(currentIndex);
|
|
||||||
|
|
||||||
spendTxIds.add(txId);
|
|
||||||
} catch (_) {
|
|
||||||
Logging.instance.log(
|
|
||||||
"AES keypair derivation issue for derive path: $derivePath",
|
|
||||||
level: LogLevel.Warning,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Logging.instance.log(
|
|
||||||
"Unexpected coin found: $foundCoin",
|
|
||||||
level: LogLevel.Warning,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
debugPrint("Unexpected coin found: $foundCoin");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currentIndex++;
|
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
|
||||||
Logging.instance.log("Exception rethrown from isolateRestore(): $e\n$s",
|
currentIndex++;
|
||||||
level: LogLevel.Info);
|
|
||||||
rethrow;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (spendTxIds: spendTxIds, lelantusCoins: lelantusCoins);
|
return (spendTxIds: spendTxIds, lelantusCoins: lelantusCoins);
|
||||||
|
@ -235,13 +227,12 @@ abstract final class LelantusFfiWrapper {
|
||||||
List<lelantus.DartLelantusEntry> lelantusEntries,
|
List<lelantus.DartLelantusEntry> lelantusEntries,
|
||||||
bool isTestNet,
|
bool isTestNet,
|
||||||
}) data) async {
|
}) data) async {
|
||||||
Logging.instance.log("estimateJoinsplit fee", level: LogLevel.Info);
|
debugPrint("estimateJoinSplit fee");
|
||||||
// for (int i = 0; i < lelantusEntries.length; i++) {
|
// for (int i = 0; i < lelantusEntries.length; i++) {
|
||||||
// Logging.instance.log(lelantusEntries[i], addToDebugMessagesDB: false);
|
// Logging.instance.log(lelantusEntries[i], addToDebugMessagesDB: false);
|
||||||
// }
|
// }
|
||||||
Logging.instance.log(
|
debugPrint(
|
||||||
"${data.spendAmount} ${data.subtractFeeFromAmount}",
|
"${data.spendAmount} ${data.subtractFeeFromAmount}",
|
||||||
level: LogLevel.Info,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
List<int> changeToMint = List.empty(growable: true);
|
List<int> changeToMint = List.empty(growable: true);
|
||||||
|
@ -256,13 +247,15 @@ abstract final class LelantusFfiWrapper {
|
||||||
isTestnet: data.isTestNet,
|
isTestnet: data.isTestNet,
|
||||||
);
|
);
|
||||||
|
|
||||||
final estimateFeeData =
|
final estimateFeeData = LelantusFeeData(
|
||||||
LelantusFeeData(changeToMint[0], fee, spendCoinIndexes);
|
changeToMint[0],
|
||||||
Logging.instance.log(
|
fee,
|
||||||
|
spendCoinIndexes,
|
||||||
|
);
|
||||||
|
debugPrint(
|
||||||
"estimateFeeData ${estimateFeeData.changeToMint}"
|
"estimateFeeData ${estimateFeeData.changeToMint}"
|
||||||
" ${estimateFeeData.fee}"
|
" ${estimateFeeData.fee}"
|
||||||
" ${estimateFeeData.spendCoinIndexes}",
|
" ${estimateFeeData.spendCoinIndexes}",
|
||||||
level: LogLevel.Info,
|
|
||||||
);
|
);
|
||||||
return estimateFeeData;
|
return estimateFeeData;
|
||||||
}
|
}
|
||||||
|
@ -322,8 +315,7 @@ abstract final class LelantusFfiWrapper {
|
||||||
var changeToMint = estimateJoinSplitFee.changeToMint;
|
var changeToMint = estimateJoinSplitFee.changeToMint;
|
||||||
var fee = estimateJoinSplitFee.fee;
|
var fee = estimateJoinSplitFee.fee;
|
||||||
var spendCoinIndexes = estimateJoinSplitFee.spendCoinIndexes;
|
var spendCoinIndexes = estimateJoinSplitFee.spendCoinIndexes;
|
||||||
Logging.instance
|
debugPrint("$changeToMint $fee $spendCoinIndexes");
|
||||||
.log("$changeToMint $fee $spendCoinIndexes", level: LogLevel.Info);
|
|
||||||
if (spendCoinIndexes.isEmpty) {
|
if (spendCoinIndexes.isEmpty) {
|
||||||
throw Exception("Error, Not enough funds.");
|
throw Exception("Error, Not enough funds.");
|
||||||
}
|
}
|
||||||
|
@ -354,14 +346,14 @@ abstract final class LelantusFfiWrapper {
|
||||||
);
|
);
|
||||||
final derivePath = "${arg.partialDerivationPath}$MINT_INDEX/${arg.index}";
|
final derivePath = "${arg.partialDerivationPath}$MINT_INDEX/${arg.index}";
|
||||||
|
|
||||||
final root = coinlib.HDPrivateKey.fromKeyAndChainCode(
|
final root = BIP32.fromPrivateKey(
|
||||||
coinlib.ECPrivateKey.fromHex(arg.hexRootPrivateKey),
|
arg.hexRootPrivateKey.toUint8ListFromHex,
|
||||||
arg.chaincode,
|
arg.chaincode,
|
||||||
);
|
);
|
||||||
|
|
||||||
final jmintKeyPair = root.derivePath(derivePath);
|
final jmintKeyPair = root.derivePath(derivePath);
|
||||||
|
|
||||||
final String jmintprivatekey = jmintKeyPair.privateKey.data.toHex;
|
final String jmintprivatekey = jmintKeyPair.privateKey!.toHex;
|
||||||
|
|
||||||
final keyPath = lelantus.getMintKeyPath(
|
final keyPath = lelantus.getMintKeyPath(
|
||||||
changeToMint,
|
changeToMint,
|
||||||
|
@ -373,7 +365,7 @@ abstract final class LelantusFfiWrapper {
|
||||||
final _derivePath = "${arg.partialDerivationPath}$JMINT_INDEX/$keyPath";
|
final _derivePath = "${arg.partialDerivationPath}$JMINT_INDEX/$keyPath";
|
||||||
|
|
||||||
final aesKeyPair = root.derivePath(_derivePath);
|
final aesKeyPair = root.derivePath(_derivePath);
|
||||||
final aesPrivateKey = aesKeyPair.privateKey.data.toHex;
|
final aesPrivateKey = aesKeyPair.privateKey!.toHex;
|
||||||
|
|
||||||
final jmintData = lelantus.createJMintScript(
|
final jmintData = lelantus.createJMintScript(
|
||||||
changeToMint,
|
changeToMint,
|
||||||
|
@ -467,8 +459,6 @@ abstract final class LelantusFfiWrapper {
|
||||||
|
|
||||||
final txHex = extTx.toHex();
|
final txHex = extTx.toHex();
|
||||||
final txId = extTx.getId();
|
final txId = extTx.getId();
|
||||||
Logging.instance.log("txid $txId", level: LogLevel.Info);
|
|
||||||
Logging.instance.log("txHex: $txHex", level: LogLevel.Info);
|
|
||||||
|
|
||||||
final amountAmount = Amount(
|
final amountAmount = Amount(
|
||||||
rawValue: BigInt.from(amount),
|
rawValue: BigInt.from(amount),
|
||||||
|
|
Loading…
Reference in a new issue