3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-01-30 23:02:15 +00:00

Modify version handling to match Dogecoin 1.10 style API

Calling getVersion() on an AuxPoW block now returns the lowest 8 bits
only, and a new getRawVersion() method is introduced to support fetching
full version.
Added unit tests to check unpacking of AuxPoW version numbers.
This commit is contained in:
Ross Nicoll 2015-08-23 13:10:09 +01:00
parent 98ae05b0a1
commit a2ba8da774
8 changed files with 144 additions and 45 deletions

View File

@ -26,12 +26,14 @@ import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.util.BitSet;
import java.util.List;
import org.libdohj.core.ScryptHash;
import static org.libdohj.core.Utils.scryptDigest;
import static org.bitcoinj.core.Utils.reverseBytes;
import org.libdohj.core.AuxPoWNetworkParameters;
/**
* <p>A block is a group of transactions, and is one of the fundamental data structures of the Bitcoin system.
@ -44,17 +46,18 @@ import static org.bitcoinj.core.Utils.reverseBytes;
* specifically using {@link Peer#getBlock(Sha256Hash)}, or grab one from a downloaded {@link BlockChain}.
*/
public class AltcoinBlock extends org.bitcoinj.core.Block {
/** Bit used to indicate that a block contains an AuxPoW section, where the network supports AuxPoW */
public static final int BLOCK_VERSION_CHAIN_START = (1 << 16);
public static final int BLOCK_VERSION_CHAIN_END = (1 << 30);
public static final int BLOCK_VERSION_AUXPOW = (1 << 8);
private boolean auxpowParsed = false;
private boolean auxpowBytesValid = false;
/** AuxPoW header element, if applicable. */
@Nullable private AuxPoW auxpow;
/**
* Whether the chain this block belongs to support AuxPoW, used to avoid
* repeated instanceof checks. Initialised in parseTransactions()
*/
private boolean auxpowChain = false;
private ScryptHash scryptHash;
/** Special case constructor, used for the genesis node, cloneAsHeader and unit tests. */
@ -115,7 +118,6 @@ public class AltcoinBlock extends org.bitcoinj.core.Block {
}
public AuxPoW getAuxPoW() {
// TODO: maybeParseAuxPoW();
return this.auxpow;
}
@ -136,14 +138,65 @@ public class AltcoinBlock extends org.bitcoinj.core.Block {
return getScryptHash().toString();
}
/**
* Get the chain ID (upper 16 bits) from an AuxPoW version number.
*/
public static long getChainID(final long rawVersion) {
return rawVersion >> 16;
}
/**
* Return chain ID from block version of an AuxPoW-enabled chain.
*/
public long getChainID() {
return getChainID(this.getRawVersion());
}
/**
* Return flags from block version of an AuxPoW-enabled chain.
*
* @return flags as a bitset.
*/
public BitSet getVersionFlags() {
return BitSet.valueOf(new long[] {(this.getRawVersion() & 0xff00) >> 8});
}
/**
* Return block version without applying any filtering (i.e. for AuxPoW blocks
* which structure version differently to pack in additional data).
*/
public final long getRawVersion() {
return super.getVersion();
}
/**
* Get the base version (i.e. Bitcoin-like version number) out of a packed
* AuxPoW version number (i.e. one that contains chain ID and feature flags).
*/
public static long getBaseVersion(final long rawVersion) {
return rawVersion & 0xff;
}
@Override
public long getVersion() {
// TODO: Can we cache the individual parts on parse?
if (this.params instanceof AltcoinNetworkParameters) {
// AuxPoW networks use the higher block version bits for flags and
// chain ID.
return getBaseVersion(super.getVersion());
} else {
return super.getVersion();
}
}
protected void parseAuxPoW() throws ProtocolException {
if (this.auxpowParsed)
return;
this.auxpow = null;
if (this.params instanceof AltcoinNetworkParameters) {
final AltcoinNetworkParameters altcoinParams = (AltcoinNetworkParameters)this.params;
if (altcoinParams.isAuxPoWBlockVersion(this.getVersion())) {
if (this.auxpowChain) {
final AuxPoWNetworkParameters auxpowParams = (AuxPoWNetworkParameters)this.params;
if (auxpowParams.isAuxPoWBlockVersion(this.getRawVersion())) {
// 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, serializer);
@ -157,6 +210,7 @@ public class AltcoinBlock extends org.bitcoinj.core.Block {
@Override
protected void parseTransactions(final int offset) {
this.auxpowChain = params instanceof AuxPoWNetworkParameters;
parseAuxPoW();
if (null != this.auxpow) {
super.parseTransactions(offset + auxpow.getMessageSize());
@ -176,7 +230,7 @@ public class AltcoinBlock extends org.bitcoinj.core.Block {
/** Returns a copy of the block, but without any transactions. */
@Override
public Block cloneAsHeader() {
AltcoinBlock block = new AltcoinBlock(params, getVersion());
AltcoinBlock block = new AltcoinBlock(params, getRawVersion());
super.copyBitcoinHeaderTo(block);
block.auxpow = auxpow;
return block;
@ -187,8 +241,8 @@ public class AltcoinBlock extends org.bitcoinj.core.Block {
if (params instanceof AltcoinNetworkParameters) {
BigInteger target = getDifficultyTargetAsInteger();
final AltcoinNetworkParameters altParams = (AltcoinNetworkParameters)auxpow;
if (altParams.isAuxPoWBlockVersion(getVersion()) && null != auxpow) {
final AuxPoWNetworkParameters altParams = (AuxPoWNetworkParameters)auxpow;
if (altParams.isAuxPoWBlockVersion(getRawVersion()) && null != auxpow) {
return auxpow.checkProofOfWork(this.getHash(), target, throwException);
}

View File

@ -18,7 +18,7 @@
package org.bitcoinj.core;
import org.libdohj.core.AltcoinNetworkParameters;
import org.libdohj.core.AuxPoWNetworkParameters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -239,14 +239,14 @@ public class AuxPoW extends ChildMessage {
*/
protected boolean checkProofOfWork(Sha256Hash hashAuxBlock,
BigInteger target, boolean throwException) throws VerificationException {
if (!(params instanceof AltcoinNetworkParameters)) {
if (!(params instanceof AuxPoWNetworkParameters)) {
if (throwException) {
// Should be impossible
throw new VerificationException("Network parameters are not an instance of AltcoinNetworkParameters, AuxPoW support is not available.");
throw new VerificationException("Network parameters are not an instance of AuxPoWNetworkParameters, AuxPoW support is not available.");
}
return false;
}
final AltcoinNetworkParameters altcoinParams = (AltcoinNetworkParameters) params;
final AuxPoWNetworkParameters altcoinParams = (AuxPoWNetworkParameters) params;
if (0 != this.getCoinbaseBranch().getIndex()) {
if (throwException) {
@ -257,7 +257,7 @@ public class AuxPoW extends ChildMessage {
}
if (!altcoinParams.isTestNet()
&& getChainID(parentBlockHeader) == altcoinParams.getChainID()) {
&& parentBlockHeader.getChainID() == altcoinParams.getChainID()) {
if (throwException) {
throw new VerificationException("Aux POW parent has our chain ID");
}
@ -362,7 +362,7 @@ public class AuxPoW extends ChildMessage {
// for the same slot.
long rand = nonce;
rand = rand * 1103515245 + 12345;
rand += ((AltcoinNetworkParameters) params).getChainID();
rand += ((AuxPoWNetworkParameters) params).getChainID();
rand = rand * 1103515245 + 12345;
if (getChainMerkleBranch().getIndex() != (rand % branchSize)) {
@ -407,13 +407,6 @@ public class AuxPoW extends ChildMessage {
return true;
}
/**
* Get the chain ID from a block header.
*/
public static long getChainID(final Block blockHeader) {
return blockHeader.getVersion() / AltcoinBlock.BLOCK_VERSION_CHAIN_START;
}
/**
* Set the merkle branch used to connect the coinbase transaction to the
* parent block header.

View File

@ -16,7 +16,7 @@ public class ConvertAddress {
public static void main(final String[] argv) throws AddressFormatException {
final NetworkParameters mainParams = MainNetParams.get();
final NetworkParameters dogeParams = DogecoinMainNetParams.get();
final Address address = Address.fromBase58(mainParams, "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L");
final Address address = Address.fromBase58(mainParams, "175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W");
final Address newAddress = new Address(dogeParams, 30, address.getHash160());
System.out.println(newAddress.toBase58());

View File

@ -20,14 +20,9 @@ import org.bitcoinj.core.Sha256Hash;
/**
*
* @author jrn
* @author Ross Nicoll
*/
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

View File

@ -0,0 +1,27 @@
/*
* 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.libdohj.core;
/**
*
* @author Ross Nicoll
*/
public interface AuxPoWNetworkParameters extends AltcoinNetworkParameters {
boolean isAuxPoWBlockVersion(long version);
int getChainID();
}

View File

@ -35,18 +35,19 @@ import org.bitcoinj.utils.MonetaryFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.libdohj.core.AltcoinSerializer;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.StoredBlock;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionInput;
import org.bitcoinj.core.TransactionOutput;
import org.bitcoinj.core.Utils;
import org.libdohj.core.AltcoinSerializer;
import org.libdohj.core.AuxPoWNetworkParameters;
/**
* Parameters for the main Dogecoin production network on which people trade goods and services.
*/
public abstract class AbstractDogecoinParams extends NetworkParameters implements AltcoinNetworkParameters {
public abstract class AbstractDogecoinParams extends NetworkParameters implements AuxPoWNetworkParameters {
/** Standard format for the DOGE denomination. */
public static final MonetaryFormat DOGE;
/** Standard format for the mDOGE denomination. */
@ -69,9 +70,8 @@ public abstract class AbstractDogecoinParams extends NetworkParameters implement
/** Currency code for base 1/100,000,000 Dogecoin. */
public static final String CODE_KOINU = "Koinu";
public static final int BLOCK_VERSION_DEFAULT = 0x00000002;
public static final int BLOCK_VERSION_AUXPOW = 0x00620002;
public static final int BLOCK_VERSION_FLAG_AUXPOW = 0x00000100;
private static final int BLOCK_MIN_VERSION_AUXPOW = 0x00620002;
private static final int BLOCK_VERSION_FLAG_AUXPOW = 0x00000100;
static {
DOGE = MonetaryFormat.BTC.noCode()
@ -311,7 +311,7 @@ public abstract class AbstractDogecoinParams extends NetworkParameters implement
@Override
public boolean isAuxPoWBlockVersion(long version) {
return version >= BLOCK_VERSION_AUXPOW
return version >= BLOCK_MIN_VERSION_AUXPOW
&& (version & BLOCK_VERSION_FLAG_AUXPOW) > 0;
}
}

View File

@ -2,17 +2,20 @@ package org.bitcoinj.core;
import java.util.Arrays;
import java.util.Collections;
import org.libdohj.core.AltcoinSerializer;
import org.libdohj.core.AuxPoWNetworkParameters;
import org.libdohj.params.DogecoinMainNetParams;
import org.junit.Test;
import static org.bitcoinj.core.Util.getBytes;
import static org.bitcoinj.core.Utils.reverseBytes;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
/**
@ -97,7 +100,9 @@ public class AuxPoWTest {
byte[] payload = Util.getBytes(getClass().getResourceAsStream("dogecoin_block371337.bin"));
AltcoinSerializer serializer = (AltcoinSerializer)params.getDefaultSerializer();
final AltcoinBlock block = (AltcoinBlock)serializer.makeBlock(payload);
assertEquals(98, block.getChainID());
final AuxPoW auxpow = block.getAuxPoW();
assertNotNull(auxpow);
auxpow.setParentBlockHeader((AltcoinBlock)block.cloneAsHeader());
expectedEx.expect(org.bitcoinj.core.VerificationException.class);
expectedEx.expectMessage("Aux POW parent has our chain ID");

View File

@ -12,6 +12,7 @@ import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
@ -27,6 +28,24 @@ public class DogecoinBlockTest {
Context context = new Context(params);
}
@Test
public void shouldExtractChainID() {
final long baseVersion = 2;
final long flags = 1;
final long chainID = 98;
final long auxpowVersion = (chainID << 16) | (flags << 8) | baseVersion;
assertEquals(chainID, AltcoinBlock.getChainID(auxpowVersion));
}
@Test
public void shouldExtractBaseVersion() {
final long baseVersion = 2;
final long flags = 1;
final long chainID = 98;
final long auxpowVersion = (chainID << 16) | (flags << 8) | baseVersion;
assertEquals(baseVersion, AltcoinBlock.getBaseVersion(auxpowVersion));
}
@Test
public void shouldParseBlock1() throws IOException {
byte[] payload = Util.getBytes(getClass().getResourceAsStream("dogecoin_block1.bin"));
@ -65,6 +84,12 @@ public class DogecoinBlockTest {
final AltcoinBlock block = (AltcoinBlock)serializer.makeBlock(payload);
assertEquals("60323982f9c5ff1b5a954eac9dc1269352835f47c2c5222691d80f0d50dcf053", block.getHashAsString());
assertEquals(0, block.getNonce());
// Check block version values
assertEquals(2, block.getVersion());
assertEquals(98, block.getChainID());
assertTrue(block.getVersionFlags().get(0));
final AuxPoW auxpow = block.getAuxPoW();
assertNotNull(auxpow);
final Transaction auxpowCoinbase = auxpow.getCoinbase();
@ -75,18 +100,18 @@ public class DogecoinBlockTest {
final MerkleBranch blockchainMerkleBranch = auxpow.getChainMerkleBranch();
Sha256Hash[] expected = new Sha256Hash[] {
new Sha256Hash("b541c848bc001d07d2bdf8643abab61d2c6ae50d5b2495815339a4b30703a46f"),
new Sha256Hash("78d6abe48cee514cf3496f4042039acb7e27616dcfc5de926ff0d6c7e5987be7"),
new Sha256Hash("a0469413ce64d67c43902d54ee3a380eff12ded22ca11cbd3842e15d48298103")
Sha256Hash.wrap("b541c848bc001d07d2bdf8643abab61d2c6ae50d5b2495815339a4b30703a46f"),
Sha256Hash.wrap("78d6abe48cee514cf3496f4042039acb7e27616dcfc5de926ff0d6c7e5987be7"),
Sha256Hash.wrap("a0469413ce64d67c43902d54ee3a380eff12ded22ca11cbd3842e15d48298103")
};
assertArrayEquals(expected, blockchainMerkleBranch.getHashes().toArray(new Sha256Hash[blockchainMerkleBranch.size()]));
final MerkleBranch coinbaseMerkleBranch = auxpow.getCoinbaseBranch();
expected = new Sha256Hash[] {
new Sha256Hash("cd3947cd5a0c26fde01b05a3aa3d7a38717be6ae11d27239365024db36a679a9"),
new Sha256Hash("48f9e8fef3411944e27f49ec804462c9e124dca0954c71c8560e8a9dd218a452"),
new Sha256Hash("d11293660392e7c51f69477a6130237c72ecee2d0c1d3dc815841734c370331a")
Sha256Hash.wrap("cd3947cd5a0c26fde01b05a3aa3d7a38717be6ae11d27239365024db36a679a9"),
Sha256Hash.wrap("48f9e8fef3411944e27f49ec804462c9e124dca0954c71c8560e8a9dd218a452"),
Sha256Hash.wrap("d11293660392e7c51f69477a6130237c72ecee2d0c1d3dc815841734c370331a")
};
assertArrayEquals(expected, coinbaseMerkleBranch.getHashes().toArray(new Sha256Hash[coinbaseMerkleBranch.size()]));