mirror of
https://github.com/MAGICGrants/flutter_libsparkmobile.git
synced 2026-01-09 21:17:56 -05:00
WIP id and recover spark coin
This commit is contained in:
@@ -88,18 +88,57 @@ abstract final class LibSpark {
|
||||
String serializedCoin, {
|
||||
required String privateKeyHex,
|
||||
required int index,
|
||||
bool isTestNet = false,
|
||||
}) {
|
||||
final result = _bindings.identifyCoin(
|
||||
final result = _bindings.idAndRecoverCoin(
|
||||
serializedCoin.toNativeUtf8().cast<Char>(),
|
||||
serializedCoin.length,
|
||||
privateKeyHex.toNativeUtf8().cast<Char>(),
|
||||
index,
|
||||
isTestNet ? 1 : 0,
|
||||
);
|
||||
|
||||
throw UnimplementedError(
|
||||
"Have ffi return all the data we need for SparkCoin."
|
||||
" This may or may not encompass all fields currently in SparkCoin.",
|
||||
final LibSparkCoinType coinType;
|
||||
switch (result.ref.type) {
|
||||
case 0:
|
||||
coinType = LibSparkCoinType.mint;
|
||||
break;
|
||||
case 1:
|
||||
coinType = LibSparkCoinType.mint;
|
||||
break;
|
||||
default:
|
||||
throw Exception("Unknown coin type \"${result.ref.type}\" found.");
|
||||
}
|
||||
|
||||
final ret = LibSparkCoin(
|
||||
type: coinType,
|
||||
nonce: result.ref.nonce.toUint8List(result.ref.nonceLength),
|
||||
address: result.ref.address.cast<Utf8>().toDartString(),
|
||||
value: BigInt.from(result.ref.value),
|
||||
memo: result.ref.memo.cast<Utf8>().toDartString(),
|
||||
diversifier: BigInt.from(result.ref.diversifier),
|
||||
encryptedDiversifier:
|
||||
result.ref.serial.toUint8List(result.ref.encryptedDiversifierLength),
|
||||
serial: result.ref.serial.toUint8List(result.ref.serialLength),
|
||||
lTagHash: result.ref.lTagHash.cast<Utf8>().toDartString(),
|
||||
);
|
||||
|
||||
malloc.free(result.ref.address);
|
||||
malloc.free(result.ref.memo);
|
||||
malloc.free(result.ref.lTagHash);
|
||||
malloc.free(result.ref.encryptedDiversifier);
|
||||
malloc.free(result.ref.nonce);
|
||||
malloc.free(result.ref.serial);
|
||||
malloc.free(result);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
extension on Pointer<UnsignedChar> {
|
||||
Uint8List toUint8List(int length) {
|
||||
// TODO needs free?
|
||||
return Uint8List.fromList(cast<Uint8>().asTypedList(length));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,34 @@ class FlutterLibsparkmobileBindings {
|
||||
CIdentifiedCoinData Function(
|
||||
ffi.Pointer<ffi.Char>, int, ffi.Pointer<ffi.Char>, int)>();
|
||||
|
||||
ffi.Pointer<AggregateCoinData> idAndRecoverCoin(
|
||||
ffi.Pointer<ffi.Char> serializedCoin,
|
||||
int serializedCoinLength,
|
||||
ffi.Pointer<ffi.Char> keyDataHex,
|
||||
int index,
|
||||
int isTestNet,
|
||||
) {
|
||||
return _idAndRecoverCoin(
|
||||
serializedCoin,
|
||||
serializedCoinLength,
|
||||
keyDataHex,
|
||||
index,
|
||||
isTestNet,
|
||||
);
|
||||
}
|
||||
|
||||
late final _idAndRecoverCoinPtr = _lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Pointer<AggregateCoinData> Function(
|
||||
ffi.Pointer<ffi.Char>,
|
||||
ffi.Int,
|
||||
ffi.Pointer<ffi.Char>,
|
||||
ffi.Int,
|
||||
ffi.Int)>>('idAndRecoverCoin');
|
||||
late final _idAndRecoverCoin = _idAndRecoverCoinPtr.asFunction<
|
||||
ffi.Pointer<AggregateCoinData> Function(
|
||||
ffi.Pointer<ffi.Char>, int, ffi.Pointer<ffi.Char>, int, int)>();
|
||||
|
||||
/// FFI-friendly wrapper for spark::createSparkMintRecipients.
|
||||
///
|
||||
/// createSparkMintRecipients: https://github.com/firoorg/sparkmobile/blob/8bf17cd3deba6c3b0d10e89282e02936d7e71cdd/src/spark.cpp#L43
|
||||
@@ -396,3 +424,36 @@ final class OutputScript extends ffi.Struct {
|
||||
@ffi.Int()
|
||||
external int length;
|
||||
}
|
||||
|
||||
/// Aggregate data structure to handle passing spark mint/spend data across FFI
|
||||
final class AggregateCoinData extends ffi.Struct {
|
||||
@ffi.Char()
|
||||
external int type;
|
||||
|
||||
@ffi.Uint64()
|
||||
external int diversifier;
|
||||
|
||||
@ffi.Uint64()
|
||||
external int value;
|
||||
|
||||
external ffi.Pointer<ffi.Char> address;
|
||||
|
||||
external ffi.Pointer<ffi.Char> memo;
|
||||
|
||||
external ffi.Pointer<ffi.Char> lTagHash;
|
||||
|
||||
external ffi.Pointer<ffi.UnsignedChar> encryptedDiversifier;
|
||||
|
||||
@ffi.Int()
|
||||
external int encryptedDiversifierLength;
|
||||
|
||||
external ffi.Pointer<ffi.UnsignedChar> serial;
|
||||
|
||||
@ffi.Int()
|
||||
external int serialLength;
|
||||
|
||||
external ffi.Pointer<ffi.UnsignedChar> nonce;
|
||||
|
||||
@ffi.Int()
|
||||
external int nonceLength;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ enum LibSparkCoinType {
|
||||
class LibSparkCoin {
|
||||
final LibSparkCoinType type;
|
||||
|
||||
final Uint8List? k; // TODO: proper name (not single char!!) is this nonce???
|
||||
final Uint8List? nonce;
|
||||
|
||||
final String? address;
|
||||
|
||||
@@ -26,11 +26,11 @@ class LibSparkCoin {
|
||||
final Uint8List? serial;
|
||||
final Uint8List? tag;
|
||||
|
||||
final Uint8List? lTagHash;
|
||||
final String? lTagHash;
|
||||
|
||||
LibSparkCoin({
|
||||
required this.type,
|
||||
this.k,
|
||||
this.nonce,
|
||||
this.address,
|
||||
this.value,
|
||||
this.memo,
|
||||
@@ -44,7 +44,7 @@ class LibSparkCoin {
|
||||
|
||||
LibSparkCoin copyWith({
|
||||
LibSparkCoinType? type,
|
||||
Uint8List? k,
|
||||
Uint8List? nonce,
|
||||
String? address,
|
||||
BigInt? value,
|
||||
String? memo,
|
||||
@@ -53,11 +53,11 @@ class LibSparkCoin {
|
||||
Uint8List? encryptedDiversifier,
|
||||
Uint8List? serial,
|
||||
Uint8List? tag,
|
||||
Uint8List? lTagHash,
|
||||
String? lTagHash,
|
||||
}) {
|
||||
return LibSparkCoin(
|
||||
type: type ?? this.type,
|
||||
k: k ?? this.k,
|
||||
nonce: nonce ?? this.nonce,
|
||||
address: address ?? this.address,
|
||||
value: value ?? this.value,
|
||||
memo: memo ?? this.memo,
|
||||
@@ -74,7 +74,7 @@ class LibSparkCoin {
|
||||
String toString() {
|
||||
return 'LibSparkCoin('
|
||||
', type: $type'
|
||||
', k: $k'
|
||||
', k: $nonce'
|
||||
', address: $address'
|
||||
', value: $value'
|
||||
', memo: $memo'
|
||||
|
||||
@@ -61,6 +61,63 @@ CIdentifiedCoinData identifyCoin(const char* serializedCoin, int serializedCoinL
|
||||
}
|
||||
}
|
||||
|
||||
FFI_PLUGIN_EXPORT
|
||||
AggregateCoinData* idAndRecoverCoin(
|
||||
const char* serializedCoin,
|
||||
int serializedCoinLength,
|
||||
const char* keyDataHex,
|
||||
int index,
|
||||
int isTestNet) {
|
||||
try {
|
||||
spark::Coin coin = deserializeCoin(serializedCoin, serializedCoinLength);
|
||||
|
||||
// Derive the incoming view key from the key data and index.
|
||||
spark::SpendKey spendKey = createSpendKeyFromData(keyDataHex, index);
|
||||
spark::FullViewKey fullViewKey(spendKey);
|
||||
spark::IncomingViewKey incomingViewKey(fullViewKey);
|
||||
|
||||
spark::IdentifiedCoinData identifiedCoinData = coin.identify(incomingViewKey);
|
||||
|
||||
spark::RecoveredCoinData data = coin.recover(fullViewKey, identifiedCoinData);
|
||||
|
||||
spark::Address address = getAddress(incomingViewKey, identifiedCoinData.i);
|
||||
std::string addressString = address.encode(isTestNet ? spark::ADDRESS_NETWORK_TESTNET : spark::ADDRESS_NETWORK_MAINNET);
|
||||
|
||||
AggregateCoinData* result = (AggregateCoinData*)malloc(sizeof(AggregateCoinData));
|
||||
|
||||
result->type = coin.type;
|
||||
result->diversifier = identifiedCoinData.i;
|
||||
result->value = identifiedCoinData.v;
|
||||
|
||||
result->address = (char*)malloc((addressString.length() + 1) * sizeof(char));
|
||||
std::strcpy(result->address, addressString.c_str());
|
||||
|
||||
result->memo = (char*)malloc((identifiedCoinData.memo.length() + 1) * sizeof(char));
|
||||
std::strcpy(result->memo, identifiedCoinData.memo.c_str());
|
||||
|
||||
uint256 lTagHash = primitives::GetLTagHash(data.T);
|
||||
result->lTagHash = (char*)malloc((lTagHash.GetHex().length() + 1) * sizeof(char));
|
||||
std::strcpy(result->lTagHash, lTagHash.GetHex().c_str());
|
||||
|
||||
result->encryptedDiversifier = (unsigned char*)malloc(identifiedCoinData.d.size() * sizeof(unsigned char));
|
||||
result->encryptedDiversifierLength = identifiedCoinData.d.size();
|
||||
memcpy(result->encryptedDiversifier,identifiedCoinData.d.data(),identifiedCoinData.d.size() * sizeof(unsigned char));
|
||||
|
||||
result->nonce = (unsigned char*)malloc(32 * sizeof(unsigned char));
|
||||
result->nonceLength = 32;
|
||||
identifiedCoinData.k.serialize(result->nonce);
|
||||
|
||||
result->serial = (unsigned char*)malloc(data.s.GetHex().length() * sizeof(unsigned char));
|
||||
result->serialLength = data.s.GetHex().length();
|
||||
memcpy(result->serial,data.s.GetHex().c_str(),data.s.GetHex().length() * sizeof(unsigned char));
|
||||
|
||||
return result;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Exception: " << e.what() << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* FFI-friendly wrapper for spark::createSparkMintRecipients.
|
||||
*
|
||||
|
||||
@@ -34,6 +34,15 @@ const char* createIncomingViewKey(const char* keyData, int index);
|
||||
FFI_PLUGIN_EXPORT
|
||||
struct CIdentifiedCoinData identifyCoin(const char* serializedCoin, int serializedCoinLength, const char* keyDataHex, int index);
|
||||
|
||||
FFI_PLUGIN_EXPORT
|
||||
struct AggregateCoinData* idAndRecoverCoin(
|
||||
const char* serializedCoin,
|
||||
int serializedCoinLength,
|
||||
const char* keyDataHex,
|
||||
int index,
|
||||
int isTestNet
|
||||
);
|
||||
|
||||
/*
|
||||
* FFI-friendly wrapper for spark::createSparkMintRecipients.
|
||||
*
|
||||
|
||||
@@ -169,6 +169,41 @@ struct OutputScript {
|
||||
int length;
|
||||
};
|
||||
|
||||
/*
|
||||
* Aggregate data structure to handle passing spark mint/spend data across FFI
|
||||
*/
|
||||
struct AggregateCoinData {
|
||||
char type;
|
||||
uint64_t diversifier;
|
||||
uint64_t value;
|
||||
char *address;
|
||||
char *memo;
|
||||
char *lTagHash;
|
||||
|
||||
unsigned char *encryptedDiversifier;
|
||||
int encryptedDiversifierLength;
|
||||
|
||||
unsigned char *serial;
|
||||
int serialLength;
|
||||
|
||||
unsigned char *nonce;
|
||||
int nonceLength;
|
||||
};
|
||||
|
||||
|
||||
//struct IdentifiedCoinData {
|
||||
// uint64_t i; // diversifier
|
||||
// std::vector<unsigned char> d; // encrypted diversifier
|
||||
// uint64_t v; // value
|
||||
// Scalar k; // nonce
|
||||
// std::string memo; // memo
|
||||
//};
|
||||
//
|
||||
//struct RecoveredCoinData {
|
||||
// Scalar s; // serial
|
||||
// GroupElement T; // tag
|
||||
//};
|
||||
|
||||
//#ifdef __cplusplus
|
||||
//}
|
||||
//#endif
|
||||
|
||||
Reference in New Issue
Block a user