Bitcoin Core – Unable to parse UTXO on chain state.

Problem reading tx from Bitcoin Core LevelDB chain state store. I know that the structure of the current version of chainstate is as follows: key => value
Where are the keys? prefix (0x43) | tx_id | coin_n
(https://github.com/bitcoin/bitcoin/blob/0d509bab45d292caeaf34600e57b5928757c6005/src/txdb.cpp#L39) The value is obfuscated using an obfuscation key. Coin XOR obfuscation_key
value Coin
.
Reference: https://github.com/bitcoin/bitcoin/blob/0d509bab45d292caeaf34600e57b5928757c6005/src/txdb.cpp#L68
So I successfully read the prefix, obfuscation key and txId. output_n
key:
prefix: (1)byte
(COutPoint)
txID: (32)byte
outN: VARINT
Reference: https://github.com/bitcoin/bitcoin/blob/0d509bab45d292caeaf34600e57b5928757c6005/src/primitives/transaction.h#L28
Coin structure (https://github.com/bitcoin/bitcoin/blob/0d509bab45d292caeaf34600e57b5928757c6005/src/coins.h#L24)
code: VARINT((coinbase ? 1 : 0) | (height << 1))
txOut: CTxOut (via TxOutCompression)
Now I’m having trouble reading the code. Some values ​​have valid codes with actual block height and coinbase booleans. However, some values ​​have invalid data where the block height is not the same as the transaction’s block height.
How can I reproduce this to help me?
My input (LevelDB view):
Key | Value
0e006f62667573636174655f6b6579 | 08f05564aa8317fe24
42 | bde0dff15a88e0eb3d71c37fd986c4cdfd8916bd0b8d0b13343afef66829284d
4300c4626bf6dc028730d3a1f3951d398a71a164d8a898965d11bb8a699091144c00 | 8f6778aa979ddbbc669c187cbf11ad2e30ca53025f3f3b961e
43047ae5966a25bdc32ab4a258c57e28ffa763fc273c08b38fe85839e99e71402500 | ef6778aa979ddbbc669c187cbf11ad2e30ca53025f3f3b961e
430e3afb85bb66cc8a5edfd96f4aad1e65e4e6cf38f1dcc584c525d0b25b89599c00 | 706e56b68303740168c3add6552bf877fa95fb9d2bcbd6e142bb
If you read and obfuscate the first value, you will get the actual block height and coinbase. But when I read the last value I get:
~ 706e56b68303740168c3add6552bf877fa95fb9d2bcbd6e142bb XOR f05564aa8317fe24
~ 803b321c00148a259896c97cd63c06530ac09f37a8dc28c5b2ee
So if you try to read uvarint here you will get incorrect code with wrong block height.
package main
import (
"bytes"
"encoding/binary"
"encoding/hex"
"log"
)
func main()
utxo, _ := hex.DecodeString("803b321c00148a259896c97cd63c06530ac09f37a8dc28c5b2ee")
utxoReader := bytes.NewReader(utxo)
code, _ := binary.ReadUvarint(utxoReader)
log.Println("code", code)
blockHeight := code >> 1
log.Println("blockHeight", blockHeight)
isCoinbase := code
result
2024/04/03 15:24:06 code 7552
2024/04/03 15:24:06 blockHeight 3776
2024/04/03 15:24:06 isCoinbase false
Expected results: blockHeight should be: 93
coinbase false
.
Other information. My obfuscation function code:
func deobfuscateValue(obfuscationKey, value ()byte) ()byte
deobfuscatedValue := make(()byte, len(value))
copy(deobfuscatedValue, value)
for i, c := range value
deobfuscatedValue(i) = c ^ obfuscationKey(i%len(obfuscationKey))
return deobfuscatedValue
I’m running a private registration test network on Bitcoin Core v0.21 (I tried it on version 24 and the results were the same). I am using the bitcoin-testnet-box docker image to test my program. I also tested it on mainnet, but the same problem occurred. My blockchain now only has 105 blocks.
Thank you for your help. I seem to have missed something simple and don’t know anything about chained state or C++ implementation or Golang 🙂