WIP mint serial context serialization

This commit is contained in:
julian
2023-12-16 14:28:58 -06:00
parent 1574ed2e29
commit a618beb697
8 changed files with 460 additions and 0 deletions

View File

@@ -160,6 +160,28 @@ abstract final class LibSpark {
return ret;
}
static Uint8List serializeMintContext({required List<(String, int)> inputs}) {
final inputsPtr =
malloc.allocate<DartInputData>(sizeOf<DartInputData>() * inputs.length);
for (int i = 0; i < inputs.length; i++) {
final hash =
Uint8List.fromList(inputs[i].$1.to32BytesFromHex().reversed.toList());
inputsPtr[i].txHashLength = hash.length;
inputsPtr[i].txHash = hash.unsignedCharPointer();
inputsPtr[i].vout = inputs[i].$2;
}
final result = _bindings.serializeMintContext(inputsPtr, inputs.length);
final serialized = result.ref.context.toUint8List(result.ref.contextLength);
// TODO frees
return serialized;
}
///
/// Create spark mint recipients
///

View File

@@ -229,6 +229,24 @@ class FlutterLibsparkmobileBindings {
late final _selectSparkCoins = _selectSparkCoinsPtr.asFunction<
ffi.Pointer<SelectSparkCoinsResult> Function(
int, int, ffi.Pointer<CCSparkMintMeta>, int, int)>();
ffi.Pointer<SerializedMintContextResult> serializeMintContext(
ffi.Pointer<DartInputData> inputs,
int inputsLength,
) {
return _serializeMintContext(
inputs,
inputsLength,
);
}
late final _serializeMintContextPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<SerializedMintContextResult> Function(
ffi.Pointer<DartInputData>, ffi.Int)>>('serializeMintContext');
late final _serializeMintContext = _serializeMintContextPtr.asFunction<
ffi.Pointer<SerializedMintContextResult> Function(
ffi.Pointer<DartInputData>, int)>();
}
/// FFI-friendly wrapper for a spark::Coin.
@@ -539,3 +557,20 @@ final class SparkSpendTransactionResult extends ffi.Struct {
@ffi.Int()
external int isError;
}
final class DartInputData extends ffi.Struct {
external ffi.Pointer<ffi.UnsignedChar> txHash;
@ffi.Int()
external int txHashLength;
@ffi.Int()
external int vout;
}
final class SerializedMintContextResult extends ffi.Struct {
external ffi.Pointer<ffi.UnsignedChar> context;
@ffi.Int()
external int contextLength;
}

View File

@@ -17,6 +17,7 @@ endif()
add_library(flutter_libsparkmobile SHARED
"flutter_libsparkmobile.cpp"
"utils.cpp"
"transaction.cpp"
)
add_subdirectory(deps/boost-cmake)

View File

@@ -4,6 +4,8 @@
#include "deps/sparkmobile/src/spark.h"
#include "deps/sparkmobile/bitcoin/uint256.h"
#include "structs.h"
#include "transaction.h"
#include "deps/sparkmobile/bitcoin/script.h" // For CScript.
#include <cstring>
#include <iostream> // Just for printing.
@@ -475,4 +477,27 @@ SelectSparkCoinsResult* selectSparkCoins(
return result;
}
}
FFI_PLUGIN_EXPORT
SerializedMintContextResult* serializeMintContext(
DartInputData* inputs,
int inputsLength
) {
CDataStream serialContextStream(SER_NETWORK, PROTOCOL_VERSION);
for (int i = 0; i < inputsLength; i++) {
std::vector<unsigned char> vec(inputs[i].txHash, inputs[i].txHash + inputs[i].txHashLength);
CTxIn input(
uint256(vec),
inputs[i].vout,
CScript(),
std::numeric_limits<unsigned int>::max() - 1);
serialContextStream << input;
}
SerializedMintContextResult* result = (SerializedMintContextResult*)malloc(sizeof(SerializedMintContextResult));
result->contextLength = serialContextStream.size();
result->context = (unsigned char*) malloc(sizeof(unsigned char) * serialContextStream.size());
memcpy(result->context, serialContextStream.data(), sizeof(unsigned char) * serialContextStream.size());
return result;
}

