Implementation of Overwinter transaction format ZIP 202.

This commit is contained in:
Simon
2018-02-15 22:19:36 -08:00
parent d527116d46
commit 072099d788
25 changed files with 1125 additions and 66 deletions

View File

@@ -304,6 +304,13 @@ public:
std::string ToString() const;
};
// The maximum value which is valid for expiry height, used by CTransaction and CMutableTransaction
static constexpr uint32_t TX_EXPIRY_HEIGHT_THRESHOLD = 500000000;
// Overwinter version group id
static constexpr uint32_t OVERWINTER_VERSION_GROUP_ID = 0x03C48270;
static_assert(OVERWINTER_VERSION_GROUP_ID != 0, "version group id must be non-zero as specified in ZIP 202");
struct CMutableTransaction;
/** The basic transaction that is broadcasted on the network and contained in
@@ -316,14 +323,29 @@ private:
const uint256 hash;
void UpdateHash() const;
protected:
/** Developer testing only. Set evilDeveloperFlag to true.
* Convert a CMutableTransaction into a CTransaction without invoking UpdateHash()
*/
CTransaction(const CMutableTransaction &tx, bool evilDeveloperFlag);
public:
typedef boost::array<unsigned char, 64> joinsplit_sig_t;
// Transactions that include a list of JoinSplits are version 2.
static const int32_t MIN_CURRENT_VERSION = 1;
static const int32_t MAX_CURRENT_VERSION = 2;
// Transactions that include a list of JoinSplits are >= version 2.
static const int32_t SPROUT_MIN_CURRENT_VERSION = 1;
static const int32_t SPROUT_MAX_CURRENT_VERSION = 2;
static const int32_t OVERWINTER_MIN_CURRENT_VERSION = 3;
static const int32_t OVERWINTER_MAX_CURRENT_VERSION = 3;
static_assert(MIN_CURRENT_VERSION >= MIN_TX_VERSION,
static_assert(SPROUT_MIN_CURRENT_VERSION >= SPROUT_MIN_TX_VERSION,
"standard rule for tx version should be consistent with network rule");
static_assert(OVERWINTER_MIN_CURRENT_VERSION >= OVERWINTER_MIN_TX_VERSION,
"standard rule for tx version should be consistent with network rule");
static_assert( (OVERWINTER_MAX_CURRENT_VERSION <= OVERWINTER_MAX_TX_VERSION &&
OVERWINTER_MAX_CURRENT_VERSION >= OVERWINTER_MIN_CURRENT_VERSION),
"standard rule for tx version should be consistent with network rule");
// The local variables are made const to prevent unintended modification
@@ -331,10 +353,13 @@ public:
// actually immutable; deserialization and assignment are implemented,
// and bypass the constness. This is safe, as they update the entire
// structure, including the hash.
const bool fOverwintered;
const int32_t nVersion;
const uint32_t nVersionGroupId;
const std::vector<CTxIn> vin;
const std::vector<CTxOut> vout;
const uint32_t nLockTime;
const uint32_t nExpiryHeight;
const std::vector<JSDescription> vjoinsplit;
const uint256 joinSplitPubKey;
const joinsplit_sig_t joinSplitSig = {{0}};
@@ -351,11 +376,39 @@ public:
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(*const_cast<int32_t*>(&this->nVersion));
if (ser_action.ForRead()) {
// When deserializing, unpack the 4 byte header to extract fOverwintered and nVersion.
uint32_t header;
READWRITE(header);
*const_cast<bool*>(&fOverwintered) = header >> 31;
*const_cast<int32_t*>(&this->nVersion) = header & 0x7FFFFFFF;
} else {
// When serializing v1 and v2, the 4 byte header is nVersion
uint32_t header = this->nVersion;
// When serializing Overwintered tx, the 4 byte header is the combination of fOverwintered and nVersion
if (fOverwintered) {
header |= 1 << 31;
}
READWRITE(header);
}
nVersion = this->nVersion;
if (fOverwintered) {
READWRITE(*const_cast<uint32_t*>(&this->nVersionGroupId));
}
bool isOverwinterV3 = fOverwintered &&
nVersionGroupId == OVERWINTER_VERSION_GROUP_ID &&
nVersion == 3;
if (fOverwintered && !isOverwinterV3) {
throw std::ios_base::failure("Unknown transaction format");
}
READWRITE(*const_cast<std::vector<CTxIn>*>(&vin));
READWRITE(*const_cast<std::vector<CTxOut>*>(&vout));
READWRITE(*const_cast<uint32_t*>(&nLockTime));
if (isOverwinterV3) {
READWRITE(*const_cast<uint32_t*>(&nExpiryHeight));
}
if (nVersion >= 2) {
READWRITE(*const_cast<std::vector<JSDescription>*>(&vjoinsplit));
if (vjoinsplit.size() > 0) {
@@ -410,10 +463,13 @@ public:
/** A mutable version of CTransaction. */
struct CMutableTransaction
{
bool fOverwintered;
int32_t nVersion;
uint32_t nVersionGroupId;
std::vector<CTxIn> vin;
std::vector<CTxOut> vout;
uint32_t nLockTime;
uint32_t nExpiryHeight;
std::vector<JSDescription> vjoinsplit;
uint256 joinSplitPubKey;
CTransaction::joinsplit_sig_t joinSplitSig = {{0}};
@@ -425,11 +481,39 @@ struct CMutableTransaction
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
READWRITE(this->nVersion);
if (ser_action.ForRead()) {
// When deserializing, unpack the 4 byte header to extract fOverwintered and nVersion.
uint32_t header;
READWRITE(header);
fOverwintered = header >> 31;
this->nVersion = header & 0x7FFFFFFF;
} else {
// When serializing v1 and v2, the 4 byte header is nVersion
uint32_t header = this->nVersion;
// When serializing Overwintered tx, the 4 byte header is the combination of fOverwintered and nVersion
if (fOverwintered) {
header |= 1 << 31;
}
READWRITE(header);
}
nVersion = this->nVersion;
if (fOverwintered) {
READWRITE(nVersionGroupId);
}
bool isOverwinterV3 = fOverwintered &&
nVersionGroupId == OVERWINTER_VERSION_GROUP_ID &&
nVersion == 3;
if (fOverwintered && !isOverwinterV3) {
throw std::ios_base::failure("Unknown transaction format");
}
READWRITE(vin);
READWRITE(vout);
READWRITE(nLockTime);
if (isOverwinterV3) {
READWRITE(nExpiryHeight);
}
if (nVersion >= 2) {
READWRITE(vjoinsplit);
if (vjoinsplit.size() > 0) {