ViaBTC's mutated blocks without witness data

bad-witness-nonce-size
Monday, March 18, 2024

I noticed multiple ERROR: AcceptBlock: bad-witness-nonce-size errors in the debug log of my Bitcoin Core node. These indicate that a block my node received is invalid and not accepted. It turned out that these are ViaBTC’s blocks, broadcast by their mining pool software, where transaction witness data is missing. In this post, I’ve written down my notes on this observation.


Some Bitcoin Core node operators might have noticed the following error in the log of their listening node:

ERROR: AcceptBlock: bad-witness-nonce-size, ContextualCheckBlock: invalid witness reserved value size
ERROR: ProcessNewBlock: AcceptBlock FAILED (bad-witness-nonce-size, ContextualCheckBlock: invalid witness reserved value size)

This error can be found on bitcointalk as early as November 2019 and was a topic in a bitcoin-core-dev IRC meeting in May 2023 as well as in multiple conversations I had. As part of my ongoing Bitcoin P2P network anomaly monitoring, I decided to look into the origin of this error.

The log message tells us that, while processing a newly received block, a block validation check failed with the reason bad-witness-nonce-size and the note: invalid witness reserved value size. Bitcoin Core1 validates new blocks in four steps. First, the block header’s proof of work, the timestamp, and the block version are checked. As a second step, the block’s properties that don’t require context are checked. These checks verify that the merkle root matches the transactions, the block is not too large, that only the first transaction is a coinbase transaction, the transactions are consensus valid (inputs and outputs not empty, output value not too large, …), and that the block is under the sigops limit. The checks in the third step involve context. The transaction locktime, that the coinbase script starts with a block height, that the witness commitment in the coinbase transaction is valid, and that the block weight is below the maximum weight is checked. The fourth and final step checks, for example, the UTXO’s, input scripts, and block reward. If all checks are successful, the block is accepted.

The witness commitment is checked in step three. As specified in BIP 141, the witness commitment is the hash of the witness root hash and witness reserved value. The witness root hash is the root of a merkle tree using the witness transaction hashes, wtxid’s, as leaves. Compared to the merkle root in the block header, the witness root also commits to the transaction witness data. The witness reserved value is a 32 byte value stored in the coinbase witness. It’s currently unused, but can be used by future softforks to add a new commitment. The coinbase witness is required to be exactly one 32 byte element, otherwise, the bad-witness-nonce-size error is raised. The witness reserved value was previously called witness nonce and the error was likely never changed.

if (block.vtx[0]->vin[0].scriptWitness.stack.size() != 1 
  || block.vtx[0]->vin[0].scriptWitness.stack[0].size() != 32) {
  return state.Invalid(BlockValidationResult::BLOCK_MUTATED, "bad-witness-nonce-size",
    strprintf("%s : invalid witness reserved value size", __func__));
}

The proof-of-work of the received blocks is valid, which means that someone invested a large amount of energy into mining the block. As Bitcoin Core only processes new blocks it does not yet know, this could indicate that my node received a block shortly after being mined. Maybe the block came directly from a mining pool?

Digging deeper, the Bitcoin Core debug.log, with the net category turned on, reveals that these blocks are sent to us with an unsolicited (we didn’t ask for this block in a getdata message) Bitcoin P2P block message. These block messages stand out, as blocks on the Bitcoin network are typically relayed as compact blocks2. The peers sending these blocks are all inbound connections. The version message they send to us contains the user agent /Satoshi:0.19.0.1/, a version of 70015, and has only the NODE_WITNESS service flag set. The user agent is clearly fake as Bitcoin Core version 0.19.0.1 never sets only the NODE_WITNESS service flag. The peer’s IPv4 addresses all belong to IP ranges owned by AWS - but belong to different AWS regions. The logs also show the block hash of the received block. Looking up the block hashes in a block explorer shows that the blocks are all mined by ViaBTC and, apparently, a valid version of them was relayed and accepted by the network.