View File

@@ -93,4 +93,10 @@ struct SelectSparkCoinsResult* selectSparkCoins(
int mintNum
);
FFI_PLUGIN_EXPORT
struct SerializedMintContextResult* serializeMintContext(
struct DartInputData* inputs,
int inputsLength
);
#endif //ORG_FIRO_SPARK_DART_INTERFACE_H

View File

@@ -220,6 +220,18 @@ struct SparkSpendTransactionResult {
int isError;
};
struct DartInputData {
unsigned char *txHash;
int txHashLength;
int vout;
};
struct SerializedMintContextResult {
unsigned char *context;
int contextLength;
};
//#ifdef __cplusplus
//}
//#endif

89
src/transaction.cpp Normal file
View File

@@ -0,0 +1,89 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "transaction.h"
//#include "script/interpreter.h"
#include "deps/sparkmobile/bitcoin/hash.h"
#include "deps/sparkmobile/bitcoin/tinyformat.h"
#include "deps/sparkmobile/bitcoin/utilstrencodings.h"
/** Default for -blockprioritysize, maximum space for zero/low-fee transactions **/
static const unsigned int DEFAULT_BLOCK_PRIORITY_SIZE = 50000; // 50KB
/** Dust Soft Limit, allowed with additional fee per output */
static const int64_t DUST_SOFT_LIMIT = 100000; // 0.001 FIRO
/** The maximum allowed size for a serialized block, in bytes (network rule) */
static const unsigned int MAX_BLOCK_SIZE = 2000000; // 2000KB block hard limit
/** Obsolete: maximum size for mined blocks */
static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/4; // 500KB block soft limit
std::string COutPoint::ToString() const
{
return strprintf("COutPoint(%s, %u)", hash.ToString(), n);
}
std::string COutPoint::ToStringShort() const
{
return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,64), n);
}
CTxIn::CTxIn(COutPoint prevoutIn, CScript scriptSigIn, uint32_t nSequenceIn)
{
prevout = prevoutIn;
scriptSig = scriptSigIn;
nSequence = nSequenceIn;
}
CTxIn::CTxIn(uint256 hashPrevTx, uint32_t nOut, CScript scriptSigIn, uint32_t nSequenceIn)
{
prevout = COutPoint(hashPrevTx, nOut);
scriptSig = scriptSigIn;
nSequence = nSequenceIn;
}
bool CTxIn::IsZerocoinSpend() const
{
return (prevout.IsNull() && scriptSig.size() > 0 && (scriptSig[0] == OP_ZEROCOINSPEND) );
}
bool CTxIn::IsSigmaSpend() const
{
return (prevout.IsSigmaMintGroup() && scriptSig.size() > 0 && (scriptSig[0] == OP_SIGMASPEND) );
}
bool CTxIn::IsLelantusJoinSplit() const
{
return (prevout.IsNull() && scriptSig.size() > 0 && (scriptSig[0] == OP_LELANTUSJOINSPLIT || scriptSig[0] == OP_LELANTUSJOINSPLITPAYLOAD) );
}
bool CTxIn::IsZerocoinRemint() const
{
return (prevout.IsNull() && scriptSig.size() > 0 && (scriptSig[0] == OP_ZEROCOINTOSIGMAREMINT));
}
std::string CTxIn::ToString() const
{
std::string str;
str += "CTxIn(";
str += prevout.ToString();
if (prevout.IsNull())
str += strprintf(", coinbase %s", HexStr(scriptSig).substr(0, 24));
else
str += strprintf(", scriptSig=%s", HexStr(scriptSig).substr(0, 24));
if (nSequence != SEQUENCE_FINAL)
str += strprintf(", nSequence=%u", nSequence);
str += ")";
return str;
}
CTxOut::CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn)
{
nValue = nValueIn;
scriptPubKey = scriptPubKeyIn;
}
std::string CTxOut::ToString() const
{
return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, HexStr(scriptPubKey).substr(0, 30));
}

