bitcoin/bitcoin — pull request #1823 by roques (2012-09-13 14:38:18+00:00)
Title: CBigNum::GetCompact / SetCompact

The problem
===========

`SetCompact` as currently implemented interprets the compact number `0x01810000` as `-1` instead of `0x81`, because it is implemented using `BN_mpi2bn`, which interprets the highest bit in the first byte of its input as sign and this is not compensated for. — Currently this is not a problem as none of the bitcoin-implementations I've looked at generate compact numbers with the `0x00800000` bit set. Many contain a matching bug in their compact encoding routines.

Satoshi:
* `CBigNum.SetCompact(0x81)` => `0x02008100`
* `CBigNum.GetCompact(0x01810000)` => `-1`

Defensive encoding.
Decoding results in negative number with highest bit stripped.

bitcoinj:
* No compact encoding.
* `Utils.decodeCompactBits(0x01810000)` => `-1`

Decoding will lead to `Block::getDifficultyTargetAsInteger()` throwing a `VerificationException`.

bitcoinjs:
* `encodeDiffBits(0x80)` => `0x02008000`
* `decodeDiffBits(0x01810000)` => `0x81` (OK)

Defensive encoding and correct decoding

libbitcoin:
* `big_number::compact(0x80)` => `0x02008000`
* `big_number::set_compact(0x01810000)` => `-1`

Defensive encoding.
Decoding results in block_work() returning 0 and
validate_block::check_proof_of_work returning false.

This branch
===========

The first commit adds several test cases for `GetCompact()` and `SetCompact()`, including one showcasing the erroneous decoding.

The second commit reimplements `GetCompact()` and `SetCompact()` using shifts instead of going through the MPI-representation.

`GetCompact()` now is careful not to generate compact numbers with the `0x00800000` bit set (`GetCompact(0xff)` yields `0x0200ff00` instead of `0x01ff0000`). It results in identical compact numbers for positive inputs as the old code.

`SetCompact` now interprets the compact number `0x01810000` as `0x81` instead of `-1`.