coins: pack Coin height/coinbase consistently

Serialize `Coin` metadata using the canonical (height << 1) | coinbase packing across `Coin` serialization, undo records, and coinstats hashing.

Cast the 31-bit `nHeight` bitfield to `uint32_t` before shifting to avoid signed promotion undefined behaviour.
This commit is contained in:
Lőrinc
2024-12-09 13:30:04 +01:00
parent 1f309d1aa2
commit 76190489e6
3 changed files with 5 additions and 4 deletions

View File

@@ -27,7 +27,7 @@
* A UTXO entry.
*
* Serialized format:
* - VARINT((coinbase ? 1 : 0) | (height << 1))
* - VARINT((height << 1) | (coinbase ? 1 : 0))
* - the non-spent CTxOut (via TxOutCompression)
*/
class Coin
@@ -62,7 +62,7 @@ public:
template<typename Stream>
void Serialize(Stream &s) const {
assert(!IsSpent());
uint32_t code = nHeight * uint32_t{2} + fCoinBase;
uint32_t code{(uint32_t{nHeight} << 1) | uint32_t{fCoinBase}};
::Serialize(s, VARINT(code));
::Serialize(s, Using<TxOutCompression>(out));
}

View File

@@ -47,7 +47,7 @@ template <typename T>
static void TxOutSer(T& ss, const COutPoint& outpoint, const Coin& coin)
{
ss << outpoint;
ss << static_cast<uint32_t>((coin.nHeight << 1) + coin.fCoinBase);
ss << ((uint32_t{coin.nHeight} << 1) | uint32_t{coin.fCoinBase});
ss << coin.out;
}

View File

@@ -23,7 +23,8 @@ struct TxInUndoFormatter
{
template<typename Stream>
void Ser(Stream &s, const Coin& txout) {
::Serialize(s, VARINT(txout.nHeight * uint32_t{2} + txout.fCoinBase ));
uint32_t nCode{(uint32_t{txout.nHeight} << 1) | uint32_t{txout.fCoinBase}};
::Serialize(s, VARINT(nCode));
if (txout.nHeight > 0) {
// Required to maintain compatibility with older undo format.
::Serialize(s, (unsigned char)0);