mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2025-01-19 05:33:44 +01:00
Correctness fix: compare nBits directly not in BigInteger form as nBits has multiple non-canonical encodings.
This commit is contained in:
parent
4a5854a599
commit
ed2948cef1
@ -854,15 +854,16 @@ public abstract class AbstractBlockChain {
|
||||
}
|
||||
|
||||
int accuracyBytes = (int) (nextBlock.getDifficultyTarget() >>> 24) - 3;
|
||||
BigInteger receivedDifficulty = nextBlock.getDifficultyTargetAsInteger();
|
||||
long receivedDifficultyCompact = nextBlock.getDifficultyTarget();
|
||||
|
||||
// The calculated difficulty is to a higher precision than received, so reduce here.
|
||||
BigInteger mask = BigInteger.valueOf(0xFFFFFFL).shiftLeft(accuracyBytes * 8);
|
||||
newDifficulty = newDifficulty.and(mask);
|
||||
long newDifficultyCompact = Utils.encodeCompactBits(newDifficulty);
|
||||
|
||||
if (newDifficulty.compareTo(receivedDifficulty) != 0)
|
||||
if (newDifficultyCompact != receivedDifficultyCompact)
|
||||
throw new VerificationException("Network provided difficulty bits do not match what was calculated: " +
|
||||
receivedDifficulty.toString(16) + " vs " + newDifficulty.toString(16));
|
||||
newDifficultyCompact + " vs " + receivedDifficultyCompact);
|
||||
}
|
||||
|
||||
private void checkTestnetDifficulty(StoredBlock storedPrev, Block prev, Block next) throws VerificationException, BlockStoreException {
|
||||
|
@ -434,8 +434,19 @@ public class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
// The representation of nBits uses another home-brew encoding, as a way to represent a large
|
||||
// hash value in only 32 bits.
|
||||
/**
|
||||
* <p>The "compact" format is a representation of a whole number N using an unsigned 32 bit number similar to a
|
||||
* floating point format. The most significant 8 bits are the unsigned exponent of base 256. This exponent can
|
||||
* be thought of as "number of bytes of N". The lower 23 bits are the mantissa. Bit number 24 (0x800000) represents
|
||||
* the sign of N. Therefore, N = (-1^sign) * mantissa * 256^(exponent-3).</p>
|
||||
*
|
||||
* <p>Satoshi's original implementation used BN_bn2mpi() and BN_mpi2bn(). MPI uses the most significant bit of the
|
||||
* first byte as sign. Thus 0x1234560000 is compact 0x05123456 and 0xc0de000000 is compact 0x0600c0de. Compact
|
||||
* 0x05c0de00 would be -0x40de000000.</p>
|
||||
*
|
||||
* <p>Bitcoin only uses this "compact" format for encoding difficulty targets, which are unsigned 256bit quantities.
|
||||
* Thus, all the complexities of the sign bit and using base 256 are probably an implementation accident.</p>
|
||||
*/
|
||||
public static BigInteger decodeCompactBits(long compact) {
|
||||
int size = ((int) (compact >> 24)) & 0xFF;
|
||||
byte[] bytes = new byte[4 + size];
|
||||
@ -446,6 +457,27 @@ public class Utils {
|
||||
return decodeMPI(bytes, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Utils#decodeCompactBits(long)
|
||||
*/
|
||||
public static long encodeCompactBits(BigInteger value) {
|
||||
long result;
|
||||
int size = value.toByteArray().length;
|
||||
if (size <= 3)
|
||||
result = value.longValue() << 8 * (3 - size);
|
||||
else
|
||||
result = value.shiftRight(8 * (size - 3)).longValue();
|
||||
// The 0x00800000 bit denotes the sign.
|
||||
// Thus, if it is already set, divide the mantissa by 256 and increase the exponent.
|
||||
if ((result & 0x00800000L) != 0) {
|
||||
result >>= 8;
|
||||
size++;
|
||||
}
|
||||
result |= size << 24;
|
||||
result |= value.signum() == -1 ? 0x00800000 : 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* If non-null, overrides the return value of now().
|
||||
*/
|
||||
|
@ -105,15 +105,15 @@ public class UtilsTest {
|
||||
|
||||
@Test
|
||||
public void testReverseBytes() {
|
||||
Assert.assertArrayEquals(new byte[] {1,2,3,4,5}, Utils.reverseBytes(new byte[] {5,4,3,2,1}));
|
||||
assertArrayEquals(new byte[]{1, 2, 3, 4, 5}, Utils.reverseBytes(new byte[]{5, 4, 3, 2, 1}));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReverseDwordBytes() {
|
||||
Assert.assertArrayEquals(new byte[] {1,2,3,4,5,6,7,8}, Utils.reverseDwordBytes(new byte[] {4,3,2,1,8,7,6,5}, -1));
|
||||
Assert.assertArrayEquals(new byte[] {1,2,3,4}, Utils.reverseDwordBytes(new byte[] {4,3,2,1,8,7,6,5}, 4));
|
||||
Assert.assertArrayEquals(new byte[0], Utils.reverseDwordBytes(new byte[] {4,3,2,1,8,7,6,5}, 0));
|
||||
Assert.assertArrayEquals(new byte[0], Utils.reverseDwordBytes(new byte[0], 0));
|
||||
assertArrayEquals(new byte[]{1, 2, 3, 4, 5, 6, 7, 8}, Utils.reverseDwordBytes(new byte[]{4, 3, 2, 1, 8, 7, 6, 5}, -1));
|
||||
assertArrayEquals(new byte[]{1, 2, 3, 4}, Utils.reverseDwordBytes(new byte[]{4, 3, 2, 1, 8, 7, 6, 5}, 4));
|
||||
assertArrayEquals(new byte[0], Utils.reverseDwordBytes(new byte[]{4, 3, 2, 1, 8, 7, 6, 5}, 0));
|
||||
assertArrayEquals(new byte[0], Utils.reverseDwordBytes(new byte[0], 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -124,4 +124,12 @@ public class UtilsTest {
|
||||
assertEquals(1, Utils.maxOfMostFreq(1, 1, 2, 2, 1));
|
||||
assertEquals(-1, Utils.maxOfMostFreq(-1, -1, 2, 2, -1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compactEncoding() throws Exception {
|
||||
assertEquals(new BigInteger("1234560000", 16), Utils.decodeCompactBits(0x05123456L));
|
||||
assertEquals(new BigInteger("c0de000000", 16), Utils.decodeCompactBits(0x0600c0de));
|
||||
assertEquals(0x05123456L, Utils.encodeCompactBits(new BigInteger("1234560000", 16)));
|
||||
assertEquals(0x0600c0deL, Utils.encodeCompactBits(new BigInteger("c0de000000", 16)));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user