Skip to content

Commit 6cb2989

Browse files
committed
Remove/ignore tx version in utxo and undo
>>> backports bitcoin/bitcoin@d342424 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 03b9724 commit 6cb2989

File tree

9 files changed

+41
-49
lines changed

9 files changed

+41
-49
lines changed

doc/release-notes.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@ Notable Changes
3434

3535
(Developers: add your notes here as part of your pull requests whenever possible)
3636

37+
Low-level RPC changes
38+
---------------------
39+
40+
- The new database model no longer stores information about transaction
41+
versions of unspent outputs. This means that:
42+
- The `gettxout` RPC no longer has a `version` field in the response.
43+
- The `gettxoutsetinfo` RPC reports `hash_serialized_2` instead of `hash_serialized`,
44+
which does not commit to the transaction versions of unspent outputs, but does
45+
commit to the height and coinbase/coinstake information.
46+
- The `getutxos` REST path no longer reports the `txvers` field in JSON format,
47+
and always reports 0 for transaction versions in the binary format
48+
49+
3750
*version* Change log
3851
==============
3952

src/coins.h

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

88-
//! version of the CTransaction; accesses to this value should probably check for nHeight as well,
89-
//! as new tx version will probably only be introduced at certain heights
90-
int nVersion;
91-
9288
void FromTx(const CTransaction& tx, int nHeightIn)
9389
{
9490
fCoinBase = tx.IsCoinBase();
9591
fCoinStake = tx.IsCoinStake();
9692
vout = tx.vout;
9793
nHeight = nHeightIn;
98-
nVersion = tx.nVersion;
9994
ClearUnspendable();
10095
}
10196

@@ -111,11 +106,10 @@ class CCoins
111106
fCoinStake = false;
112107
std::vector<CTxOut>().swap(vout);
113108
nHeight = 0;
114-
nVersion = 0;
115109
}
116110

117111
//! empty constructor
118-
CCoins() : fCoinBase(false), fCoinStake(false), vout(0), nHeight(0), nVersion(0) {}
112+
CCoins() : fCoinBase(false), fCoinStake(false), vout(0), nHeight(0) {}
119113

120114
//!remove spent outputs at the end of vout
121115
void Cleanup()
@@ -141,7 +135,6 @@ class CCoins
141135
std::swap(to.fCoinStake, fCoinStake);
142136
to.vout.swap(vout);
143137
std::swap(to.nHeight, nHeight);
144-
std::swap(to.nVersion, nVersion);
145138
}
146139

