mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-11 17:55:53 +00:00
Completed AuxPoW header validation code.
This commit is contained in:
parent
453819ccb2
commit
c6659bad12
@ -20,12 +20,11 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.math.BigInteger;
|
||||
import org.bitcoinj.core.AltcoinBlock;
|
||||
|
||||
import org.bitcoinj.core.AuxPoWNetworkParameters;
|
||||
import org.bitcoinj.core.AltcoinNetworkParameters;
|
||||
import org.bitcoinj.core.Block;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import static org.bitcoinj.core.Coin.COIN;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.VerificationException;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.script.ScriptOpCodes;
|
||||
@ -36,7 +35,8 @@ import org.bitcoinj.utils.MonetaryFormat;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import org.bitcoinj.core.AltcoinSerializer;
|
||||
import org.bitcoinj.core.Sha256Hash;
|
||||
import org.bitcoinj.core.StoredBlock;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.TransactionInput;
|
||||
@ -46,7 +46,7 @@ import org.bitcoinj.core.Utils;
|
||||
/**
|
||||
* Parameters for the main Dogecoin production network on which people trade goods and services.
|
||||
*/
|
||||
public abstract class AbstractDogecoinParams extends NetworkParameters implements AuxPoWNetworkParameters {
|
||||
public abstract class AbstractDogecoinParams extends NetworkParameters implements AltcoinNetworkParameters {
|
||||
/** Standard format for the DOGE denomination. */
|
||||
public static final MonetaryFormat DOGE;
|
||||
/** Standard format for the mDOGE denomination. */
|
||||
@ -55,6 +55,7 @@ public abstract class AbstractDogecoinParams extends NetworkParameters implement
|
||||
public static final MonetaryFormat KOINU;
|
||||
|
||||
public static final int DIGISHIELD_BLOCK_HEIGHT = 145000; // Block height to use Digishield from
|
||||
public static final int AUXPOW_CHAIN_ID = 0x0062; // 98
|
||||
public static final int DOGE_TARGET_TIMESPAN = 4 * 60 * 60; // 4 hours per difficulty cycle, on average.
|
||||
public static final int DOGE_TARGET_TIMESPAN_NEW = 60; // 60s per difficulty cycle, on average. Kicks in after block 145k.
|
||||
public static final int DOGE_TARGET_SPACING = 1 * 60; // 1 minute per block.
|
||||
@ -290,6 +291,24 @@ public abstract class AbstractDogecoinParams extends NetworkParameters implement
|
||||
return DIGISHIELD_BLOCK_HEIGHT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChainID() {
|
||||
return AUXPOW_CHAIN_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hash to use for a block.
|
||||
*/
|
||||
@Override
|
||||
public Sha256Hash getBlockDifficultyHash(Block block) {
|
||||
return ((AltcoinBlock) block).getScryptHash();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AltcoinSerializer getSerializer(boolean parseLazy, boolean parseRetain) {
|
||||
return new AltcoinSerializer(this, parseLazy, parseRetain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuxPoWBlockVersion(long version) {
|
||||
return version >= BLOCK_VERSION_AUXPOW
|
||||
|
@ -17,7 +17,6 @@
|
||||
package org.altcoinj.params;
|
||||
|
||||
import org.altcoinj.core.ScryptHash;
|
||||
import org.bitcoinj.core.AltcoinBlock;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
|
@ -59,50 +59,30 @@ public class AltcoinBlock extends org.bitcoinj.core.Block {
|
||||
super(params);
|
||||
}
|
||||
|
||||
/** Constructs a block object from the Bitcoin wire format. */
|
||||
public AltcoinBlock(NetworkParameters params, byte[] payloadBytes) throws ProtocolException {
|
||||
super(params, payloadBytes);
|
||||
/** Special case constructor, used for the genesis node, cloneAsHeader and unit tests. */
|
||||
public AltcoinBlock(NetworkParameters params, byte[] payloadBytes) {
|
||||
this(params, payloadBytes, params.getDefaultSerializer(), payloadBytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contruct a block object from the Bitcoin wire format.
|
||||
* Construct a block object from the Bitcoin wire format.
|
||||
* @param params NetworkParameters object.
|
||||
* @param parseLazy Whether to perform a full parse immediately or delay until a read is requested.
|
||||
* @param parseRetain Whether to retain the backing byte array for quick reserialization.
|
||||
* If true and the backing byte array is invalidated due to modification of a field then
|
||||
* the cached bytes may be repopulated and retained if the message is serialized again in the future.
|
||||
* @param serializer the serializer to use for this message.
|
||||
* @param length The length of message if known. Usually this is provided when deserializing of the wire
|
||||
* as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
|
||||
* @throws ProtocolException
|
||||
*/
|
||||
public AltcoinBlock(NetworkParameters params, byte[] payloadBytes, boolean parseLazy, boolean parseRetain, int length)
|
||||
public AltcoinBlock(NetworkParameters params, byte[] payloadBytes, MessageSerializer serializer, int length)
|
||||
throws ProtocolException {
|
||||
super(params, payloadBytes, parseLazy, parseRetain, length);
|
||||
super(params, payloadBytes, serializer, length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contruct a block object from the Bitcoin wire format. Used in the case of a block
|
||||
* contained within another message (i.e. for AuxPoW header).
|
||||
*
|
||||
* @param params NetworkParameters object.
|
||||
* @param payloadBytes Bitcoin protocol formatted byte array containing message content.
|
||||
* @param offset The location of the first payload byte within the array.
|
||||
* @param parent The message element which contains this block, maybe null for no parent.
|
||||
* @param parseLazy Whether to perform a full parse immediately or delay until a read is requested.
|
||||
* @param parseRetain Whether to retain the backing byte array for quick reserialization.
|
||||
* If true and the backing byte array is invalidated due to modification of a field then
|
||||
* the cached bytes may be repopulated and retained if the message is serialized again in the future.
|
||||
* @param length The length of message if known. Usually this is provided when deserializing of the wire
|
||||
* as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
|
||||
* @throws ProtocolException
|
||||
*/
|
||||
public AltcoinBlock(NetworkParameters params, byte[] payloadBytes, int offset, @Nullable Message parent, boolean parseLazy, boolean parseRetain, int length)
|
||||
throws ProtocolException {
|
||||
// TODO: Keep the parent
|
||||
super(params, payloadBytes, offset, parent, parseLazy, parseRetain, length);
|
||||
public AltcoinBlock(NetworkParameters params, byte[] payloadBytes, int offset,
|
||||
Message parent, MessageSerializer serializer, int length)
|
||||
throws ProtocolException {
|
||||
super(params, payloadBytes, serializer, length);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a block initialized with all the given fields.
|
||||
* @param params Which network the block is for.
|
||||
@ -158,18 +138,18 @@ public class AltcoinBlock extends org.bitcoinj.core.Block {
|
||||
return;
|
||||
|
||||
this.auxpow = null;
|
||||
if (this.params instanceof AuxPoWNetworkParameters) {
|
||||
final AuxPoWNetworkParameters altcoinParams = (AuxPoWNetworkParameters)this.params;
|
||||
if (this.params instanceof AltcoinNetworkParameters) {
|
||||
final AltcoinNetworkParameters altcoinParams = (AltcoinNetworkParameters)this.params;
|
||||
if (altcoinParams.isAuxPoWBlockVersion(this.getVersion())) {
|
||||
// The following is used in dogecoinj, but I don't think we necessarily need it
|
||||
// payload.length >= 160) { // We have at least 2 headers in an Aux block. Workaround for StoredBlocks
|
||||
this.auxpow = new AuxPoW(params, payload, cursor, this, parseLazy, parseRetain);
|
||||
this.auxpow = new AuxPoW(params, payload, cursor, this, serializer);
|
||||
optimalEncodingMessageSize += auxpow.getOptimalEncodingMessageSize();
|
||||
}
|
||||
}
|
||||
|
||||
this.auxpowParsed = true;
|
||||
this.auxpowBytesValid = parseRetain;
|
||||
this.auxpowBytesValid = serializer.isParseRetainMode();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -194,22 +174,24 @@ public class AltcoinBlock extends org.bitcoinj.core.Block {
|
||||
// Ignore the header since it has fixed length. If length is not provided we will have to
|
||||
// invoke a light parse of transactions to calculate the length.
|
||||
if (length == UNKNOWN_LENGTH) {
|
||||
Preconditions.checkState(parseLazy,
|
||||
Preconditions.checkState(serializer.isParseLazyMode(),
|
||||
"Performing lite parse of block transaction as block was initialised from byte array " +
|
||||
"without providing length. This should never need to happen.");
|
||||
parseAuxPoW();
|
||||
parseTransactions();
|
||||
length = cursor - offset;
|
||||
} else {
|
||||
transactionBytesValid = !transactionsParsed || parseRetain && length > HEADER_SIZE;
|
||||
transactionBytesValid = !transactionsParsed || serializer.isParseRetainMode() && length > HEADER_SIZE;
|
||||
}
|
||||
headerBytesValid = !headerParsed || parseRetain && length >= HEADER_SIZE;
|
||||
headerBytesValid = !headerParsed || serializer.isParseRetainMode() && length >= HEADER_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
void writeHeader(OutputStream stream) throws IOException {
|
||||
super.writeHeader(stream);
|
||||
// TODO: Write the AuxPoW header
|
||||
if (null != this.auxpow) {
|
||||
this.auxpow.bitcoinSerialize(stream);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a copy of the block, but without any transactions. */
|
||||
@ -223,28 +205,27 @@ public class AltcoinBlock extends org.bitcoinj.core.Block {
|
||||
|
||||
/** Returns true if the hash of the block is OK (lower than difficulty target). */
|
||||
protected boolean checkProofOfWork(boolean throwException) throws VerificationException {
|
||||
// TODO: Add AuxPoW support
|
||||
if (params instanceof AltcoinNetworkParameters) {
|
||||
BigInteger target = getDifficultyTargetAsInteger();
|
||||
|
||||
final AltcoinNetworkParameters altParams = (AltcoinNetworkParameters)auxpow;
|
||||
if (altParams.isAuxPoWBlockVersion(getVersion()) && null != auxpow) {
|
||||
return auxpow.checkProofOfWork(this.getHash(), target, throwException);
|
||||
}
|
||||
|
||||
// This part is key - it is what proves the block was as difficult to make as it claims
|
||||
// to be. Note however that in the context of this function, the block can claim to be
|
||||
// as difficult as it wants to be .... if somebody was able to take control of our network
|
||||
// connection and fork us onto a different chain, they could send us valid blocks with
|
||||
// ridiculously easy difficulty and this function would accept them.
|
||||
//
|
||||
// To prevent this attack from being possible, elsewhere we check that the difficultyTarget
|
||||
// field is of the right value. This requires us to have the preceeding blocks.
|
||||
BigInteger target = getDifficultyTargetAsInteger();
|
||||
|
||||
BigInteger h = getHash().toBigInteger();
|
||||
if (h.compareTo(target) > 0) {
|
||||
// Proof of work check failed!
|
||||
if (throwException)
|
||||
throw new VerificationException("Hash is higher than target: " + getHashAsString() + " vs "
|
||||
+ target.toString(16));
|
||||
else
|
||||
return false;
|
||||
BigInteger h = altParams.getBlockDifficultyHash(this).toBigInteger();
|
||||
if (h.compareTo(target) > 0) {
|
||||
// Proof of work check failed!
|
||||
if (throwException)
|
||||
throw new VerificationException("Hash is higher than target: " + getHashAsString() + " vs "
|
||||
+ target.toString(16));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return super.checkProofOfWork(throwException);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2015 Ross Nicoll
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.bitcoinj.core;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jrn
|
||||
*/
|
||||
public interface AltcoinNetworkParameters {
|
||||
|
||||
boolean isAuxPoWBlockVersion(long version);
|
||||
|
||||
int getChainID();
|
||||
|
||||
/**
|
||||
* Get the hash for the given block, for comparing against target difficulty.
|
||||
* This provides an extension hook for networks which use a hash other than
|
||||
* SHA256 twice (Bitcoin standard) for proof of work.
|
||||
*/
|
||||
Sha256Hash getBlockDifficultyHash(Block block);
|
||||
}
|
27
src/main/java/org/bitcoinj/core/AltcoinSerializer.java
Normal file
27
src/main/java/org/bitcoinj/core/AltcoinSerializer.java
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.bitcoinj.core;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jrn
|
||||
*/
|
||||
public class AltcoinSerializer extends BitcoinSerializer {
|
||||
|
||||
public AltcoinSerializer(NetworkParameters params, boolean parseLazy, boolean parseRetain) {
|
||||
super(params, parseLazy, parseRetain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block makeBlock(byte[] payloadBytes) throws ProtocolException {
|
||||
return new AltcoinBlock(getParameters(), payloadBytes, this, payloadBytes.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block makeBlock(byte[] payloadBytes, int length) throws ProtocolException {
|
||||
return new AltcoinBlock(getParameters(), payloadBytes, this, length);
|
||||
}
|
||||
}
|
@ -23,23 +23,31 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.*;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* <p>An AuxPoW header wraps a block header from another coin, enabling the foreign
|
||||
* chain's proof of work to be used for this chain as well.</p>
|
||||
* chain's proof of work to be used for this chain as well. <b>Note: </b>
|
||||
* NetworkParameters for AuxPoW networks <b>must</b> implement AltcoinNetworkParameters
|
||||
* in order for AuxPoW to work.</p>
|
||||
*/
|
||||
public class AuxPoW extends ChildMessage implements Serializable {
|
||||
|
||||
|
||||
public static final byte[] MERGED_MINING_HEADER = new byte[] {
|
||||
(byte) 0xfa, (byte) 0xbe, "m".getBytes()[0], "m".getBytes()[0]
|
||||
};
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AuxPoW.class);
|
||||
private static final long serialVersionUID = -8567546957352643140L;
|
||||
|
||||
private Transaction transaction;
|
||||
private Transaction transaction;
|
||||
private Sha256Hash hashBlock;
|
||||
private MerkleBranch coinbaseBranch;
|
||||
private MerkleBranch blockchainBranch;
|
||||
private Block parentBlockHeader;
|
||||
private MerkleBranch chainMerkleBranch;
|
||||
private AltcoinBlock parentBlockHeader;
|
||||
|
||||
// Transactions can be encoded in a way that will use more bytes than is optimal
|
||||
// (due to VarInts having multiple encodings)
|
||||
@ -50,36 +58,38 @@ public class AuxPoW extends ChildMessage implements Serializable {
|
||||
|
||||
public AuxPoW(NetworkParameters params, @Nullable Message parent) {
|
||||
super(params);
|
||||
transaction = new Transaction(params);
|
||||
hashBlock = Sha256Hash.ZERO_HASH;
|
||||
coinbaseBranch = new MerkleBranch(params, this);
|
||||
blockchainBranch = new MerkleBranch(params, this);
|
||||
parentBlockHeader = null;
|
||||
transaction = new Transaction(params);
|
||||
hashBlock = Sha256Hash.ZERO_HASH;
|
||||
coinbaseBranch = new MerkleBranch(params, this);
|
||||
chainMerkleBranch = new MerkleBranch(params, this);
|
||||
parentBlockHeader = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AuxPoW header by reading payload starting from offset bytes in. Length of header is fixed.
|
||||
* @param params NetworkParameters object.1
|
||||
* @param params NetworkParameters object.
|
||||
* @param payload Bitcoin protocol formatted byte array containing message content.
|
||||
* @param offset The location of the first payload byte within the array.
|
||||
* @param parent The message element which contains this header.
|
||||
* @param parseLazy Whether to perform a full parse immediately or delay until a read is requested.
|
||||
* @param parseRetain Whether to retain the backing byte array for quick reserialization.
|
||||
* If true and the backing byte array is invalidated due to modification of a field then
|
||||
* the cached bytes may be repopulated and retained if the message is serialized again in the future.
|
||||
* @param serializer the serializer to use for this message.
|
||||
* @throws ProtocolException
|
||||
*/
|
||||
public AuxPoW(NetworkParameters params, byte[] payload, int offset, Message parent, boolean parseLazy, boolean parseRetain)
|
||||
public AuxPoW(NetworkParameters params, byte[] payload, int offset, Message parent, MessageSerializer serializer)
|
||||
throws ProtocolException {
|
||||
super(params, payload, offset, parent, parseLazy, parseRetain, Message.UNKNOWN_LENGTH);
|
||||
super(params, payload, offset, parent, serializer, Message.UNKNOWN_LENGTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an AuxPoW header by reading payload starting from offset bytes in. Length of header is fixed.
|
||||
*
|
||||
* @param params NetworkParameters object.
|
||||
* @param payload Bitcoin protocol formatted byte array containing message content.
|
||||
* @param parent The message element which contains this header.
|
||||
* @param serializer the serializer to use for this message.
|
||||
*/
|
||||
public AuxPoW(NetworkParameters params, byte[] payload, @Nullable Message parent, boolean parseLazy, boolean parseRetain)
|
||||
public AuxPoW(NetworkParameters params, byte[] payload, @Nullable Message parent, MessageSerializer serializer)
|
||||
throws ProtocolException {
|
||||
super(params, payload, 0, parent, parseLazy, parseRetain, Message.UNKNOWN_LENGTH);
|
||||
super(params, payload, 0, parent, serializer, Message.UNKNOWN_LENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -96,11 +106,11 @@ public class AuxPoW extends ChildMessage implements Serializable {
|
||||
// jump past header hash
|
||||
cursor += 4;
|
||||
|
||||
// Coin base branch
|
||||
cursor += MerkleBranch.calcLength(buf, offset);
|
||||
// Coin base branch
|
||||
cursor += MerkleBranch.calcLength(buf, offset);
|
||||
|
||||
// Block chain branch
|
||||
cursor += MerkleBranch.calcLength(buf, offset);
|
||||
// Block chain branch
|
||||
cursor += MerkleBranch.calcLength(buf, offset);
|
||||
|
||||
// Block header
|
||||
cursor += Block.HEADER_SIZE;
|
||||
@ -115,26 +125,26 @@ public class AuxPoW extends ChildMessage implements Serializable {
|
||||
return;
|
||||
|
||||
cursor = offset;
|
||||
transaction = new Transaction(params, payload, cursor, this, parseLazy, parseRetain, Message.UNKNOWN_LENGTH);
|
||||
transaction = new Transaction(params, payload, cursor, this, serializer, Message.UNKNOWN_LENGTH);
|
||||
cursor += transaction.getOptimalEncodingMessageSize();
|
||||
optimalEncodingMessageSize = transaction.getOptimalEncodingMessageSize();
|
||||
|
||||
hashBlock = readHash();
|
||||
optimalEncodingMessageSize += 32; // Add the hash size to the optimal encoding
|
||||
optimalEncodingMessageSize += 32; // Add the hash size to the optimal encoding
|
||||
|
||||
coinbaseBranch = new MerkleBranch(params, this, payload, cursor, parseLazy, parseRetain);
|
||||
cursor += coinbaseBranch.getOptimalEncodingMessageSize();
|
||||
optimalEncodingMessageSize += coinbaseBranch.getOptimalEncodingMessageSize();
|
||||
coinbaseBranch = new MerkleBranch(params, this, payload, cursor, serializer);
|
||||
cursor += coinbaseBranch.getOptimalEncodingMessageSize();
|
||||
optimalEncodingMessageSize += coinbaseBranch.getOptimalEncodingMessageSize();
|
||||
|
||||
blockchainBranch = new MerkleBranch(params, this, payload, cursor, parseLazy, parseRetain);
|
||||
cursor += blockchainBranch.getOptimalEncodingMessageSize();
|
||||
optimalEncodingMessageSize += blockchainBranch.getOptimalEncodingMessageSize();
|
||||
chainMerkleBranch = new MerkleBranch(params, this, payload, cursor, serializer);
|
||||
cursor += chainMerkleBranch.getOptimalEncodingMessageSize();
|
||||
optimalEncodingMessageSize += chainMerkleBranch.getOptimalEncodingMessageSize();
|
||||
|
||||
// Make a copy of JUST the contained block header, so the block parser doesn't try reading
|
||||
// transactions past the end
|
||||
byte[] blockBytes = Arrays.copyOfRange(payload, cursor, cursor + Block.HEADER_SIZE);
|
||||
cursor += Block.HEADER_SIZE;
|
||||
parentBlockHeader = new AltcoinBlock(params, blockBytes, 0, this, parseLazy, parseRetain, Block.HEADER_SIZE);
|
||||
parentBlockHeader = new AltcoinBlock(params, blockBytes, 0, this, serializer, Block.HEADER_SIZE);
|
||||
|
||||
length = cursor - offset;
|
||||
}
|
||||
@ -164,13 +174,13 @@ public class AuxPoW extends ChildMessage implements Serializable {
|
||||
|
||||
@Override
|
||||
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
|
||||
transaction.bitcoinSerialize(stream);
|
||||
transaction.bitcoinSerialize(stream);
|
||||
stream.write(Utils.reverseBytes(hashBlock.getBytes()));
|
||||
|
||||
coinbaseBranch.bitcoinSerialize(stream);
|
||||
blockchainBranch.bitcoinSerialize(stream);
|
||||
coinbaseBranch.bitcoinSerialize(stream);
|
||||
chainMerkleBranch.bitcoinSerialize(stream);
|
||||
|
||||
parentBlockHeader.bitcoinSerializeToStream(stream);
|
||||
parentBlockHeader.bitcoinSerializeToStream(stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -178,11 +188,11 @@ public class AuxPoW extends ChildMessage implements Serializable {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
AuxPoW input = (AuxPoW) o;
|
||||
if (!transaction.equals(input.transaction)) return false;
|
||||
if (!hashBlock.equals(input.hashBlock)) return false;
|
||||
if (!coinbaseBranch.equals(input.hashBlock)) return false;
|
||||
if (!blockchainBranch.equals(input.hashBlock)) return false;
|
||||
if (!parentBlockHeader.equals(input.hashBlock)) return false;
|
||||
if (!transaction.equals(input.transaction)) return false;
|
||||
if (!hashBlock.equals(input.hashBlock)) return false;
|
||||
if (!coinbaseBranch.equals(input.hashBlock)) return false;
|
||||
if (!chainMerkleBranch.equals(input.hashBlock)) return false;
|
||||
if (!parentBlockHeader.equals(input.hashBlock)) return false;
|
||||
return getHash().equals(input.getHash());
|
||||
}
|
||||
|
||||
@ -192,7 +202,7 @@ public class AuxPoW extends ChildMessage implements Serializable {
|
||||
result = 31 * result + transaction.hashCode();
|
||||
result = 31 * result + hashBlock.hashCode();
|
||||
result = 31 * result + coinbaseBranch.hashCode();
|
||||
result = 31 * result + blockchainBranch.hashCode();
|
||||
result = 31 * result + chainMerkleBranch.hashCode();
|
||||
result = 31 * result + parentBlockHeader.hashCode();
|
||||
return result;
|
||||
}
|
||||
@ -213,7 +223,7 @@ public class AuxPoW extends ChildMessage implements Serializable {
|
||||
* not necessarily part of the parent blockchain, they simply must be valid
|
||||
* blocks at the difficulty of the child blockchain.
|
||||
*/
|
||||
public Block getParentBlockHeader() {
|
||||
public AltcoinBlock getParentBlockHeader() {
|
||||
return parentBlockHeader;
|
||||
}
|
||||
|
||||
@ -229,8 +239,8 @@ public class AuxPoW extends ChildMessage implements Serializable {
|
||||
/**
|
||||
* Get the Merkle branch used to connect the AuXPow header with this blockchain.
|
||||
*/
|
||||
public MerkleBranch getBlockchainBranch() {
|
||||
return blockchainBranch;
|
||||
public MerkleBranch getChainMerkleBranch() {
|
||||
return chainMerkleBranch;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -260,4 +270,170 @@ public class AuxPoW extends ChildMessage implements Serializable {
|
||||
maybeParse();
|
||||
// TODO: Verify the AuxPoW data
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the proof of work for this AuxPoW header meets the target
|
||||
* difficulty.
|
||||
*
|
||||
* @param hashAuxBlock hash of the block the AuxPoW header is attached to.
|
||||
* @param target the difficulty target after decoding from compact bits.
|
||||
*/
|
||||
protected boolean checkProofOfWork(Sha256Hash hashAuxBlock,
|
||||
BigInteger target, boolean throwException) throws VerificationException {
|
||||
if (0 != this.getCoinbaseBranch().getIndex()) {
|
||||
if (throwException) {
|
||||
// I don't like the message, but it correlates with what's in the reference client.
|
||||
throw new VerificationException("AuxPow is not a generate");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* if (!TestNet()
|
||||
parentBlockHeader.getChainID() == ((AuxPoWNetworkParameters) params).getChainID()) {
|
||||
if (throwException) {
|
||||
throw new VerificationException("Aux POW parent has our chain ID");
|
||||
}
|
||||
return false;
|
||||
} */
|
||||
|
||||
if (this.getChainMerkleBranch().size() > 30) {
|
||||
if (throwException) {
|
||||
throw new VerificationException("Aux POW chain merkle branch too long");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the chain merkle root is in the coinbase
|
||||
Sha256Hash nRootHash = getChainMerkleBranch().calculateMerkleRoot(hashAuxBlock);
|
||||
final byte[] vchRootHash = nRootHash.getBytes();
|
||||
//std::reverse(vchRootHash.begin(), vchRootHash.end()); // correct endian// correct endian
|
||||
|
||||
// Check that we are in the parent block merkle tree
|
||||
if (!getCoinbaseBranch().calculateMerkleRoot(getCoinbase().getHash()).equals(parentBlockHeader.getMerkleRoot())) {
|
||||
if (throwException) {
|
||||
throw new VerificationException("Aux POW merkle root incorrect");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
final byte[] script = this.getCoinbase().getInput(0).getScriptBytes();
|
||||
|
||||
// Check that the same work is not submitted twice to our chain.
|
||||
//
|
||||
int pcHead = -1;
|
||||
int pc = -1;
|
||||
|
||||
for (int scriptIdx = 0; scriptIdx < script.length; scriptIdx++) {
|
||||
if (arrayMatch(script, scriptIdx, MERGED_MINING_HEADER)) {
|
||||
// Enforce only one chain merkle root by checking that a single instance of the merged
|
||||
// mining header exists just before.
|
||||
if (pcHead >= 0) {
|
||||
if (throwException) {
|
||||
throw new VerificationException("Multiple merged mining headers in coinbase");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
pcHead = scriptIdx;
|
||||
}
|
||||
if (arrayMatch(script, scriptIdx, vchRootHash)) {
|
||||
pc = scriptIdx;
|
||||
}
|
||||
}
|
||||
|
||||
if (-1 == pcHead) {
|
||||
if (throwException) {
|
||||
throw new VerificationException("MergedMiningHeader missing from parent coinbase");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (-1 == pc) {
|
||||
if (throwException) {
|
||||
throw new VerificationException("Aux POW missing chain merkle root in parent coinbase");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pcHead + MERGED_MINING_HEADER.length != pc) {
|
||||
if (throwException) {
|
||||
throw new VerificationException("Merged mining header is not just before chain merkle root");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure we are at a deterministic point in the merkle leaves by hashing
|
||||
// a nonce and our chain ID and comparing to the index.
|
||||
pc += vchRootHash.length;
|
||||
if ((script.length - pc) < 8) {
|
||||
if (throwException) {
|
||||
throw new VerificationException("Aux POW missing chain merkle tree size and nonce in parent coinbase");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] sizeBytes = Utils.reverseBytes(Arrays.copyOfRange(script, pc, pc + 4));
|
||||
int branchSize = ByteBuffer.wrap(sizeBytes).getInt();
|
||||
if (branchSize != (1 << getChainMerkleBranch().size())) {
|
||||
if (throwException) {
|
||||
throw new VerificationException("Aux POW merkle branch size does not match parent coinbase");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] nonceBytes = Utils.reverseBytes(Arrays.copyOfRange(script, pc + 4, pc + 8));
|
||||
int nonce = ByteBuffer.wrap(nonceBytes).getInt();
|
||||
|
||||
// Choose a pseudo-random slot in the chain merkle tree
|
||||
// but have it be fixed for a size/nonce/chain combination.
|
||||
//
|
||||
// This prevents the same work from being used twice for the
|
||||
// same chain while reducing the chance that two chains clash
|
||||
// for the same slot.
|
||||
long rand = nonce;
|
||||
rand = rand * 1103515245 + 12345;
|
||||
rand += ((AltcoinNetworkParameters) params).getChainID();
|
||||
rand = rand * 1103515245 + 12345;
|
||||
|
||||
if (getChainMerkleBranch().getIndex() != (rand % branchSize)) {
|
||||
if (throwException) {
|
||||
throw new VerificationException("Aux POW wrong index");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
BigInteger h = ((AltcoinNetworkParameters) params)
|
||||
.getBlockDifficultyHash(getParentBlockHeader())
|
||||
.toBigInteger();
|
||||
if (h.compareTo(target) > 0) {
|
||||
// Proof of work check failed!
|
||||
if (throwException) {
|
||||
throw new VerificationException("Hash is higher than target: " + getParentBlockHeader().getHashAsString() + " vs "
|
||||
+ target.toString(16));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Transaction getTransaction() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether one array is at a specific offset within the other.
|
||||
*
|
||||
* @param script the longer array to test for containing another array.
|
||||
* @param offset the offset to start at within the larger array.
|
||||
* @param subArray the shorter array to test for presence in the longer array.
|
||||
* @return true if the shorter array is present at the offset, false otherwise.
|
||||
*/
|
||||
private boolean arrayMatch(byte[] script, int offset, byte[] subArray) {
|
||||
for (int matchIdx = 0; matchIdx + offset < script.length && matchIdx < subArray.length; matchIdx++) {
|
||||
if (script[offset + matchIdx] != subArray[matchIdx]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +0,0 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package org.bitcoinj.core;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jrn
|
||||
*/
|
||||
public interface AuxPoWNetworkParameters {
|
||||
|
||||
public boolean isAuxPoWBlockVersion(long version);
|
||||
|
||||
}
|
@ -35,6 +35,9 @@ import java.util.List;
|
||||
* up to its root, plus a bitset used to define how the hashes are applied.
|
||||
* Given the hash of the leaf, this can be used to calculate the tree
|
||||
* root. This is useful for proving that a leaf belongs to a given tree.
|
||||
*
|
||||
* TODO: Has a lot of similarity to PartialMerkleTree, should attempt to merge
|
||||
* the two.
|
||||
*/
|
||||
public class MerkleBranch extends ChildMessage implements Serializable {
|
||||
private static final long serialVersionUID = 2;
|
||||
@ -46,15 +49,15 @@ public class MerkleBranch extends ChildMessage implements Serializable {
|
||||
// can properly keep track of optimal encoded size
|
||||
private transient int optimalEncodingMessageSize;
|
||||
|
||||
private List<Sha256Hash> branchHashes;
|
||||
private long branchSideMask;
|
||||
private List<Sha256Hash> branchHashes;
|
||||
private long index;
|
||||
|
||||
public MerkleBranch(NetworkParameters params, @Nullable ChildMessage parent) {
|
||||
super(params);
|
||||
setParent(parent);
|
||||
|
||||
this.branchHashes = new ArrayList<Sha256Hash>();
|
||||
this.branchSideMask = 0;
|
||||
this.index = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,17 +73,13 @@ public class MerkleBranch extends ChildMessage implements Serializable {
|
||||
* @param params NetworkParameters object.
|
||||
* @param payload Bitcoin protocol formatted byte array containing message content.
|
||||
* @param offset The location of the first payload byte within the array.
|
||||
* @param parseLazy Whether to perform a full parse immediately or delay until a read is requested.
|
||||
* @param parseRetain Whether to retain the backing byte array for quick reserialization.
|
||||
* If true and the backing byte array is invalidated due to modification of a field then
|
||||
* the cached bytes may be repopulated and retained if the message is serialized again in the future.
|
||||
* as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
|
||||
* @param serializer the serializer to use for this message.
|
||||
* @throws ProtocolException
|
||||
*/
|
||||
public MerkleBranch(NetworkParameters params, ChildMessage parent, byte[] payload, int offset,
|
||||
boolean parseLazy, boolean parseRetain)
|
||||
MessageSerializer serializer)
|
||||
throws ProtocolException {
|
||||
super(params, payload, offset, parent, parseLazy, parseRetain, UNKNOWN_LENGTH);
|
||||
super(params, payload, offset, parent, serializer, UNKNOWN_LENGTH);
|
||||
}
|
||||
|
||||
public MerkleBranch(NetworkParameters params, @Nullable ChildMessage parent,
|
||||
@ -89,7 +88,7 @@ public class MerkleBranch extends ChildMessage implements Serializable {
|
||||
setParent(parent);
|
||||
|
||||
this.branchHashes = hashes;
|
||||
this.branchSideMask = branchSideMask;
|
||||
this.index = branchSideMask;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -118,7 +117,7 @@ public class MerkleBranch extends ChildMessage implements Serializable {
|
||||
branchHashes.add(readHash());
|
||||
}
|
||||
optimalEncodingMessageSize += 32 * hashCount;
|
||||
branchSideMask = readUint32();
|
||||
index = readUint32();
|
||||
optimalEncodingMessageSize += 4;
|
||||
}
|
||||
|
||||
@ -128,7 +127,7 @@ public class MerkleBranch extends ChildMessage implements Serializable {
|
||||
for (Sha256Hash hash: branchHashes) {
|
||||
stream.write(Utils.reverseBytes(hash.getBytes()));
|
||||
}
|
||||
Utils.uint32ToByteStreamLE(branchSideMask, stream);
|
||||
Utils.uint32ToByteStreamLE(index, stream);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -137,7 +136,7 @@ public class MerkleBranch extends ChildMessage implements Serializable {
|
||||
*/
|
||||
public Sha256Hash calculateMerkleRoot(final Sha256Hash leaf) {
|
||||
byte[] target = reverseBytes(leaf.getBytes());
|
||||
long mask = branchSideMask;
|
||||
long mask = index;
|
||||
|
||||
for (Sha256Hash hash: branchHashes) {
|
||||
target = (mask & 1) == 0
|
||||
@ -155,10 +154,19 @@ public class MerkleBranch extends ChildMessage implements Serializable {
|
||||
return Collections.unmodifiableList(this.branchHashes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mask used to determine which side the hashes are applied on.
|
||||
* Each bit represents a hash. Zero means it goes on the right, one means
|
||||
* on the left.
|
||||
*/
|
||||
public long getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of hashes in this branch.
|
||||
*/
|
||||
public int getSize() {
|
||||
public int size() {
|
||||
return branchHashes.size();
|
||||
}
|
||||
|
||||
@ -207,8 +215,8 @@ public class MerkleBranch extends ChildMessage implements Serializable {
|
||||
|
||||
MerkleBranch input = (MerkleBranch) o;
|
||||
|
||||
if (!branchHashes.equals(input.branchHashes)) return false;
|
||||
if (branchSideMask != input.branchSideMask) return false;
|
||||
if (!branchHashes.equals(input.branchHashes)) return false;
|
||||
if (index != input.index) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -217,7 +225,7 @@ public class MerkleBranch extends ChildMessage implements Serializable {
|
||||
public int hashCode() {
|
||||
int result = 1;
|
||||
result = 31 * result + branchHashes.hashCode();
|
||||
result = 31 * result + (int) branchSideMask;
|
||||
result = 31 * result + (int) index;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.bitcoinj.core;
|
||||
|
||||
import org.bitcoinj.params.TestNet3Params;
|
||||
import java.math.BigInteger;
|
||||
import org.altcoinj.params.DogecoinTestNet3Params;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.bitcoinj.core.Util.getBytes;
|
||||
@ -12,7 +13,7 @@ import static org.junit.Assert.assertEquals;
|
||||
* AuxPoW header parsing/serialization and validation
|
||||
*/
|
||||
public class AuxPoWTest {
|
||||
static final NetworkParameters params = TestNet3Params.get();
|
||||
static final NetworkParameters params = DogecoinTestNet3Params.get();
|
||||
|
||||
/**
|
||||
* Parse the AuxPoW header from Dogecoin block #403,931.
|
||||
@ -20,13 +21,13 @@ public class AuxPoWTest {
|
||||
@Test
|
||||
public void parseAuxPoWHeader() throws Exception {
|
||||
byte[] auxpowAsBytes = getBytes(getClass().getResourceAsStream("auxpow_header.bin"));
|
||||
AuxPoW auxpow = new AuxPoW(params, auxpowAsBytes, (ChildMessage) null, false, false);
|
||||
AuxPoW auxpow = new AuxPoW(params, auxpowAsBytes, (ChildMessage) null, params.getDefaultSerializer());
|
||||
MerkleBranch branch = auxpow.getCoinbaseBranch();
|
||||
Sha256Hash expected = new Sha256Hash("089b911f5e471c0e1800f3384281ebec5b372fbb6f358790a92747ade271ccdf");
|
||||
|
||||
assertEquals(expected, auxpow.getCoinbase().getHash());
|
||||
assertEquals(3, auxpow.getCoinbaseBranch().getSize());
|
||||
assertEquals(6, auxpow.getBlockchainBranch().getSize());
|
||||
assertEquals(3, auxpow.getCoinbaseBranch().size());
|
||||
assertEquals(6, auxpow.getChainMerkleBranch().size());
|
||||
|
||||
expected = new Sha256Hash("a22a9b01671d639fa6389f62ecf8ce69204c8ed41d5f1a745e0c5ba7116d5b4c");
|
||||
assertEquals(expected, auxpow.getParentBlockHeader().getHash());
|
||||
@ -38,10 +39,21 @@ public class AuxPoWTest {
|
||||
@Test
|
||||
public void serializeAuxPoWHeader() throws Exception {
|
||||
byte[] auxpowAsBytes = getBytes(getClass().getResourceAsStream("auxpow_header.bin"));
|
||||
AuxPoW auxpow = new AuxPoW(params, auxpowAsBytes, (ChildMessage) null, false, false);
|
||||
AuxPoW auxpow = new AuxPoW(params, auxpowAsBytes, (ChildMessage) null, params.getDefaultSerializer());
|
||||
byte[] expected = auxpowAsBytes;
|
||||
byte[] actual = auxpow.bitcoinSerialize();
|
||||
|
||||
assertArrayEquals(expected, actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the AuxPoW header from Dogecoin block #403,931.
|
||||
*/
|
||||
@Test
|
||||
public void checkAuxPoWHeader() throws Exception {
|
||||
byte[] auxpowAsBytes = getBytes(getClass().getResourceAsStream("auxpow_header.bin"));
|
||||
AuxPoW auxpow = new AuxPoW(params, auxpowAsBytes, (ChildMessage) null, params.getDefaultSerializer());
|
||||
auxpow.checkProofOfWork(new Sha256Hash("0c836b86991631d34a8a68054e2f62db919b39d1ee43c27ab3344d6aa82fa609"),
|
||||
Utils.decodeCompactBits(0x1b06f8f0), true);
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,8 @@ public class DogecoinBlockTest {
|
||||
@Test
|
||||
public void shouldParseBlock1() throws IOException {
|
||||
byte[] payload = Util.getBytes(getClass().getResourceAsStream("dogecoin_block1.bin"));
|
||||
final AltcoinBlock block = new AltcoinBlock(params, payload);
|
||||
AltcoinSerializer serializer = (AltcoinSerializer)params.getDefaultSerializer();
|
||||
final AltcoinBlock block = (AltcoinBlock)serializer.makeBlock(payload);
|
||||
assertEquals("82bc68038f6034c0596b6e313729793a887fded6e92a31fbdf70863f89d9bea2", block.getHashAsString());
|
||||
assertEquals(1, block.getTransactions().size());
|
||||
}
|
||||
@ -41,7 +42,8 @@ public class DogecoinBlockTest {
|
||||
@Test
|
||||
public void shouldParseBlock250000() throws IOException {
|
||||
byte[] payload = Util.getBytes(getClass().getResourceAsStream("dogecoin_block250000.bin"));
|
||||
final AltcoinBlock block = new AltcoinBlock(params, payload);
|
||||
AltcoinSerializer serializer = (AltcoinSerializer)params.getDefaultSerializer();
|
||||
final AltcoinBlock block = (AltcoinBlock)serializer.makeBlock(payload);
|
||||
assertEquals(2469341065L, block.getNonce());
|
||||
final AuxPoW auxpow = block.getAuxPoW();
|
||||
assertNull(auxpow);
|
||||
@ -58,7 +60,8 @@ public class DogecoinBlockTest {
|
||||
@Test
|
||||
public void shouldParseBlock371337() throws IOException {
|
||||
byte[] payload = Util.getBytes(getClass().getResourceAsStream("dogecoin_block371337.bin"));
|
||||
final AltcoinBlock block = new AltcoinBlock(params, payload);
|
||||
AltcoinSerializer serializer = (AltcoinSerializer)params.getDefaultSerializer();
|
||||
final AltcoinBlock block = (AltcoinBlock)serializer.makeBlock(payload);
|
||||
assertEquals("60323982f9c5ff1b5a954eac9dc1269352835f47c2c5222691d80f0d50dcf053", block.getHashAsString());
|
||||
assertEquals(0, block.getNonce());
|
||||
final AuxPoW auxpow = block.getAuxPoW();
|
||||
@ -69,14 +72,14 @@ public class DogecoinBlockTest {
|
||||
assertEquals("45df41e40aba5b2a03d08bd1202a1c02ef3954d8aa22ea6c5ae62fd00f290ea9", parentBlock.getHashAsString());
|
||||
assertNull(parentBlock.getTransactions());
|
||||
|
||||
final MerkleBranch blockchainMerkleBranch = auxpow.getBlockchainBranch();
|
||||
final MerkleBranch blockchainMerkleBranch = auxpow.getChainMerkleBranch();
|
||||
Sha256Hash[] expected = new Sha256Hash[] {
|
||||
new Sha256Hash("b541c848bc001d07d2bdf8643abab61d2c6ae50d5b2495815339a4b30703a46f"),
|
||||
new Sha256Hash("78d6abe48cee514cf3496f4042039acb7e27616dcfc5de926ff0d6c7e5987be7"),
|
||||
new Sha256Hash("a0469413ce64d67c43902d54ee3a380eff12ded22ca11cbd3842e15d48298103")
|
||||
};
|
||||
|
||||
assertArrayEquals(expected, blockchainMerkleBranch.getHashes().toArray(new Sha256Hash[blockchainMerkleBranch.getSize()]));
|
||||
assertArrayEquals(expected, blockchainMerkleBranch.getHashes().toArray(new Sha256Hash[blockchainMerkleBranch.size()]));
|
||||
|
||||
final MerkleBranch coinbaseMerkleBranch = auxpow.getCoinbaseBranch();
|
||||
expected = new Sha256Hash[] {
|
||||
@ -84,7 +87,7 @@ public class DogecoinBlockTest {
|
||||
new Sha256Hash("48f9e8fef3411944e27f49ec804462c9e124dca0954c71c8560e8a9dd218a452"),
|
||||
new Sha256Hash("d11293660392e7c51f69477a6130237c72ecee2d0c1d3dc815841734c370331a")
|
||||
};
|
||||
assertArrayEquals(expected, coinbaseMerkleBranch.getHashes().toArray(new Sha256Hash[coinbaseMerkleBranch.getSize()]));
|
||||
assertArrayEquals(expected, coinbaseMerkleBranch.getHashes().toArray(new Sha256Hash[coinbaseMerkleBranch.size()]));
|
||||
|
||||
assertEquals(6, block.getTransactions().size());
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.bitcoinj.core;
|
||||
|
||||
|
||||
import static org.bitcoinj.core.AuxPoWTest.params;
|
||||
import org.bitcoinj.params.TestNet3Params;
|
||||
import org.junit.Test;
|
||||
|
||||
@ -28,7 +29,7 @@ public class MerkleBranchTest {
|
||||
new Sha256Hash("d8c6fe42ca25076159cd121a5e20c48c1bc53ab90730083e44a334566ea6bbcb")
|
||||
};
|
||||
|
||||
assertArrayEquals(expected, branch.getHashes().toArray(new Sha256Hash[branch.getSize()]));
|
||||
assertArrayEquals(expected, branch.getHashes().toArray(new Sha256Hash[branch.size()]));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -39,7 +40,7 @@ public class MerkleBranchTest {
|
||||
public void serializeMerkleBranch() throws Exception {
|
||||
byte[] expected = getBytes(getClass().getResourceAsStream("auxpow_merkle_branch.bin"));
|
||||
MerkleBranch branch = new MerkleBranch(params, (ChildMessage) null, expected, 0,
|
||||
false, false);
|
||||
params.getDefaultSerializer());
|
||||
byte[] actual = branch.bitcoinSerialize();
|
||||
|
||||
assertArrayEquals(expected, actual);
|
||||
|
Loading…
x
Reference in New Issue
Block a user