159 lines
5.5 KiB
C++
159 lines
5.5 KiB
C++
/*
|
|
* This file is part of the Flowee project
|
|
* Copyright (C) 2019-2021 Tom Zander <tom@flowee.org>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#ifndef DOUBLESPENDPROOF_H
|
|
#define DOUBLESPENDPROOF_H
|
|
|
|
#include <uint256.h>
|
|
#include <primitives/Tx.h>
|
|
#include <primitives/script.h>
|
|
#include <primitives/ScriptDefines.h> // for MAX_SCRIPT_ELEMENT_SIZE
|
|
#include "serialize.h"
|
|
|
|
#include <utility>
|
|
|
|
class CTxMemPool;
|
|
|
|
class DoubleSpendProof
|
|
{
|
|
public:
|
|
//! limit for the size of a `pushData` vector below
|
|
static constexpr size_t MaxPushDataSize = MAX_SCRIPT_ELEMENT_SIZE;
|
|
|
|
/** Creates an empty, invalid object */
|
|
DoubleSpendProof();
|
|
|
|
/** Create a proof object, given two conflicting transactions */
|
|
static DoubleSpendProof create(const Tx &tx1, const Tx &tx2);
|
|
|
|
/** loads a DSProof from a serialized stream. */
|
|
static DoubleSpendProof load(const Streaming::ConstBuffer &buffer);
|
|
|
|
/** Returns true if this object is invalid, i.e. does not represent a double spend proof */
|
|
bool isEmpty() const;
|
|
|
|
/** Return codes for the 'validate' function */
|
|
enum Validity {
|
|
Valid, //< Double spend proof is valid
|
|
MissingTransaction, //< We cannot determine the validity of this proof because we don't have one of the spends
|
|
MissingUTXO, //< We cannot determine the validity of this proof because the prevout is not available
|
|
Invalid //< This object does not contain a valid doublespend proof
|
|
};
|
|
|
|
/**
|
|
* Returns whether this doublespend proof is valid, or why its
|
|
* validity cannot be determined.
|
|
*/
|
|
Validity validate(const CTxMemPool &mempool) const;
|
|
|
|
/** Returns the hash of the input transaction (UTXO) that is being doublespent */
|
|
uint256 prevTxId() const;
|
|
/** Returns the index of the output that is being doublespent */
|
|
int prevOutIndex() const;
|
|
|
|
/** This struction tracks information about each doublespend transaction */
|
|
struct Spender {
|
|
uint32_t txVersion = 0, outSequence = 0, lockTime = 0;
|
|
uint256 hashPrevOutputs, hashSequence, hashOutputs;
|
|
std::vector<std::vector<uint8_t>> pushData;
|
|
};
|
|
|
|
/// Return the first spender, sorted by hashOutputs
|
|
Spender firstSpender() const {
|
|
return m_spender1;
|
|
}
|
|
/// return the 2nd spender, sorted by hashOutputs
|
|
Spender secondSpender() const {
|
|
return m_spender2;
|
|
}
|
|
|
|
// old fashioned serialization.
|
|
ADD_SERIALIZE_METHODS
|
|
template <typename Stream, typename Operation>
|
|
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
|
READWRITE(m_prevTxId);
|
|
READWRITE(m_prevOutIndex);
|
|
|
|
READWRITE(m_spender1.txVersion);
|
|
READWRITE(m_spender1.outSequence);
|
|
READWRITE(m_spender1.lockTime);
|
|
READWRITE(m_spender1.hashPrevOutputs);
|
|
READWRITE(m_spender1.hashSequence);
|
|
READWRITE(m_spender1.hashOutputs);
|
|
readwritePushData(s, ser_action, nType, nVersion, m_spender1.pushData);
|
|
|
|
READWRITE(m_spender2.txVersion);
|
|
READWRITE(m_spender2.outSequence);
|
|
READWRITE(m_spender2.lockTime);
|
|
READWRITE(m_spender2.hashPrevOutputs);
|
|
READWRITE(m_spender2.hashSequence);
|
|
READWRITE(m_spender2.hashOutputs);
|
|
readwritePushData(s, ser_action, nType, nVersion, m_spender2.pushData);
|
|
}
|
|
|
|
/** create the ID of this doublespend proof */
|
|
uint256 createHash() const;
|
|
|
|
private:
|
|
template <typename Stream>
|
|
static void readPushData(Stream &s, std::vector<std::vector<uint8_t>> &pushData)
|
|
{
|
|
const unsigned int count = ReadCompactSizeAsUnsignedInt(s);
|
|
if (count != 1)
|
|
throw std::ios_base::failure("DSProof must contain exactly one pushData item");
|
|
|
|
const unsigned int vectorSize = ReadCompactSizeAsUnsignedInt(s);
|
|
if (vectorSize == 0 || vectorSize > MaxPushDataSize)
|
|
throw std::ios_base::failure("DSProof pushData size out of range");
|
|
|
|
std::vector<uint8_t> data(vectorSize);
|
|
s.read(reinterpret_cast<char*>(data.data()), data.size());
|
|
|
|
pushData.clear();
|
|
pushData.push_back(std::move(data));
|
|
}
|
|
|
|
template <typename Stream>
|
|
static void readwritePushData(Stream &s, CSerActionUnserialize, int, int,
|
|
std::vector<std::vector<uint8_t>> &pushData)
|
|
{
|
|
readPushData(s, pushData);
|
|
}
|
|
|
|
template <typename Stream>
|
|
static void readwritePushData(Stream &s, CSerActionSerialize ser_action, int nType, int nVersion,
|
|
std::vector<std::vector<uint8_t>> &pushData)
|
|
{
|
|
::SerReadWrite(s, pushData, nType, nVersion, ser_action);
|
|
}
|
|
|
|
/// Throws std::runtime_error if the proof breaks the sanity of:
|
|
/// - isEmpty()
|
|
/// - does not have exactly 1 pushData per spender vector
|
|
/// - any pushData size >520 bytes
|
|
/// Called from: `create()` and `validate()` (`validate()` won't throw but will return Invalid)
|
|
void checkSanityOrThrow() const;
|
|
|
|
|
|
uint256 m_prevTxId;
|
|
int32_t m_prevOutIndex = -1;
|
|
|
|
Spender m_spender1, m_spender2;
|
|
};
|
|
|
|
#endif
|