147140
//! equality test
@@ -153,7 +146,6 @@ class CCoins
153146
return a.fCoinBase == b.fCoinBase &&
154147
a.fCoinStake == b.fCoinStake &&
155148
a.nHeight == b.nHeight &&
156-
a.nVersion == b.nVersion &&
157149
a.vout == b.vout;
158150
}
159151
friend bool operator!=(const CCoins& a, const CCoins& b)
@@ -184,7 +176,8 @@ class CCoins
184176
assert(fFirst || fSecond || nMaskCode);
185177
unsigned int nCode = 16 * (nMaskCode - (fFirst || fSecond ? 0 : 1)) + (fCoinBase ? 1 : 0) + (fCoinStake ? 2 : 0) + (fFirst ? 4 : 0) + (fSecond ? 8 : 0);
186178
// version
187-
::Serialize(s, VARINT(this->nVersion));
179+
int nVersionDummy = 0;
180+
::Serialize(s, VARINT(nVersionDummy));
188181
// header code
189182
::Serialize(s, VARINT(nCode));
190183
// spentness bitmask
@@ -209,7 +202,8 @@ class CCoins
209202
{
210203
unsigned int nCode = 0;
211204
// version
212-
::Unserialize(s, VARINT(nVersion));
205+
int nVersionDummy;
206+
::Unserialize(s, VARINT(nVersionDummy));
213207
// header code
214208
::Unserialize(s, VARINT(nCode));
215209
fCoinBase = nCode & 1; //0001 - means coinbase

src/main.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1751,7 +1751,6 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo& txund
17511751
undo.nHeight = coins->nHeight;
17521752
undo.fCoinBase = coins->fCoinBase;
17531753
undo.fCoinStake = coins->fCoinStake;
1754-
undo.nVersion = coins->nVersion;
17551754
}
17561755
}
17571756
}
@@ -2047,7 +2046,6 @@ int ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const COutPoint&
20472046
coins->fCoinBase = undo.fCoinBase;
20482047
coins->fCoinStake = undo.fCoinStake;
20492048
coins->nHeight = undo.nHeight;
2050-
coins->nVersion = undo.nVersion;
20512049
} else {
20522050
if (coins->IsPruned()) fClean = false; // adding output to missing transaction
20532051
}
@@ -2114,11 +2112,6 @@ DisconnectResult DisconnectBlock(CBlock& block, CBlockIndex* pindex, CCoinsViewC
21142112
outs->ClearUnspendable();
21152113

21162114
CCoins outsBlock(tx, pindex->nHeight);
2117-
// The CCoins serialization does not serialize negative numbers.
2118-
// No network rules currently depend on the version here, so an inconsistency is harmless
2119-
// but it must be corrected before txout nversion ever influences a network rule.
2120-
if (outsBlock.nVersion < 0)
2121-
outs->nVersion = outsBlock.nVersion;
21222115
if (*outs != outsBlock) fClean = false; // transaction mismatch
21232116

21242117
// remove outputs

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
}
@@ -514,7 +514,6 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
514514
// Safe to index into vout here because IsAvailable checked if it's off the end of the array, or if
515515
// n is valid but points to an already spent output (IsNull).
516516
CCoin coin;
517-
coin.nTxVer = coins.nVersion;
518517
coin.nHeight = coins.nHeight;
519518
coin.out = coins.vout.at(vOutPoints[i].n);
520519
assert(!coin.out.IsNull());
@@ -562,7 +561,6 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart)
562561
UniValue utxos(UniValue::VARR);
563562
for (const CCoin& coin : outs) {
564563
UniValue utxo(UniValue::VOBJ);
565-
utxo.push_back(Pair("txvers", (int32_t)coin.nTxVer));
566564
utxo.push_back(Pair("height", (int32_t)coin.nHeight));
567565
utxo.push_back(Pair("value", ValueFromAmount(coin.out.nValue)));
568566

src/rpc/blockchain.cpp

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -598,17 +598,16 @@ struct CCoinsStats
598598
uint256 hashBlock;
599599
uint64_t nTransactions;
600600
uint64_t nTransactionOutputs;
601-
uint64_t nSerializedSize;
602601
uint256 hashSerialized;
603602
CAmount nTotalAmount;
604603

605-
CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nSerializedSize(0), nTotalAmount(0) {}
604+
CCoinsStats() : nHeight(0), nTransactions(0), nTransactionOutputs(0), nTotalAmount(0) {}
606605
};
607606