270
src/transaction.h Normal file
View File

@@ -0,0 +1,270 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_PRIMITIVES_TRANSACTION_H
#define BITCOIN_PRIMITIVES_TRANSACTION_H
#include "deps/sparkmobile/bitcoin/amount.h"
#include "deps/sparkmobile/bitcoin/script.h"
#include "deps/sparkmobile/bitcoin/serialize.h"
#include "deps/sparkmobile/bitcoin/uint256.h"
#include <exception>
static const int SERIALIZE_TRANSACTION_NO_WITNESS = 0x40000000;
static const int WITNESS_SCALE_FACTOR = 4;
class CBadTxIn : public std::exception
{
};
class CBadSequence : public CBadTxIn
{
};
///** Transaction types */
//enum {
// TRANSACTION_NORMAL = 0,
// TRANSACTION_PROVIDER_REGISTER = 1,
// TRANSACTION_PROVIDER_UPDATE_SERVICE = 2,
// TRANSACTION_PROVIDER_UPDATE_REGISTRAR = 3,
// TRANSACTION_PROVIDER_UPDATE_REVOKE = 4,
// TRANSACTION_COINBASE = 5,
// TRANSACTION_QUORUM_COMMITMENT = 6,
// TRANSACTION_SPORK = 7,
// TRANSACTION_LELANTUS = 8,
// TRANSACTION_SPARK = 9
//};
/** An outpoint - a combination of a transaction hash and an index n into its vout */
class COutPoint
{
public:
uint256 hash;
uint32_t n;
COutPoint() { SetNull(); }
COutPoint(uint256 hashIn, uint32_t nIn) { hash = hashIn; n = nIn; }
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(hash);
READWRITE(n);
}
void SetNull() { hash.SetNull(); n = (uint32_t) -1; }
bool IsNull() const { return (hash.IsNull() && n == (uint32_t) -1); }
bool IsSigmaMintGroup() const { return hash.IsNull() && n >= 1; }
friend bool operator<(const COutPoint& a, const COutPoint& b)
{
int cmp = a.hash.Compare(b.hash);
return cmp < 0 || (cmp == 0 && a.n < b.n);
}
friend bool operator==(const COutPoint& a, const COutPoint& b)
{
return (a.hash == b.hash && a.n == b.n);
}
friend bool operator!=(const COutPoint& a, const COutPoint& b)
{
return !(a == b);
}
std::string ToString() const;
std::string ToStringShort() const;
};
/** An input of a transaction. It contains the location of the previous
* transaction's output that it claims and a signature that matches the
* output's public key.
*/
class CTxIn
{
public:
COutPoint prevout;
CScript scriptSig;
uint32_t nSequence;
CScript prevPubKey;
CScriptWitness scriptWitness; //! Only serialized through CTransaction
/* Setting nSequence to this value for every input in a transaction
* disables nLockTime. */
static const uint32_t SEQUENCE_FINAL = 0xffffffff;
/* Below flags apply in the context of BIP 68*/
/* If this flag set, CTxIn::nSequence is NOT interpreted as a
* relative lock-time. */
static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1 << 31);
/* If CTxIn::nSequence encodes a relative lock-time and this flag
* is set, the relative lock-time has units of 512 seconds,
* otherwise it specifies blocks with a granularity of 1. */
static const uint32_t SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22);
/* If CTxIn::nSequence encodes a relative lock-time, this mask is
* applied to extract that lock-time from the sequence field. */
static const uint32_t SEQUENCE_LOCKTIME_MASK = 0x0000ffff;
/* In order to use the same number of bits to encode roughly the
* same wall-clock duration, and because blocks are naturally
* limited to occur every 600s on average, the minimum granularity
* for time-based relative lock-time is fixed at 512 seconds.
* Converting from CTxIn::nSequence to seconds is performed by
* multiplying by 512 = 2^9, or equivalently shifting up by
* 9 bits. */
static const int SEQUENCE_LOCKTIME_GRANULARITY = 9;
CTxIn()
{
nSequence = SEQUENCE_FINAL;
}
explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=SEQUENCE_FINAL);
CTxIn(uint256 hashPrevTx, uint32_t nOut, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=SEQUENCE_FINAL);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(prevout);
READWRITE(*(CScriptBase*)(&scriptSig));
READWRITE(nSequence);
}
friend bool operator==(const CTxIn& a, const CTxIn& b)
{
return (a.prevout == b.prevout &&
a.scriptSig == b.scriptSig &&
a.nSequence == b.nSequence);
}
friend bool operator!=(const CTxIn& a, const CTxIn& b)
{
return !(a == b);
}
friend bool operator<(const CTxIn& a, const CTxIn& b)
{
return a.prevout<b.prevout;
}
std::string ToString() const;
bool IsZerocoinSpend() const;
bool IsSigmaSpend() const;
bool IsLelantusJoinSplit() const;
bool IsZerocoinRemint() const;
};
/** An output of a transaction. It contains the public key that the next input
* must be able to sign with to claim it.
*/
class CTxOut
{
public:
CAmount nValue;
CScript scriptPubKey;
int nRounds;
CTxOut()
{
SetNull();
}
CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action) {
READWRITE(nValue);
READWRITE(*(CScriptBase*)(&scriptPubKey));
if (ser_action.ForRead())
nRounds = -10;
}
void SetNull()
{
nValue = -1;
scriptPubKey.clear();
nRounds = -10; // an initial value, should be no way to get this by calculations
}
bool IsNull() const
{
return (nValue == -1);
}
CAmount GetDustThreshold(const CFeeRate &minRelayTxFee) const
{
// "Dust" is defined in terms of CTransaction::minRelayTxFee,
// which has units satoshis-per-kilobyte.
// If you'd pay more than 1/3 in fees
// to spend something, then we consider it dust.
// A typical spendable non-segwit txout is 34 bytes big, and will
// need a CTxIn of at least 148 bytes to spend:
// so dust is a spendable txout less than
// 546*minRelayTxFee/1000 (in satoshis).
// A typical spendable segwit txout is 31 bytes big, and will
// need a CTxIn of at least 67 bytes to spend:
// so dust is a spendable txout less than
// 294*minRelayTxFee/1000 (in satoshis).
if (scriptPubKey.IsUnspendable())
return 0;
size_t nSize = GetSerializeSize(*this, SER_DISK, 0);
int witnessversion = 0;
std::vector<unsigned char> witnessprogram;
if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
// sum the sizes of the parts of a transaction input
// with 75% segwit discount applied to the script size.
nSize += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4);
} else {
nSize += (32 + 4 + 1 + 107 + 4); // the 148 mentioned above
}
return 3 * minRelayTxFee.GetFee(nSize);
}
bool IsDust() const
{
return false;
}
bool IsDust(const CFeeRate &minRelayTxFee) const
{
// return (nValue < GetDustThreshold(minRelayTxFee));
//firo: disable dust
return false;
}
friend bool operator==(const CTxOut& a, const CTxOut& b)
{
return (a.nValue == b.nValue &&
a.scriptPubKey == b.scriptPubKey);
}
friend bool operator!=(const CTxOut& a, const CTxOut& b)
{
return !(a == b);
}
friend bool operator<(const CTxOut& a, const CTxOut& b)
{
return a.nValue < b.nValue || (a.nValue == b.nValue && a.scriptPubKey < b.scriptPubKey);
}
uint256 GetHash() const { return SerializeHash(*this); }
std::string ToString() const;
};
#endif // BITCOIN_PRIMITIVES_TRANSACTION_H