3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-11 17:55:53 +00:00

Handle legacy AuxPoW blocks

Adds handling of legacy AuxPoW blocks before there was a merged mining
header.
This commit is contained in:
Ross Nicoll 2016-02-28 11:03:08 +00:00
parent 9afda0e8e9
commit 89cf3c6b17
3 changed files with 60 additions and 29 deletions

View File

@ -41,6 +41,12 @@ public class AuxPoW extends ChildMessage {
(byte) 0xfa, (byte) 0xbe, "m".getBytes()[0], "m".getBytes()[0] (byte) 0xfa, (byte) 0xbe, "m".getBytes()[0], "m".getBytes()[0]
}; };
/**
* Maximum index of the merkle root hash in the coinbase transaction script,
* where no merged mining header is present.
*/
protected static final int MAX_INDEX_PC_BACKWARDS_COMPATIBILITY = 20;
private static final Logger log = LoggerFactory.getLogger(AuxPoW.class); private static final Logger log = LoggerFactory.getLogger(AuxPoW.class);
private static final long serialVersionUID = -8567546957352643140L; private static final long serialVersionUID = -8567546957352643140L;
@ -290,44 +296,49 @@ public class AuxPoW extends ChildMessage {
// Check that the same work is not submitted twice to our chain, by // Check that the same work is not submitted twice to our chain, by
// confirming that the child block hash is in the coinbase merkle tree // confirming that the child block hash is in the coinbase merkle tree
int pcHeader = -1; int pcHead = -1;
int pc = -1; int pc = -1;
for (int scriptIdx = 0; scriptIdx < script.length; scriptIdx++) { for (int scriptIdx = 0; scriptIdx < script.length; scriptIdx++) {
if (arrayMatch(script, scriptIdx, MERGED_MINING_HEADER)) { if (arrayMatch(script, scriptIdx, MERGED_MINING_HEADER)) {
// Enforce only one chain merkle root by checking that a single instance of the merged // Enforce only one chain merkle root by checking that a single instance of the merged
// mining header exists just before. // mining header exists just before.
if (pcHeader >= 0) { if (pcHead >= 0) {
if (throwException) { if (throwException) {
throw new VerificationException("Multiple merged mining headers in coinbase"); throw new VerificationException("Multiple merged mining headers in coinbase");
} }
return false; return false;
} }
pcHeader = scriptIdx; pcHead = scriptIdx;
} else if (arrayMatch(script, scriptIdx, vchRootHash)) { } else if (arrayMatch(script, scriptIdx, vchRootHash)) {
pc = scriptIdx; pc = scriptIdx;
} }
} }
if (-1 == pcHeader) { if (pc == -1) {
if (throwException) {
throw new VerificationException("MergedMiningHeader missing from parent coinbase");
}
return false;
}
if (-1 == pc) {
if (throwException) { if (throwException) {
throw new VerificationException("Aux POW missing chain merkle root in parent coinbase"); throw new VerificationException("Aux POW missing chain merkle root in parent coinbase");
} }
return false; return false;
} }
if (pcHeader + MERGED_MINING_HEADER.length != pc) { if (pcHead != -1) {
if (throwException) { if (pcHead + MERGED_MINING_HEADER.length != pc) {
throw new VerificationException("Merged mining header is not just before chain merkle root"); if (throwException) {
throw new VerificationException("Merged mining header is not just before chain merkle root");
}
return false;
}
} else {
// For backward compatibility.
// Enforce only one chain merkle root by checking that it starts early in the coinbase.
// 8-12 bytes are enough to encode extraNonce and nBits.
if (pc > MAX_INDEX_PC_BACKWARDS_COMPATIBILITY) {
if (throwException) {
throw new VerificationException("Aux POW chain merkle root must start in the first 20 bytes of the parent coinbase");
}
return false;
} }
return false;
} }
// Ensure we are at a deterministic point in the merkle leaves by hashing // Ensure we are at a deterministic point in the merkle leaves by hashing
@ -361,12 +372,12 @@ public class AuxPoW extends ChildMessage {
return false; return false;
} }
BigInteger h = altcoinParams.getBlockDifficultyHash(getParentBlockHeader()) Sha256Hash hash = altcoinParams.getBlockDifficultyHash(getParentBlockHeader());
.toBigInteger(); BigInteger hashVal = hash.toBigInteger();
if (h.compareTo(target) > 0) { if (hashVal.compareTo(target) > 0) {
// Proof of work check failed! // Proof of work check failed!
if (throwException) { if (throwException) {
throw new VerificationException("Hash is higher than target: " + getParentBlockHeader().getHashAsString() + " vs " throw new VerificationException("Hash is higher than target: " + hash.toString() + " vs "
+ target.toString(16)); + target.toString(16));
} }
return false; return false;

View File

@ -1,17 +1,13 @@
package org.bitcoinj.core; package org.bitcoinj.core;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import static org.bitcoinj.core.AuxPoW.MERGED_MINING_HEADER;
import org.libdohj.core.AltcoinSerializer; import org.libdohj.core.AltcoinSerializer;
import org.libdohj.core.AuxPoWNetworkParameters;
import org.libdohj.params.DogecoinMainNetParams; import org.libdohj.params.DogecoinMainNetParams;
import org.libdohj.params.DogecoinTestNet3Params;
import static org.bitcoinj.core.Util.getBytes; import static org.bitcoinj.core.Util.getBytes;
import static org.bitcoinj.core.Utils.reverseBytes;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import org.junit.Before; import org.junit.Before;
@ -74,6 +70,26 @@ public class AuxPoWTest {
auxpow.checkProofOfWork(Sha256Hash.wrap("0c836b86991631d34a8a68054e2f62db919b39d1ee43c27ab3344d6aa82fa609"), auxpow.checkProofOfWork(Sha256Hash.wrap("0c836b86991631d34a8a68054e2f62db919b39d1ee43c27ab3344d6aa82fa609"),
Utils.decodeCompactBits(0x1b06f8f0), true); Utils.decodeCompactBits(0x1b06f8f0), true);
} }
/**
* Validate the AuxPoW header with no explicit data header in the coinbase
* transaction. Namecoin block #19,414
*/
@Test
public void checkAuxPoWHeaderNoTxHeader() throws Exception {
// Emulate Namecoin block hashing for this test
final NetworkParameters namecoinLikeParams = new DogecoinTestNet3Params() {
@Override
public Sha256Hash getBlockDifficultyHash(Block block) {
// Namecoin uses SHA256 hashes
return block.getHash();
}
};
byte[] auxpowAsBytes = getBytes(getClass().getResourceAsStream("auxpow_header_no_tx_header.bin"));
AuxPoW auxpow = new AuxPoW(namecoinLikeParams, auxpowAsBytes, (ChildMessage) null, namecoinLikeParams.getDefaultSerializer());
auxpow.checkProofOfWork(Sha256Hash.wrap("5fb89c3b18c27bc38d351d516177cbd3504c95ca0494cbbbbd52f2fb5f2ff1ec"),
Utils.decodeCompactBits(0x1b00b269), true);
}
@Rule @Rule
public ExpectedException expectedEx = ExpectedException.none(); public ExpectedException expectedEx = ExpectedException.none();
@ -164,8 +180,9 @@ public class AuxPoWTest {
} }
/** /**
* Catch the case that the merged mine header is missing from the coinbase * Catch the case that the coinbase transaction does not contain details of
* transaction. * the merged block. In this case we make the transaction script too short
* for it to do so.
*/ */
@Test @Test
public void shouldRejectIfMergedMineHeaderMissing() throws Exception { public void shouldRejectIfMergedMineHeaderMissing() throws Exception {
@ -175,11 +192,14 @@ public class AuxPoWTest {
// This will also break the difficulty check, but as that doesn't occur // This will also break the difficulty check, but as that doesn't occur
// until the end, we can get away with it. // until the end, we can get away with it.
final TransactionInput in = auxpow.getCoinbase().getInput(0); final TransactionInput in = auxpow.getCoinbase().getInput(0);
in.getScriptBytes()[4] = 0; // Break the first byte of the header final byte[] paddedScriptBytes = new byte[in.getScriptBytes().length + (AuxPoW.MAX_INDEX_PC_BACKWARDS_COMPATIBILITY + 4)];
Arrays.fill(paddedScriptBytes, (byte) 0);
System.arraycopy(in.getScriptBytes(), 8, paddedScriptBytes, (AuxPoW.MAX_INDEX_PC_BACKWARDS_COMPATIBILITY + 4), in.getScriptBytes().length - 8);
in.setScriptBytes(paddedScriptBytes);
updateMerkleRootToMatchCoinbase(auxpow); updateMerkleRootToMatchCoinbase(auxpow);
expectedEx.expect(org.bitcoinj.core.VerificationException.class); expectedEx.expect(org.bitcoinj.core.VerificationException.class);
expectedEx.expectMessage("MergedMiningHeader missing from parent coinbase"); expectedEx.expectMessage("Aux POW chain merkle root must start in the first 20 bytes of the parent coinbase");
auxpow.checkProofOfWork(Sha256Hash.wrap("0c836b86991631d34a8a68054e2f62db919b39d1ee43c27ab3344d6aa82fa609"), auxpow.checkProofOfWork(Sha256Hash.wrap("0c836b86991631d34a8a68054e2f62db919b39d1ee43c27ab3344d6aa82fa609"),
Utils.decodeCompactBits(0x1b06f8f0), true); Utils.decodeCompactBits(0x1b06f8f0), true);
} }
@ -337,7 +357,7 @@ public class AuxPoWTest {
final AuxPoW auxpow = new AuxPoW(params, auxpowAsBytes, (ChildMessage) null, params.getDefaultSerializer()); final AuxPoW auxpow = new AuxPoW(params, auxpowAsBytes, (ChildMessage) null, params.getDefaultSerializer());
expectedEx.expect(org.bitcoinj.core.VerificationException.class); expectedEx.expect(org.bitcoinj.core.VerificationException.class);
expectedEx.expectMessage("Hash is higher than target: a22a9b01671d639fa6389f62ecf8ce69204c8ed41d5f1a745e0c5ba7116d5b4c vs 0"); expectedEx.expectMessage("Hash is higher than target: 000000000003178bb23160cdbc81af53f47cae9f479acf1e69849da42fd5bfca vs 0");
auxpow.checkProofOfWork(Sha256Hash.wrap("0c836b86991631d34a8a68054e2f62db919b39d1ee43c27ab3344d6aa82fa609"), auxpow.checkProofOfWork(Sha256Hash.wrap("0c836b86991631d34a8a68054e2f62db919b39d1ee43c27ab3344d6aa82fa609"),
Utils.decodeCompactBits(0x00), true); Utils.decodeCompactBits(0x00), true);