608607
//! Calculate statistics about the unspent transaction output set
609608
static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats)
610609
{
611-
boost::scoped_ptr<CCoinsViewCursor> pcursor(view->Cursor());
610+
std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
612611

613612
CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
614613
stats.hashBlock = pcursor->GetBestBlock();
@@ -624,16 +623,18 @@ static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats)
624623
CCoins coins;
625624
if (pcursor->GetKey(key) && pcursor->GetValue(coins)) {
626625
stats.nTransactions++;
626+
ss << key;
627+
ss << VARINT(coins.nHeight * 4 + (coins.fCoinBase ? 2 : 0) + (coins.fCoinStake ? 1 : 0));
627628
for (unsigned int i=0; i<coins.vout.size(); i++) {
628629
const CTxOut &out = coins.vout[i];
629630
if (!out.IsNull()) {
630631
stats.nTransactionOutputs++;
631632
ss << VARINT(i+1);
632-
ss << out;
633+
ss << *(const CScriptBase*)(&out.scriptPubKey);
634+
ss << VARINT(out.nValue);
633635
nTotalAmount += out.nValue;
634636
}
635637
}
636-
stats.nSerializedSize += 32 + pcursor->GetValueSize();
637638
ss << VARINT(0);
638639
} else {
639640
return error("%s: unable to read value", __func__);
@@ -659,8 +660,7 @@ UniValue gettxoutsetinfo(const JSONRPCRequest& request)
659660
" \"bestblock\": \"hex\", (string) the best block hash hex\n"
660661
" \"transactions\": n, (numeric) The number of transactions\n"
661662
" \"txouts\": n, (numeric) The number of output transactions\n"
662-
" \"bytes_serialized\": n, (numeric) The serialized size\n"
663-
" \"hash_serialized\": \"hash\", (string) The serialized hash\n"
663+
" \"hash_serialized_2\": \"hash\", (string) The serialized hash\n"
664664
" \"total_amount\": x.xxx (numeric) The total amount\n"
665665
"}\n"
666666

@@ -676,8 +676,7 @@ UniValue gettxoutsetinfo(const JSONRPCRequest& request)
676676
ret.push_back(Pair("bestblock", stats.hashBlock.GetHex()));
677677
ret.push_back(Pair("transactions", (int64_t)stats.nTransactions));
678678
ret.push_back(Pair("txouts", (int64_t)stats.nTransactionOutputs));
679-
ret.push_back(Pair("bytes_serialized", (int64_t)stats.nSerializedSize));
680-
ret.push_back(Pair("hash_serialized", stats.hashSerialized.GetHex()));
679+
ret.push_back(Pair("hash_serialized_2", stats.hashSerialized.GetHex()));
681680
ret.push_back(Pair("total_amount", ValueFromAmount(stats.nTotalAmount)));
682681
}
683682
return ret;
@@ -710,7 +709,6 @@ UniValue gettxout(const JSONRPCRequest& request)
710709
" ,...\n"
711710
" ]\n"
712711
" },\n"
713-
" \"version\" : n, (numeric) The version\n"
714712
" \"coinbase\" : true|false (boolean) Coinbase or not\n"
715713
"}\n"
716714

@@ -758,7 +756,6 @@ UniValue gettxout(const JSONRPCRequest& request)
758756
UniValue o(UniValue::VOBJ);
759757
ScriptPubKeyToJSON(coins.vout[n].scriptPubKey, o, true);
760758
ret.push_back(Pair("scriptPubKey", o));
761-
ret.push_back(Pair("version", coins.nVersion));
762759
ret.push_back(Pair("coinbase", coins.fCoinBase));
763760

764761
return ret;

src/test/coins_tests.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,6 @@ BOOST_AUTO_TEST_CASE(coins_cache_simulation_test)
141141
} else {
142142
updated_an_entry = true;
143143
}
144-
coins.nVersion = InsecureRand32();
145144
coins.vout.resize(1);
146145
coins.vout[0].nValue = InsecureRand32();
147146
*entry = coins;
@@ -402,7 +401,6 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
402401
CDataStream ss1(ParseHex("0108835800816115944e077fe7c803cfa57f29b36bf87c1d35b4934b"), SER_DISK, CLIENT_VERSION);
403402
CCoins cc1;
404403
ss1 >> cc1;
405-
BOOST_CHECK_EQUAL(cc1.nVersion, 1);
406404
BOOST_CHECK_EQUAL(cc1.fCoinBase, false);
407405
BOOST_CHECK_EQUAL(cc1.nHeight, 870987);
408406
BOOST_CHECK_EQUAL(cc1.vout.size(), 2);
@@ -415,7 +413,6 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
415413
CDataStream ss2(ParseHex("0111044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa482d21f"), SER_DISK, CLIENT_VERSION);
416414
CCoins cc2;
417415
ss2 >> cc2;
418-
BOOST_CHECK_EQUAL(cc2.nVersion, 1);
419416
BOOST_CHECK_EQUAL(cc2.fCoinBase, true);
420417
BOOST_CHECK_EQUAL(cc2.nHeight, 59807);
421418
BOOST_CHECK_EQUAL(cc2.vout.size(), 17);
@@ -434,7 +431,6 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
434431
CDataStream ss3(ParseHex("0004000600"), SER_DISK, CLIENT_VERSION);
435432
CCoins cc3;
436433
ss3 >> cc3;
437-
BOOST_CHECK_EQUAL(cc3.nVersion, 0);
438434
BOOST_CHECK_EQUAL(cc3.fCoinBase, false);
439435
BOOST_CHECK_EQUAL(cc3.nHeight, 0);
440436
BOOST_CHECK_EQUAL(cc3.vout.size(), 1);

