This commit is contained in:
julian 2024-05-09 11:56:42 -06:00
parent 9bfac51926
commit 12a0e4c289

View file

@ -188,11 +188,12 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
final transparentSumOut = final transparentSumOut =
(txData.recipients ?? []).map((e) => e.amount).fold( (txData.recipients ?? []).map((e) => e.amount).fold(
Amount( Amount(
rawValue: BigInt.zero, rawValue: BigInt.zero,
fractionDigits: cryptoCurrency.fractionDigits, fractionDigits: cryptoCurrency.fractionDigits,
), ),
(p, e) => p + e); (p, e) => p + e,
);
// See SPARK_VALUE_SPEND_LIMIT_PER_TRANSACTION at https://github.com/firoorg/sparkmobile/blob/ef2e39aae18ecc49e0ddc63a3183e9764b96012e/include/spark.h#L17 // See SPARK_VALUE_SPEND_LIMIT_PER_TRANSACTION at https://github.com/firoorg/sparkmobile/blob/ef2e39aae18ecc49e0ddc63a3183e9764b96012e/include/spark.h#L17
// and COIN https://github.com/firoorg/sparkmobile/blob/ef2e39aae18ecc49e0ddc63a3183e9764b96012e/bitcoin/amount.h#L17 // and COIN https://github.com/firoorg/sparkmobile/blob/ef2e39aae18ecc49e0ddc63a3183e9764b96012e/bitcoin/amount.h#L17
@ -203,16 +204,18 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
fractionDigits: cryptoCurrency.fractionDigits, fractionDigits: cryptoCurrency.fractionDigits,
)) { )) {
throw Exception( throw Exception(
"Spend to transparent address limit exceeded (10,000 Firo per transaction)."); "Spend to transparent address limit exceeded (10,000 Firo per transaction).",
);
} }
final sparkSumOut = final sparkSumOut =
(txData.sparkRecipients ?? []).map((e) => e.amount).fold( (txData.sparkRecipients ?? []).map((e) => e.amount).fold(
Amount( Amount(
rawValue: BigInt.zero, rawValue: BigInt.zero,
fractionDigits: cryptoCurrency.fractionDigits, fractionDigits: cryptoCurrency.fractionDigits,
), ),
(p, e) => p + e); (p, e) => p + e,
);
final txAmount = transparentSumOut + sparkSumOut; final txAmount = transparentSumOut + sparkSumOut;
@ -239,12 +242,14 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
// prepare coin data for ffi // prepare coin data for ffi
final serializedCoins = coins final serializedCoins = coins
.map((e) => ( .map(
serializedCoin: e.serializedCoinB64!, (e) => (
serializedCoinContext: e.contextB64!, serializedCoin: e.serializedCoinB64!,
groupId: e.groupId, serializedCoinContext: e.contextB64!,
height: e.height!, groupId: e.groupId,
)) height: e.height!,
),
)
.toList(); .toList();
final currentId = await electrumXClient.getSparkLatestCoinId(); final currentId = await electrumXClient.getSparkLatestCoinId();
@ -266,16 +271,20 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
} }
final allAnonymitySets = setMaps final allAnonymitySets = setMaps
.map((e) => ( .map(
setId: e["coinGroupID"] as int, (e) => (
setHash: e["setHash"] as String, setId: e["coinGroupID"] as int,
set: (e["coins"] as List) setHash: e["setHash"] as String,
.map((e) => ( set: (e["coins"] as List)
serializedCoin: e[0] as String, .map(
txHash: e[1] as String, (e) => (
)) serializedCoin: e[0] as String,
.toList(), txHash: e[1] as String,
)) ),
)
.toList(),
),
)
.toList(); .toList();
final root = await getRootHDNode(); final root = await getRootHDNode();
@ -437,27 +446,32 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
privateKeyHex: privateKey.toHex, privateKeyHex: privateKey.toHex,
index: kDefaultSparkIndex, index: kDefaultSparkIndex,
recipients: txData.recipients recipients: txData.recipients
?.map((e) => ( ?.map(
address: e.address, (e) => (
amount: e.amount.raw.toInt(), address: e.address,
subtractFeeFromAmount: isSendAll, amount: e.amount.raw.toInt(),
)) subtractFeeFromAmount: isSendAll,
),
)
.toList() ?? .toList() ??
[], [],
privateRecipients: txData.sparkRecipients privateRecipients: txData.sparkRecipients
?.map((e) => ( ?.map(
sparkAddress: e.address, (e) => (
amount: e.amount.raw.toInt(), sparkAddress: e.address,
subtractFeeFromAmount: isSendAll, amount: e.amount.raw.toInt(),
memo: e.memo, subtractFeeFromAmount: isSendAll,
)) memo: e.memo,
),
)
.toList() ?? .toList() ??
[], [],
serializedCoins: serializedCoins, serializedCoins: serializedCoins,
allAnonymitySets: allAnonymitySets, allAnonymitySets: allAnonymitySets,
idAndBlockHashes: idAndBlockHashes idAndBlockHashes: idAndBlockHashes
.map( .map(
(e) => (setId: e.groupId, blockHash: base64Decode(e.blockHash))) (e) => (setId: e.groupId, blockHash: base64Decode(e.blockHash)),
)
.toList(), .toList(),
txHash: extractedTx.getHash(), txHash: extractedTx.getHash(),
), ),
@ -504,16 +518,20 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
for (final usedCoin in spend.usedCoins) { for (final usedCoin in spend.usedCoins) {
try { try {
usedSparkCoins.add(coins usedSparkCoins.add(
.firstWhere((e) => coins
usedCoin.height == e.height && .firstWhere(
usedCoin.groupId == e.groupId && (e) =>
base64Decode(e.serializedCoinB64!) usedCoin.height == e.height &&
.toHex usedCoin.groupId == e.groupId &&
.startsWith(base64Decode(usedCoin.serializedCoin).toHex)) base64Decode(e.serializedCoinB64!).toHex.startsWith(
.copyWith( base64Decode(usedCoin.serializedCoin).toHex,
isUsed: true, ),
)); )
.copyWith(
isUsed: true,
),
);
} catch (_) { } catch (_) {
throw Exception( throw Exception(
"Unexpectedly did not find used spark coin. This should never happen.", "Unexpectedly did not find used spark coin. This should never happen.",
@ -576,8 +594,10 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
return await updateSentCachedTxData(txData: txData); return await updateSentCachedTxData(txData: txData);
} catch (e, s) { } catch (e, s) {
Logging.instance.log("Exception rethrown from confirmSend(): $e\n$s", Logging.instance.log(
level: LogLevel.Error); "Exception rethrown from confirmSend(): $e\n$s",
level: LogLevel.Error,
);
rethrow; rethrow;
} }
} }
@ -695,9 +715,11 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
); );
final spendable = Amount( final spendable = Amount(
rawValue: unusedCoins rawValue: unusedCoins
.where((e) => .where(
e.height != null && (e) =>
e.height! + cryptoCurrency.minConfirms <= currentHeight) e.height != null &&
e.height! + cryptoCurrency.minConfirms <= currentHeight,
)
.map((e) => e.value) .map((e) => e.value)
.fold(BigInt.zero, (prev, e) => prev + e), .fold(BigInt.zero, (prev, e) => prev + e),
fractionDigits: cryptoCurrency.fractionDigits, fractionDigits: cryptoCurrency.fractionDigits,
@ -802,7 +824,7 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
} }
// organise utxos // organise utxos
Map<String, List<UTXO>> utxosByAddress = {}; final Map<String, List<UTXO>> utxosByAddress = {};
for (final utxo in availableUtxos) { for (final utxo in availableUtxos) {
utxosByAddress[utxo.address!] ??= []; utxosByAddress[utxo.address!] ??= [];
utxosByAddress[utxo.address!]!.add(utxo); utxosByAddress[utxo.address!]!.add(utxo);
@ -811,7 +833,7 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
// setup some vars // setup some vars
int nChangePosInOut = -1; int nChangePosInOut = -1;
int nChangePosRequest = nChangePosInOut; final int nChangePosRequest = nChangePosInOut;
List<MutableSparkRecipient> outputs_ = outputs List<MutableSparkRecipient> outputs_ = outputs
.map((e) => MutableSparkRecipient(e.address, e.value, e.memo)) .map((e) => MutableSparkRecipient(e.address, e.value, e.memo))
.toList(); // deep copy .toList(); // deep copy
@ -934,11 +956,13 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
// Generate dummy mint coins to save time // Generate dummy mint coins to save time
final dummyRecipients = LibSpark.createSparkMintRecipients( final dummyRecipients = LibSpark.createSparkMintRecipients(
outputs: singleTxOutputs outputs: singleTxOutputs
.map((e) => ( .map(
sparkAddress: e.address, (e) => (
value: e.value.toInt(), sparkAddress: e.address,
memo: "", value: e.value.toInt(),
)) memo: "",
),
)
.toList(), .toList(),
serialContext: Uint8List(0), serialContext: Uint8List(0),
generate: false, generate: false,
@ -952,11 +976,13 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
if (recipient.amount < cryptoCurrency.dustLimit.raw.toInt()) { if (recipient.amount < cryptoCurrency.dustLimit.raw.toInt()) {
throw Exception("Output amount too small"); throw Exception("Output amount too small");
} }
vout.add(( vout.add(
recipient.scriptPubKey, (
recipient.amount, recipient.scriptPubKey,
singleTxOutputs[i].address, recipient.amount,
)); singleTxOutputs[i].address,
),
);
} }
// Choose coins to use // Choose coins to use
@ -973,7 +999,7 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
// priority stuff??? // priority stuff???
BigInt nChange = nValueIn - nValueToSelect; final BigInt nChange = nValueIn - nValueToSelect;
if (nChange > BigInt.zero) { if (nChange > BigInt.zero) {
if (nChange < cryptoCurrency.dustLimit.raw) { if (nChange < cryptoCurrency.dustLimit.raw) {
nChangePosInOut = -1; nChangePosInOut = -1;
@ -1106,10 +1132,12 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
// Generate real mint coins // Generate real mint coins
final serialContext = LibSpark.serializeMintContext( final serialContext = LibSpark.serializeMintContext(
inputs: setCoins inputs: setCoins
.map((e) => ( .map(
e.utxo.txid, (e) => (
e.utxo.vout, e.utxo.txid,
)) e.utxo.vout,
),
)
.toList(), .toList(),
); );
final recipients = LibSpark.createSparkMintRecipients( final recipients = LibSpark.createSparkMintRecipients(
@ -1126,7 +1154,7 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
generate: true, generate: true,
); );
int i = 0; final int i = 0;
for (int i = 0; i < recipients.length; i++) { for (int i = 0; i < recipients.length; i++) {
final recipient = recipients[i]; final recipient = recipients[i];
final out = ( final out = (
@ -1267,9 +1295,11 @@ mixin SparkInterface on Bip39HDWallet, ElectrumXInterface {
.where() .where()
.walletIdEqualTo(walletId) .walletIdEqualTo(walletId)
.filter() .filter()
.valueEqualTo(addressOrScript is Uint8List .valueEqualTo(
? output.$3! addressOrScript is Uint8List
: addressOrScript as String) ? output.$3!
: addressOrScript as String,
)
.valueProperty() .valueProperty()
.findFirst()) != .findFirst()) !=
null, null,
@ -1643,42 +1673,38 @@ Future<
String serializedCoinContext String serializedCoinContext
})> usedCoins, })> usedCoins,
})> _createSparkSend( })> _createSparkSend(
({ ({
String privateKeyHex, String privateKeyHex,
int index, int index,
List< List<({String address, int amount, bool subtractFeeFromAmount})> recipients,
({ List<
String address, ({
int amount, String sparkAddress,
bool subtractFeeFromAmount int amount,
})> recipients, bool subtractFeeFromAmount,
List< String memo
({ })> privateRecipients,
String sparkAddress, List<
int amount, ({
bool subtractFeeFromAmount, String serializedCoin,
String memo String serializedCoinContext,
})> privateRecipients, int groupId,
List< int height,
({ })> serializedCoins,
String serializedCoin, List<
String serializedCoinContext, ({
int groupId, int setId,
int height, String setHash,
})> serializedCoins, List<({String serializedCoin, String txHash})> set
List< })> allAnonymitySets,
({ List<
int setId, ({
String setHash, int setId,
List<({String serializedCoin, String txHash})> set Uint8List blockHash,
})> allAnonymitySets, })> idAndBlockHashes,
List< Uint8List txHash,
({ }) args,
int setId, ) async {
Uint8List blockHash,
})> idAndBlockHashes,
Uint8List txHash,
}) args) async {
final spend = LibSpark.createSparkSendTransaction( final spend = LibSpark.createSparkSendTransaction(
privateKeyHex: args.privateKeyHex, privateKeyHex: args.privateKeyHex,
index: args.index, index: args.index,
@ -1695,14 +1721,15 @@ Future<
/// Top level function which should be called wrapped in [compute] /// Top level function which should be called wrapped in [compute]
Future<List<SparkCoin>> _identifyCoins( Future<List<SparkCoin>> _identifyCoins(
({ ({
List<dynamic> anonymitySetCoins, List<dynamic> anonymitySetCoins,
int groupId, int groupId,
Set<String> spentCoinTags, Set<String> spentCoinTags,
Set<String> privateKeyHexSet, Set<String> privateKeyHexSet,
String walletId, String walletId,
bool isTestNet, bool isTestNet,
}) args) async { }) args,
) async {
final List<SparkCoin> myCoins = []; final List<SparkCoin> myCoins = [];
for (final privateKeyHex in args.privateKeyHexSet) { for (final privateKeyHex in args.privateKeyHexSet) {