Skip to content

Commit d342424

Browse files
committed
Remove/ignore tx version in utxo and undo
This makes the following changes: * In undo data and the chainstate database, the transaction nVersion field is removed from the data structures, always written as 0, and ignored when reading. * The definition of hash_serialized in gettxoutsetinfo is changed to no longer incude the nVersion field. It is renamed to hash_serialized_2 to avoid confusion. The new definition also includes transaction height and coinbase information, as this information was missing before. This depends on having a CHashVerifier-based undo data checksum verifier. Apart from changing the definition of serialized_hash, downgrading after using this patch is supported, as no release ever used the value of nVersion field in UTXO entries.
1 parent 7e00322 commit d342424

File tree

9 files changed

+36
-47
lines changed

9 files changed

+36
-47
lines changed

doc/release-notes.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ Notable changes
3636
Low-level RPC changes
3737
---------------------
3838

39+
- The new database model no longer stores information about transaction
40+
versions of unspent outputs. This means that:
41+
- The `gettxout` RPC no longer has a `version` field in the response.
42+
- The `gettxoutsetinfo` RPC reports `hash_serialized_2` instead of `hash_serialized`,
43+
which does not commit to the transaction versions of unspent outputs, but does
44+
commit to the height and coinbase information.
45+
- The `getutxos` REST path no longer reports the `txvers` field in JSON format,
46+
and always reports 0 for transaction versions in the binary format
47+
3948
- Error codes have been updated to be more accurate for the following error cases:
4049
- `getblock` now returns RPC_MISC_ERROR if the block can't be found on disk (for
4150
example if the block has been pruned). Previously returned RPC_INTERNAL_ERROR.

src/coins.h

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,10 @@ class CCoins
8484
//! at which height this transaction was included in the active block chain
8585
int nHeight;
8686

87-
//! version of the CTransaction; accesses to this value should probably check for nHeight as well,
88-
//! as new tx version will probably only be introduced at certain heights
89-
int nVersion;
90-
9187
void FromTx(const CTransaction &tx, int nHeightIn) {
9288
fCoinBase = tx.IsCoinBase();
9389
vout = tx.vout;
9490
nHeight = nHeightIn;
95-
nVersion = tx.nVersion;
9691
ClearUnspendable();
9792
}
9893

@@ -105,11 +100,10 @@ class CCoins
105100
fCoinBase = false;
106101
std::vector<CTxOut>().swap(vout);
107102
nHeight = 0;
108-
nVersion = 0;
109103
}
110104

111105
//! empty constructor
112-
CCoins() : fCoinBase(false), vout(0), nHeight(0), nVersion(0) { }
106+
CCoins() : fCoinBase(false), vout(0), nHeight(0) { }
113107

114108
//!remove spent outputs at the end of vout
115109
void Cleanup() {
@@ -131,7 +125,6 @@ class CCoins
131125
std::swap(to.fCoinBase, fCoinBase);
132126
to.vout.swap(vout);
133127
std::swap(to.nHeight, nHeight);
134-
std::swap(to.nVersion, nVersion);
135128
}
136129