src/test/transaction_tests.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,8 +382,6 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction) {
382382
threadGroup.create_thread(boost::bind(&CCheckQueue<CScriptCheck>::Thread, boost::ref(scriptcheckqueue)));
383383

384384
CCoins coins;
385-
coins.nVersion = 1;
386-
coins.fCoinBase = false;
387385
for(uint32_t i = 0; i < mtx.vin.size(); i++) {
388386
CTxOut txout;
389387
txout.nValue = 1000;

src/undo.h

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
*
1717
* Contains the prevout's CTxOut being spent, and if this was the
1818
* last output of the affected transaction, its metadata as well
19-
* (coinbase/coinstake or not, height, transaction version)
19+
* (coinbase/coinstake or not, height, transaction version).
20+
* Earlier versions also stored the transaction version.
2021
*/
2122
class CTxInUndo
2223
{
@@ -25,17 +26,18 @@ class CTxInUndo
2526
bool fCoinBase; // if the outpoint was the last unspent: whether it belonged to a coinbase
2627
bool fCoinStake;
2728
unsigned int nHeight; // if the outpoint was the last unspent: its height
28-
int nVersion; // if the outpoint was the last unspent: its version
2929

30-
CTxInUndo() : txout(), fCoinBase(false), fCoinStake(false), nHeight(0), nVersion(0) {}
31-
CTxInUndo(const CTxOut& txoutIn, bool fCoinBaseIn = false, bool fCoinStakeIn = false, unsigned int nHeightIn = 0, int nVersionIn = 0) : txout(txoutIn), fCoinBase(fCoinBaseIn), fCoinStake(fCoinStakeIn), nHeight(nHeightIn), nVersion(nVersionIn) {}
30+
CTxInUndo() : txout(), fCoinBase(false), fCoinStake(false), nHeight(0) {}
31+
CTxInUndo(const CTxOut& txoutIn, bool fCoinBaseIn = false, bool fCoinStakeIn = false, unsigned int nHeightIn = 0) : txout(txoutIn), fCoinBase(fCoinBaseIn), fCoinStake(fCoinStakeIn), nHeight(nHeightIn) {}
3232

3333
template <typename Stream>
3434
void Serialize(Stream& s) const
3535
{
3636
::Serialize(s, VARINT(nHeight * 4 + (fCoinBase ? 2 : 0) + (fCoinStake ? 1 : 0)));
37-
if (nHeight > 0)
38-
::Serialize(s, VARINT(this->nVersion));
37+
if (nHeight > 0) {
38+
int nVersionDummy = 0;
39+
::Serialize(s, VARINT(nVersionDummy));
40+
}
3941
::Serialize(s, CTxOutCompressor(REF(txout)));
4042
}
4143

@@ -47,8 +49,10 @@ class CTxInUndo
4749
nHeight = nCode >> 2;
4850
fCoinBase = nCode & 2;
4951
fCoinStake = nCode & 1;
50-
if (nHeight > 0)
51-
::Unserialize(s, VARINT(this->nVersion));
52+
if (nHeight > 0) {
53+
int nVersionDummy;
54+
::Unserialize(s, VARINT(nVersionDummy));
55+
}
5256
::Unserialize(s, REF(CTxOutCompressor(REF(txout))));
5357
}
5458
};

test/functional/rpc_blockchain.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,8 @@ def _test_gettxoutsetinfo(self):
6969
assert_equal(res['transactions'], 200)
7070
assert_equal(res['height'], 200)
7171
assert_equal(res['txouts'], 200)
72-
assert_equal(res['bytes_serialized'], 11673),
7372
assert_equal(len(res['bestblock']), 64)
74-
assert_equal(len(res['hash_serialized']), 64)
73+
assert_equal(len(res['hash_serialized_2']), 64)
7574

7675
def _test_getblockheader(self):
7776
node = self.nodes[0]

0 commit comments

Comments
 (0)