mirror of
https://github.com/MAGICGrants/flutter_libsparkmobile.git
synced 2026-01-09 21:17:56 -05:00
get coins to spend
This commit is contained in:
@@ -10,7 +10,8 @@ import 'package:flutter_libsparkmobile/src/models/spark_coin.dart';
|
||||
|
||||
import 'src/flutter_libsparkmobile_bindings_generated.dart';
|
||||
|
||||
const kSparkChain = 6;
|
||||
const kSparkChain = 0x6;
|
||||
const kSparkChange = 0x270F;
|
||||
const kSparkBaseDerivationPath = "m/44'/136'/0'/$kSparkChain/";
|
||||
const kSparkBaseDerivationPathTestnet = "m/44'/1'/0'/$kSparkChain/";
|
||||
|
||||
@@ -358,6 +359,110 @@ abstract final class LibSpark {
|
||||
|
||||
return (serializedSpendPayload: message, fee: fee, outputScripts: scripts);
|
||||
}
|
||||
|
||||
static ({int changeToMint, List<LibSparkCoin> coins}) getCoinsToSpend({
|
||||
required int sendAmount,
|
||||
required int recipientsToSubtractFee,
|
||||
required List<LibSparkCoin> coins,
|
||||
required int privateRecipientsCount,
|
||||
required int recipientsCount,
|
||||
}) {
|
||||
final coinsPtr = malloc.allocate<CCSparkMintMeta>(
|
||||
sizeOf<CCSparkMintMeta>() * coins.length,
|
||||
);
|
||||
|
||||
for (int i = 0; i < coins.length; i++) {
|
||||
coinsPtr[i].height = coins[i].height!;
|
||||
coinsPtr[i].id = coins[i].id!;
|
||||
coinsPtr[i].isUsed = coins[i].isUsed! ? 1 : 0;
|
||||
coinsPtr[i].txid = coins[i].txHash!.unsignedCharPointer();
|
||||
coinsPtr[i].i = coins[i].diversifier!.toInt();
|
||||
coinsPtr[i].d = coins[i].encryptedDiversifier!.unsignedCharPointer();
|
||||
coinsPtr[i].dLength = coins[i].encryptedDiversifier!.length;
|
||||
coinsPtr[i].k = coins[i].nonce!.unsignedCharPointer();
|
||||
coinsPtr[i].kLength = coins[i].nonce!.length;
|
||||
coinsPtr[i].memo = coins[i].memo!.toNativeUtf8().cast<Char>();
|
||||
coinsPtr[i].memoLength = coins[i].memo!.length;
|
||||
coinsPtr[i].serial_context =
|
||||
coins[i].serialContext!.unsignedCharPointer();
|
||||
coinsPtr[i].serial_contextLength = coins[i].serialContext!.length;
|
||||
coinsPtr[i].type = coins[i].type.value;
|
||||
|
||||
final serCoin = base64Decode(coins[i].serializedCoin!);
|
||||
coinsPtr[i].serializedCoin = serCoin.unsignedCharPointer();
|
||||
coinsPtr[i].serializedCoinLength = serCoin.length;
|
||||
}
|
||||
|
||||
final result = _bindings.getCoinsToSpend(
|
||||
sendAmount,
|
||||
coinsPtr,
|
||||
coins.length,
|
||||
);
|
||||
|
||||
for (int i = 0; i < coins.length; i++) {
|
||||
malloc.free(coinsPtr[i].txid);
|
||||
malloc.free(coinsPtr[i].d);
|
||||
malloc.free(coinsPtr[i].k);
|
||||
malloc.free(coinsPtr[i].memo);
|
||||
malloc.free(coinsPtr[i].serial_context);
|
||||
malloc.free(coinsPtr[i].serializedCoin);
|
||||
}
|
||||
malloc.free(coinsPtr);
|
||||
|
||||
final ({int changeToMint, List<LibSparkCoin> coins}) ret = (
|
||||
changeToMint: result.ref.changeToMint,
|
||||
coins: <LibSparkCoin>[],
|
||||
);
|
||||
|
||||
for (int i = 0; i < result.ref.length; i++) {
|
||||
final LibSparkCoinType coinType;
|
||||
switch (result.ref.list[i].type) {
|
||||
case 0:
|
||||
coinType = LibSparkCoinType.mint;
|
||||
break;
|
||||
case 1:
|
||||
coinType = LibSparkCoinType.mint;
|
||||
break;
|
||||
default:
|
||||
throw Exception(
|
||||
"Unknown coin type \"${result.ref.list[i].type}\" found.",
|
||||
);
|
||||
}
|
||||
|
||||
final coin = LibSparkCoin(
|
||||
type: coinType,
|
||||
id: result.ref.list[i].id,
|
||||
height: result.ref.list[i].height,
|
||||
isUsed: result.ref.list[i].isUsed > 0,
|
||||
nonce: result.ref.list[i].k.toUint8List(result.ref.list[i].kLength),
|
||||
value: BigInt.from(result.ref.list[i].v),
|
||||
memo: result.ref.list[i].memo
|
||||
.cast<Utf8>()
|
||||
.toDartString(length: result.ref.list[i].memoLength),
|
||||
txHash: result.ref.list[i].txid.toUint8List(32),
|
||||
serialContext: result.ref.list[i].serial_context
|
||||
.toUint8List(result.ref.list[i].serial_contextLength),
|
||||
diversifier: BigInt.from(result.ref.list[i].i),
|
||||
encryptedDiversifier:
|
||||
result.ref.list[i].d.toUint8List(result.ref.list[i].dLength),
|
||||
serializedCoin: base64Encode(result.ref.list[i].serializedCoin
|
||||
.toUint8List(result.ref.list[i].serializedCoinLength)),
|
||||
);
|
||||
|
||||
ret.coins.add(coin);
|
||||
|
||||
malloc.free(result.ref.list[i].txid);
|
||||
malloc.free(result.ref.list[i].d);
|
||||
malloc.free(result.ref.list[i].k);
|
||||
malloc.free(result.ref.list[i].memo);
|
||||
malloc.free(result.ref.list[i].serial_context);
|
||||
malloc.free(result.ref.list[i].serializedCoin);
|
||||
}
|
||||
|
||||
malloc.free(result);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
extension on Pointer<UnsignedChar> {
|
||||
|
||||
@@ -188,6 +188,26 @@ class FlutterLibsparkmobileBindings {
|
||||
int,
|
||||
ffi.Pointer<CCoverSetData>,
|
||||
int)>();
|
||||
|
||||
ffi.Pointer<SelectedSparkSpendCoins> getCoinsToSpend(
|
||||
int spendAmount,
|
||||
ffi.Pointer<CCSparkMintMeta> coins,
|
||||
int coinLength,
|
||||
) {
|
||||
return _getCoinsToSpend(
|
||||
spendAmount,
|
||||
coins,
|
||||
coinLength,
|
||||
);
|
||||
}
|
||||
|
||||
late final _getCoinsToSpendPtr = _lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Pointer<SelectedSparkSpendCoins> Function(ffi.Int64,
|
||||
ffi.Pointer<CCSparkMintMeta>, ffi.Int)>>('getCoinsToSpend');
|
||||
late final _getCoinsToSpend = _getCoinsToSpendPtr.asFunction<
|
||||
ffi.Pointer<SelectedSparkSpendCoins> Function(
|
||||
int, ffi.Pointer<CCSparkMintMeta>, int)>();
|
||||
}
|
||||
|
||||
/// FFI-friendly wrapper for a spark::Coin.
|
||||
@@ -330,6 +350,70 @@ final class CCDataStream extends ffi.Struct {
|
||||
external int length;
|
||||
}
|
||||
|
||||
/// FFI-friendly wrapper for a spark::CSparkMintMeta.
|
||||
///
|
||||
/// CSparkMintMeta: https://github.com/firoorg/sparkmobile/blob/8bf17cd3deba6c3b0d10e89282e02936d7e71cdd/src/primitives.h#L9
|
||||
final class CCSparkMintMeta extends ffi.Struct {
|
||||
@ffi.Int()
|
||||
external int height;
|
||||
|
||||
@ffi.Int()
|
||||
external int id;
|
||||
|
||||
@ffi.Int()
|
||||
external int isUsed;
|
||||
|
||||
external ffi.Pointer<ffi.UnsignedChar> txid;
|
||||
|
||||
/// Diversifier.
|
||||
@ffi.Uint64()
|
||||
external int i;
|
||||
|
||||
/// Encrypted diversifier.
|
||||
external ffi.Pointer<ffi.UnsignedChar> d;
|
||||
|
||||
@ffi.Int()
|
||||
external int dLength;
|
||||
|
||||
/// Value.
|
||||
@ffi.Uint64()
|
||||
external int v;
|
||||
|
||||
/// Nonce.
|
||||
external ffi.Pointer<ffi.UnsignedChar> k;
|
||||
|
||||
@ffi.Int()
|
||||
external int kLength;
|
||||
|
||||
external ffi.Pointer<ffi.Char> memo;
|
||||
|
||||
@ffi.Int()
|
||||
external int memoLength;
|
||||
|
||||
external ffi.Pointer<ffi.UnsignedChar> serial_context;
|
||||
|
||||
@ffi.Int()
|
||||
external int serial_contextLength;
|
||||
|
||||
@ffi.Char()
|
||||
external int type;
|
||||
|
||||
external ffi.Pointer<ffi.UnsignedChar> serializedCoin;
|
||||
|
||||
@ffi.Int()
|
||||
external int serializedCoinLength;
|
||||
}
|
||||
|
||||
final class SelectedSparkSpendCoins extends ffi.Struct {
|
||||
external ffi.Pointer<CCSparkMintMeta> list;
|
||||
|
||||
@ffi.Int()
|
||||
external int length;
|
||||
|
||||
@ffi.Int64()
|
||||
external int changeToMint;
|
||||
}
|
||||
|
||||
/// FFI-friendly wrapper for a spark::CoverSetData.
|
||||
///
|
||||
/// CoverSetData: https://github.com/firoorg/sparkmobile/blob/8bf17cd3deba6c3b0d10e89282e02936d7e71cdd/src/spend_transaction.h#L28
|
||||
|
||||
@@ -11,6 +11,11 @@ enum LibSparkCoinType {
|
||||
class LibSparkCoin {
|
||||
final LibSparkCoinType type;
|
||||
|
||||
final int? id;
|
||||
final int? height;
|
||||
|
||||
final bool? isUsed;
|
||||
|
||||
final Uint8List? nonce;
|
||||
|
||||
final String? address;
|
||||
@@ -18,6 +23,9 @@ class LibSparkCoin {
|
||||
final BigInt? value;
|
||||
|
||||
final String? memo;
|
||||
|
||||
final Uint8List? txHash;
|
||||
|
||||
final Uint8List? serialContext;
|
||||
|
||||
final BigInt? diversifier;
|
||||
@@ -28,45 +36,62 @@ class LibSparkCoin {
|
||||
|
||||
final String? lTagHash;
|
||||
|
||||
final String? serializedCoin;
|
||||
|
||||
LibSparkCoin({
|
||||
required this.type,
|
||||
this.id,
|
||||
this.height,
|
||||
this.isUsed,
|
||||
this.nonce,
|
||||
this.address,
|
||||
this.value,
|
||||
this.memo,
|
||||
this.txHash,
|
||||
this.serialContext,
|
||||
this.diversifier,
|
||||
this.encryptedDiversifier,
|
||||
this.serial,
|
||||
this.tag,
|
||||
this.lTagHash,
|
||||
this.serializedCoin,
|
||||
});
|
||||
|
||||
LibSparkCoin copyWith({
|
||||
LibSparkCoinType? type,
|
||||
int? id,
|
||||
int? height,
|
||||
bool? isUsed,
|
||||
Uint8List? nonce,
|
||||
String? address,
|
||||
BigInt? value,
|
||||
String? memo,
|
||||
Uint8List? txHash,
|
||||
Uint8List? serialContext,
|
||||
BigInt? diversifier,
|
||||
Uint8List? encryptedDiversifier,
|
||||
Uint8List? serial,
|
||||
Uint8List? tag,
|
||||
String? lTagHash,
|
||||
String? serializedCoin,
|
||||
}) {
|
||||
return LibSparkCoin(
|
||||
type: type ?? this.type,
|
||||
id: id ?? this.id,
|
||||
height: height ?? this.height,
|
||||
isUsed: isUsed ?? this.isUsed,
|
||||
nonce: nonce ?? this.nonce,
|
||||
address: address ?? this.address,
|
||||
value: value ?? this.value,
|
||||
memo: memo ?? this.memo,
|
||||
txHash: txHash ?? this.txHash,
|
||||
serialContext: serialContext ?? this.serialContext,
|
||||
diversifier: diversifier ?? this.diversifier,
|
||||
encryptedDiversifier: encryptedDiversifier ?? this.encryptedDiversifier,
|
||||
serial: serial ?? this.serial,
|
||||
tag: tag ?? this.tag,
|
||||
lTagHash: lTagHash ?? this.lTagHash,
|
||||
serializedCoin: serializedCoin ?? this.serializedCoin,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -74,16 +99,21 @@ class LibSparkCoin {
|
||||
String toString() {
|
||||
return 'LibSparkCoin('
|
||||
', type: $type'
|
||||
', id: $id'
|
||||
', height: $height'
|
||||
', isUsed: $isUsed'
|
||||
', k: $nonce'
|
||||
', address: $address'
|
||||
', value: $value'
|
||||
', memo: $memo'
|
||||
', txHash: $txHash'
|
||||
', serialContext: $serialContext'
|
||||
', diversifier: $diversifier'
|
||||
', encryptedDiversifier: $encryptedDiversifier'
|
||||
', serial: $serial'
|
||||
', tag: $tag'
|
||||
', lTagHash: $lTagHash'
|
||||
', serializedCoin: $serializedCoin'
|
||||
')';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,3 +291,78 @@ SparkSpendTransactionResult* cCreateSparkSpendTransaction(
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FFI_PLUGIN_EXPORT
|
||||
SelectedSparkSpendCoins* getCoinsToSpend(int64_t spendAmount, CCSparkMintMeta* coins, int coinsLength) {
|
||||
std::vector<CSparkMintMeta> coinsToSpend_out;
|
||||
int64_t change;
|
||||
|
||||
std::list<CSparkMintMeta> _coins;
|
||||
|
||||
for (int i = 0; i < coinsLength; i++) {
|
||||
CSparkMintMeta meta;
|
||||
meta.nHeight = coins[i].height;
|
||||
meta.nId = coins[i].id;
|
||||
meta.isUsed = coins[i].isUsed > 0;
|
||||
meta.txid = uint256S(coins[i].txid);
|
||||
meta.i = coins[i].i;
|
||||
meta.d = std::vector<unsigned char>(coins[i].d, coins[i].d + coins[i].dLength);
|
||||
meta.v = coins[i].v;
|
||||
meta.k = Scalar(coins[i].k);
|
||||
meta.memo = std::string(coins[i].memo, coins[i].memoLength);
|
||||
meta.serial_context = std::vector<unsigned char>(coins[i].serial_context, coins[i].serial_context + coins[i].serial_contextLength);
|
||||
meta.type = coins[i].type;
|
||||
meta.coin = deserializeCoin(coins[i].serializedCoin, coins[i].serializedCoinLength);
|
||||
|
||||
_coins.push_back(meta);
|
||||
}
|
||||
|
||||
GetCoinsToSpend(spendAmount, coinsToSpend_out, _coins, change);
|
||||
|
||||
SelectedSparkSpendCoins* result = (SelectedSparkSpendCoins*)malloc(sizeof(SelectedSparkSpendCoins));
|
||||
result->changeToMint = change;
|
||||
result->length = coinsToSpend_out.size();
|
||||
result->list = (CCSparkMintMeta*)malloc(sizeof(CCSparkMintMeta) * result->length);
|
||||
|
||||
for (int i = 0; i < coinsToSpend_out.size(); i++) {
|
||||
result->list[i].height = coinsToSpend_out[i].nHeight;
|
||||
|
||||
result->list[i].id = coinsToSpend_out[i].nId;
|
||||
|
||||
result->list[i].isUsed = coinsToSpend_out[i].isUsed;
|
||||
|
||||
result->list[i].txid = (const char*)malloc(sizeof(const char*) * coinsToSpend_out[i].txid.size());
|
||||
memcpy(result->list[i].txid, coinsToSpend_out[i].txid.begin(), sizeof(const char*) * coinsToSpend_out[i].txid.size());
|
||||
|
||||
result->list[i].i = coinsToSpend_out[i].i;
|
||||
|
||||
result->list[i].dLength = coinsToSpend_out[i].d.size();
|
||||
result->list[i].d = (const unsigned char*)malloc(sizeof(const unsigned char) * coinsToSpend_out[i].d.size());
|
||||
memcpy(result->list[i].d, coinsToSpend_out[i].d.begin(), sizeof(const unsigned char*) * coinsToSpend_out[i].d.size());
|
||||
|
||||
result->list[i].v = coinsToSpend_out[i].v;
|
||||
|
||||
result->list[i].kLength = coinsToSpend_out[i].k.s;
|
||||
result->list[i].k = (const unsigned char*)malloc(sizeof(const unsigned char) * coinsToSpend_out[i].k.size());
|
||||
memcpy(result->list[i].k, coinsToSpend_out[i].k.begin(), sizeof(const unsigned char*) * coinsToSpend_out[i].k.size());
|
||||
|
||||
result->list[i].memo = (const char*)malloc(sizeof(const char) * coinsToSpend_out[i].memo.size());
|
||||
memcpy(result->list[i].memo, coinsToSpend_out[i].memo.begin(), sizeof(const char*) * coinsToSpend_out[i].memo.size());
|
||||
result->list[i].memoLength = coinsToSpend_out[i].memo.size();
|
||||
|
||||
result->list[i].serial_context = (unsigned char*)malloc(sizeof(unsigned char) * coinsToSpend_out[i].serial_context.size());
|
||||
memcpy(result->list[i].serial_context, coinsToSpend_out[i].serial_context.begin(), sizeof(unsigned char*) * coinsToSpend_out[i].serial_context.size());
|
||||
result->list[i].serial_contextLength = coinsToSpend_out[i].serial_context.size();
|
||||
|
||||
CDataStream coinStream(SER_NETWORK, PROTOCOL_VERSION);
|
||||
coinStream << coinsToSpend_out[i].coin;
|
||||
result->list[i].serializedCoinLength = coinStream.size();
|
||||
result->list[i].serializedCoin = (unsigned char*)malloc(sizeof(unsigned char) * coinStream.size());
|
||||
memcpy(result->list[i].serializedCoin, coinStream.data(), coinStream.size());
|
||||
|
||||
result->list[i].type = coinsToSpend_out[i].type;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -75,4 +75,11 @@ struct SparkSpendTransactionResult* cCreateSparkSpendTransaction(
|
||||
int cover_set_data_allLength
|
||||
);
|
||||
|
||||
FFI_PLUGIN_EXPORT
|
||||
struct SelectedSparkSpendCoins* getCoinsToSpend(
|
||||
int64_t spendAmount,
|
||||
struct CCSparkMintMeta* coins,
|
||||
int coinLength
|
||||
);
|
||||
|
||||
#endif //ORG_FIRO_SPARK_DART_INTERFACE_H
|
||||
|
||||
@@ -119,33 +119,32 @@ struct CCDataStream {
|
||||
*
|
||||
* CSparkMintMeta: https://github.com/firoorg/sparkmobile/blob/8bf17cd3deba6c3b0d10e89282e02936d7e71cdd/src/primitives.h#L9
|
||||
*/
|
||||
//struct CCSparkMintMeta {
|
||||
// uint64_t height;
|
||||
// const char *id;
|
||||
// int isUsed;
|
||||
// const char *txid;
|
||||
// uint64_t i; // Diversifier.
|
||||
// const unsigned char *d; // Encrypted diversifier.
|
||||
// int dLength;
|
||||
// uint64_t v; // Value.
|
||||
// const unsigned char *k; // Nonce.
|
||||
// int kLength;
|
||||
// const char *memo;
|
||||
// int memoLength;
|
||||
// unsigned char *serial_context;
|
||||
// int serial_contextLength;
|
||||
// char type;
|
||||
// struct CCDataStream coin;
|
||||
//
|
||||
// CCSparkMintMeta(uint64_t height, const char *id, int isUsed,
|
||||
// const char *txid, uint64_t i, const unsigned char *d,
|
||||
// int dLength, uint64_t v, const unsigned char *k,
|
||||
// int kLength, const char *memo, int memoLength,
|
||||
// unsigned char *serial_context, int serial_contextLength,
|
||||
// char type, const CCDataStream &coinData);
|
||||
//
|
||||
// ~CCSparkMintMeta();
|
||||
//};
|
||||
struct CCSparkMintMeta {
|
||||
int height;
|
||||
int id;
|
||||
int isUsed;
|
||||
unsigned char *txid;
|
||||
uint64_t i; // Diversifier.
|
||||
const unsigned char *d; // Encrypted diversifier.
|
||||
int dLength;
|
||||
uint64_t v; // Value.
|
||||
const unsigned char *k; // Nonce.
|
||||
int kLength;
|
||||
const char *memo;
|
||||
int memoLength;
|
||||
unsigned char *serial_context;
|
||||
int serial_contextLength;
|
||||
char type;
|
||||
unsigned char* serializedCoin;
|
||||
int serializedCoinLength;
|
||||
};
|
||||
|
||||
struct SelectedSparkSpendCoins {
|
||||
struct CCSparkMintMeta* list;
|
||||
int length;
|
||||
|
||||
int64_t changeToMint;
|
||||
};
|
||||
|
||||
/*
|
||||
* FFI-friendly wrapper for a spark::CoverSetData.
|
||||
|
||||
Reference in New Issue
Block a user