WIP spark spend ffi binding

This commit is contained in:
julian
2023-12-08 12:18:48 -06:00
parent 1c7903a859
commit e2765a2fca
7 changed files with 449 additions and 418 deletions

View File

@@ -223,18 +223,140 @@ abstract final class LibSpark {
}
///
/// Attempt to create and sign a spark spend transaction.
/// Attempt to create a spark spend.
///
/// Returns the raw transaction hex if successful, otherwise null.
/// Returns the serialized spark spend.
///
static String? createSparkSendTransaction({
static ({
String serializedSpendPayload,
List<Uint8List> outputScripts,
int fee,
}) createSparkSendTransaction({
required String privateKeyHex,
// TODO what do we need?
int index = 1,
required List<({String address, int amount, bool subtractFeeFromAmount})>
recipients,
required List<
({
String sparkAddress,
int amount,
bool subtractFeeFromAmount,
String memo
})>
privateRecipients,
required List<Uint8List> serializedMintMetas,
required List<
({
int setId,
String setHash,
List<({String serializedCoin, String txHash})> set
})>
allAnonymitySets,
}) {
// TODO allocate/create data structures required by the generated bindings
final privateKeyPtr =
privateKeyHex.to32BytesFromHex().unsignedCharPointer();
// some kind of failure
return null;
final recipientsPtr =
malloc.allocate<CRecip>(sizeOf<CRecip>() * recipients.length);
for (int i = 0; i < recipients.length; i++) {
recipientsPtr[i].amount = recipients[i].amount;
recipientsPtr[i].subtractFee =
recipients[i].subtractFeeFromAmount ? 1 : 0;
}
final privateRecipientsPtr = malloc.allocate<COutputRecipient>(
sizeOf<COutputRecipient>() * recipients.length);
for (int i = 0; i < recipients.length; i++) {
privateRecipientsPtr[i].subtractFee =
recipients[i].subtractFeeFromAmount ? 1 : 0;
privateRecipientsPtr[i].output =
malloc.allocate<COutputCoinData>(sizeOf<COutputCoinData>());
privateRecipientsPtr[i].output.ref.value = privateRecipients[i].amount;
privateRecipientsPtr[i].output.ref.memo =
privateRecipients[i].memo.toNativeUtf8().cast<Char>();
privateRecipientsPtr[i].output.ref.address =
privateRecipients[i].sparkAddress.toNativeUtf8().cast<Char>();
}
final serializedMintMetasPtr = malloc.allocate<CCDataStream>(
sizeOf<CCDataStream>() * serializedMintMetas.length);
for (int i = 0; i < serializedMintMetas.length; i++) {
serializedMintMetasPtr[i].data =
serializedMintMetas[i].unsignedCharPointer();
serializedMintMetasPtr[i].length = serializedMintMetas[i].length;
}
final coverSetDataAllPtr = malloc.allocate<CCoverSetData>(
sizeOf<CCoverSetData>() * allAnonymitySets.length);
for (int i = 0; i < allAnonymitySets.length; i++) {
coverSetDataAllPtr[i].setId = allAnonymitySets[i].setId;
coverSetDataAllPtr[i].cover_set = malloc.allocate<CCDataStream>(
sizeOf<CCDataStream>() * allAnonymitySets[i].set.length);
coverSetDataAllPtr[i].cover_setLength = allAnonymitySets[i].set.length;
for (int j = 0; j < allAnonymitySets[i].set.length; j++) {
final b64CoinDecoded =
base64Decode(allAnonymitySets[i].set[j].serializedCoin);
coverSetDataAllPtr[i].cover_set[j].length = b64CoinDecoded.length;
coverSetDataAllPtr[i].cover_set[j].data =
b64CoinDecoded.unsignedCharPointer();
}
final setHash = base64Decode(allAnonymitySets[i].setHash);
coverSetDataAllPtr[i].cover_set_representation =
setHash.unsignedCharPointer();
coverSetDataAllPtr[i].cover_set_representationLength = setHash.length;
}
final result = _bindings.cCreateSparkSpendTransaction(
privateKeyPtr,
index,
recipientsPtr,
recipients.length,
privateRecipientsPtr,
privateRecipients.length,
serializedMintMetasPtr,
serializedMintMetas.length,
coverSetDataAllPtr,
allAnonymitySets.length,
);
// todo: more comprehensive frees
malloc.free(privateKeyPtr);
malloc.free(recipientsPtr);
malloc.free(privateRecipientsPtr);
malloc.free(serializedMintMetasPtr);
malloc.free(coverSetDataAllPtr);
if (result.address == nullptr.address) {
throw Exception(
"createSparkSendTransaction() failed for an unknown reason",
);
}
final messageBytes = result.ref.data.toUint8List(result.ref.dataLength);
final message = utf8.decode(messageBytes);
malloc.free(result.ref.data);
if (result.ref.isError > 0) {
throw Exception(message);
}
final fee = result.ref.fee;
final List<Uint8List> scripts = [];
for (int i = 0; i < result.ref.outputScriptsLength; i++) {
final script = result.ref.outputScripts[i].bytes
.toUint8List(result.ref.outputScripts[i].length);
malloc.free(result.ref.outputScripts[i].bytes);
scripts.add(script);
}
malloc.free(result.ref.outputScripts);
return (serializedSpendPayload: message, fee: fee, outputScripts: scripts);
}
}

View File

@@ -136,22 +136,17 @@ class FlutterLibsparkmobileBindings {
/// FFI-friendly wrapper for spark::createSparkSpendTransaction.
///
/// createSparkSpendTransaction: https://github.com/firoorg/sparkmobile/blob/23099b0d9010a970ad75b9cfe05d568d634088f3/src/spark.cpp#L190
ffi.Pointer<ffi.UnsignedChar> cCreateSparkSpendTransaction(
ffi.Pointer<SparkSpendTransactionResult> cCreateSparkSpendTransaction(
ffi.Pointer<ffi.UnsignedChar> keyData,
int index,
ffi.Pointer<CRecip> recipients,
int recipientsLength,
ffi.Pointer<COutputRecipient> privateRecipients,
int privateRecipientsLength,
ffi.Pointer<CCSparkMintMeta> coins,
int coinsLength,
ffi.Pointer<CCoverSets> cover_set_data_all,
ffi.Pointer<CCDataStream> serializedMintMetas,
int serializedMintMetasLength,
ffi.Pointer<CCoverSetData> cover_set_data_all,
int cover_set_data_allLength,
ffi.Pointer<ffi.Char> txHashSig,
int txHashSigLength,
int fee,
ffi.Pointer<OutputScript> outputScripts,
int outputScriptsLength,
) {
return _cCreateSparkSpendTransaction(
keyData,
@@ -160,53 +155,38 @@ class FlutterLibsparkmobileBindings {
recipientsLength,
privateRecipients,
privateRecipientsLength,
coins,
coinsLength,
serializedMintMetas,
serializedMintMetasLength,
cover_set_data_all,
cover_set_data_allLength,
txHashSig,
txHashSigLength,
fee,
outputScripts,
outputScriptsLength,
);
}
late final _cCreateSparkSpendTransactionPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<ffi.UnsignedChar> Function(
ffi.Pointer<SparkSpendTransactionResult> Function(
ffi.Pointer<ffi.UnsignedChar>,
ffi.Int,
ffi.Pointer<CRecip>,
ffi.Int,
ffi.Pointer<COutputRecipient>,
ffi.Int,
ffi.Pointer<CCSparkMintMeta>,
ffi.Pointer<CCDataStream>,
ffi.Int,
ffi.Pointer<CCoverSets>,
ffi.Int,
ffi.Pointer<ffi.Char>,
ffi.Int,
ffi.Uint64,
ffi.Pointer<OutputScript>,
ffi.Pointer<CCoverSetData>,
ffi.Int)>>('cCreateSparkSpendTransaction');
late final _cCreateSparkSpendTransaction =
_cCreateSparkSpendTransactionPtr.asFunction<
ffi.Pointer<ffi.UnsignedChar> Function(
ffi.Pointer<SparkSpendTransactionResult> Function(
ffi.Pointer<ffi.UnsignedChar>,
int,
ffi.Pointer<CRecip>,
int,
ffi.Pointer<COutputRecipient>,
int,
ffi.Pointer<CCSparkMintMeta>,
ffi.Pointer<CCDataStream>,
int,
ffi.Pointer<CCoverSets>,
int,
ffi.Pointer<ffi.Char>,
int,
int,
ffi.Pointer<OutputScript>,
ffi.Pointer<CCoverSetData>,
int)>();
}
@@ -337,75 +317,25 @@ final class COutputCoinData extends ffi.Struct {
///
/// See https://github.com/firoorg/sparkmobile/blob/23099b0d9010a970ad75b9cfe05d568d634088f3/src/spark.cpp#L195
final class COutputRecipient extends ffi.Struct {
external COutputCoinData output;
external ffi.Pointer<COutputCoinData> output;
@ffi.Int()
external int subtractFee;
}
final class CCDataStream extends ffi.Struct {
external ffi.Pointer<ffi.Char> data;
external ffi.Pointer<ffi.UnsignedChar> data;
@ffi.Int()
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.Uint64()
external int height;
external ffi.Pointer<ffi.Char> id;
@ffi.Int()
external int isUsed;
external ffi.Pointer<ffi.Char> 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 CCDataStream coin;
}
/// FFI-friendly wrapper for a spark::CoverSetData.
///
/// CoverSetData: https://github.com/firoorg/sparkmobile/blob/8bf17cd3deba6c3b0d10e89282e02936d7e71cdd/src/spend_transaction.h#L28
final class CCoverSetData extends ffi.Struct {
/// vs. struct CCoin* cover_set;
external ffi.Pointer<ffi.Pointer<CCDataStream>> cover_set;
external ffi.Pointer<CCDataStream> cover_set;
@ffi.Int()
external int cover_setLength;
@@ -414,16 +344,9 @@ final class CCoverSetData extends ffi.Struct {
@ffi.Int()
external int cover_set_representationLength;
}
/// FFI-friendly wrapper for a std::unordered_map<uint64_t, spark::CoverSetData>.
///
/// See https://github.com/firoorg/sparkmobile/blob/23099b0d9010a970ad75b9cfe05d568d634088f3/src/spark.cpp#L197
final class CCoverSets extends ffi.Struct {
external ffi.Pointer<CCoverSetData> cover_sets;
@ffi.Int()
external int cover_setsLength;
external int setId;
}
final class OutputScript extends ffi.Struct {
@@ -465,3 +388,24 @@ final class AggregateCoinData extends ffi.Struct {
@ffi.Int()
external int nonceLength;
}
/// Aggregate data structure to handle passing spark spend data across FFI.
///
/// Contains the serialized transaction or the error message if isError is true.
final class SparkSpendTransactionResult extends ffi.Struct {
external ffi.Pointer<ffi.UnsignedChar> data;
@ffi.Int()
external int dataLength;
external ffi.Pointer<OutputScript> outputScripts;
@ffi.Int()
external int outputScriptsLength;
@ffi.Int()
external int fee;
@ffi.Int()
external int isError;
}

View File

@@ -174,25 +174,17 @@ CCRecipientList* cCreateSparkMintRecipients(
* createSparkSpendTransaction: https://github.com/firoorg/sparkmobile/blob/23099b0d9010a970ad75b9cfe05d568d634088f3/src/spark.cpp#L190
*/
FFI_PLUGIN_EXPORT
unsigned char* cCreateSparkSpendTransaction(
SparkSpendTransactionResult* cCreateSparkSpendTransaction(
unsigned char* keyData,
int index,
struct CRecip* recipients, // This CRecip(ient) is not the same as a CRecipient.
int recipientsLength,
struct COutputRecipient* privateRecipients,
int privateRecipientsLength,
struct CCSparkMintMeta* coins,
int coinsLength,
struct CCoverSets* cover_set_data_all,
int cover_set_data_allLength,
// idAndBlockHashes_all is unused in spark::createSparkSpendTransaction so we ignore it here.
const char* txHashSig, // A hex string.
int txHashSigLength,
uint64_t fee,
// spark's createSparkSpendTransaction returns void, writing to the serializedSpend parameter.
// We return it instead.
const OutputScript* outputScripts,
int outputScriptsLength
struct CCDataStream* serializedMintMetas,
int serializedMintMetasLength,
struct CCoverSetData* cover_set_data_all,
int cover_set_data_allLength
) {
try {
// Derive the keys from the key data and index.
@@ -209,66 +201,50 @@ unsigned char* cCreateSparkSpendTransaction(
// Convert COutputRecipient* privateRecipients to std::vector<std::pair<spark::OutputCoinData, bool>> cppPrivateRecipients.
std::vector<std::pair<spark::OutputCoinData, bool>> cppPrivateRecipients;
for (int i = 0; i < privateRecipientsLength; i++) {
cppPrivateRecipients.push_back(std::make_pair(fromFFI(privateRecipients[i].output), privateRecipients[i].subtractFee));
spark::OutputCoinData outputCoinData;
outputCoinData.memo = std::string(privateRecipients[i].output->memo);
outputCoinData.v = (uint64_t)privateRecipients[i].output->value;
std::string addrString = privateRecipients[i].output->address;
outputCoinData.address = decodeAddress(addrString);
cppPrivateRecipients.push_back(std::make_pair(outputCoinData, privateRecipients[i].subtractFee));
}
// Convert CCSparkMintMeta* coins to std::list<CSparkMintMeta> cppCoins.
// Convert CCSparkMintMeta* serializedMintMetas to std::list<CSparkMintMeta> cppCoins.
std::list<CSparkMintMeta> cppCoins;
for (int i = 0; i < coinsLength; i++) {
cppCoins.push_back(fromFFI(coins[i]));
for (int i = 0; i < serializedMintMetasLength; i++) {
std::vector<unsigned char> vec(serializedMintMetas[i].data, serializedMintMetas[i].data + serializedMintMetas[i].length);
CDataStream stream(vec, SER_NETWORK, PROTOCOL_VERSION);
CSparkMintMeta meta;
stream >> meta;
cppCoins.push_back(meta);
}
// Convert CCoverSets* cover_set_data_all to a std::unordered_map<uint64_t, spark::CoverSetData> cppCoverSetDataAll
// TODO verify correctness.
std::unordered_map<uint64_t, spark::CoverSetData> cppCoverSetDataAll;
for (int i = 0; i < cover_set_data_allLength; i++) {
for (int j = 0; j < cover_set_data_all[i].cover_setsLength; j++) {
// Convert CCoverSetData to vector of Coins.
for (int j = 0; j < cover_set_data_all[i].cover_setLength; j++) {
std::vector<spark::Coin> cppCoverSetCoins;
spark::Coin coin = fromFFI(*cover_set_data_all[i].cover_sets[j].cover_set[0]);
spark::Coin coin = fromFFI(cover_set_data_all[i].cover_set[j]);
cppCoverSetCoins.push_back(coin);
// Convert CCoverSetData to vector of vector of unsigned chars.
std::vector<CCoverSetData> coverSets(cover_set_data_all[i].cover_sets, cover_set_data_all[i].cover_sets + cover_set_data_all[i].cover_setsLength);
// Combine all the cover set representations into one vector.
std::vector<unsigned char> coverSetReps;
for (int k = 0; k < cover_set_data_all[i].cover_setsLength; k++) {
for (int l = 0; l < cover_set_data_all[i].cover_sets[k].cover_set_representationLength; l++) {
coverSetReps.push_back(cover_set_data_all[i].cover_sets[k].cover_set_representation[l]);
}
}
// Construct spark::CoverSetData.
spark::CoverSetData cppCoverSetData;
cppCoverSetData.cover_set = cppCoverSetCoins;
cppCoverSetData.cover_set_representation = coverSetReps;
cppCoverSetData.cover_set_representation = std::vector<unsigned char>(cover_set_data_all[i].cover_set_representation, cover_set_data_all[i].cover_set_representation + cover_set_data_all[i].cover_set_representationLength);
cppCoverSetDataAll[cover_set_data_all[i].cover_sets[j].cover_setLength] = cppCoverSetData;
cppCoverSetDataAll[cover_set_data_all[i].setId] = cppCoverSetData;
}
}
// Convert const char * txHashSig to a uint256 cppTxHashSig.
// uint256 cppTxHashSig(txHashSig); // This throws `error: no matching function for call to uint256::uint256(const char*&)`, we need to:
uint256 cppTxHashSig;
cppTxHashSig.SetHex(txHashSig);
// Make a CAmount cppFee from fee.
CAmount cppFee = fee;
// Make a dummy for idAndBlockHashes_all.
// Required but unused params.
std::map<uint64_t, uint256> cppIdAndBlockHashesAll;
uint256 cppTxHashSig;
// Make a placeholder for serializedSpend.
// Output data
std::vector<uint8_t> cppSerializedSpend;
// Convert outputScripts to std::vector<std::vector<unsigned char>> cppOutputScripts.
CAmount cppFee;
std::vector<std::vector<unsigned char>> cppOutputScripts;
for (int i = 0; i < outputScriptsLength; i++) {
for (int j = 0; j < outputScripts[i].length; j++) {
cppOutputScripts[i].push_back(outputScripts[i].bytes[j]);
}
}
// Call spark::createSparkSpendTransaction.
createSparkSpendTransaction(
@@ -286,18 +262,32 @@ unsigned char* cCreateSparkSpendTransaction(
cppOutputScripts
);
// Allocate memory for the C-style string.
unsigned char* cstr = new unsigned char[cppSerializedSpend.size()];
SparkSpendTransactionResult *result = (SparkSpendTransactionResult*)malloc(sizeof(SparkSpendTransactionResult));
result->isError = false;
result->fee = cppFee;
// Copy the data from the vector to the array.
for (int i = 0; i < cppSerializedSpend.size(); i++) {
cstr[i] = cppSerializedSpend[i];
result->outputScriptsLength = cppOutputScripts.size();
result->outputScripts = (OutputScript*)malloc(sizeof(OutputScript) * result->outputScriptsLength);
for (int i = 0; i < result->outputScriptsLength; i++) {
result->outputScripts[i].length = cppOutputScripts[i].size();
result->outputScripts[i].bytes = (unsigned char*)malloc(result->outputScripts[i].length);
memcpy(result->outputScripts[i].bytes, cppOutputScripts[i].data(), result->outputScripts[i].length);
}
// Return the array.
return cstr;
result->dataLength = cppSerializedSpend.size();
result->data = (unsigned char*)malloc(result->dataLength);
memcpy(result->data, cppSerializedSpend.data(), result->dataLength);
return result;
} catch (const std::exception& e) {
std::cerr << "Exception: " << e.what() << std::endl;
return nullptr;
SparkSpendTransactionResult *result = (SparkSpendTransactionResult*)malloc(sizeof(SparkSpendTransactionResult));
result->isError = true;
result->dataLength = strlen(e.what());
result->data = (unsigned char*)malloc(sizeof(unsigned char) * result->dataLength);
memcpy(result->data, e.what(), result->dataLength);
return result;
}
}

View File

@@ -62,22 +62,17 @@ struct CCRecipientList* cCreateSparkMintRecipients(
* createSparkSpendTransaction: https://github.com/firoorg/sparkmobile/blob/23099b0d9010a970ad75b9cfe05d568d634088f3/src/spark.cpp#L190
*/
FFI_PLUGIN_EXPORT
unsigned char* cCreateSparkSpendTransaction(
struct SparkSpendTransactionResult* cCreateSparkSpendTransaction(
unsigned char* keyData,
int index,
struct CRecip* recipients,
struct CRecip* recipients, // This CRecip(ient) is not the same as a CRecipient.
int recipientsLength,
struct COutputRecipient* privateRecipients,
int privateRecipientsLength,
struct CCSparkMintMeta* coins,
int coinsLength,
struct CCoverSets* cover_set_data_all,
int cover_set_data_allLength,
const char* txHashSig,
int txHashSigLength,
uint64_t fee,
const struct OutputScript* outputScripts,
int outputScriptsLength
struct CCDataStream* serializedMintMetas,
int serializedMintMetasLength,
struct CCoverSetData* cover_set_data_all,
int cover_set_data_allLength
);
#endif //ORG_FIRO_SPARK_DART_INTERFACE_H

View File

@@ -105,12 +105,12 @@ struct COutputCoinData {
* See https://github.com/firoorg/sparkmobile/blob/23099b0d9010a970ad75b9cfe05d568d634088f3/src/spark.cpp#L195
*/
struct COutputRecipient {
struct COutputCoinData output;
struct COutputCoinData* output;
int subtractFee;
};
struct CCDataStream {
const char *data;
unsigned char *data;
int length;
};
@@ -119,24 +119,24 @@ 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;
//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,
@@ -145,7 +145,7 @@ struct CCSparkMintMeta {
// char type, const CCDataStream &coinData);
//
// ~CCSparkMintMeta();
};
//};
/*
* FFI-friendly wrapper for a spark::CoverSetData.
@@ -153,21 +153,15 @@ struct CCSparkMintMeta {
* CoverSetData: https://github.com/firoorg/sparkmobile/blob/8bf17cd3deba6c3b0d10e89282e02936d7e71cdd/src/spend_transaction.h#L28
*/
struct CCoverSetData {
struct CCDataStream **cover_set; // vs. struct CCoin* cover_set;
struct CCDataStream *cover_set; // vs. struct CCoin* cover_set;
int cover_setLength;
const unsigned char *cover_set_representation;
int cover_set_representationLength;
int setId;
};
/*
* FFI-friendly wrapper for a std::unordered_map<uint64_t, spark::CoverSetData>.
*
* See https://github.com/firoorg/sparkmobile/blob/23099b0d9010a970ad75b9cfe05d568d634088f3/src/spark.cpp#L197
*/
struct CCoverSets {
struct CCoverSetData* cover_sets;
int cover_setsLength;
};
struct OutputScript {
unsigned char *bytes;
@@ -195,19 +189,22 @@ struct AggregateCoinData {
int nonceLength;
};
/*
* Aggregate data structure to handle passing spark spend data across FFI.
*
* Contains the serialized transaction or the error message if isError is true.
*/
struct SparkSpendTransactionResult {
unsigned char *data;
int dataLength;
//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
//};
struct OutputScript* outputScripts;
int outputScriptsLength;
int fee;
int isError;
};
//#ifdef __cplusplus
//}

View File

@@ -64,27 +64,12 @@ spark::Coin fromFFI(const CCoin& c_struct) {
spark::Coin fromFFI(CCDataStream& cdStream) {
spark::Coin coin;
CDataStream coinStream(cdStream.data, &cdStream.data[cdStream.length - 1], SER_NETWORK, PROTOCOL_VERSION);
std::vector<unsigned char> vec(cdStream.data, cdStream.data + cdStream.length);
CDataStream coinStream(vec, SER_NETWORK, PROTOCOL_VERSION);
coinStream >> coin;
return coin;
}
/*
* Utility function to convert a C++ Coin struct to an FFI-friendly C CCDataStream struct.
*/
CCDataStream toFFI(const spark::Coin& coin) {
// Serialize the Coin object into a CDataStream
CDataStream ccoinStream(SER_NETWORK, PROTOCOL_VERSION);
ccoinStream << coin;
CCDataStream ccStream;
ccStream.data = ccoinStream.data();
ccStream.length = ccoinStream.size();
return ccStream;
}
/*
* Utility function to convert a C++ IdentifiedCoinData struct to an FFI-friendly struct.
*/
@@ -355,27 +340,27 @@ CSparkMintMeta createCSparkMintMeta(
/*
* Utility function to convert a C++ CSparkMintMeta struct to an FFI-friendly C CCSparkMintMeta.
*/
CSparkMintMeta fromFFI(const CCSparkMintMeta& c_struct) {
CSparkMintMeta cpp_struct;
cpp_struct.nHeight = c_struct.height;
cpp_struct.nId = std::stoull(c_struct.id);
cpp_struct.isUsed = c_struct.isUsed;
cpp_struct.txid = uint256S(c_struct.txid);
cpp_struct.i = c_struct.i;
cpp_struct.d = std::vector<unsigned char>(c_struct.d, c_struct.d + c_struct.dLength);
cpp_struct.v = c_struct.v;
cpp_struct.k = bytesToScalar(c_struct.k, c_struct.kLength);
cpp_struct.memo = std::string(c_struct.memo);
cpp_struct.serial_context = std::vector<unsigned char>(c_struct.serial_context,
c_struct.serial_context +
c_struct.serial_contextLength);
cpp_struct.type = c_struct.type;
// c_struct.coin is a const, but we need a non-const reference to pass to fromFFI.
// Convert c_struct.coin to a non-const.
CCDataStream coin = c_struct.coin;
cpp_struct.coin = fromFFI(coin);
return cpp_struct;
}
//CSparkMintMeta fromFFI(const CCSparkMintMeta& c_struct) {
// CSparkMintMeta cpp_struct;
// cpp_struct.nHeight = c_struct.height;
// cpp_struct.nId = std::stoull(c_struct.id);
// cpp_struct.isUsed = c_struct.isUsed;
// cpp_struct.txid = uint256S(c_struct.txid);
// cpp_struct.i = c_struct.i;
// cpp_struct.d = std::vector<unsigned char>(c_struct.d, c_struct.d + c_struct.dLength);
// cpp_struct.v = c_struct.v;
// cpp_struct.k = bytesToScalar(c_struct.k, c_struct.kLength);
// cpp_struct.memo = std::string(c_struct.memo);
// cpp_struct.serial_context = std::vector<unsigned char>(c_struct.serial_context,
// c_struct.serial_context +
// c_struct.serial_contextLength);
// cpp_struct.type = c_struct.type;
// // c_struct.coin is a const, but we need a non-const reference to pass to fromFFI.
// // Convert c_struct.coin to a non-const.
// CCDataStream coin = c_struct.coin;
// cpp_struct.coin = fromFFI(coin);
// return cpp_struct;
//}
///*
// * CCSparkMintMeta constructor.
@@ -409,161 +394,161 @@ CSparkMintMeta fromFFI(const CCSparkMintMeta& c_struct) {
// free(const_cast<char*>(memo));
// delete[] serial_context;
//}
/*
* CCSparkMintMeta factory.
*
* A CSparkMintMeta is a struct that contains a height, id, isUsed, txid, diversifier, encrypted
* diversifier, value, nonce, memo, serial context, type, and coin. We accept these as a
* CCSparkMintMeta from the Dart interface, and convert them to a C++ CSparkMintMeta struct.
*/
CCSparkMintMeta createCCSparkMintMeta(const uint64_t height, const uint64_t id, const int isUsed, const char* txid, const uint64_t diversifier, const char* encryptedDiversifier, const uint64_t value, const char* nonce, const char* memo, const unsigned char* serial_context, const int serial_contextLength, const char type, const CCoin coin) {
// Create a string version of the id
std::string idStr = std::to_string(id);
const char* idCStr = idStr.c_str();
// Convert encryptedDiversifier and nonce to unsigned char*
auto encryptedDiversifierCStr = reinterpret_cast<const unsigned char*>(encryptedDiversifier);
auto nonceCStr = reinterpret_cast<const unsigned char*>(nonce);
// Convert coin to CDataStream
spark::Coin coinStruct = fromFFI(coin);
CCDataStream coinStream = toFFI(coinStruct);
CCSparkMintMeta meta;
meta.height = height;
meta.id = idCStr;
meta.isUsed = isUsed;
meta.txid = txid;
meta.i = diversifier;
meta.d = encryptedDiversifierCStr;
meta.dLength = std::strlen(encryptedDiversifier);
meta.v = value;
meta.k = nonceCStr;
meta.kLength = std::strlen(nonce);
meta.memo = memo;
meta.memoLength = std::strlen(memo);
meta.serial_context = const_cast<unsigned char*>(serial_context);
meta.serial_contextLength = serial_contextLength;
meta.type = type;
meta.coin = coinStream;
return meta;
}
/*
* Utility function to convert an FFI-friendly C CCSparkMintMeta struct to a C++ CSparkMintMeta.
*/
CCSparkMintMeta toFFI(const CSparkMintMeta& cpp_struct) {
// Convert the id, txid, d, k, memo, and serial_context to appropriate types
std::string idStr = std::to_string(cpp_struct.nId);
const char* idCStr = idStr.c_str();
CCDataStream coinStream = toFFI(cpp_struct.coin);
std::vector<unsigned char> scalarBytes(32);
cpp_struct.k.serialize(scalarBytes.data());
CCSparkMintMeta meta;
meta.height = cpp_struct.nHeight;
meta.id = idCStr;
meta.isUsed = cpp_struct.isUsed;
meta.txid = cpp_struct.txid.ToString().c_str();
meta.i = cpp_struct.i;
meta.d = copyBytes(cpp_struct.d.data(), cpp_struct.d.size());
meta.dLength = cpp_struct.d.size();
meta.v = cpp_struct.v;
meta.k = copyBytes(scalarBytes.data(), scalarBytes.size());
meta.kLength = 32;
meta.memo = strdup(cpp_struct.memo.c_str());
meta.memoLength = cpp_struct.memo.size();
meta.serial_context = copyBytes(cpp_struct.serial_context.data(), cpp_struct.serial_context.size());
meta.serial_contextLength = cpp_struct.serial_context.size();
meta.type = cpp_struct.type;
meta.coin = coinStream;
return meta;
}
/*
* CoverSetData factory.
*/
spark::CoverSetData createCoverSetData(
const std::vector<spark::Coin>& cover_set,
const std::vector<unsigned char>& cover_set_representations
) {
spark::CoverSetData coverSetData;
coverSetData.cover_set = cover_set;
coverSetData.cover_set_representation = cover_set_representations;
return coverSetData;
}
/*
* Utility function to convert an FFI-friendly C CCoverSetData struct to a C++ CoverSetData.
*/
spark::CoverSetData fromFFI(const CCoverSetData& c_struct) {
std::vector<spark::Coin> cover_set;
for (int i = 0; i < c_struct.cover_setLength; i++) {
spark::Coin coin;
CDataStream coinStream(
*c_struct.cover_set[i]->data,
c_struct.cover_set[i]->data[c_struct.cover_set[i]->length - 1],
SER_NETWORK, PROTOCOL_VERSION);
coinStream >> coin;
cover_set.emplace_back(coin);
}
std::vector<unsigned char> cover_set_representation(c_struct.cover_set_representation, c_struct.cover_set_representation + c_struct.cover_set_representationLength);
return createCoverSetData(cover_set, cover_set_representation);
}
/*
* CCoverSetData factory.
*/
CCoverSetData createCCoverSetData(const CCoin* cover_set,
const unsigned char* cover_set_representation,
const int cover_set_representationLength) {
std::vector<CCDataStream> cover_set_streams;
for (int i = 0; i < cover_set_representationLength; i++) {
// Convert CCoin to Coin.
spark::Coin coin = fromFFI(cover_set[i]);
// Serialize Coin.
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << coin;
CCDataStream ccStream;
ccStream.data = stream.data();
ccStream.length = stream.size();
// Add stream to vector.
cover_set_streams.push_back(ccStream);
}
// Create array containing the address of cover_set_streams
CCDataStream** cover_set_streams_pp = new CCDataStream*[cover_set_representationLength];
for (int i = 0; i < cover_set_representationLength; i++) {
cover_set_streams_pp[i] = &cover_set_streams[i];
}
// Create the CCoverSetData and set the cover_set.
CCoverSetData c_struct;
// Assign the pointer to pointer
c_struct.cover_set = cover_set_streams_pp;
// Assign the cover_set_representation.
c_struct.cover_set_representation = cover_set_representation;
c_struct.cover_set_representationLength = cover_set_representationLength;
return c_struct;
}
//
///*
// * CCSparkMintMeta factory.
// *
// * A CSparkMintMeta is a struct that contains a height, id, isUsed, txid, diversifier, encrypted
// * diversifier, value, nonce, memo, serial context, type, and coin. We accept these as a
// * CCSparkMintMeta from the Dart interface, and convert them to a C++ CSparkMintMeta struct.
// */
//CCSparkMintMeta createCCSparkMintMeta(const uint64_t height, const uint64_t id, const int isUsed, const char* txid, const uint64_t diversifier, const char* encryptedDiversifier, const uint64_t value, const char* nonce, const char* memo, const unsigned char* serial_context, const int serial_contextLength, const char type, const CCoin coin) {
// // Create a string version of the id
// std::string idStr = std::to_string(id);
// const char* idCStr = idStr.c_str();
//
// // Convert encryptedDiversifier and nonce to unsigned char*
// auto encryptedDiversifierCStr = reinterpret_cast<const unsigned char*>(encryptedDiversifier);
// auto nonceCStr = reinterpret_cast<const unsigned char*>(nonce);
//
// // Convert coin to CDataStream
// spark::Coin coinStruct = fromFFI(coin);
// CCDataStream coinStream = toFFI(coinStruct);
//
// CCSparkMintMeta meta;
//
// meta.height = height;
// meta.id = idCStr;
// meta.isUsed = isUsed;
// meta.txid = txid;
// meta.i = diversifier;
// meta.d = encryptedDiversifierCStr;
// meta.dLength = std::strlen(encryptedDiversifier);
// meta.v = value;
// meta.k = nonceCStr;
// meta.kLength = std::strlen(nonce);
// meta.memo = memo;
// meta.memoLength = std::strlen(memo);
// meta.serial_context = const_cast<unsigned char*>(serial_context);
// meta.serial_contextLength = serial_contextLength;
// meta.type = type;
// meta.coin = coinStream;
//
// return meta;
//}
//
///*
// * Utility function to convert an FFI-friendly C CCSparkMintMeta struct to a C++ CSparkMintMeta.
// */
//CCSparkMintMeta toFFI(const CSparkMintMeta& cpp_struct) {
// // Convert the id, txid, d, k, memo, and serial_context to appropriate types
// std::string idStr = std::to_string(cpp_struct.nId);
// const char* idCStr = idStr.c_str();
//
// CCDataStream coinStream = toFFI(cpp_struct.coin);
//
// std::vector<unsigned char> scalarBytes(32);
// cpp_struct.k.serialize(scalarBytes.data());
//
// CCSparkMintMeta meta;
//
// meta.height = cpp_struct.nHeight;
// meta.id = idCStr;
// meta.isUsed = cpp_struct.isUsed;
// meta.txid = cpp_struct.txid.ToString().c_str();
// meta.i = cpp_struct.i;
// meta.d = copyBytes(cpp_struct.d.data(), cpp_struct.d.size());
// meta.dLength = cpp_struct.d.size();
// meta.v = cpp_struct.v;
// meta.k = copyBytes(scalarBytes.data(), scalarBytes.size());
// meta.kLength = 32;
// meta.memo = strdup(cpp_struct.memo.c_str());
// meta.memoLength = cpp_struct.memo.size();
// meta.serial_context = copyBytes(cpp_struct.serial_context.data(), cpp_struct.serial_context.size());
// meta.serial_contextLength = cpp_struct.serial_context.size();
// meta.type = cpp_struct.type;
// meta.coin = coinStream;
//
// return meta;
//}
//
///*
// * CoverSetData factory.
// */
//spark::CoverSetData createCoverSetData(
// const std::vector<spark::Coin>& cover_set,
// const std::vector<unsigned char>& cover_set_representations
//) {
// spark::CoverSetData coverSetData;
// coverSetData.cover_set = cover_set;
// coverSetData.cover_set_representation = cover_set_representations;
// return coverSetData;
//}
//
///*
// * Utility function to convert an FFI-friendly C CCoverSetData struct to a C++ CoverSetData.
// */
//spark::CoverSetData fromFFI(const CCoverSetData& c_struct) {
// std::vector<spark::Coin> cover_set;
//
// for (int i = 0; i < c_struct.cover_setLength; i++) {
// spark::Coin coin = fromFFI(c_struct.cover_set[i].data)
// CDataStream coinStream(
// *c_struct.cover_set[i]->data,
// c_struct.cover_set[i]->data[c_struct.cover_set[i]->length - 1],
// SER_NETWORK, PROTOCOL_VERSION);
// coinStream >> coin;
// cover_set.emplace_back(coin);
// }
//
// std::vector<unsigned char> cover_set_representation(c_struct.cover_set_representation, c_struct.cover_set_representation + c_struct.cover_set_representationLength);
//
// return createCoverSetData(cover_set, cover_set_representation);
//}
//
///*
// * CCoverSetData factory.
// */
//CCoverSetData createCCoverSetData(const CCoin* cover_set,
// const unsigned char* cover_set_representation,
// const int cover_set_representationLength) {
// std::vector<CCDataStream> cover_set_streams;
//
// for (int i = 0; i < cover_set_representationLength; i++) {
// // Convert CCoin to Coin.
// spark::Coin coin = fromFFI(cover_set[i]);
//
// // Serialize Coin.
// CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
// stream << coin;
//
//
// CCDataStream ccStream;
// ccStream.data = stream.data();
// ccStream.length = stream.size();
//
// // Add stream to vector.
// cover_set_streams.push_back(ccStream);
// }
//
// // Create array containing the address of cover_set_streams
// CCDataStream** cover_set_streams_pp = new CCDataStream*[cover_set_representationLength];
//
// for (int i = 0; i < cover_set_representationLength; i++) {
// cover_set_streams_pp[i] = &cover_set_streams[i];
// }
//
// // Create the CCoverSetData and set the cover_set.
// CCoverSetData c_struct;
//
// // Assign the pointer to pointer
// c_struct.cover_set = cover_set_streams_pp;
//
// // Assign the cover_set_representation.
// c_struct.cover_set_representation = cover_set_representation;
// c_struct.cover_set_representationLength = cover_set_representationLength;
// return c_struct;
//}
/*
* Utility function to convert a C++ CoverSetData struct to an FFI-friendly CCoverSetData.

View File

@@ -17,8 +17,6 @@ spark::Coin fromFFI(CCDataStream& coinStream);
CCoin createCCoin(char type, const unsigned char* k, int kLength, const char* address, uint64_t v, const unsigned char* memo, int memoLength, const unsigned char* serial_context, int serial_contextLength);
CCDataStream toFFI(const spark::Coin& cpp_struct);
spark::IdentifiedCoinData fromFFI(const CIdentifiedCoinData& c_struct);
CIdentifiedCoinData toFFI(const spark::IdentifiedCoinData& cpp_struct);
@@ -53,19 +51,19 @@ COutputCoinData createCOutputCoinData(const char* address, uint64_t value, const
COutputCoinData toFFI(const spark::OutputCoinData& cpp_struct, int isTestNet);
CSparkMintMeta createCSparkMintMeta(const uint64_t height, const uint64_t id, const int isUsed, const char* txid, const uint64_t diversifier, const char* encryptedDiversifier, const uint64_t value, const char* nonce, const char* memo, const unsigned char* serialContext, const int serialContextLength, const char type, const CCoin coin);
//CSparkMintMeta createCSparkMintMeta(const uint64_t height, const uint64_t id, const int isUsed, const char* txid, const uint64_t diversifier, const char* encryptedDiversifier, const uint64_t value, const char* nonce, const char* memo, const unsigned char* serialContext, const int serialContextLength, const char type, const CCoin coin);
//
//CSparkMintMeta fromFFI(const CCSparkMintMeta& c_struct);
CSparkMintMeta fromFFI(const CCSparkMintMeta& c_struct);
//CCSparkMintMeta createCCSparkMintMeta(const uint64_t height, const uint64_t id, const int isUsed, const char* txid, const uint64_t diversifier, const char* encryptedDiversifier, const uint64_t value, const char* nonce, const char* memo, const unsigned char* serialContext, const int serialContextLength, const char type, const CCoin coin);
//
//CCSparkMintMeta toFFI(const CSparkMintMeta& cpp_struct);
CCSparkMintMeta createCCSparkMintMeta(const uint64_t height, const uint64_t id, const int isUsed, const char* txid, const uint64_t diversifier, const char* encryptedDiversifier, const uint64_t value, const char* nonce, const char* memo, const unsigned char* serialContext, const int serialContextLength, const char type, const CCoin coin);
CCSparkMintMeta toFFI(const CSparkMintMeta& cpp_struct);
spark::CoverSetData createCoverSetData(const std::vector<spark::Coin>& cover_set, const std::vector<std::vector<unsigned char>>& cover_set_representations);
spark::CoverSetData fromFFI(const CCoverSetData& c_struct);
CCoverSetData createCCoverSetData(const CCoin* cover_set, const unsigned char* cover_set_representation, const int cover_set_representationLength);
//spark::CoverSetData createCoverSetData(const std::vector<spark::Coin>& cover_set, const std::vector<std::vector<unsigned char>>& cover_set_representations);
//
//spark::CoverSetData fromFFI(const CCoverSetData& c_struct);
//
//CCoverSetData createCCoverSetData(const CCoin* cover_set, const unsigned char* cover_set_representation, const int cover_set_representationLength);
char const hexArray[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
'e', 'f'};