Bitcoin

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: 93coinbase 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 🙂

Related Articles

Back to top button