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 🙂