137130
//! equality test
@@ -141,7 +134,6 @@ class CCoins
141134
return true;
142135
return a.fCoinBase == b.fCoinBase &&
143136
a.nHeight == b.nHeight &&
144-
a.nVersion == b.nVersion &&
145137
a.vout == b.vout;
146138
}
147139
friend bool operator!=(const CCoins &a, const CCoins &b) {
@@ -163,7 +155,8 @@ class CCoins
163155
assert(fFirst || fSecond || nMaskCode);
164156
unsigned int nCode = 8*(nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fFirst ? 2 : 0) + (fSecond ? 4 : 0);
165157
// version
166-
::Serialize(s, VARINT(this->nVersion));
158+
int nVersionDummy = 0;
159+
::Serialize(s, VARINT(nVersionDummy));
167160
// header code
168161
::Serialize(s, VARINT(nCode));
169162
// spentness bitmask
@@ -187,7 +180,8 @@ class CCoins
187180
void Unserialize(Stream &s) {
188181
unsigned int nCode = 0;
189182
// version
190-
::Unserialize(s, VARINT(this->nVersion));
183+
int nVersionDummy;
184+
::Unserialize(s, VARINT(nVersionDummy));
191185
// header code
192186
::Unserialize(s, VARINT(nCode));
193187
fCoinBase = nCode & 1;

src/rest.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ static const struct {
4242
};
4343

4444
struct CCoin {
45-
uint32_t nTxVer; // Don't call this nVersion, that name has a special meaning inside IMPLEMENT_SERIALIZE
4645
uint32_t nHeight;
4746
CTxOut out;
4847

@@ -51,7 +50,8 @@ struct CCoin {
5150
template <typename Stream, typename Operation>
5251
inline void SerializationOp(Stream& s, Operation ser_action)
5352
{
54-
READWRITE(nTxVer);
53+
uint32_t nTxVerDummy = 0;
54+
READWRITE(nTxVerDummy);
5555
READWRITE(nHeight);
5656
READWRITE(out);
5757
}
@@ -519,7 +519,6 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
519519
// Safe to index into vout here because IsAvailable checked if it's off the end of the array, or if
520520
// n is valid but points to an already spent output (IsNull).
521521
CCoin coin;
522-
coin.nTxVer = coins.nVersion;
523522
coin.nHeight = coins.nHeight;
524523
coin.out = coins.vout.at(vOutPoints[i].n);
525524
assert(!coin.out.IsNull());
@@ -568,7 +567,6 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
568567
UniValue utxos(UniValue::VARR);
569568
BOOST_FOREACH (const CCoin& coin, outs) {
570569
UniValue utxo(UniValue::VOBJ);
571-
utxo.push_back(Pair("txvers", (int32_t)coin.nTxVer));
572570
utxo.push_back(Pair("height", (int32_t)coin.nHeight));
573571
utxo.push_back(Pair("value", ValueFromAmount(coin.out.nValue)));
574572

src/rpc/blockchain.cpp

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -781,11 +781,10 @@ struct CCoinsStats
781781
uint256 hashBlock;
782782
uint64_t nTransactions;
783783
uint64_t nTransactionOutputs;
784-
uint64_t nSerializedSize;
785784
uint256 hashSerialized;
786785
CAmount nTotalAmount;
787786

788-
CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nSerializedSize(0), nTotalAmount(0) {}
787+
CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nTotalAmount(0) {}
789788
};
790789

791790
//! Calculate statistics about the unspent transaction output set
@@ -808,16 +807,17 @@ static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats)
808807
if (pcursor->GetKey(key) && pcursor->GetValue(coins)) {
809808
stats.nTransactions++;
810809
ss << key;
810+
ss << VARINT(coins.nHeight * 2 + coins.fCoinBase);
811811
for (unsigned int i=0; i<coins.vout.size(); i++) {
812812
const CTxOut &out = coins.vout[i];
813813
if (!out.IsNull()) {
814814
stats.nTransactionOutputs++;
815815
ss << VARINT(i+1);
816-
ss << out;
816+
ss << *(const CScriptBase*)(&out.scriptPubKey);
817+
ss << VARINT(out.nValue);
817818
nTotalAmount += out.nValue;
818819
}
819820
}
820-
stats.nSerializedSize += 32 + pcursor->GetValueSize();
821821
ss << VARINT(0);
822822
} else {
823823
return error("%s: unable to read value", __func__);
@@ -891,7 +891,6 @@ UniValue gettxoutsetinfo(const JSONRPCRequest& request)
891891
" \"bestblock\": \"hex\", (string) the best block hash hex\n"
892892
" \"transactions\": n, (numeric) The number of transactions\n"
893893
" \"txouts\": n, (numeric) The number of output transactions\n"
894-
" \"bytes_serialized\": n, (numeric) The serialized size\n"
895894
" \"hash_serialized\": \"hash\", (string) The serialized hash\n"
896895
" \"total_amount\": x.xxx (numeric) The total amount\n"
897896
"}\n"
@@ -909,8 +908,7 @@ UniValue gettxoutsetinfo(const JSONRPCRequest& request)
909908
ret.push_back(Pair("bestblock", stats.hashBlock.GetHex()));
910909
ret.push_back(Pair("transactions", (int64_t)stats.nTransactions));
911910
ret.push_back(Pair("txouts", (int64_t)stats.nTransactionOutputs));
912-
ret.push_back(Pair("bytes_serialized", (int64_t)stats.nSerializedSize));
913-
ret.push_back(Pair("hash_serialized", stats.hashSerialized.GetHex()));
911+
ret.push_back(Pair("hash_serialized_2", stats.hashSerialized.GetHex()));
914912
ret.push_back(Pair("total_amount", ValueFromAmount(stats.nTotalAmount)));
915913
} else {
916914
throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set");
@@ -992,7 +990,6 @@ UniValue gettxout(const JSONRPCRequest& request)
992990
UniValue o(UniValue::VOBJ);
993991
ScriptPubKeyToUniv(coins.vout[n].scriptPubKey, o, true);
994992
ret.push_back(Pair("scriptPubKey", o));
995-
ret.push_back(Pair("version", coins.nVersion));
996993
ret.push_back(Pair("coinbase", coins.fCoinBase));
997994

998995
return ret;

src/test/coins_tests.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
142142
} else {
143143
updated_an_entry = true;
144144
}
145-
coins.nVersion = insecure_rand();
146145
coins.vout.resize(1);
147146
coins.vout[0].nValue = insecure_rand();
148147
*entry = coins;
@@ -436,7 +435,6 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
436435
CDataStream ss1(ParseHex("0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e"), SER_DISK, CLIENT_VERSION);
437436
CCoins cc1;
438437
ss1 >> cc1;
439-
BOOST_CHECK_EQUAL(cc1.nVersion, 1);
440438
BOOST_CHECK_EQUAL(cc1.fCoinBase, false);
441439
BOOST_CHECK_EQUAL(cc1.nHeight, 203998);
442440
BOOST_CHECK_EQUAL(cc1.vout.size(), 2);
@@ -449,7 +447,6 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
449447
CDataStream ss2(ParseHex("0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b"), SER_DISK, CLIENT_VERSION);
450448
CCoins cc2;
451449
ss2 >> cc2;
452-
BOOST_CHECK_EQUAL(cc2.nVersion, 1);
453450
BOOST_CHECK_EQUAL(cc2.fCoinBase, true);
454451
BOOST_CHECK_EQUAL(cc2.nHeight, 120891);
455452
BOOST_CHECK_EQUAL(cc2.vout.size(), 17);
@@ -468,7 +465,6 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
468465
CDataStream ss3(ParseHex("0002000600"), SER_DISK, CLIENT_VERSION);
469466
CCoins cc3;
470467
ss3 >> cc3;
471-
BOOST_CHECK_EQUAL(cc3.nVersion, 0);
472468
BOOST_CHECK_EQUAL(cc3.fCoinBase, false);
473469
BOOST_CHECK_EQUAL(cc3.nHeight, 0);
474470
BOOST_CHECK_EQUAL(cc3.vout.size(), 1);

src/test/transaction_tests.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,6 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction) {
471471
threadGroup.create_thread(boost::bind(&CCheckQueue<CScriptCheck>::Thread, boost::ref(scriptcheckqueue)));
472472

473473
CCoins coins;
474-
coins.nVersion = 1;
475474
coins.fCoinBase = false;
476475
for(uint32_t i = 0; i < mtx.vin.size(); i++) {
477476
CTxOut txout;

src/undo.h

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,26 @@
1414
*
1515
* Contains the prevout's CTxOut being spent, and if this was the
1616
* last output of the affected transaction, its metadata as well
17-
* (coinbase or not, height, transaction version)
17+
* (coinbase or not, height). Earlier versions also stored the transaction
18+
* version.
1819
*/
1920
class CTxInUndo
2021
{
2122
public:
2223
CTxOut txout; // the txout data before being spent
2324
bool fCoinBase; // if the outpoint was the last unspent: whether it belonged to a coinbase
2425
unsigned int nHeight; // if the outpoint was the last unspent: its height
25-
int nVersion; // if the outpoint was the last unspent: its version
2626

27-
CTxInUndo() : txout(), fCoinBase(false), nHeight(0), nVersion(0) {}
28-
CTxInUndo(const CTxOut &txoutIn, bool fCoinBaseIn = false, unsigned int nHeightIn = 0, int nVersionIn = 0) : txout(txoutIn), fCoinBase(fCoinBaseIn), nHeight(nHeightIn), nVersion(nVersionIn) { }
27+
CTxInUndo() : txout(), fCoinBase(false), nHeight(0) {}
28+
CTxInUndo(const CTxOut &txoutIn, bool fCoinBaseIn = false, unsigned int nHeightIn = 0) : txout(txoutIn), fCoinBase(fCoinBaseIn), nHeight(nHeightIn) { }
2929

3030
template<typename Stream>
3131
void Serialize(Stream &s) const {
3232
::Serialize(s, VARINT(nHeight*2+(fCoinBase ? 1 : 0)));
33-
if (nHeight > 0)
34-
::Serialize(s, VARINT(this->nVersion));
33+
if (nHeight > 0) {
34+
int nVersionDummy = 0;
35+
::Serialize(s, VARINT(nVersionDummy));
36+
}
3537
::Serialize(s, CTxOutCompressor(REF(txout)));
3638
}
3739

@@ -41,8 +43,10 @@ class CTxInUndo
4143
::Unserialize(s, VARINT(nCode));
4244
nHeight = nCode / 2;
4345
fCoinBase = nCode & 1;
44-
if (nHeight > 0)
45-
::Unserialize(s, VARINT(this->nVersion));
46+
if (nHeight > 0) {
47+
int nVersionDummy;
48+
::Unserialize(s, VARINT(nVersionDummy));
49+
}
4650
::Unserialize(s, REF(CTxOutCompressor(REF(txout))));
4751
}
4852
};

src/validation.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,7 +1086,6 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund
10861086
CTxInUndo& undo = txundo.vprevout.back();
10871087
undo.nHeight = coins->nHeight;
10881088
undo.fCoinBase = coins->fCoinBase;
1089-
undo.nVersion = coins->nVersion;
10901089
}
10911090
}
10921091
}
@@ -1271,7 +1270,6 @@ int ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const COutPoint&
12711270
if (!coins->IsPruned()) fClean = false; // overwriting existing transaction
12721271
coins->fCoinBase = undo.fCoinBase;
12731272
coins->nHeight = undo.nHeight;
1274-
coins->nVersion = undo.nVersion;
12751273
} else {
12761274
if (coins->IsPruned()) fClean = false; // adding output to missing transaction
12771275
}
@@ -1319,11 +1317,6 @@ static DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex*
13191317
outs->ClearUnspendable();
13201318

