mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-07-31 12:01:24 +00:00
Step 4: Core changes, including difficulty, auxpow and fee calculation
This commit is contained in:
@@ -834,6 +834,7 @@ public abstract class AbstractBlockChain {
|
||||
|
||||
// February 16th 2012
|
||||
private static final Date testnetDiffDate = new Date(1329264000000L);
|
||||
private static final int testNetRetargetFix = 157500;
|
||||
|
||||
/**
|
||||
* Throws an exception if the blocks difficulty is not correct.
|
||||
@@ -841,9 +842,25 @@ public abstract class AbstractBlockChain {
|
||||
private void checkDifficultyTransitions(StoredBlock storedPrev, Block nextBlock) throws BlockStoreException, VerificationException {
|
||||
checkState(lock.isHeldByCurrentThread());
|
||||
Block prev = storedPrev.getHeader();
|
||||
|
||||
boolean newDiffAlgo = storedPrev.getHeight() + 1 >= params.getDiffChangeTarget();
|
||||
int retargetInterval = params.getInterval();
|
||||
int retargetTimespan = params.getTargetTimespan();
|
||||
if (newDiffAlgo)
|
||||
{
|
||||
retargetInterval = params.getNewInterval();
|
||||
retargetTimespan = params.getNewTargetTimespan();
|
||||
}
|
||||
|
||||
if (params.getId().equals(NetworkParameters.ID_TESTNET)
|
||||
&& storedPrev.getHeight() >= testNetRetargetFix
|
||||
&& nextBlock.getTimeSeconds() > storedPrev.getHeader().getTimeSeconds() + retargetTimespan * 2 ) {
|
||||
checkTestnetDifficulty(storedPrev, prev, nextBlock);
|
||||
return;
|
||||
}
|
||||
|
||||
// Is this supposed to be a difficulty transition point?
|
||||
if ((storedPrev.getHeight() + 1) % params.getInterval() != 0) {
|
||||
if ((storedPrev.getHeight() + 1) % retargetInterval != 0) {
|
||||
|
||||
// TODO: Refactor this hack after 0.5 is released and we stop supporting deserialization compatibility.
|
||||
// This should be a method of the NetworkParameters, which should in turn be using singletons and a subclass
|
||||
@@ -865,7 +882,11 @@ public abstract class AbstractBlockChain {
|
||||
// two weeks after the initial block chain download.
|
||||
long now = System.currentTimeMillis();
|
||||
StoredBlock cursor = blockStore.get(prev.getHash());
|
||||
for (int i = 0; i < params.getInterval() - 1; i++) {
|
||||
int goBack = retargetInterval - 1;
|
||||
if (cursor.getHeight()+1 != retargetInterval)
|
||||
goBack = retargetInterval;
|
||||
|
||||
for (int i = 0; i < goBack; i++) {
|
||||
if (cursor == null) {
|
||||
// This should never happen. If it does, it means we are following an incorrect or busted chain.
|
||||
throw new VerificationException(
|
||||
@@ -873,18 +894,50 @@ public abstract class AbstractBlockChain {
|
||||
}
|
||||
cursor = blockStore.get(cursor.getHeader().getPrevBlockHash());
|
||||
}
|
||||
|
||||
//We used checkpoints...
|
||||
if(cursor == null)
|
||||
{
|
||||
log.debug("Difficulty transition: Hit checkpoint!");
|
||||
return;
|
||||
}
|
||||
|
||||
long elapsed = System.currentTimeMillis() - now;
|
||||
if (elapsed > 50)
|
||||
log.info("Difficulty transition traversal took {}msec", elapsed);
|
||||
|
||||
Block blockIntervalAgo = cursor.getHeader();
|
||||
int timespan = (int) (prev.getTimeSeconds() - blockIntervalAgo.getTimeSeconds());
|
||||
final int targetTimespan = retargetTimespan;
|
||||
|
||||
if (newDiffAlgo)
|
||||
{
|
||||
timespan = retargetTimespan + (timespan - retargetTimespan)/8;
|
||||
if (timespan < (retargetTimespan - (retargetTimespan/4)) ) timespan = (retargetTimespan - (retargetTimespan/4));
|
||||
if (timespan > (retargetTimespan + (retargetTimespan/2)) ) timespan = (retargetTimespan + (retargetTimespan/2));
|
||||
}
|
||||
// Limit the adjustment step.
|
||||
final int targetTimespan = params.getTargetTimespan();
|
||||
if (timespan < targetTimespan / 4)
|
||||
timespan = targetTimespan / 4;
|
||||
if (timespan > targetTimespan * 4)
|
||||
timespan = targetTimespan * 4;
|
||||
else if (storedPrev.getHeight()+1 > 10000)
|
||||
{
|
||||
if (timespan < targetTimespan / 4)
|
||||
timespan = targetTimespan / 4;
|
||||
if (timespan > targetTimespan * 4)
|
||||
timespan = targetTimespan * 4;
|
||||
}
|
||||
else if (storedPrev.getHeight()+1 > 5000)
|
||||
{
|
||||
if (timespan < targetTimespan / 8)
|
||||
timespan = targetTimespan / 8;
|
||||
if (timespan > targetTimespan * 4)
|
||||
timespan = targetTimespan * 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (timespan < targetTimespan / 16)
|
||||
timespan = targetTimespan / 16;
|
||||
if (timespan > targetTimespan * 4)
|
||||
timespan = targetTimespan * 4;
|
||||
}
|
||||
|
||||
BigInteger newTarget = Utils.decodeCompactBits(prev.getDifficultyTarget());
|
||||
newTarget = newTarget.multiply(BigInteger.valueOf(timespan));
|
||||
|
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* 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 com.dogecoin.dogecoinj.core;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* This is the code to deserialize the AuxPoW header data
|
||||
*/
|
||||
public class AuxPoWMessage extends Message {
|
||||
private static final Logger log = LoggerFactory.getLogger(AuxPoWMessage.class);
|
||||
|
||||
private AuxHeader header;
|
||||
|
||||
public AuxPoWMessage(byte[] payload, int cursor) throws ProtocolException {
|
||||
this.payload = payload;
|
||||
this.cursor = cursor;
|
||||
this.header = new AuxHeader();
|
||||
}
|
||||
|
||||
@Override
|
||||
void parse() throws ProtocolException {
|
||||
header.parentCoinbaseVerion = readUint32();
|
||||
header.parentCoinbaseTxInCount = readVarInt();
|
||||
header.parentCointbasePrevOut = readBytes(36); // Always the same on coinbase
|
||||
header.parentCoinbaseInScriptLength = readVarInt();
|
||||
header.parentCoinbaseInScript = readBytes((int) header.parentCoinbaseInScriptLength); // Script length is limited so this cast should be fine.
|
||||
header.parentCoinBaseSequenceNumber = readUint32();
|
||||
header.parentCoinbaseTxOutCount = readVarInt();
|
||||
header.parentCoinbaseOuts = new ArrayList<AuxCoinbaseOut>();
|
||||
for (int i = 0; i < header.parentCoinbaseTxOutCount; i++) {
|
||||
AuxCoinbaseOut out = new AuxCoinbaseOut();
|
||||
out.amount = readInt64();
|
||||
out.scriptLength = readVarInt();
|
||||
out.script = readBytes((int) out.scriptLength); // Script length is limited so this cast should be fine.
|
||||
header.parentCoinbaseOuts.add(out);
|
||||
}
|
||||
header.parentCoinbaseLockTime = readUint32();
|
||||
|
||||
header.parentBlockHeaderHash = readHash();
|
||||
header.numOfCoinbaseLinks = readVarInt();
|
||||
header.coinbaseLinks = new ArrayList<Sha256Hash>();
|
||||
for (int i = 0; i < header.numOfCoinbaseLinks; i++) {
|
||||
header.coinbaseLinks.add(readHash());
|
||||
}
|
||||
header.coinbaseBranchBitmask = readUint32();
|
||||
|
||||
header.numOfAuxChainLinks = readVarInt();
|
||||
header.auxChainLinks = new ArrayList<Sha256Hash>();
|
||||
for (int i = 0; i < header.numOfAuxChainLinks; i++) {
|
||||
header.auxChainLinks.add(readHash());
|
||||
}
|
||||
header.auxChainBranchBitmask = readUint32();
|
||||
|
||||
header.parentBlockVersion = readUint32();
|
||||
header.parentBlockPrev = readHash();
|
||||
header.parentBlockMerkleRoot = readHash();
|
||||
header.parentBlockTime = readUint32();
|
||||
header.parentBlockBits = readUint32();
|
||||
header.parentBlockNonce = readUint32();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void parseLite() throws ProtocolException {
|
||||
// noop
|
||||
}
|
||||
|
||||
public byte[] constructParentHeader() {
|
||||
|
||||
ByteArrayOutputStream stream = new UnsafeByteArrayOutputStream(Block.HEADER_SIZE);
|
||||
try {
|
||||
Utils.uint32ToByteStreamLE(header.parentBlockVersion, stream);
|
||||
stream.write(Utils.reverseBytes(header.parentBlockPrev.getBytes()));
|
||||
stream.write(Utils.reverseBytes(header.parentBlockMerkleRoot.getBytes()));
|
||||
Utils.uint32ToByteStreamLE(header.parentBlockTime, stream);
|
||||
Utils.uint32ToByteStreamLE(header.parentBlockBits, stream);
|
||||
Utils.uint32ToByteStreamLE(header.parentBlockNonce, stream);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(); // Can't actually happen
|
||||
}
|
||||
return stream.toByteArray();
|
||||
}
|
||||
|
||||
public class AuxHeader {
|
||||
|
||||
// Parent coinbase
|
||||
public long parentCoinbaseVerion;
|
||||
public long parentCoinbaseTxInCount;
|
||||
public byte[] parentCointbasePrevOut;
|
||||
public long parentCoinbaseInScriptLength;
|
||||
public byte[] parentCoinbaseInScript;
|
||||
public long parentCoinBaseSequenceNumber;
|
||||
public long parentCoinbaseTxOutCount;
|
||||
public ArrayList<AuxCoinbaseOut> parentCoinbaseOuts;
|
||||
public long parentCoinbaseLockTime;
|
||||
|
||||
// Coinbase link
|
||||
public Sha256Hash parentBlockHeaderHash;
|
||||
public long numOfCoinbaseLinks;
|
||||
public ArrayList<Sha256Hash> coinbaseLinks;
|
||||
public long coinbaseBranchBitmask;
|
||||
|
||||
// Aux chanin link
|
||||
public long numOfAuxChainLinks;
|
||||
public ArrayList<Sha256Hash> auxChainLinks;
|
||||
public long auxChainBranchBitmask;
|
||||
|
||||
// Parent block header
|
||||
public long parentBlockVersion;
|
||||
public Sha256Hash parentBlockPrev;
|
||||
public Sha256Hash parentBlockMerkleRoot;
|
||||
public long parentBlockTime;
|
||||
public long parentBlockBits;
|
||||
public long parentBlockNonce;
|
||||
}
|
||||
|
||||
public class AuxCoinbaseOut {
|
||||
public long amount;
|
||||
public long scriptLength;
|
||||
public byte[] script;
|
||||
}
|
||||
}
|
@@ -38,6 +38,7 @@ import java.util.List;
|
||||
import static com.dogecoin.dogecoinj.core.Coin.FIFTY_COINS;
|
||||
import static com.dogecoin.dogecoinj.core.Utils.doubleDigest;
|
||||
import static com.dogecoin.dogecoinj.core.Utils.doubleDigestTwoBuffers;
|
||||
import static com.dogecoin.dogecoinj.core.Utils.scryptDigest;
|
||||
|
||||
/**
|
||||
* <p>A block is a group of transactions, and is one of the fundamental data structures of the Bitcoin system.
|
||||
@@ -74,6 +75,10 @@ public class Block extends Message {
|
||||
/** A value for difficultyTarget (nBits) that allows half of all possible hash solutions. Used in unit testing. */
|
||||
public static final long EASIEST_DIFFICULTY_TARGET = 0x207fFFFFL;
|
||||
|
||||
public static final int BLOCK_VERSION_DEFAULT = 0x00000002;
|
||||
public static final int BLOCK_VERSION_AUXPOW = 0x00620002;
|
||||
public static final int BLOCK_VERSION_AUXPOW_AUXBLOCK = 0x00620102;
|
||||
|
||||
// Fields defined as part of the protocol format.
|
||||
private long version;
|
||||
private Sha256Hash prevBlockHash;
|
||||
@@ -82,11 +87,15 @@ public class Block extends Message {
|
||||
private long difficultyTarget; // "nBits"
|
||||
private long nonce;
|
||||
|
||||
// The parent block header for AuxPoW blocks
|
||||
private Block parentBlock;
|
||||
|
||||
/** If null, it means this object holds only the headers. */
|
||||
List<Transaction> transactions;
|
||||
|
||||
/** Stores the hash of the block. If null, getHash() will recalculate it. */
|
||||
private transient Sha256Hash hash;
|
||||
private transient Sha256Hash scryptHash;
|
||||
|
||||
private transient boolean headerParsed;
|
||||
private transient boolean transactionsParsed;
|
||||
@@ -116,6 +125,12 @@ public class Block extends Message {
|
||||
super(params, payloadBytes, 0, false, false, payloadBytes.length);
|
||||
}
|
||||
|
||||
/** Constructs a block object from the Bitcoin wire format. */
|
||||
public Block(NetworkParameters params, byte[] payloadBytes, Block parentBlock) throws ProtocolException {
|
||||
super(params, payloadBytes, 0, false, false, payloadBytes.length);
|
||||
this.parentBlock = parentBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contruct a block object from the Bitcoin wire format.
|
||||
* @param params NetworkParameters object.
|
||||
@@ -131,7 +146,11 @@ public class Block extends Message {
|
||||
throws ProtocolException {
|
||||
super(params, payloadBytes, 0, parseLazy, parseRetain, length);
|
||||
}
|
||||
|
||||
public Block(NetworkParameters params, byte[] payloadBytes, boolean parseLazy, boolean parseRetain, int length, Block parentBlock)
|
||||
throws ProtocolException {
|
||||
super(params, payloadBytes, 0, parseLazy, parseRetain, length);
|
||||
this.parentBlock = parentBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a block initialized with all the given fields.
|
||||
@@ -196,11 +215,20 @@ public class Block extends Message {
|
||||
headerBytesValid = parseRetain;
|
||||
}
|
||||
|
||||
private void parseAuxData() throws ProtocolException {
|
||||
AuxPoWMessage auxPoWMessage = new AuxPoWMessage(payload, cursor);
|
||||
auxPoWMessage.parse();
|
||||
this.cursor = auxPoWMessage.cursor;
|
||||
this.parentBlock = new Block(params, auxPoWMessage.constructParentHeader());
|
||||
}
|
||||
|
||||
private void parseTransactions() throws ProtocolException {
|
||||
if (transactionsParsed)
|
||||
return;
|
||||
|
||||
cursor = offset + HEADER_SIZE;
|
||||
if (version != BLOCK_VERSION_AUXPOW_AUXBLOCK) {
|
||||
cursor = offset + HEADER_SIZE;
|
||||
}
|
||||
optimalEncodingMessageSize = HEADER_SIZE;
|
||||
if (payload.length == cursor) {
|
||||
// This message is just a header, it has no transactions.
|
||||
@@ -229,6 +257,9 @@ public class Block extends Message {
|
||||
@Override
|
||||
void parse() throws ProtocolException {
|
||||
parseHeader();
|
||||
if (version == BLOCK_VERSION_AUXPOW_AUXBLOCK && payload.length >= 160) { // We have at least 2 headers in an Aux block. Workaround for StoredBlocks
|
||||
parseAuxData();
|
||||
}
|
||||
parseTransactions();
|
||||
length = cursor - offset;
|
||||
}
|
||||
@@ -516,6 +547,16 @@ public class Block extends Message {
|
||||
}
|
||||
}
|
||||
|
||||
private Sha256Hash calculateScryptHash() {
|
||||
try {
|
||||
ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(HEADER_SIZE);
|
||||
writeHeader(bos);
|
||||
return new Sha256Hash(Utils.reverseBytes(scryptDigest(bos.toByteArray())));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e); // Cannot happen.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hash of the block (which for a valid, solved block should be below the target) in the form seen on
|
||||
* the block explorer. If you call this on block 1 in the production chain
|
||||
@@ -525,6 +566,10 @@ public class Block extends Message {
|
||||
return getHash().toString();
|
||||
}
|
||||
|
||||
public String getScryptHashAsString() {
|
||||
return getScryptHash().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hash of the block (which for a valid, solved block should be
|
||||
* below the target). Big endian.
|
||||
@@ -536,6 +581,12 @@ public class Block extends Message {
|
||||
return hash;
|
||||
}
|
||||
|
||||
public Sha256Hash getScryptHash() {
|
||||
if (scryptHash == null)
|
||||
scryptHash = calculateScryptHash();
|
||||
return scryptHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number that is one greater than the largest representable SHA-256
|
||||
* hash.
|
||||
@@ -567,6 +618,9 @@ public class Block extends Message {
|
||||
block.difficultyTarget = difficultyTarget;
|
||||
block.transactions = null;
|
||||
block.hash = getHash().duplicate();
|
||||
if (version == BLOCK_VERSION_AUXPOW_AUXBLOCK) {
|
||||
block.parentBlock = parentBlock;
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
@@ -652,7 +706,12 @@ public class Block extends Message {
|
||||
// field is of the right value. This requires us to have the preceeding blocks.
|
||||
BigInteger target = getDifficultyTargetAsInteger();
|
||||
|
||||
BigInteger h = getHash().toBigInteger();
|
||||
BigInteger h;
|
||||
if (this.version == BLOCK_VERSION_AUXPOW_AUXBLOCK) {
|
||||
h = this.parentBlock.getScryptHash().toBigInteger();
|
||||
} else {
|
||||
h = getScryptHash().toBigInteger();
|
||||
}
|
||||
if (h.compareTo(target) > 0) {
|
||||
// Proof of work check failed!
|
||||
if (throwException)
|
||||
|
@@ -26,7 +26,7 @@ import java.util.*;
|
||||
*/
|
||||
public class FilteredBlock extends Message {
|
||||
/** The protocol version at which Bloom filtering started to be supported. */
|
||||
public static final int MIN_PROTOCOL_VERSION = 70000;
|
||||
public static final int MIN_PROTOCOL_VERSION = 70001;
|
||||
private Block header;
|
||||
|
||||
private PartialMerkleTree merkleTree;
|
||||
@@ -57,12 +57,28 @@ public class FilteredBlock extends Message {
|
||||
|
||||
@Override
|
||||
void parse() throws ProtocolException {
|
||||
long blockVersion = Utils.readUint32(payload, cursor);
|
||||
|
||||
byte[] headerBytes = new byte[Block.HEADER_SIZE];
|
||||
System.arraycopy(payload, 0, headerBytes, 0, Block.HEADER_SIZE);
|
||||
header = new Block(params, headerBytes);
|
||||
|
||||
merkleTree = new PartialMerkleTree(params, payload, Block.HEADER_SIZE);
|
||||
|
||||
|
||||
|
||||
if (blockVersion == Block.BLOCK_VERSION_AUXPOW_AUXBLOCK) {
|
||||
AuxPoWMessage auxPoWMessage = new AuxPoWMessage(payload, cursor + Block.HEADER_SIZE);
|
||||
auxPoWMessage.parse();
|
||||
this.cursor = auxPoWMessage.cursor;
|
||||
|
||||
header = new Block(params, headerBytes, new Block(params, auxPoWMessage.constructParentHeader()));
|
||||
|
||||
byte[] filteredBlock = new byte[Block.HEADER_SIZE + payload.length - cursor];
|
||||
System.arraycopy(headerBytes, 0, filteredBlock, 0, headerBytes.length-1);
|
||||
System.arraycopy(payload, cursor, filteredBlock, Block.HEADER_SIZE, payload.length-cursor);
|
||||
merkleTree = new PartialMerkleTree(params, filteredBlock, Block.HEADER_SIZE);
|
||||
} else {
|
||||
header = new Block(params, headerBytes);
|
||||
merkleTree = new PartialMerkleTree(params, payload, Block.HEADER_SIZE);
|
||||
}
|
||||
|
||||
length = Block.HEADER_SIZE + merkleTree.getMessageSize();
|
||||
}
|
||||
|
||||
|
@@ -76,17 +76,38 @@ public class HeadersMessage extends Message {
|
||||
long numHeaders = readVarInt();
|
||||
if (numHeaders > MAX_HEADERS)
|
||||
throw new ProtocolException("Too many headers: got " + numHeaders + " which is larger than " +
|
||||
MAX_HEADERS);
|
||||
MAX_HEADERS);
|
||||
|
||||
blockHeaders = new ArrayList<Block>();
|
||||
|
||||
for (int i = 0; i < numHeaders; ++i) {
|
||||
// Read 80 bytes of the header and one more byte for the transaction list, which is always a 00 because the
|
||||
// transaction list is empty.
|
||||
byte[] blockHeader = readBytes(81);
|
||||
// Read the block version. If it's not an aux block all is fine, else throw out the aux stuff
|
||||
long blockVersion = Utils.readUint32(payload, cursor);
|
||||
byte[] blockHeader;
|
||||
Block newBlockHeader;
|
||||
if (blockVersion == Block.BLOCK_VERSION_AUXPOW_AUXBLOCK) {
|
||||
blockHeader = readBytes(80);
|
||||
byte[] tmp = new byte[81];
|
||||
System.arraycopy(blockHeader, 0, tmp, 0, blockHeader.length);
|
||||
tmp[80] = 0;
|
||||
blockHeader = tmp;
|
||||
|
||||
AuxPoWMessage auxPoWMessage = new AuxPoWMessage(payload, cursor);
|
||||
auxPoWMessage.parse();
|
||||
this.cursor = auxPoWMessage.cursor;
|
||||
|
||||
if (readBytes(1)[0] != 0)
|
||||
throw new ProtocolException("Block header does not end with a null byte");
|
||||
|
||||
newBlockHeader = new Block(this.params, blockHeader, true, true, 81, new Block(params, auxPoWMessage.constructParentHeader()));
|
||||
} else {
|
||||
// Read 80 bytes of the header and one more byte for the transaction list, which is always a 00 because the
|
||||
// transaction list is empty.
|
||||
blockHeader = readBytes(81);
|
||||
newBlockHeader = new Block(this.params, blockHeader, true, true, 81);
|
||||
}
|
||||
if (blockHeader[80] != 0)
|
||||
throw new ProtocolException("Block header does not end with a null byte");
|
||||
Block newBlockHeader = new Block(this.params, blockHeader, true, true, 81);
|
||||
blockHeaders.add(newBlockHeader);
|
||||
}
|
||||
|
||||
|
@@ -43,7 +43,7 @@ public abstract class NetworkParameters implements Serializable {
|
||||
/**
|
||||
* The protocol version this library implements.
|
||||
*/
|
||||
public static final int PROTOCOL_VERSION = 70001;
|
||||
public static final int PROTOCOL_VERSION = 70003;
|
||||
|
||||
/**
|
||||
* The alert signing key originally owned by Satoshi, and now passed on to Gavin along with a few others.
|
||||
|
@@ -125,6 +125,8 @@ public class Peer extends PeerSocketHandler {
|
||||
private final HashSet<Sha256Hash> pendingBlockDownloads = new HashSet<Sha256Hash>();
|
||||
// The lowest version number we're willing to accept. Lower than this will result in an immediate disconnect.
|
||||
private volatile int vMinProtocolVersion = Pong.MIN_PROTOCOL_VERSION;
|
||||
// A string to be checked inside the subversion to distinguis true 70001 nodes from 1.4.2 nodes.
|
||||
private String ACCEPTED_SUBVERSION = "Shibetoshi";
|
||||
// When an API user explicitly requests a block or transaction from a peer, the InventoryItem is put here
|
||||
// whilst waiting for the response. Is not used for downloads Peer generates itself.
|
||||
private static class GetDataRequest {
|
||||
@@ -152,6 +154,10 @@ public class Peer extends PeerSocketHandler {
|
||||
// A future representing the results of doing a getUTXOs call.
|
||||
@Nullable private SettableFuture<UTXOsMessage> utxosFuture;
|
||||
|
||||
// A minimum needed block height to allow the implementation to connect to a peer.
|
||||
// Is usually kept somewhen after a fork.
|
||||
private final long MIN_PEER_BLOCK_HEIGHT = 380000;
|
||||
|
||||
/**
|
||||
* <p>Construct a peer that reads/writes from the given block chain.</p>
|
||||
*
|
||||
@@ -390,6 +396,18 @@ public class Peer extends PeerSocketHandler {
|
||||
vPeerVersionMessage.clientVersion, version);
|
||||
close();
|
||||
}
|
||||
if (vPeerVersionMessage.bestHeight < MIN_PEER_BLOCK_HEIGHT && params.getId().equals(NetworkParameters.ID_MAINNET))
|
||||
{
|
||||
log.warn("Connected to a peer with just {} blocks. Don't accept it.",
|
||||
vPeerVersionMessage.bestHeight);
|
||||
close();
|
||||
}
|
||||
if (!vPeerVersionMessage.subVer.contains(ACCEPTED_SUBVERSION) && params.getId().equals(NetworkParameters.ID_MAINNET))
|
||||
{
|
||||
log.warn("Connected to a peer with subVer {}. Don't accept it.",
|
||||
vPeerVersionMessage.subVer);
|
||||
close();
|
||||
}
|
||||
} else if (m instanceof UTXOsMessage) {
|
||||
if (utxosFuture != null) {
|
||||
SettableFuture<UTXOsMessage> future = utxosFuture;
|
||||
|
@@ -17,6 +17,7 @@
|
||||
|
||||
package com.dogecoin.dogecoinj.core;
|
||||
|
||||
import com.dogecoin.dogecoinj.params.MainNetParams;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -113,7 +114,7 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
||||
// until we reach this count.
|
||||
@GuardedBy("lock") private int maxConnections;
|
||||
// Minimum protocol version we will allow ourselves to connect to: require Bloom filtering.
|
||||
private volatile int vMinRequiredProtocolVersion = FilteredBlock.MIN_PROTOCOL_VERSION;
|
||||
private volatile int vMinRequiredProtocolVersion = MainNetParams.PROTOCOL_VERSION;
|
||||
|
||||
// Runs a background thread that we use for scheduling pings to our peers, so we can measure their performance
|
||||
// and network latency. We ping peers every pingIntervalMsec milliseconds.
|
||||
|
@@ -21,7 +21,7 @@ import java.io.OutputStream;
|
||||
|
||||
public class Pong extends Message {
|
||||
/** The smallest protocol version that supports the pong response (BIP 31). Anything beyond version 60000. */
|
||||
public static final int MIN_PROTOCOL_VERSION = 60001;
|
||||
public static final int MIN_PROTOCOL_VERSION = 60003;
|
||||
|
||||
private long nonce;
|
||||
|
||||
|
@@ -92,14 +92,14 @@ public class Transaction extends ChildMessage implements Serializable {
|
||||
* If fee is lower than this value (in satoshis), a default reference client will treat it as if there were no fee.
|
||||
* Currently this is 10000 satoshis.
|
||||
*/
|
||||
public static final Coin REFERENCE_DEFAULT_MIN_TX_FEE = Coin.valueOf(10000);
|
||||
public static final Coin REFERENCE_DEFAULT_MIN_TX_FEE = Coin.COIN; // 1 DOGE min fee
|
||||
|
||||
/**
|
||||
* Any standard (ie pay-to-address) output smaller than this value (in satoshis) will most likely be rejected by the network.
|
||||
* This is calculated by assuming a standard output will be 34 bytes, and then using the formula used in
|
||||
* {@link TransactionOutput#getMinNonDustValue(Coin)}. Currently it's 5460 satoshis.
|
||||
* {@link TransactionOutput#getMinNonDustValue(Coin)}.
|
||||
*/
|
||||
public static final Coin MIN_NONDUST_OUTPUT = Coin.valueOf(5460);
|
||||
public static final Coin MIN_NONDUST_OUTPUT = Coin.SATOSHI; //DOGE: We can send one "shibetoshi" but this will cost us extra fee!
|
||||
|
||||
// These are serialized in both bitcoin and java serialization.
|
||||
private long version;
|
||||
|
@@ -246,9 +246,13 @@ public class TransactionOutput extends ChildMessage implements Serializable {
|
||||
// formula is wrong for anything that's not a pay-to-address output, unfortunately, we must follow the reference
|
||||
// clients wrongness in order to ensure we're considered standard. A better formula would either estimate the
|
||||
// size of data needed to satisfy all different script types, or just hard code 33 below.
|
||||
final long size = this.bitcoinSerialize().length + 148;
|
||||
Coin[] nonDustAndRemainder = feePerKbRequired.multiply(size).divideAndRemainder(1000);
|
||||
return nonDustAndRemainder[1].equals(Coin.ZERO) ? nonDustAndRemainder[0] : nonDustAndRemainder[0].add(Coin.SATOSHI);
|
||||
|
||||
// DOGE doesn't enforce these rules. Therefore we consider each output as valid.
|
||||
return Coin.ZERO;
|
||||
/**final BigInteger size = BigInteger.valueOf(this.bitcoinSerialize().length + 148);
|
||||
BigInteger[] nonDustAndRemainder = feePerKbRequired.multiply(size).divideAndRemainder(BigInteger.valueOf(1000));
|
||||
return nonDustAndRemainder[1].equals(BigInteger.ZERO) ? nonDustAndRemainder[0] : nonDustAndRemainder[0].add(BigInteger.ONE);**/
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -25,6 +25,7 @@ import com.google.common.io.BaseEncoding;
|
||||
import com.google.common.io.Resources;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.UnsignedLongs;
|
||||
import com.lambdaworks.crypto.SCrypt;
|
||||
import org.spongycastle.crypto.digests.RIPEMD160Digest;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -159,6 +160,14 @@ public class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] scryptDigest(byte[] input) {
|
||||
try {
|
||||
return SCrypt.scrypt(input, input, 1024, 1, 1, 32);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] singleDigest(byte[] input, int offset, int length) {
|
||||
synchronized (digest) {
|
||||
digest.reset();
|
||||
|
@@ -77,7 +77,7 @@ public class VersionMessage extends Message {
|
||||
/** The version of this library release, as a string. */
|
||||
public static final String BITCOINJ_VERSION = "0.13-SNAPSHOT";
|
||||
/** The value that is prepended to the subVer field of this application. */
|
||||
public static final String LIBRARY_SUBVER = "/bitcoinj:" + BITCOINJ_VERSION + "/";
|
||||
public static final String LIBRARY_SUBVER = "/dogecoinj:" + BITCOINJ_VERSION + "/";
|
||||
|
||||
public VersionMessage(NetworkParameters params, byte[] payload) throws ProtocolException {
|
||||
super(params, payload, 0);
|
||||
|
@@ -3348,6 +3348,7 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
||||
|
||||
// If any inputs have already been added, we don't need to get their value from wallet
|
||||
Coin totalInput = Coin.ZERO;
|
||||
Coin totalOutput = value;
|
||||
for (TransactionInput input : req.tx.getInputs())
|
||||
if (input.getConnectedOutput() != null)
|
||||
totalInput = totalInput.add(input.getConnectedOutput().getValue());
|
||||
@@ -3357,15 +3358,17 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
||||
|
||||
List<TransactionInput> originalInputs = new ArrayList<TransactionInput>(req.tx.getInputs());
|
||||
|
||||
// We need to know if we need to add an additional fee because one of our values are smaller than 0.01 BTC
|
||||
// We need to know if we need to add an additional fee because one of our values are smaller than 1 DOGE
|
||||
boolean needAtLeastReferenceFee = false;
|
||||
int txOutDustFeeCount = 0;
|
||||
if (req.ensureMinRequiredFee && !req.emptyWallet) { // min fee checking is handled later for emptyWallet
|
||||
for (TransactionOutput output : req.tx.getOutputs())
|
||||
if (output.getValue().compareTo(Coin.CENT) < 0) {
|
||||
if (output.getValue().compareTo(output.getMinNonDustValue()) < 0)
|
||||
throw new DustySendRequested();
|
||||
if (output.getValue().compareTo(Coin.COIN) < 0) { //TXOut lower than 1 DOGE have a 2 DOGE fee!
|
||||
//TODO Currently Dogecoin doesn't have this. We can put it back in later.
|
||||
// if (output.getValue().compareTo(output.getMinNonDustValue()) < 0)
|
||||
// throw new IllegalArgumentException("Tried to send dust with ensureMinRequiredFee set - no way to complete this");
|
||||
needAtLeastReferenceFee = true;
|
||||
break;
|
||||
txOutDustFeeCount++; //DOGE: Each TXOut < 1 DOGE needs +1 DOGE fee!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3381,7 +3384,7 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
||||
if (!req.emptyWallet) {
|
||||
// This can throw InsufficientMoneyException.
|
||||
FeeCalculation feeCalculation;
|
||||
feeCalculation = calculateFee(req, value, originalInputs, needAtLeastReferenceFee, candidates);
|
||||
feeCalculation = calculateFee(req, value, originalInputs, needAtLeastReferenceFee, candidates, txOutDustFeeCount);
|
||||
bestCoinSelection = feeCalculation.bestCoinSelection;
|
||||
bestChangeOutput = feeCalculation.bestChangeOutput;
|
||||
} else {
|
||||
@@ -3393,6 +3396,10 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
||||
candidates = null; // Selector took ownership and might have changed candidates. Don't access again.
|
||||
req.tx.getOutput(0).setValue(bestCoinSelection.valueGathered);
|
||||
log.info(" emptying {}", bestCoinSelection.valueGathered.toFriendlyString());
|
||||
totalOutput = bestCoinSelection.valueGathered;
|
||||
if (totalOutput.isLessThan(Coin.valueOf(2, 0)))
|
||||
throw new InsufficientMoneyException(totalOutput.subtract(Coin.valueOf(2,0)), "Can't spend this due to fee.");
|
||||
|
||||
}
|
||||
|
||||
for (TransactionOutput output : bestCoinSelection.gathered)
|
||||
@@ -3404,10 +3411,18 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
||||
Transaction tx = req.tx;
|
||||
if (!adjustOutputDownwardsForFee(tx, bestCoinSelection, baseFee, feePerKb))
|
||||
throw new CouldNotAdjustDownwards();
|
||||
|
||||
//Set total output again after we reduced it by the fee. There are 3 cases here at emptying:
|
||||
//1: Balance is >= 2: The fee will be 1 (or more if the size of the tx is bigger.
|
||||
//2: Balance is >= 1 but < 2: The fee will be 1, but the output will be below 1 so additional fee is needed, which we don't have.
|
||||
//3: Balance is > 0 but < 1: We can never spend this due to fee.
|
||||
//4: Balance is 0: This won't happen here.
|
||||
totalOutput = tx.getOutput(0).getValue();
|
||||
}
|
||||
|
||||
if (bestChangeOutput != null) {
|
||||
req.tx.addOutput(bestChangeOutput);
|
||||
totalOutput = totalOutput.add(bestChangeOutput.getValue());
|
||||
log.info(" with {} change", bestChangeOutput.getValue().toFriendlyString());
|
||||
}
|
||||
|
||||
@@ -3425,7 +3440,7 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
||||
if (size > Transaction.MAX_STANDARD_TX_SIZE)
|
||||
throw new ExceededMaxTransactionSize();
|
||||
|
||||
final Coin calculatedFee = req.tx.getFee();
|
||||
final Coin calculatedFee = totalInput.subtract(totalOutput);
|
||||
if (calculatedFee != null) {
|
||||
log.info(" with a fee of {}", calculatedFee.toFriendlyString());
|
||||
}
|
||||
@@ -3987,7 +4002,8 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
||||
//region Fee calculation code
|
||||
|
||||
public FeeCalculation calculateFee(SendRequest req, Coin value, List<TransactionInput> originalInputs,
|
||||
boolean needAtLeastReferenceFee, LinkedList<TransactionOutput> candidates) throws InsufficientMoneyException {
|
||||
boolean needAtLeastReferenceFee, LinkedList<TransactionOutput> candidates,
|
||||
int txOutDustFeeCount ) throws InsufficientMoneyException {
|
||||
checkState(lock.isHeldByCurrentThread());
|
||||
FeeCalculation result = new FeeCalculation();
|
||||
// There are 3 possibilities for what adding change might do:
|
||||
@@ -4018,7 +4034,11 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
||||
fees = fees.add(req.feePerKb); // First time around the loop.
|
||||
}
|
||||
if (needAtLeastReferenceFee && fees.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0)
|
||||
fees = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE;
|
||||
fees = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE;
|
||||
//DOGE: Add 1 DOGE fee per txOut < 1 DOGE
|
||||
if (txOutDustFeeCount > 0)
|
||||
fees = fees.add(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.multiply(txOutDustFeeCount));
|
||||
|
||||
|
||||
valueNeeded = value.add(fees);
|
||||
if (additionalValueForNextCategory != null)
|
||||
@@ -4049,12 +4069,11 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
||||
if (additionalValueSelected != null)
|
||||
change = change.add(additionalValueSelected);
|
||||
|
||||
// If change is < 0.01 BTC, we will need to have at least minfee to be accepted by the network
|
||||
if (req.ensureMinRequiredFee && !change.equals(Coin.ZERO) &&
|
||||
change.compareTo(Coin.CENT) < 0 && fees.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0) {
|
||||
// If change is < 1 DOGE, we will need to have at least minfee to be accepted by the network
|
||||
if (req.ensureMinRequiredFee && !change.equals(Coin.ZERO) && change.isLessThan(Coin.COIN)) {
|
||||
// This solution may fit into category 2, but it may also be category 3, we'll check that later
|
||||
eitherCategory2Or3 = true;
|
||||
additionalValueForNextCategory = Coin.CENT;
|
||||
additionalValueForNextCategory = Coin.COIN;
|
||||
// If the change is smaller than the fee we want to add, this will be negative
|
||||
change = change.subtract(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.subtract(fees));
|
||||
}
|
||||
@@ -4070,11 +4089,13 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
||||
changeAddress = getChangeAddress();
|
||||
changeOutput = new TransactionOutput(params, req.tx, change, changeAddress);
|
||||
// If the change output would result in this transaction being rejected as dust, just drop the change and make it a fee
|
||||
if (req.ensureMinRequiredFee && Transaction.MIN_NONDUST_OUTPUT.compareTo(change) >= 0) {
|
||||
if (req.ensureMinRequiredFee && Coin.COIN.compareTo(change) > 0) {
|
||||
// This solution definitely fits in category 3
|
||||
isCategory3 = true;
|
||||
additionalValueForNextCategory = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.add(
|
||||
Transaction.MIN_NONDUST_OUTPUT.add(Coin.SATOSHI));
|
||||
//additionalValueForNextCategory = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.add(
|
||||
// Transaction.MIN_NONDUST_OUTPUT.add(BigInteger.ONE));
|
||||
additionalValueForNextCategory = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.add(Coin.COIN);
|
||||
//DOGE: We don't have a min value, but we add more fees for tx < 1
|
||||
} else {
|
||||
size += changeOutput.bitcoinSerialize().length + VarInt.sizeOf(req.tx.getOutputs().size()) - VarInt.sizeOf(req.tx.getOutputs().size() - 1);
|
||||
// This solution is either category 1 or 2
|
||||
@@ -4114,7 +4135,7 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
||||
// If we are in selection2, we will require at least CENT additional. If we do that, there is no way
|
||||
// we can end up back here because CENT additional will always get us to 1
|
||||
checkState(selection2 == null);
|
||||
checkState(additionalValueForNextCategory.equals(Coin.CENT));
|
||||
checkState(additionalValueForNextCategory.equals(Coin.COIN));
|
||||
selection2 = selection;
|
||||
selection2Change = checkNotNull(changeOutput); // If we get no change in category 2, we are actually in category 3
|
||||
} else {
|
||||
|
@@ -54,12 +54,12 @@ public final class MonetaryFormat {
|
||||
public static final MonetaryFormat UBTC = new MonetaryFormat().shift(6).minDecimals(0).optionalDecimals(2);
|
||||
/** Standard format for fiat amounts. */
|
||||
public static final MonetaryFormat FIAT = new MonetaryFormat().shift(0).minDecimals(2).repeatOptionalDecimals(2, 1);
|
||||
/** Currency code for base 1 Bitcoin. */
|
||||
public static final String CODE_BTC = "BTC";
|
||||
/** Currency code for base 1/1000 Bitcoin. */
|
||||
public static final String CODE_MBTC = "mBTC";
|
||||
/** Currency code for base 1/1000000 Bitcoin. */
|
||||
public static final String CODE_UBTC = "µBTC";
|
||||
/** Currency code for base 1 Dogecoin. */
|
||||
public static final String CODE_BTC = "DOGE";
|
||||
/** Currency code for base 1/1000 Dogecoin. */
|
||||
public static final String CODE_MBTC = "mDOGE";
|
||||
/** Currency code for base 1/1000000 Dogecoin. */
|
||||
public static final String CODE_UBTC = "µDOGE";
|
||||
|
||||
private final char negativeSign;
|
||||
private final char positiveSign;
|
||||
|
Reference in New Issue
Block a user