WIP id and recover spark coin

This commit is contained in:
julian
2023-12-04 13:21:25 -06:00
parent 3ae2cf44ce
commit 5c6a10195f
6 changed files with 212 additions and 11 deletions

View File

@@ -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));
}
}

View File

@@ -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;
}

View File

@@ -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'

View File

@@ -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.
*

View File

@@ -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.
*

View File

@@ -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