13211319
CCoins outsBlock(tx, pindex->nHeight);
1322-
// The CCoins serialization does not serialize negative numbers.
1323-
// No network rules currently depend on the version here, so an inconsistency is harmless
1324-
// but it must be corrected before txout nversion ever influences a network rule.
1325-
if (outsBlock.nVersion < 0)
1326-
outs->nVersion = outsBlock.nVersion;
13271320
if (*outs != outsBlock) fClean = false; // transaction mismatch
13281321

13291322
// remove outputs

test/functional/blockchain.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,9 @@ def _test_gettxoutsetinfo(self):
4949
assert_equal(res['transactions'], 200)
5050
assert_equal(res['height'], 200)
5151
assert_equal(res['txouts'], 200)
52-
assert_equal(res['bytes_serialized'], 13924),
5352
assert_equal(res['bestblock'], node.getblockhash(200))
5453
assert_equal(len(res['bestblock']), 64)
55-
assert_equal(len(res['hash_serialized']), 64)
54+
assert_equal(len(res['hash_serialized_2']), 64)
5655

5756
self.log.info("Test that gettxoutsetinfo() works for blockchain with just the genesis block")
5857
b1hash = node.getblockhash(1)
@@ -64,7 +63,7 @@ def _test_gettxoutsetinfo(self):
6463
assert_equal(res2['height'], 0)
6564
assert_equal(res2['txouts'], 0)
6665
assert_equal(res2['bestblock'], node.getblockhash(0))
67-
assert_equal(len(res2['hash_serialized']), 64)
66+
assert_equal(len(res2['hash_serialized_2']), 64)
6867

6968
self.log.info("Test that gettxoutsetinfo() returns the same result after invalidate/reconsider block")
7069
node.reconsiderblock(b1hash)
@@ -75,7 +74,7 @@ def _test_gettxoutsetinfo(self):
7574
assert_equal(res['height'], res3['height'])
7675
assert_equal(res['txouts'], res3['txouts'])
7776
assert_equal(res['bestblock'], res3['bestblock'])
78-
assert_equal(res['hash_serialized'], res3['hash_serialized'])
77+
assert_equal(res['hash_serialized_2'], res3['hash_serialized_2'])
7978

8079
def _test_getblockheader(self):
8180
node = self.nodes[0]

0 commit comments

Comments
 (0)