On GitHub, an old version of the ViaBTC mining server can be found. The user agent, version, and service flag match the observed peers. These connections are made using the ViaBTC “bitpeer” module. ViaBTC runs multiple bitpeer instances that connect to the Bitcoin P2P network to propagate ViaBTC’s blocks and feed blocks from other pools into their mining server.

To figure out why the blocks received from ViaBTC’s bitpeer peers don’t have a valid witness reserved value, I started recording the P2P communication between my Bitcoin Core node and the bitpeer clients. Inspecting the transactions in the received blocks showed that all transactions are serialized without the witness data. A SegWit commitment in the coinbase exists. Without witness data, the SegWit commitment in the coinbase transaction can’t be verified as the witness reserved value is missing. The verification fails with the reason: bad-witness-nonce-size.

The following shows the raw coinbase transaction from block 832535 mined by ViaBTC. The witness parts of the transaction, missing from blocks send by ViaBTC’s bitpeer clients, are colored in red. These are the SegWit maker 00 and flag 01, as well as the witness stack size 01, witness element size 20, and the witness reserved value 0000000000000000000000000000000000000000000000000000000000000000.

010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff620317b40c1b2f5669614254432f4d696e6564206279206b7a616c67736176652f2cfabe6d6d7182edded3ec31023b380b5ecb0cfdb8bc19b7b64b038620edc9f3f0d73115ad100000000000000010cdbc33017bb0ae8f449fc454faf2010000000000ffffffff0327609e26000000001976a914536ffa992491508dca0354e52f32a3a7a679a53a88ac00000000000000002b6a2952534b424c4f434b3a5f520faece8244c1a89cc42225671c5e95eabe95f00186984778b119005d76cd0000000000000000266a24aa21a9ede8f8b5f251110e1c7b7c6f9e4082514e245b6fdb10b06e8285835f8c80eec86c0120000000000000000000000000000000000000000000000000000000000000000000000000

While the ViaBTC bitpeer clients incorrectly broadcast the ViaBTC blocks without witness data, the blocks (with witness data) still somehow propagate through the network. Probably, ViaBTC’s Bitcoin Core nodes announce the blocks as compact blocks to their peers. It’s not clear to me where in the ViaBTC mining server the witness data is lost. In most cases I’ve observed, the block messages from ViaBTC bitpeer instances arrive after the compact blocks3. Thus, fixing this might only slightly improve the block propagation of ViaBTC’s blocks on the Bitcoin network. I’m not sure if the bitpeer clients are required at all to ensure the blocks propagate well.

I let ViaBTC know about this in late February 2024, and they thanked me for informing them. However, I don’t know if they are working on a fix for their mining server.

In the recently merged pull request #29412 an early mutation check for blocks received in a block message was added. The mutation check is now done right after receiving the block before doing any other block validation. If a mutation is detected, we disconnect the peer. Previously, we only checked blocks we hadn’t seen before for mutations. Now, all blocks are checked. This means we also disconnect more aggressively. Starting with the upcoming Bitcoin Core version 27.0, ViaBTC’s bitpeer clients will be disconnected for each mutated block they send.


  1. Here specifically version 26.0 as the block processing will slightly change in version 27.0. ↩︎

  2. The block message is however used during initial block download. ↩︎

  3. Bitcoin Core only logs the error when the bitpeer block message arrives before the compact block message. ↩︎

All text and images in this work are licensed under a Creative Commons Attribution-ShareAlike 4.0 International License Creative Commons License

Next

Image for Invalid F2Pool blocks 783426 and 784121 (April 2023)

April 2, 2024

Invalid F2Pool blocks 783426 and 784121 (April 2023)

My notes on the two bad-blk-sigops: too many sigops invalid blocks, 783426 and 784121, mined by F2Pool in April 2023.

Previous

Image for An overview of recent non-standard Bitcoin transactions

January 29, 2024

An overview of recent non-standard Bitcoin transactions

This blog post provides an overview of non-standard transactions that mining pools included in the last 117000 Bitcoin blocks.