#ifndef BITCOIN_PRIMITIVES_TRANSACTION_H
#define BITCOIN_PRIMITIVES_TRANSACTION_H
#include <attributes.h>
#include <consensus/amount.h>
#include <script/script.h>
#include <serialize.h>
#include <uint256.h>
#include <util/transaction_identifier.h>
#include <cstddef>
#include <cstdint>
#include <ios>
#include <limits>
#include <memory>
#include <numeric>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
class COutPoint
{
public:
Txid hash;
uint32_t n;
static constexpr uint32_t NULL_INDEX = std::numeric_limits<uint32_t>::max();
COutPoint(): n(NULL_INDEX) { }
COutPoint(const Txid& hashIn, uint32_t nIn): hash(hashIn), n(nIn) { }
SERIALIZE_METHODS(COutPoint, obj) { READWRITE(obj.hash, obj.n); }
void SetNull() { hash.SetNull(); n = NULL_INDEX; }
bool IsNull() const { return (hash.IsNull() && n == NULL_INDEX); }
friend bool operator<(const COutPoint& a, const COutPoint& b)
{
return std::tie(a.hash, a.n) < std::tie(b.hash, 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;
};
class CTxIn
{
public:
COutPoint prevout;
CScript scriptSig;
uint32_t nSequence;
CScriptWitness scriptWitness;
static const uint32_t SEQUENCE_FINAL = 0xffffffff;
static const uint32_t MAX_SEQUENCE_NONFINAL{SEQUENCE_FINAL - 1};
static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1U << 31);
static const uint32_t SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22);
static const uint32_t SEQUENCE_LOCKTIME_MASK = 0x0000ffff;
static const int SEQUENCE_LOCKTIME_GRANULARITY = 9;
CTxIn()
{
nSequence = SEQUENCE_FINAL;
}
explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=SEQUENCE_FINAL);
CTxIn(Txid hashPrevTx, uint32_t nOut, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=SEQUENCE_FINAL);
SERIALIZE_METHODS(CTxIn, obj) { READWRITE(obj.prevout, obj.scriptSig, obj.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);
}
std::string ToString() const;
};
class CTxOut
{
public:
CAmount nValue;
CScript scriptPubKey;
CTxOut()
{
SetNull();
}
CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn);
SERIALIZE_METHODS(CTxOut, obj) { READWRITE(obj.nValue, obj.scriptPubKey); }
void SetNull()
{
nValue = -1;
scriptPubKey.clear();
}
bool IsNull() const
{
return (nValue == -1);
}
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);
}
std::string ToString() const;
};
struct CMutableTransaction;
struct TransactionSerParams {
const bool allow_witness;
SER_PARAMS_OPFUNC
};
static constexpr TransactionSerParams TX_WITH_WITNESS{.allow_witness = true};
static constexpr TransactionSerParams TX_NO_WITNESS{.allow_witness = false};
template<typename Stream, typename TxType>
void UnserializeTransaction(TxType& tx, Stream& s, const TransactionSerParams& params)
{
const bool fAllowWitness = params.allow_witness;
s >> tx.version;
unsigned char flags = 0;
tx.vin.clear();
tx.vout.clear();
s >> tx.vin;
if (tx.vin.size() == 0 && fAllowWitness) {
s >> flags;
if (flags != 0) {
s >> tx.vin;
s >> tx.vout;
}
} else {
s >> tx.vout;
}
if ((flags & 1) && fAllowWitness) {
flags ^= 1;
for (size_t i = 0; i < tx.vin.size(); i++) {
s >> tx.vin[i].scriptWitness.stack;
}
if (!tx.HasWitness()) {
throw std::ios_base::failure("Superfluous witness record");
}
}
if (flags) {
throw std::ios_base::failure("Unknown transaction optional data");
}
s >> tx.nLockTime;
}
template<typename Stream, typename TxType>
void SerializeTransaction(const TxType& tx, Stream& s, const TransactionSerParams& params)
{
const bool fAllowWitness = params.allow_witness;
s << tx.version;
unsigned char flags = 0;
if (fAllowWitness) {
if (tx.HasWitness()) {
flags |= 1;
}
}
if (flags) {
std::vector<CTxIn> vinDummy;
s << vinDummy;
s << flags;
}
s << tx.vin;
s << tx.vout;
if (flags & 1) {
for (size_t i = 0; i < tx.vin.size(); i++) {
s << tx.vin[i].scriptWitness.stack;
}
}
s << tx.nLockTime;
}
template<typename TxType>
inline CAmount CalculateOutputValue(const TxType& tx)
{
return std::accumulate(tx.vout.cbegin(), tx.vout.cend(), CAmount{0}, [](CAmount sum, const auto& txout) { return sum + txout.nValue; });
}
class CTransaction
{
public:
static const uint32_t CURRENT_VERSION{2};
const std::vector<CTxIn> vin;
const std::vector<CTxOut> vout;
const uint32_t version;
const uint32_t nLockTime;
private:
const bool m_has_witness;
const Txid hash;
const Wtxid m_witness_hash;
Txid ComputeHash() const;
Wtxid ComputeWitnessHash() const;
bool ComputeHasWitness() const;
public:
explicit CTransaction(const CMutableTransaction& tx);
explicit CTransaction(CMutableTransaction&& tx);
template <typename Stream>
inline void Serialize(Stream& s) const {
SerializeTransaction(*this, s, s.template GetParams<TransactionSerParams>());
}
template <typename Stream>
CTransaction(deserialize_type, const TransactionSerParams& params, Stream& s) : CTransaction(CMutableTransaction(deserialize, params, s)) {}
template <typename Stream>
CTransaction(deserialize_type, Stream& s) : CTransaction(CMutableTransaction(deserialize, s)) {}
bool IsNull() const {
return vin.empty() && vout.empty();
}
const Txid& GetHash() const LIFETIMEBOUND { return hash; }
const Wtxid& GetWitnessHash() const LIFETIMEBOUND { return m_witness_hash; };
CAmount GetValueOut() const;
unsigned int GetTotalSize() const;
bool IsCoinBase() const
{
return (vin.size() == 1 && vin[0].prevout.IsNull());
}
friend bool operator==(const CTransaction& a, const CTransaction& b)
{
return a.GetWitnessHash() == b.GetWitnessHash();
}
friend bool operator!=(const CTransaction& a, const CTransaction& b)
{
return !operator==(a, b);
}
std::string ToString() const;
bool HasWitness() const { return m_has_witness; }
};
struct CMutableTransaction
{
std::vector<CTxIn> vin;
std::vector<CTxOut> vout;
uint32_t version;
uint32_t nLockTime;
explicit CMutableTransaction();
explicit CMutableTransaction(const CTransaction& tx);
template <typename Stream>
inline void Serialize(Stream& s) const {
SerializeTransaction(*this, s, s.template GetParams<TransactionSerParams>());
}
template <typename Stream>
inline void Unserialize(Stream& s) {
UnserializeTransaction(*this, s, s.template GetParams<TransactionSerParams>());
}
template <typename Stream>
CMutableTransaction(deserialize_type, const TransactionSerParams& params, Stream& s) {
UnserializeTransaction(*this, s, params);
}
template <typename Stream>
CMutableTransaction(deserialize_type, Stream& s) {
Unserialize(s);
}
Txid GetHash() const;
bool HasWitness() const
{
for (size_t i = 0; i < vin.size(); i++) {
if (!vin[i].scriptWitness.IsNull()) {
return true;
}
}
return false;
}
};
typedef std::shared_ptr<const CTransaction> CTransactionRef;
template <typename Tx> static inline CTransactionRef MakeTransactionRef(Tx&& txIn) { return std::make_shared<const CTransaction>(std::forward<Tx>(txIn)); }
#endif