mirror of
https://github.com/MAGICGrants/flutter_libsparkmobile.git
synced 2026-01-09 13:08:00 -05:00
update dart interface from cypherstack/sparkmobile main
with minor changes eg EXPORT_DART->FFI_PLUGIN_EXPORT add missing streams import (for CDataStream) and formatting
This commit is contained in:
@@ -3,109 +3,237 @@
|
||||
#include "deps/sparkmobile/include/spark.h"
|
||||
|
||||
#include <cstring>
|
||||
#include "deps/sparkmobile/bitcoin/uint256.h"
|
||||
#include <iostream> // Just for printing.
|
||||
|
||||
using namespace spark;
|
||||
|
||||
|
||||
/*
|
||||
* FFI-friendly wrapper for spark::getAddress.
|
||||
*
|
||||
* getAddress: https://github.com/firoorg/sparkmobile/blob/8bf17cd3deba6c3b0d10e89282e02936d7e71cdd/src/spark.cpp#L388
|
||||
*/
|
||||
FFI_PLUGIN_EXPORT
|
||||
const char* getAddress(const char* keyDataHex, int index, int diversifier, int isTestNet) {
|
||||
try {
|
||||
// Use the hex string directly to create the SpendKey.
|
||||
spark::SpendKey spendKey = createSpendKeyFromData(keyDataHex, index);
|
||||
try {
|
||||
// Use the hex string directly to create the SpendKey.
|
||||
spark::SpendKey spendKey = createSpendKeyFromData(keyDataHex, index);
|
||||
|
||||
spark::FullViewKey fullViewKey(spendKey);
|
||||
spark::IncomingViewKey incomingViewKey(fullViewKey);
|
||||
spark::Address address(incomingViewKey, static_cast<uint64_t>(diversifier));
|
||||
spark::FullViewKey fullViewKey(spendKey);
|
||||
spark::IncomingViewKey incomingViewKey(fullViewKey);
|
||||
spark::Address address(incomingViewKey, static_cast<uint64_t>(diversifier));
|
||||
|
||||
// Encode the Address object into a string using the appropriate network.
|
||||
std::string encodedAddress = address.encode(isTestNet ? spark::ADDRESS_NETWORK_TESTNET : spark::ADDRESS_NETWORK_MAINNET);
|
||||
// Encode the Address object into a string using the appropriate network.
|
||||
std::string encodedAddress = address.encode(isTestNet ? spark::ADDRESS_NETWORK_TESTNET : spark::ADDRESS_NETWORK_MAINNET);
|
||||
|
||||
// Allocate memory for the C-style string.
|
||||
char* cstr = new char[encodedAddress.length() + 1];
|
||||
std::strcpy(cstr, encodedAddress.c_str());
|
||||
// Allocate memory for the C-style string.
|
||||
char* cstr = new char[encodedAddress.length() + 1];
|
||||
std::strcpy(cstr, encodedAddress.c_str());
|
||||
|
||||
return cstr;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Exception: " << e.what() << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
return cstr;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Exception: " << e.what() << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* FFI-friendly wrapper for spark:identifyCoin.
|
||||
*
|
||||
* Uses the utility functions spark::Coin fromFFI(const CCoin& c_struct) to pass parameters to the
|
||||
* C++ function spark::identifyCoin(const Coin& coin), then uses the utility function
|
||||
* CIdentifiedCoinData toFFI(const spark::IdentifiedCoinData& cpp_struct) to convert the result back
|
||||
* to a C struct.
|
||||
*
|
||||
* We also need the incoming view key or we need to derive it, so accept keyDataHex and index.
|
||||
* identifyCoin: https://github.com/firoorg/sparkmobile/blob/8bf17cd3deba6c3b0d10e89282e02936d7e71cdd/src/spark.cpp#L400
|
||||
*/
|
||||
FFI_PLUGIN_EXPORT
|
||||
struct CIdentifiedCoinData identifyCoin(struct CCoin c_struct, const char* keyDataHex, int index) {
|
||||
try {
|
||||
spark::Coin coin = fromFFI(c_struct);
|
||||
try {
|
||||
spark::Coin coin = fromFFI(c_struct);
|
||||
|
||||
// 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);
|
||||
// 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);
|
||||
return toFFI(identifiedCoinData);
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Exception: " << e.what() << std::endl;
|
||||
return CIdentifiedCoinData();
|
||||
}
|
||||
spark::IdentifiedCoinData identifiedCoinData = coin.identify(incomingViewKey);
|
||||
return toFFI(identifiedCoinData);
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Exception: " << e.what() << std::endl;
|
||||
return CIdentifiedCoinData();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* FFI-friendly wrapper for spark::createSparkMintRecipients.
|
||||
*
|
||||
* createSparkMintRecipients: https://github.com/firoorg/sparkmobile/blob/8bf17cd3deba6c3b0d10e89282e02936d7e71cdd/src/spark.cpp#L43
|
||||
*/
|
||||
FFI_PLUGIN_EXPORT
|
||||
struct CCRecipient* createSparkMintRecipients(
|
||||
int numRecipients,
|
||||
struct PubKeyScript* pubKeyScripts,
|
||||
uint64_t* amounts,
|
||||
const char* memo,
|
||||
int subtractFee)
|
||||
{
|
||||
try {
|
||||
std::vector<CRecipient> recipients;
|
||||
struct CMintedCoinData* cOutputs,
|
||||
int outputsLength,
|
||||
const char* serial_context,
|
||||
int serial_contextLength,
|
||||
int generate
|
||||
) {
|
||||
// Construct vector of spark::MintedCoinData.
|
||||
std::vector<spark::MintedCoinData> outputs;
|
||||
|
||||
for (int i = 0; i < numRecipients; i++) {
|
||||
CScript scriptPubKey = createCScriptFromBytes(
|
||||
pubKeyScripts[i].bytes,
|
||||
pubKeyScripts[i].length
|
||||
);
|
||||
|
||||
CRecipient recipient;
|
||||
recipient.pubKey = scriptPubKey;
|
||||
|
||||
recipient.amount = amounts[i];
|
||||
|
||||
recipient.subtractFeeFromAmount = (bool)subtractFee;
|
||||
|
||||
recipients.push_back(recipient);
|
||||
// Copy the data from the array to the vector.
|
||||
for (int i = 0; i < outputsLength; i++) {
|
||||
outputs.push_back(fromFFI(cOutputs[i]));
|
||||
}
|
||||
|
||||
std::vector<CCRecipient> ccRecipients;
|
||||
// Construct vector of unsigned chars.
|
||||
std::vector<unsigned char> blankSerialContext;
|
||||
|
||||
for (const CRecipient& recipient : recipients) {
|
||||
CCRecipient ccRecipient = toFFI(recipient);
|
||||
ccRecipients.push_back(ccRecipient);
|
||||
// Copy the data from the array to the vector.
|
||||
for (int i = 0; i < serial_contextLength; i++) {
|
||||
blankSerialContext.push_back(serial_context[i]);
|
||||
}
|
||||
|
||||
CCRecipient* result = new CCRecipient[numRecipients];
|
||||
std::copy(ccRecipients.begin(), ccRecipients.end(), result);
|
||||
// Call spark::createSparkMintRecipients.
|
||||
std::vector<CRecipient> recipients = createSparkMintRecipients(outputs, blankSerialContext, generate);
|
||||
|
||||
return result;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Exception: " << e.what() << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
// Create a CRecipient* array.
|
||||
CCRecipient* cRecipients = new CCRecipient[recipients.size()];
|
||||
|
||||
// Copy the data from the vector to the array.
|
||||
for (int i = 0; i < recipients.size(); i++) {
|
||||
cRecipients[i] = toFFI(recipients[i]);
|
||||
}
|
||||
|
||||
// Return the array.
|
||||
return cRecipients;
|
||||
}
|
||||
|
||||
/*
|
||||
* FFI-friendly wrapper for spark::createSparkSpendTransaction.
|
||||
*
|
||||
* createSparkSpendTransaction: https://github.com/firoorg/sparkmobile/blob/23099b0d9010a970ad75b9cfe05d568d634088f3/src/spark.cpp#L190
|
||||
*/
|
||||
FFI_PLUGIN_EXPORT
|
||||
unsigned char* cCreateSparkSpendTransaction(
|
||||
const char* keyDataHex,
|
||||
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
|
||||
) {
|
||||
try {
|
||||
// Derive the keys from the key data and index.
|
||||
spark::SpendKey spendKey = createSpendKeyFromData(keyDataHex, index);
|
||||
spark::FullViewKey fullViewKey(spendKey);
|
||||
spark::IncomingViewKey incomingViewKey(fullViewKey);
|
||||
|
||||
// Convert CRecipient* recipients to std::vector<std::pair<CAmount, bool>> cppRecipients.
|
||||
std::vector<std::pair<CAmount, bool>> cppRecipients;
|
||||
for (int i = 0; i < recipientsLength; i++) {
|
||||
cppRecipients.push_back(std::make_pair(recipients[i].amount, recipients[i].subtractFee));
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
// Convert CCSparkMintMeta* coins to std::list<CSparkMintMeta> cppCoins.
|
||||
std::list<CSparkMintMeta> cppCoins;
|
||||
for (int i = 0; i < coinsLength; i++) {
|
||||
cppCoins.push_back(fromFFI(coins[i]));
|
||||
}
|
||||
|
||||
// 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.
|
||||
std::vector<spark::Coin> cppCoverSetCoins;
|
||||
spark::Coin coin = fromFFI(*cover_set_data_all[i].cover_sets[j].cover_set[0]);
|
||||
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;
|
||||
|
||||
cppCoverSetDataAll[cover_set_data_all[i].cover_sets[j].cover_setLength] = 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.
|
||||
std::map<uint64_t, uint256> cppIdAndBlockHashesAll;
|
||||
|
||||
// Make a placeholder for serializedSpend.
|
||||
std::vector<uint8_t> cppSerializedSpend;
|
||||
|
||||
// Convert outputScripts to std::vector<std::vector<unsigned char>> cppOutputScripts.
|
||||
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(
|
||||
spendKey,
|
||||
fullViewKey,
|
||||
incomingViewKey,
|
||||
cppRecipients,
|
||||
cppPrivateRecipients,
|
||||
cppCoins,
|
||||
cppCoverSetDataAll,
|
||||
cppIdAndBlockHashesAll,
|
||||
cppTxHashSig,
|
||||
cppFee,
|
||||
cppSerializedSpend,
|
||||
cppOutputScripts
|
||||
);
|
||||
|
||||
// Allocate memory for the C-style string.
|
||||
unsigned char* cstr = new unsigned char[cppSerializedSpend.size()];
|
||||
|
||||
// Copy the data from the vector to the array.
|
||||
for (int i = 0; i < cppSerializedSpend.size(); i++) {
|
||||
cstr[i] = cppSerializedSpend[i];
|
||||
}
|
||||
|
||||
// Return the array.
|
||||
return cstr;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Exception: " << e.what() << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
#ifndef ORG_FIRO_SPARK_DART_INTERFACE_H
|
||||
#define ORG_FIRO_SPARK_DART_INTERFACE_H
|
||||
|
||||
@@ -15,10 +14,6 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* FFI-friendly wrapper for spark::getAddress.
|
||||
*/
|
||||
FFI_PLUGIN_EXPORT
|
||||
const char* getAddress(const char* keyDataHex, int index, int diversifier, int isTestNet);
|
||||
|
||||
@@ -33,15 +28,13 @@ const char* createIncomingViewKey(const char* keyData, int index);
|
||||
/*
|
||||
* FFI-friendly wrapper for a spark::Coin.
|
||||
*
|
||||
* A Coin is a type, a key, an index, a value, a memo, and a serial context. We accept these params
|
||||
* as a C struct, deriving the key from the keyData and index.
|
||||
* Coin: https://github.com/firoorg/sparkmobile/blob/8bf17cd3deba6c3b0d10e89282e02936d7e71cdd/src/coin.h#L66
|
||||
*/
|
||||
struct CCoin {
|
||||
char type;
|
||||
const unsigned char* k;
|
||||
int kLength;
|
||||
const char* keyData;
|
||||
int index;
|
||||
const char* address;
|
||||
uint64_t v;
|
||||
const unsigned char* memo;
|
||||
int memoLength;
|
||||
@@ -52,8 +45,7 @@ struct CCoin {
|
||||
/*
|
||||
* FFI-friendly wrapper for a spark::IdentifiedCoinData.
|
||||
*
|
||||
* An IdentifiedCoinData is a diversifier, encrypted diversifier, value, nonce, and memo. We accept
|
||||
* these params as a C struct.
|
||||
* IdentifiedCoinData: https://github.com/firoorg/sparkmobile/blob/8bf17cd3deba6c3b0d10e89282e02936d7e71cdd/src/coin.h#L19
|
||||
*/
|
||||
struct CIdentifiedCoinData {
|
||||
uint64_t i;
|
||||
@@ -68,6 +60,8 @@ struct CIdentifiedCoinData {
|
||||
|
||||
/*
|
||||
* FFI-friendly wrapper for spark::identifyCoin.
|
||||
*
|
||||
* identifyCoin: https://github.com/firoorg/sparkmobile/blob/8bf17cd3deba6c3b0d10e89282e02936d7e71cdd/src/spark.cpp#L400
|
||||
*/
|
||||
FFI_PLUGIN_EXPORT
|
||||
struct CIdentifiedCoinData identifyCoin(struct CCoin c_struct, const char* keyDataHex, int index);
|
||||
@@ -75,9 +69,7 @@ struct CIdentifiedCoinData identifyCoin(struct CCoin c_struct, const char* keyDa
|
||||
/*
|
||||
* FFI-friendly wrapper for a spark::CRecipient.
|
||||
*
|
||||
* A CRecipient is a CScript, CAmount, and a bool. We accept a C-style, FFI-friendly CCRecipient
|
||||
* struct in order to construct a C++ CRecipient. A CScript is constructed from a hex string, a
|
||||
* CAmount is just a uint64_t, and the bool will be an int.
|
||||
* CRecipient: https://github.com/firoorg/sparkmobile/blob/8bf17cd3deba6c3b0d10e89282e02936d7e71cdd/include/spark.h#L27
|
||||
*/
|
||||
struct CCRecipient {
|
||||
const unsigned char* pubKey;
|
||||
@@ -89,9 +81,7 @@ struct CCRecipient {
|
||||
/*
|
||||
* FFI-friendly wrapper for a spark::MintedCoinData.
|
||||
*
|
||||
* A MintedCoinData is a struct that contains an Address, a uint64_t value, and a string memo. We
|
||||
* accept these as a CMintedCoinData from the Dart interface, and convert them to a MintedCoinData
|
||||
* struct.
|
||||
* MintedCoinData: https://github.com/firoorg/sparkmobile/blob/8bf17cd3deba6c3b0d10e89282e02936d7e71cdd/src/mint_transaction.h#L12
|
||||
*/
|
||||
struct CMintedCoinData {
|
||||
const char* address;
|
||||
@@ -106,13 +96,128 @@ struct PubKeyScript {
|
||||
|
||||
/*
|
||||
* FFI-friendly wrapper for spark::createSparkMintRecipients.
|
||||
*
|
||||
* createSparkMintRecipients: https://github.com/firoorg/sparkmobile/blob/8bf17cd3deba6c3b0d10e89282e02936d7e71cdd/src/spark.cpp#L43
|
||||
*/
|
||||
FFI_PLUGIN_EXPORT
|
||||
struct CCRecipient* createSparkMintRecipients(
|
||||
int numRecipients,
|
||||
struct PubKeyScript* pubKeyScripts,
|
||||
uint64_t* amounts,
|
||||
const char* memo,
|
||||
int subtractFee);
|
||||
struct CMintedCoinData* outputs,
|
||||
int outputsLength,
|
||||
const char* serial_context,
|
||||
int serial_contextLength,
|
||||
int generate);
|
||||
|
||||
#endif //ORG_FIRO_SPARK_DART_INTERFACE_H
|
||||
/*
|
||||
* FFI-friendly wrapper for a std::pair<CAmount, bool>.
|
||||
*
|
||||
* Note this is an ambiguation of a spark::CRecipient. This CRecip(ient) is just a wrapper for a
|
||||
* CAmount and bool pair, and is not the same as the spark::CRecipient struct above, which gets
|
||||
* wrapped for us as a CCRecipient and is unrelated to this struct.
|
||||
*
|
||||
* See https://github.com/firoorg/sparkmobile/blob/23099b0d9010a970ad75b9cfe05d568d634088f3/src/spark.cpp#L190
|
||||
*/
|
||||
struct CRecip {
|
||||
uint64_t amount;
|
||||
int subtractFee;
|
||||
};
|
||||
|
||||
/*
|
||||
* FFI-friendly wrapper for a spark::OutputCoinData.
|
||||
*
|
||||
* OutputCoinData: https://github.com/firoorg/sparkmobile/blob/8bf17cd3deba6c3b0d10e89282e02936d7e71cdd/src/spend_transaction.h#L33
|
||||
*/
|
||||
struct COutputCoinData {
|
||||
const char* address;
|
||||
uint64_t value;
|
||||
const char* memo;
|
||||
};
|
||||
|
||||
/*
|
||||
* FFI-friendly wrapper for a <spark::OutputCoinData, bool>.
|
||||
*
|
||||
* See https://github.com/firoorg/sparkmobile/blob/23099b0d9010a970ad75b9cfe05d568d634088f3/src/spark.cpp#L195
|
||||
*/
|
||||
struct COutputRecipient {
|
||||
struct COutputCoinData output;
|
||||
int subtractFee;
|
||||
};
|
||||
|
||||
/*
|
||||
* FFI-friendly wrapper for a spark::CSparkMintMeta.
|
||||
*
|
||||
* 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;
|
||||
CDataStream 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 CDataStream& coinData);
|
||||
~CCSparkMintMeta();
|
||||
};
|
||||
|
||||
/*
|
||||
* FFI-friendly wrapper for a spark::CoverSetData.
|
||||
*
|
||||
* CoverSetData: https://github.com/firoorg/sparkmobile/blob/8bf17cd3deba6c3b0d10e89282e02936d7e71cdd/src/spend_transaction.h#L28
|
||||
*/
|
||||
struct CCoverSetData {
|
||||
CDataStream** cover_set; // vs. struct CCoin* cover_set;
|
||||
int cover_setLength;
|
||||
const unsigned char* cover_set_representation;
|
||||
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
|
||||
*/
|
||||
struct CCoverSets {
|
||||
struct CCoverSetData* cover_sets;
|
||||
int cover_setsLength;
|
||||
};
|
||||
|
||||
struct OutputScript {
|
||||
unsigned char* bytes;
|
||||
int length;
|
||||
};
|
||||
|
||||
/*
|
||||
* FFI-friendly wrapper for spark::createSparkSpendTransaction.
|
||||
*
|
||||
* createSparkSpendTransaction: https://github.com/firoorg/sparkmobile/blob/23099b0d9010a970ad75b9cfe05d568d634088f3/src/spark.cpp#L190
|
||||
*/
|
||||
FFI_PLUGIN_EXPORT
|
||||
unsigned char* cCreateSparkSpendTransaction(
|
||||
const char* keyDataHex,
|
||||
int index,
|
||||
struct CRecip* recipients,
|
||||
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 OutputScript* outputScripts,
|
||||
int outputScriptsLength
|
||||
);
|
||||
|
||||
#endif //ORG_FIRO_SPARK_DART_INTERFACE_H
|
||||
703
src/utils.cpp
703
src/utils.cpp
@@ -6,7 +6,6 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
||||
#include "flutter_libsparkmobile.h"
|
||||
#include "deps/sparkmobile/src/coin.h"
|
||||
#include "deps/sparkmobile/src/keys.h"
|
||||
@@ -15,42 +14,42 @@
|
||||
/*
|
||||
* Utility function to generate an address from keyData, index, and a diversifier.
|
||||
*/
|
||||
const char* getAddressFromData(const char* keyData, int index, const uint64_t diversifier) {
|
||||
try {
|
||||
spark::SpendKey spendKey = createSpendKeyFromData(keyData, index);
|
||||
spark::FullViewKey fullViewKey(spendKey);
|
||||
spark::IncomingViewKey incomingViewKey(fullViewKey);
|
||||
spark::Address address(incomingViewKey, diversifier);
|
||||
const char* getAddressFromData(const char* keyData, int index, const uint64_t diversifier, int isTestNet) {
|
||||
try {
|
||||
spark::SpendKey spendKey = createSpendKeyFromData(keyData, index);
|
||||
spark::FullViewKey fullViewKey(spendKey);
|
||||
spark::IncomingViewKey incomingViewKey(fullViewKey);
|
||||
spark::Address address(incomingViewKey, diversifier);
|
||||
|
||||
// Encode the Address object into a string.
|
||||
std::string encodedAddress = address.encode(spark::ADDRESS_NETWORK_TESTNET);
|
||||
// Encode the Address object into a string.
|
||||
std::string encodedAddress = address.encode(isTestNet ? spark::ADDRESS_NETWORK_TESTNET : spark::ADDRESS_NETWORK_MAINNET);
|
||||
|
||||
// Allocate memory for the C-style string and return it.
|
||||
char* result = new char[encodedAddress.size() + 1];
|
||||
std::copy(encodedAddress.begin(), encodedAddress.end(), result);
|
||||
result[encodedAddress.size()] = '\0'; // Null-terminate the C string.
|
||||
// Allocate memory for the C-style string and return it.
|
||||
char* result = new char[encodedAddress.size() + 1];
|
||||
std::copy(encodedAddress.begin(), encodedAddress.end(), result);
|
||||
result[encodedAddress.size()] = '\0'; // Null-terminate the C string.
|
||||
|
||||
return result;
|
||||
} catch (const std::exception& e) {
|
||||
return nullptr;
|
||||
}
|
||||
return result;
|
||||
} catch (const std::exception& e) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility function to generate SpendKey from keyData and index.
|
||||
*/
|
||||
spark::SpendKey createSpendKeyFromData(const char *keyData, int index) {
|
||||
try {
|
||||
// Convert the keyData from hex string to binary
|
||||
unsigned char* key_data_bin = hexToBytes(keyData);
|
||||
try {
|
||||
// Convert the keyData from hex string to binary
|
||||
unsigned char* key_data_bin = hexToBytes(keyData);
|
||||
|
||||
const SpendKeyData *data = new SpendKeyData(key_data_bin, index);
|
||||
const SpendKeyData *data = new SpendKeyData(key_data_bin, index);
|
||||
|
||||
return createSpendKey(*data);
|
||||
} catch (const std::exception& e) {
|
||||
// We can't return here, so just throw the exception again.
|
||||
throw e;
|
||||
}
|
||||
return createSpendKey(*data);
|
||||
} catch (const std::exception& e) {
|
||||
// We can't return here, so just throw the exception again.
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -58,102 +57,121 @@ spark::SpendKey createSpendKeyFromData(const char *keyData, int index) {
|
||||
*
|
||||
* TODO manage the memory allocated by this function.
|
||||
*/
|
||||
struct CCoin createCCoin(char type, const unsigned char* k, int kLength, const char* keyData, int index, uint64_t v, const unsigned char* memo, int memoLength, const unsigned char* serial_context, int serial_contextLength) {
|
||||
CCoin coin;
|
||||
coin.type = type;
|
||||
coin.k = copyBytes(k, kLength);
|
||||
coin.kLength = kLength;
|
||||
coin.keyData = strdup(keyData);
|
||||
coin.index = index;
|
||||
coin.v = v;
|
||||
coin.memo = copyBytes(memo, memoLength);
|
||||
coin.memoLength = memoLength;
|
||||
coin.serial_context = copyBytes(serial_context, serial_contextLength);
|
||||
coin.serial_contextLength = serial_contextLength;
|
||||
return coin;
|
||||
struct 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) {
|
||||
CCoin coin;
|
||||
coin.type = type;
|
||||
coin.k = copyBytes(k, kLength);
|
||||
coin.kLength = kLength;
|
||||
coin.address = address;
|
||||
coin.v = v;
|
||||
coin.memo = copyBytes(memo, memoLength);
|
||||
coin.memoLength = memoLength;
|
||||
coin.serial_context = copyBytes(serial_context, serial_contextLength);
|
||||
coin.serial_contextLength = serial_contextLength;
|
||||
return coin;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility function to convert an FFI-friendly C CCoin struct to a C++ Coin struct.
|
||||
*/
|
||||
spark::Coin fromFFI(const CCoin& c_struct) {
|
||||
spark::Coin cpp_struct(
|
||||
// The test params are only used for unit tests.
|
||||
spark::Params::get_default(),
|
||||
c_struct.type,
|
||||
spark::Scalar(c_struct.k),
|
||||
spark::Address(spark::IncomingViewKey(spark::FullViewKey(createSpendKeyFromData(c_struct.keyData, c_struct.index))), c_struct.index),
|
||||
c_struct.v,
|
||||
std::string(reinterpret_cast<const char*>(c_struct.memo), c_struct.memoLength),
|
||||
std::vector<unsigned char>(c_struct.serial_context, c_struct.serial_context + c_struct.serial_contextLength)
|
||||
);
|
||||
spark::Coin cpp_struct(
|
||||
// The test params are only used for unit tests.
|
||||
spark::Params::get_default(),
|
||||
c_struct.type,
|
||||
spark::Scalar(c_struct.k),
|
||||
decodeAddress(c_struct.address),
|
||||
c_struct.v,
|
||||
std::string(reinterpret_cast<const char*>(c_struct.memo), c_struct.memoLength),
|
||||
std::vector<unsigned char>(c_struct.serial_context, c_struct.serial_context + c_struct.serial_contextLength)
|
||||
);
|
||||
|
||||
return cpp_struct;
|
||||
return cpp_struct;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility function to convert a C++ Coin struct to an FFI-friendly C CDataStream struct.
|
||||
*/
|
||||
spark::Coin fromFFI(CDataStream& coinStream) {
|
||||
spark::Coin coin;
|
||||
coinStream >> coin;
|
||||
return coin;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility function to convert a C++ Coin struct to an FFI-friendly C CDataStream struct.
|
||||
*/
|
||||
CDataStream toFFI(const spark::Coin& coin) {
|
||||
// Serialize the Coin object into a CDataStream
|
||||
CDataStream ccoinStream(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ccoinStream << coin;
|
||||
|
||||
return ccoinStream;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility function to convert a C++ IdentifiedCoinData struct to an FFI-friendly struct.
|
||||
*/
|
||||
CIdentifiedCoinData toFFI(const spark::IdentifiedCoinData& cpp_struct) {
|
||||
CIdentifiedCoinData c_struct;
|
||||
CIdentifiedCoinData c_struct;
|
||||
|
||||
c_struct.i = cpp_struct.i;
|
||||
c_struct.d = copyBytes(cpp_struct.d.data(), cpp_struct.d.size());
|
||||
c_struct.dLength = cpp_struct.d.size();
|
||||
c_struct.v = cpp_struct.v;
|
||||
c_struct.i = cpp_struct.i;
|
||||
c_struct.d = copyBytes(cpp_struct.d.data(), cpp_struct.d.size());
|
||||
c_struct.dLength = cpp_struct.d.size();
|
||||
c_struct.v = cpp_struct.v;
|
||||
|
||||
// Serialize and copy the Scalar k.
|
||||
std::vector<unsigned char> scalarBytes(32);
|
||||
cpp_struct.k.serialize(scalarBytes.data());
|
||||
c_struct.k = copyBytes(scalarBytes.data(), scalarBytes.size());
|
||||
c_struct.kLength = scalarBytes.size();
|
||||
// Serialize and copy the Scalar k.
|
||||
std::vector<unsigned char> scalarBytes(32);
|
||||
cpp_struct.k.serialize(scalarBytes.data());
|
||||
c_struct.k = copyBytes(scalarBytes.data(), scalarBytes.size());
|
||||
c_struct.kLength = scalarBytes.size();
|
||||
|
||||
// Copy the memo.
|
||||
c_struct.memo = strdup(cpp_struct.memo.c_str());
|
||||
c_struct.memoLength = cpp_struct.memo.size();
|
||||
// Copy the memo.
|
||||
c_struct.memo = strdup(cpp_struct.memo.c_str());
|
||||
c_struct.memoLength = cpp_struct.memo.size();
|
||||
|
||||
return c_struct;
|
||||
return c_struct;
|
||||
}
|
||||
|
||||
/*
|
||||
* Factory function to create a CScript from a byte array.
|
||||
*/
|
||||
CScript createCScriptFromBytes(const unsigned char* bytes, int length) {
|
||||
// Construct a CScript object
|
||||
CScript script;
|
||||
// Construct a CScript object
|
||||
CScript script;
|
||||
|
||||
// Check if bytes is not nullptr and length is positive
|
||||
if (bytes != nullptr && length > 0) {
|
||||
// Append each byte to the script
|
||||
for (int i = 0; i < length; ++i) {
|
||||
script << bytes[i];
|
||||
}
|
||||
}
|
||||
// Check if bytes is not nullptr and length is positive
|
||||
if (bytes != nullptr && length > 0) {
|
||||
// Append each byte to the script
|
||||
for (int i = 0; i < length; ++i) {
|
||||
script << bytes[i];
|
||||
}
|
||||
}
|
||||
|
||||
return script;
|
||||
return script;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility function to convert a C++ CScript to a byte array.
|
||||
*/
|
||||
std::vector<unsigned char> serializeCScript(const CScript& script) {
|
||||
return std::vector<unsigned char>(script.begin(), script.end());
|
||||
return std::vector<unsigned char>(script.begin(), script.end());
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility function to convert an FFI-friendly C CCRecipient struct to a C++ CRecipient struct.
|
||||
*/
|
||||
CRecipient fromFFI(const CCRecipient& c_struct) {
|
||||
// Use the factory function to create a CScript object.
|
||||
CScript script = createCScriptFromBytes(c_struct.pubKey, c_struct.pubKeyLength);
|
||||
// Use the factory function to create a CScript object.
|
||||
CScript script = createCScriptFromBytes(c_struct.pubKey, c_struct.pubKeyLength);
|
||||
|
||||
CRecipient cpp_struct = createCRecipient(
|
||||
script,
|
||||
c_struct.cAmount,
|
||||
static_cast<bool>(c_struct.subtractFee)
|
||||
);
|
||||
CRecipient cpp_struct = createCRecipient(
|
||||
script,
|
||||
c_struct.cAmount,
|
||||
static_cast<bool>(c_struct.subtractFee)
|
||||
);
|
||||
|
||||
return cpp_struct;
|
||||
return cpp_struct;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -162,33 +180,33 @@ CRecipient fromFFI(const CCRecipient& c_struct) {
|
||||
* TODO manage the memory allocated by this function.
|
||||
*/
|
||||
struct CCRecipient createCCRecipient(const unsigned char* pubKey, uint64_t amount, int subtractFee) {
|
||||
CCRecipient recipient;
|
||||
recipient.pubKey = copyBytes(pubKey, 32);
|
||||
recipient.cAmount = amount;
|
||||
recipient.subtractFee = subtractFee;
|
||||
return recipient;
|
||||
CCRecipient recipient;
|
||||
recipient.pubKey = copyBytes(pubKey, 32);
|
||||
recipient.cAmount = amount;
|
||||
recipient.subtractFee = subtractFee;
|
||||
return recipient;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility function to convert a C++ CRecipient struct to an FFI-friendly struct.
|
||||
*/
|
||||
CCRecipient toFFI(const CRecipient& cpp_struct) {
|
||||
CCRecipient c_struct;
|
||||
CCRecipient c_struct;
|
||||
|
||||
// Serialize CScript and copy.
|
||||
std::vector<unsigned char> scriptBytes = serializeCScript(cpp_struct.pubKey);
|
||||
if (!scriptBytes.empty()) {
|
||||
c_struct.pubKey = copyBytes(scriptBytes.data(), scriptBytes.size());
|
||||
c_struct.pubKeyLength = static_cast<int>(scriptBytes.size());
|
||||
} else {
|
||||
c_struct.pubKey = nullptr;
|
||||
c_struct.pubKeyLength = 0;
|
||||
}
|
||||
// Serialize CScript and copy.
|
||||
std::vector<unsigned char> scriptBytes = serializeCScript(cpp_struct.pubKey);
|
||||
if (!scriptBytes.empty()) {
|
||||
c_struct.pubKey = copyBytes(scriptBytes.data(), scriptBytes.size());
|
||||
c_struct.pubKeyLength = static_cast<int>(scriptBytes.size());
|
||||
} else {
|
||||
c_struct.pubKey = nullptr;
|
||||
c_struct.pubKeyLength = 0;
|
||||
}
|
||||
|
||||
c_struct.cAmount = cpp_struct.amount;
|
||||
c_struct.subtractFee = static_cast<int>(cpp_struct.subtractFeeFromAmount);
|
||||
c_struct.cAmount = cpp_struct.amount;
|
||||
c_struct.subtractFee = static_cast<int>(cpp_struct.subtractFeeFromAmount);
|
||||
|
||||
return c_struct;
|
||||
return c_struct;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -197,122 +215,463 @@ CCRecipient toFFI(const CRecipient& cpp_struct) {
|
||||
* TODO manage the memory allocated by this function.
|
||||
*/
|
||||
CRecipient createCRecipient(const CScript& script, CAmount amount, bool subtractFee) {
|
||||
CRecipient recipient;
|
||||
recipient.pubKey = script;
|
||||
recipient.amount = amount;
|
||||
recipient.subtractFeeFromAmount = subtractFee;
|
||||
return recipient;
|
||||
CRecipient recipient;
|
||||
recipient.pubKey = script;
|
||||
recipient.amount = amount;
|
||||
recipient.subtractFeeFromAmount = subtractFee;
|
||||
return recipient;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility function to decode an Address from a string.
|
||||
*/
|
||||
spark::Address decodeAddress(const std::string& str) {
|
||||
spark::Address address;
|
||||
address.decode(str);
|
||||
spark::Address address;
|
||||
address.decode(str);
|
||||
|
||||
return address;
|
||||
return address;
|
||||
}
|
||||
|
||||
/*
|
||||
* MintedCoinData factory.
|
||||
*/
|
||||
spark::MintedCoinData createMintedCoinData(const char* address, uint64_t v, const char* memo) {
|
||||
return {
|
||||
decodeAddress(address),
|
||||
v,
|
||||
memo
|
||||
};
|
||||
return {
|
||||
decodeAddress(address),
|
||||
v,
|
||||
memo
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility function to convert an FFI-friendly C CMintedCoinData struct to a C++ MintedCoinData.
|
||||
*/
|
||||
spark::MintedCoinData fromFFI(const CMintedCoinData& c_struct) {
|
||||
return createMintedCoinData(
|
||||
c_struct.address,
|
||||
c_struct.value,
|
||||
c_struct.memo
|
||||
);
|
||||
return createMintedCoinData(
|
||||
c_struct.address,
|
||||
c_struct.value,
|
||||
c_struct.memo
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* CMintedCoinData factory.
|
||||
*/
|
||||
CMintedCoinData createCMintedCoinData(const char* address, uint64_t value, const char* memo) {
|
||||
CMintedCoinData c_struct;
|
||||
c_struct.address = strdup(address);
|
||||
c_struct.value = value;
|
||||
c_struct.memo = strdup(memo);
|
||||
return c_struct;
|
||||
CMintedCoinData c_struct;
|
||||
c_struct.address = strdup(address);
|
||||
c_struct.value = value;
|
||||
c_struct.memo = strdup(memo);
|
||||
return c_struct;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility function to convert a C++ MintedCoinData struct to an FFI-friendly CMintedCoinData.
|
||||
*/
|
||||
CMintedCoinData toFFI(const spark::MintedCoinData& cpp_struct) {
|
||||
return createCMintedCoinData(
|
||||
cpp_struct.address.encode(true).c_str(),
|
||||
cpp_struct.v,
|
||||
cpp_struct.memo.c_str()
|
||||
);
|
||||
CMintedCoinData toFFI(const spark::MintedCoinData& cpp_struct, int isTestNet) {
|
||||
CMintedCoinData c_struct;
|
||||
c_struct.address = strdup(cpp_struct.address.encode(isTestNet ? spark::ADDRESS_NETWORK_TESTNET : spark::ADDRESS_NETWORK_MAINNET).c_str());
|
||||
c_struct.value = cpp_struct.v;
|
||||
c_struct.memo = strdup(cpp_struct.memo.c_str());
|
||||
return c_struct;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility function for deep copying byte arrays.
|
||||
*
|
||||
* Used by createCCoin.
|
||||
* OutputCoinData factory.
|
||||
*/
|
||||
unsigned char* copyBytes(const unsigned char* source, int length) {
|
||||
if (source == nullptr || length <= 0) return nullptr;
|
||||
spark::OutputCoinData createOutputCoinData(const char* address, uint64_t v, const char* memo) {
|
||||
return {
|
||||
decodeAddress(address),
|
||||
v,
|
||||
memo
|
||||
};
|
||||
}
|
||||
|
||||
unsigned char* dest = new unsigned char[length];
|
||||
std::memcpy(dest, source, length);
|
||||
return dest;
|
||||
/*
|
||||
* Utility function to convert an FFI-friendly C COutputCoinData struct to a C++ OutputCoinData.
|
||||
*/
|
||||
spark::OutputCoinData fromFFI(const COutputCoinData& c_struct) {
|
||||
return createOutputCoinData(
|
||||
c_struct.address,
|
||||
c_struct.value,
|
||||
c_struct.memo
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* COutputCoinData factory.
|
||||
*/
|
||||
COutputCoinData createCOutputCoinData(const char* address, uint64_t value, const char* memo) {
|
||||
COutputCoinData c_struct;
|
||||
c_struct.address = strdup(address);
|
||||
c_struct.value = value;
|
||||
c_struct.memo = strdup(memo);
|
||||
return c_struct;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility function to convert a C++ OutputCoinData struct to an FFI-friendly COutputCoinData.
|
||||
*/
|
||||
COutputCoinData toFFI(const spark::OutputCoinData& cpp_struct, int isTestNet) {
|
||||
// Encode address for testnet
|
||||
std::string address = cpp_struct.address.encode(isTestNet ? spark::ADDRESS_NETWORK_TESTNET : spark::ADDRESS_NETWORK_MAINNET);
|
||||
|
||||
return createCOutputCoinData(
|
||||
address.c_str(),
|
||||
cpp_struct.v,
|
||||
cpp_struct.memo.c_str()
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* CSparkMintMeta factory.
|
||||
*
|
||||
* A CSparkMintMeta is a C++ 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.
|
||||
*/
|
||||
CSparkMintMeta createCSparkMintMeta(
|
||||
const uint64_t height, const uint64_t id, const int isUsed,
|
||||
const char* txidStr, const uint64_t diversifier,
|
||||
const char* encryptedDiversifierStr, const uint64_t value,
|
||||
const char* nonceStr, const char* memoStr,
|
||||
const unsigned char* serialContext,
|
||||
const int serialContextLength, const char type, const CCoin coin
|
||||
) {
|
||||
CSparkMintMeta cpp_struct;
|
||||
|
||||
cpp_struct.nHeight = height;
|
||||
cpp_struct.nId = id;
|
||||
cpp_struct.isUsed = isUsed != 0;
|
||||
|
||||
if (txidStr) {
|
||||
cpp_struct.txid = uint256S(txidStr);
|
||||
}
|
||||
|
||||
if (encryptedDiversifierStr) {
|
||||
size_t edLen = std::strlen(encryptedDiversifierStr);
|
||||
cpp_struct.d = std::vector<unsigned char>(encryptedDiversifierStr, encryptedDiversifierStr + edLen);
|
||||
}
|
||||
|
||||
cpp_struct.i = diversifier;
|
||||
cpp_struct.v = value;
|
||||
|
||||
if (nonceStr) {
|
||||
size_t nonceLen = std::strlen(nonceStr);
|
||||
std::vector<unsigned char> nonceBytes(nonceStr, nonceStr + nonceLen);
|
||||
cpp_struct.k = Scalar(nonceBytes.data());
|
||||
}
|
||||
|
||||
if (memoStr) {
|
||||
cpp_struct.memo = std::string(memoStr);
|
||||
}
|
||||
|
||||
if (serialContext && serialContextLength > 0) {
|
||||
cpp_struct.serial_context = std::vector<unsigned char>(serialContext, serialContext + serialContextLength);
|
||||
}
|
||||
|
||||
cpp_struct.type = type;
|
||||
cpp_struct.coin = fromFFI(coin);
|
||||
|
||||
return cpp_struct;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
CDataStream coin = c_struct.coin;
|
||||
cpp_struct.coin = fromFFI(coin);
|
||||
return cpp_struct;
|
||||
}
|
||||
|
||||
/*
|
||||
* CCSparkMintMeta constructor.
|
||||
*
|
||||
* Needed because CDataStream has no constructor.
|
||||
*/
|
||||
CCSparkMintMeta::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 CDataStream& coinData
|
||||
) : height(height), isUsed(isUsed), i(i), v(v), dLength(dLength), kLength(kLength),
|
||||
memoLength(memoLength), serial_contextLength(serial_contextLength), type(type), coin(coinData)
|
||||
{
|
||||
this->id = strdup(id);
|
||||
this->txid = strdup(txid);
|
||||
this->d = copyBytes(d, dLength);
|
||||
this->k = copyBytes(k, kLength);
|
||||
this->memo = strdup(memo);
|
||||
this->serial_context = copyBytes(serial_context, serial_contextLength);
|
||||
}
|
||||
|
||||
/*
|
||||
* CCSparkMintMeta destructor.
|
||||
*/
|
||||
CCSparkMintMeta::~CCSparkMintMeta() {
|
||||
free(const_cast<char*>(id));
|
||||
free(const_cast<char*>(txid));
|
||||
delete[] d;
|
||||
delete[] k;
|
||||
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);
|
||||
CDataStream coinStream = toFFI(coinStruct);
|
||||
|
||||
// Construct CCSparkMintMeta using the constructor
|
||||
return CCSparkMintMeta(
|
||||
height,
|
||||
idCStr,
|
||||
isUsed,
|
||||
txid,
|
||||
diversifier,
|
||||
encryptedDiversifierCStr,
|
||||
std::strlen(encryptedDiversifier),
|
||||
value,
|
||||
nonceCStr,
|
||||
std::strlen(nonce),
|
||||
memo,
|
||||
std::strlen(memo),
|
||||
const_cast<unsigned char*>(serial_context),
|
||||
serial_contextLength,
|
||||
type,
|
||||
coinStream
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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();
|
||||
|
||||
CDataStream coinStream = toFFI(cpp_struct.coin);
|
||||
|
||||
std::vector<unsigned char> scalarBytes(32);
|
||||
cpp_struct.k.serialize(scalarBytes.data());
|
||||
|
||||
return CCSparkMintMeta(
|
||||
cpp_struct.nHeight,
|
||||
idCStr,
|
||||
cpp_struct.isUsed,
|
||||
cpp_struct.txid.ToString().c_str(),
|
||||
cpp_struct.i,
|
||||
copyBytes(cpp_struct.d.data(), cpp_struct.d.size()),
|
||||
cpp_struct.d.size(),
|
||||
cpp_struct.v,
|
||||
copyBytes(scalarBytes.data(), scalarBytes.size()), // Size should be 32.
|
||||
32,
|
||||
strdup(cpp_struct.memo.c_str()),
|
||||
cpp_struct.memo.size(),
|
||||
copyBytes(cpp_struct.serial_context.data(), cpp_struct.serial_context.size()),
|
||||
cpp_struct.serial_context.size(),
|
||||
cpp_struct.type,
|
||||
coinStream
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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];
|
||||
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<CDataStream> 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;
|
||||
|
||||
// Add stream to vector.
|
||||
cover_set_streams.push_back(stream);
|
||||
}
|
||||
|
||||
// Create array containing the address of cover_set_streams
|
||||
CDataStream** cover_set_streams_pp = new CDataStream*[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.
|
||||
*
|
||||
* This throws. We don't need toFFI yet (except to make complete tests...), so let's just comment
|
||||
* it out.
|
||||
*
|
||||
CCoverSetData toFFI(const spark::CoverSetData& cpp_struct) {
|
||||
CCoverSetData c_struct;
|
||||
// Converting the CCoins to spark::Coins.
|
||||
std::vector<spark::Coin> cover_set;
|
||||
for (int i = 0; i < cpp_struct.cover_set.size(); i++) {
|
||||
cover_set.push_back(fromFFI(cpp_struct.cover_set[i])); // This line throws.
|
||||
}
|
||||
|
||||
// Serialize the spark::Coins as CDataStreams.
|
||||
std::vector<CDataStream> cover_set_streams;
|
||||
for (int i = 0; i < cover_set.size(); i++) {
|
||||
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
|
||||
stream << cover_set[i];
|
||||
cover_set_streams.push_back(stream);
|
||||
}
|
||||
|
||||
// Convert the std::vector<CDataStream> to a CDataStream*.
|
||||
CDataStream* cover_set_streams_array = &cover_set_streams[0];
|
||||
|
||||
c_struct.cover_set_representation = cpp_struct.cover_set_representation.data();
|
||||
c_struct.cover_set_representationLength = cpp_struct.cover_set_representation.size();
|
||||
|
||||
for (int i = 0; i < cpp_struct.cover_set.size(); i++) {
|
||||
c_struct.cover_set[i] = toFFI(cpp_struct.cover_set[i]);
|
||||
}
|
||||
|
||||
return c_struct;
|
||||
}
|
||||
*/
|
||||
|
||||
unsigned char* copyBytes(const unsigned char* source, int length) {
|
||||
if (source == nullptr || length <= 0) return nullptr;
|
||||
|
||||
unsigned char* dest = new unsigned char[length];
|
||||
std::memcpy(dest, source, length);
|
||||
return dest;
|
||||
}
|
||||
|
||||
Scalar bytesToScalar(const unsigned char* bytes, int size) {
|
||||
if (bytes == nullptr || size <= 0) {
|
||||
throw std::invalid_argument("Invalid byte array for Scalar conversion.");
|
||||
}
|
||||
// Assuming Scalar can be constructed from a byte array.
|
||||
return Scalar(bytes);
|
||||
}
|
||||
|
||||
unsigned char *hexToBytes(const char *hexstr) {
|
||||
size_t length = strlen(hexstr) / 2;
|
||||
auto *chrs = (unsigned char *) malloc((length + 1) * sizeof(unsigned char));
|
||||
for (size_t i = 0, j = 0; j < length; i += 2, j++) {
|
||||
chrs[j] = (hexstr[i] % 32 + 9) % 25 * 16 + (hexstr[i + 1] % 32 + 9) % 25;
|
||||
}
|
||||
chrs[length] = '\0';
|
||||
return chrs;
|
||||
size_t length = strlen(hexstr) / 2;
|
||||
auto *chrs = (unsigned char *) malloc((length + 1) * sizeof(unsigned char));
|
||||
for (size_t i = 0, j = 0; j < length; i += 2, j++) {
|
||||
chrs[j] = (hexstr[i] % 32 + 9) % 25 * 16 + (hexstr[i + 1] % 32 + 9) % 25;
|
||||
}
|
||||
chrs[length] = '\0';
|
||||
return chrs;
|
||||
}
|
||||
|
||||
const char *bytesToHex(const unsigned char *bytes, int size) {
|
||||
std::string str;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
const unsigned char ch = bytes[i];
|
||||
str.append(&hexArray[(ch & 0xF0) >> 4], 1);
|
||||
str.append(&hexArray[ch & 0xF], 1);
|
||||
}
|
||||
char *new_str = new char[std::strlen(str.c_str()) + 1];
|
||||
std::strcpy(new_str, str.c_str());
|
||||
return new_str;
|
||||
std::string str;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
const unsigned char ch = bytes[i];
|
||||
str.append(&hexArray[(ch & 0xF0) >> 4], 1);
|
||||
str.append(&hexArray[ch & 0xF], 1);
|
||||
}
|
||||
char *new_str = new char[std::strlen(str.c_str()) + 1];
|
||||
std::strcpy(new_str, str.c_str());
|
||||
return new_str;
|
||||
}
|
||||
|
||||
const char *bytesToHex(const char *bytes, int size) {
|
||||
std::string str;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
const unsigned char ch = (const unsigned char) bytes[i];
|
||||
str.append(&hexArray[(ch & 0xF0) >> 4], 1);
|
||||
str.append(&hexArray[ch & 0xF], 1);
|
||||
}
|
||||
char *new_str = new char[std::strlen(str.c_str()) + 1];
|
||||
std::strcpy(new_str, str.c_str());
|
||||
return new_str;
|
||||
std::string str;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
const unsigned char ch = (const unsigned char) bytes[i];
|
||||
str.append(&hexArray[(ch & 0xF0) >> 4], 1);
|
||||
str.append(&hexArray[ch & 0xF], 1);
|
||||
}
|
||||
char *new_str = new char[std::strlen(str.c_str()) + 1];
|
||||
std::strcpy(new_str, str.c_str());
|
||||
return new_str;
|
||||
}
|
||||
|
||||
const char *bytesToHex(std::vector<unsigned char> bytes, int size) {
|
||||
std::string str;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
const unsigned char ch = bytes[i];
|
||||
str.append(&hexArray[(ch & 0xF0) >> 4], 1);
|
||||
str.append(&hexArray[ch & 0xF], 1);
|
||||
}
|
||||
char *new_str = new char[std::strlen(str.c_str()) + 1];
|
||||
std::strcpy(new_str, str.c_str());
|
||||
return new_str;
|
||||
std::string str;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
const unsigned char ch = bytes[i];
|
||||
str.append(&hexArray[(ch & 0xF0) >> 4], 1);
|
||||
str.append(&hexArray[ch & 0xF], 1);
|
||||
}
|
||||
char *new_str = new char[std::strlen(str.c_str()) + 1];
|
||||
std::strcpy(new_str, str.c_str());
|
||||
return new_str;
|
||||
}
|
||||
|
||||
38
src/utils.h
38
src/utils.h
@@ -8,16 +8,19 @@
|
||||
|
||||
#include "flutter_libsparkmobile.h"
|
||||
#include "deps/sparkmobile/include/spark.h"
|
||||
#include "deps/sparkmobile/bitcoin/streams.h" // For CDataStream.
|
||||
|
||||
const char* getAddressFromData(const char* keyData, int index, const uint64_t diversifier);
|
||||
const char* getAddressFromData(const char* keyData, int index, const uint64_t diversifier, int isTestNet);
|
||||
|
||||
spark::SpendKey createSpendKeyFromData(const char *keyData, int index);
|
||||
|
||||
spark::Coin fromFFI(const CCoin& c_struct);
|
||||
|
||||
CCoin toFFI(const spark::Coin& cpp_struct);
|
||||
spark::Coin fromFFI(CDataStream& coinStream);
|
||||
|
||||
struct CCoin createCCoin(char type, const unsigned char* k, int kLength, const char* keyData, int index, uint64_t v, const unsigned char* memo, int memoLength, const unsigned char* serial_context, int serial_contextLength);
|
||||
struct 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);
|
||||
|
||||
CDataStream toFFI(const spark::Coin& cpp_struct);
|
||||
|
||||
spark::IdentifiedCoinData fromFFI(const CIdentifiedCoinData& c_struct);
|
||||
|
||||
@@ -43,13 +46,37 @@ spark::MintedCoinData fromFFI(const CMintedCoinData& c_struct);
|
||||
|
||||
CMintedCoinData createCMintedCoinData(const char* address, uint64_t value, const char* memo);
|
||||
|
||||
CMintedCoinData toFFI(const spark::MintedCoinData& cpp_struct);
|
||||
CMintedCoinData toFFI(const spark::MintedCoinData& cpp_struct, int isTestNet);
|
||||
|
||||
spark::OutputCoinData fromFFI(const COutputCoinData& c_struct);
|
||||
|
||||
spark::OutputCoinData createOutputCoinData(const char* address, uint64_t v, const char* memo);
|
||||
|
||||
COutputCoinData createCOutputCoinData(const char* address, uint64_t value, const char* memo);
|
||||
|
||||
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 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);
|
||||
|
||||
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'};
|
||||
'e', 'f'};
|
||||
|
||||
unsigned char* copyBytes(const unsigned char* source, int length);
|
||||
|
||||
Scalar bytesToScalar(const unsigned char* bytes, int size);
|
||||
|
||||
unsigned char *hexToBytes(const char *str);
|
||||
|
||||
const char *bytesToHex(const unsigned char *bytes, int size);
|
||||
@@ -58,5 +85,4 @@ const char *bytesToHex(const char *bytes, int size);
|
||||
|
||||
const char *bytesToHex(std::vector<unsigned char> bytes, int size);
|
||||
|
||||
|
||||
#endif //ORG_FIRO_SPARK_UTILS_H
|
||||
|
||||
Reference in New Issue
Block a user