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

@@ -146,8 +146,9 @@ std::string CTxOut::ToString() const
return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, scriptPubKey.ToString().substr(0,30));
}
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::MIN_CURRENT_VERSION), nLockTime(0) {}
CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::SPROUT_MIN_CURRENT_VERSION), fOverwintered(false), nVersionGroupId(0), nExpiryHeight(0), nLockTime(0) {}
CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight),
vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig)
{
@@ -163,19 +164,34 @@ void CTransaction::UpdateHash() const
*const_cast<uint256*>(&hash) = SerializeHash(*this);
}
CTransaction::CTransaction() : nVersion(CTransaction::MIN_CURRENT_VERSION), vin(), vout(), nLockTime(0), vjoinsplit(), joinSplitPubKey(), joinSplitSig() { }
CTransaction::CTransaction() : nVersion(CTransaction::SPROUT_MIN_CURRENT_VERSION), fOverwintered(false), nVersionGroupId(0), nExpiryHeight(0), vin(), vout(), nLockTime(0), vjoinsplit(), joinSplitPubKey(), joinSplitSig() { }
CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), vjoinsplit(tx.vjoinsplit),
joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig)
CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight),
vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig)
{
UpdateHash();
}
// Protected constructor which only derived classes can call.
// For developer testing only.
CTransaction::CTransaction(
const CMutableTransaction &tx,
bool evilDeveloperFlag) : nVersion(tx.nVersion), fOverwintered(tx.fOverwintered), nVersionGroupId(tx.nVersionGroupId), nExpiryHeight(tx.nExpiryHeight),
vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime),
vjoinsplit(tx.vjoinsplit), joinSplitPubKey(tx.joinSplitPubKey), joinSplitSig(tx.joinSplitSig)
{
assert(evilDeveloperFlag);
}
CTransaction& CTransaction::operator=(const CTransaction &tx) {
*const_cast<bool*>(&fOverwintered) = tx.fOverwintered;
*const_cast<int*>(&nVersion) = tx.nVersion;
*const_cast<uint32_t*>(&nVersionGroupId) = tx.nVersionGroupId;
*const_cast<std::vector<CTxIn>*>(&vin) = tx.vin;
*const_cast<std::vector<CTxOut>*>(&vout) = tx.vout;
*const_cast<unsigned int*>(&nLockTime) = tx.nLockTime;
*const_cast<uint32_t*>(&nExpiryHeight) = tx.nExpiryHeight;
*const_cast<std::vector<JSDescription>*>(&vjoinsplit) = tx.vjoinsplit;
*const_cast<uint256*>(&joinSplitPubKey) = tx.joinSplitPubKey;
*const_cast<joinsplit_sig_t*>(&joinSplitSig) = tx.joinSplitSig;
@@ -248,12 +264,24 @@ unsigned int CTransaction::CalculateModifiedSize(unsigned int nTxSize) const
std::string CTransaction::ToString() const
{
std::string str;
str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%u, vout.size=%u, nLockTime=%u)\n",
GetHash().ToString().substr(0,10),
nVersion,
vin.size(),
vout.size(),
nLockTime);
if (!fOverwintered) {
str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%u, vout.size=%u, nLockTime=%u)\n",
GetHash().ToString().substr(0,10),
nVersion,
vin.size(),
vout.size(),
nLockTime);
} else if (nVersion >= 3) {
str += strprintf("CTransaction(hash=%s, ver=%d, fOverwintered=%d, nVersionGroupId=%08x, vin.size=%u, vout.size=%u, nLockTime=%u, nExpiryHeight=%u)\n",
GetHash().ToString().substr(0,10),
nVersion,
fOverwintered,
nVersionGroupId,
vin.size(),
vout.size(),
nLockTime,
nExpiryHeight);
}
for (unsigned int i = 0; i < vin.size(); i++)
str += " " + vin[i].ToString() + "\n";
for (unsigned int i = 0; i < vout.size(); i++)

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) {