mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-11-02 21:47:18 +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
|
// February 16th 2012
|
||||||
private static final Date testnetDiffDate = new Date(1329264000000L);
|
private static final Date testnetDiffDate = new Date(1329264000000L);
|
||||||
|
private static final int testNetRetargetFix = 157500;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throws an exception if the blocks difficulty is not correct.
|
* 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 {
|
private void checkDifficultyTransitions(StoredBlock storedPrev, Block nextBlock) throws BlockStoreException, VerificationException {
|
||||||
checkState(lock.isHeldByCurrentThread());
|
checkState(lock.isHeldByCurrentThread());
|
||||||
Block prev = storedPrev.getHeader();
|
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?
|
// 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.
|
// 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
|
// 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.
|
// two weeks after the initial block chain download.
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
StoredBlock cursor = blockStore.get(prev.getHash());
|
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) {
|
if (cursor == null) {
|
||||||
// This should never happen. If it does, it means we are following an incorrect or busted chain.
|
// This should never happen. If it does, it means we are following an incorrect or busted chain.
|
||||||
throw new VerificationException(
|
throw new VerificationException(
|
||||||
@@ -873,18 +894,50 @@ public abstract class AbstractBlockChain {
|
|||||||
}
|
}
|
||||||
cursor = blockStore.get(cursor.getHeader().getPrevBlockHash());
|
cursor = blockStore.get(cursor.getHeader().getPrevBlockHash());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//We used checkpoints...
|
||||||
|
if(cursor == null)
|
||||||
|
{
|
||||||
|
log.debug("Difficulty transition: Hit checkpoint!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
long elapsed = System.currentTimeMillis() - now;
|
long elapsed = System.currentTimeMillis() - now;
|
||||||
if (elapsed > 50)
|
if (elapsed > 50)
|
||||||
log.info("Difficulty transition traversal took {}msec", elapsed);
|
log.info("Difficulty transition traversal took {}msec", elapsed);
|
||||||
|
|
||||||
Block blockIntervalAgo = cursor.getHeader();
|
Block blockIntervalAgo = cursor.getHeader();
|
||||||
int timespan = (int) (prev.getTimeSeconds() - blockIntervalAgo.getTimeSeconds());
|
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.
|
// Limit the adjustment step.
|
||||||
final int targetTimespan = params.getTargetTimespan();
|
else if (storedPrev.getHeight()+1 > 10000)
|
||||||
if (timespan < targetTimespan / 4)
|
{
|
||||||
timespan = targetTimespan / 4;
|
if (timespan < targetTimespan / 4)
|
||||||
if (timespan > targetTimespan * 4)
|
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());
|
BigInteger newTarget = Utils.decodeCompactBits(prev.getDifficultyTarget());
|
||||||
newTarget = newTarget.multiply(BigInteger.valueOf(timespan));
|
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.Coin.FIFTY_COINS;
|
||||||
import static com.dogecoin.dogecoinj.core.Utils.doubleDigest;
|
import static com.dogecoin.dogecoinj.core.Utils.doubleDigest;
|
||||||
import static com.dogecoin.dogecoinj.core.Utils.doubleDigestTwoBuffers;
|
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.
|
* <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. */
|
/** 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 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.
|
// Fields defined as part of the protocol format.
|
||||||
private long version;
|
private long version;
|
||||||
private Sha256Hash prevBlockHash;
|
private Sha256Hash prevBlockHash;
|
||||||
@@ -82,11 +87,15 @@ public class Block extends Message {
|
|||||||
private long difficultyTarget; // "nBits"
|
private long difficultyTarget; // "nBits"
|
||||||
private long nonce;
|
private long nonce;
|
||||||
|
|
||||||
|
// The parent block header for AuxPoW blocks
|
||||||
|
private Block parentBlock;
|
||||||
|
|
||||||
/** If null, it means this object holds only the headers. */
|
/** If null, it means this object holds only the headers. */
|
||||||
List<Transaction> transactions;
|
List<Transaction> transactions;
|
||||||
|
|
||||||
/** Stores the hash of the block. If null, getHash() will recalculate it. */
|
/** Stores the hash of the block. If null, getHash() will recalculate it. */
|
||||||
private transient Sha256Hash hash;
|
private transient Sha256Hash hash;
|
||||||
|
private transient Sha256Hash scryptHash;
|
||||||
|
|
||||||
private transient boolean headerParsed;
|
private transient boolean headerParsed;
|
||||||
private transient boolean transactionsParsed;
|
private transient boolean transactionsParsed;
|
||||||
@@ -116,6 +125,12 @@ public class Block extends Message {
|
|||||||
super(params, payloadBytes, 0, false, false, payloadBytes.length);
|
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.
|
* Contruct a block object from the Bitcoin wire format.
|
||||||
* @param params NetworkParameters object.
|
* @param params NetworkParameters object.
|
||||||
@@ -131,7 +146,11 @@ public class Block extends Message {
|
|||||||
throws ProtocolException {
|
throws ProtocolException {
|
||||||
super(params, payloadBytes, 0, parseLazy, parseRetain, length);
|
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.
|
* Construct a block initialized with all the given fields.
|
||||||
@@ -196,11 +215,20 @@ public class Block extends Message {
|
|||||||
headerBytesValid = parseRetain;
|
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 {
|
private void parseTransactions() throws ProtocolException {
|
||||||
if (transactionsParsed)
|
if (transactionsParsed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cursor = offset + HEADER_SIZE;
|
if (version != BLOCK_VERSION_AUXPOW_AUXBLOCK) {
|
||||||
|
cursor = offset + HEADER_SIZE;
|
||||||
|
}
|
||||||
optimalEncodingMessageSize = HEADER_SIZE;
|
optimalEncodingMessageSize = HEADER_SIZE;
|
||||||
if (payload.length == cursor) {
|
if (payload.length == cursor) {
|
||||||
// This message is just a header, it has no transactions.
|
// This message is just a header, it has no transactions.
|
||||||
@@ -229,6 +257,9 @@ public class Block extends Message {
|
|||||||
@Override
|
@Override
|
||||||
void parse() throws ProtocolException {
|
void parse() throws ProtocolException {
|
||||||
parseHeader();
|
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();
|
parseTransactions();
|
||||||
length = cursor - offset;
|
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
|
* 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
|
* 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();
|
return getHash().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getScryptHashAsString() {
|
||||||
|
return getScryptHash().toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the hash of the block (which for a valid, solved block should be
|
* Returns the hash of the block (which for a valid, solved block should be
|
||||||
* below the target). Big endian.
|
* below the target). Big endian.
|
||||||
@@ -536,6 +581,12 @@ public class Block extends Message {
|
|||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Sha256Hash getScryptHash() {
|
||||||
|
if (scryptHash == null)
|
||||||
|
scryptHash = calculateScryptHash();
|
||||||
|
return scryptHash;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number that is one greater than the largest representable SHA-256
|
* The number that is one greater than the largest representable SHA-256
|
||||||
* hash.
|
* hash.
|
||||||
@@ -567,6 +618,9 @@ public class Block extends Message {
|
|||||||
block.difficultyTarget = difficultyTarget;
|
block.difficultyTarget = difficultyTarget;
|
||||||
block.transactions = null;
|
block.transactions = null;
|
||||||
block.hash = getHash().duplicate();
|
block.hash = getHash().duplicate();
|
||||||
|
if (version == BLOCK_VERSION_AUXPOW_AUXBLOCK) {
|
||||||
|
block.parentBlock = parentBlock;
|
||||||
|
}
|
||||||
return block;
|
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.
|
// field is of the right value. This requires us to have the preceeding blocks.
|
||||||
BigInteger target = getDifficultyTargetAsInteger();
|
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) {
|
if (h.compareTo(target) > 0) {
|
||||||
// Proof of work check failed!
|
// Proof of work check failed!
|
||||||
if (throwException)
|
if (throwException)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import java.util.*;
|
|||||||
*/
|
*/
|
||||||
public class FilteredBlock extends Message {
|
public class FilteredBlock extends Message {
|
||||||
/** The protocol version at which Bloom filtering started to be supported. */
|
/** 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 Block header;
|
||||||
|
|
||||||
private PartialMerkleTree merkleTree;
|
private PartialMerkleTree merkleTree;
|
||||||
@@ -57,12 +57,28 @@ public class FilteredBlock extends Message {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
void parse() throws ProtocolException {
|
void parse() throws ProtocolException {
|
||||||
|
long blockVersion = Utils.readUint32(payload, cursor);
|
||||||
|
|
||||||
byte[] headerBytes = new byte[Block.HEADER_SIZE];
|
byte[] headerBytes = new byte[Block.HEADER_SIZE];
|
||||||
System.arraycopy(payload, 0, headerBytes, 0, 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();
|
length = Block.HEADER_SIZE + merkleTree.getMessageSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,17 +76,38 @@ public class HeadersMessage extends Message {
|
|||||||
long numHeaders = readVarInt();
|
long numHeaders = readVarInt();
|
||||||
if (numHeaders > MAX_HEADERS)
|
if (numHeaders > MAX_HEADERS)
|
||||||
throw new ProtocolException("Too many headers: got " + numHeaders + " which is larger than " +
|
throw new ProtocolException("Too many headers: got " + numHeaders + " which is larger than " +
|
||||||
MAX_HEADERS);
|
MAX_HEADERS);
|
||||||
|
|
||||||
blockHeaders = new ArrayList<Block>();
|
blockHeaders = new ArrayList<Block>();
|
||||||
|
|
||||||
for (int i = 0; i < numHeaders; ++i) {
|
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
|
// Read the block version. If it's not an aux block all is fine, else throw out the aux stuff
|
||||||
// transaction list is empty.
|
long blockVersion = Utils.readUint32(payload, cursor);
|
||||||
byte[] blockHeader = readBytes(81);
|
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)
|
if (blockHeader[80] != 0)
|
||||||
throw new ProtocolException("Block header does not end with a null byte");
|
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);
|
blockHeaders.add(newBlockHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public abstract class NetworkParameters implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* The protocol version this library implements.
|
* 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.
|
* 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>();
|
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.
|
// 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;
|
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
|
// 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.
|
// whilst waiting for the response. Is not used for downloads Peer generates itself.
|
||||||
private static class GetDataRequest {
|
private static class GetDataRequest {
|
||||||
@@ -152,6 +154,10 @@ public class Peer extends PeerSocketHandler {
|
|||||||
// A future representing the results of doing a getUTXOs call.
|
// A future representing the results of doing a getUTXOs call.
|
||||||
@Nullable private SettableFuture<UTXOsMessage> utxosFuture;
|
@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>
|
* <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);
|
vPeerVersionMessage.clientVersion, version);
|
||||||
close();
|
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) {
|
} else if (m instanceof UTXOsMessage) {
|
||||||
if (utxosFuture != null) {
|
if (utxosFuture != null) {
|
||||||
SettableFuture<UTXOsMessage> future = utxosFuture;
|
SettableFuture<UTXOsMessage> future = utxosFuture;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package com.dogecoin.dogecoinj.core;
|
package com.dogecoin.dogecoinj.core;
|
||||||
|
|
||||||
|
import com.dogecoin.dogecoinj.params.MainNetParams;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
@@ -113,7 +114,7 @@ public class PeerGroup extends AbstractExecutionThreadService implements Transac
|
|||||||
// until we reach this count.
|
// until we reach this count.
|
||||||
@GuardedBy("lock") private int maxConnections;
|
@GuardedBy("lock") private int maxConnections;
|
||||||
// Minimum protocol version we will allow ourselves to connect to: require Bloom filtering.
|
// 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
|
// 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.
|
// and network latency. We ping peers every pingIntervalMsec milliseconds.
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import java.io.OutputStream;
|
|||||||
|
|
||||||
public class Pong extends Message {
|
public class Pong extends Message {
|
||||||
/** The smallest protocol version that supports the pong response (BIP 31). Anything beyond version 60000. */
|
/** 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;
|
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.
|
* 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.
|
* 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.
|
* 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
|
* 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.
|
// These are serialized in both bitcoin and java serialization.
|
||||||
private long version;
|
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
|
// 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
|
// 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.
|
// 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);
|
// DOGE doesn't enforce these rules. Therefore we consider each output as valid.
|
||||||
return nonDustAndRemainder[1].equals(Coin.ZERO) ? nonDustAndRemainder[0] : nonDustAndRemainder[0].add(Coin.SATOSHI);
|
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.io.Resources;
|
||||||
import com.google.common.primitives.Ints;
|
import com.google.common.primitives.Ints;
|
||||||
import com.google.common.primitives.UnsignedLongs;
|
import com.google.common.primitives.UnsignedLongs;
|
||||||
|
import com.lambdaworks.crypto.SCrypt;
|
||||||
import org.spongycastle.crypto.digests.RIPEMD160Digest;
|
import org.spongycastle.crypto.digests.RIPEMD160Digest;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
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) {
|
public static byte[] singleDigest(byte[] input, int offset, int length) {
|
||||||
synchronized (digest) {
|
synchronized (digest) {
|
||||||
digest.reset();
|
digest.reset();
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ public class VersionMessage extends Message {
|
|||||||
/** The version of this library release, as a string. */
|
/** The version of this library release, as a string. */
|
||||||
public static final String BITCOINJ_VERSION = "0.13-SNAPSHOT";
|
public static final String BITCOINJ_VERSION = "0.13-SNAPSHOT";
|
||||||
/** The value that is prepended to the subVer field of this application. */
|
/** 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 {
|
public VersionMessage(NetworkParameters params, byte[] payload) throws ProtocolException {
|
||||||
super(params, payload, 0);
|
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
|
// If any inputs have already been added, we don't need to get their value from wallet
|
||||||
Coin totalInput = Coin.ZERO;
|
Coin totalInput = Coin.ZERO;
|
||||||
|
Coin totalOutput = value;
|
||||||
for (TransactionInput input : req.tx.getInputs())
|
for (TransactionInput input : req.tx.getInputs())
|
||||||
if (input.getConnectedOutput() != null)
|
if (input.getConnectedOutput() != null)
|
||||||
totalInput = totalInput.add(input.getConnectedOutput().getValue());
|
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());
|
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;
|
boolean needAtLeastReferenceFee = false;
|
||||||
|
int txOutDustFeeCount = 0;
|
||||||
if (req.ensureMinRequiredFee && !req.emptyWallet) { // min fee checking is handled later for emptyWallet
|
if (req.ensureMinRequiredFee && !req.emptyWallet) { // min fee checking is handled later for emptyWallet
|
||||||
for (TransactionOutput output : req.tx.getOutputs())
|
for (TransactionOutput output : req.tx.getOutputs())
|
||||||
if (output.getValue().compareTo(Coin.CENT) < 0) {
|
if (output.getValue().compareTo(Coin.COIN) < 0) { //TXOut lower than 1 DOGE have a 2 DOGE fee!
|
||||||
if (output.getValue().compareTo(output.getMinNonDustValue()) < 0)
|
//TODO Currently Dogecoin doesn't have this. We can put it back in later.
|
||||||
throw new DustySendRequested();
|
// if (output.getValue().compareTo(output.getMinNonDustValue()) < 0)
|
||||||
|
// throw new IllegalArgumentException("Tried to send dust with ensureMinRequiredFee set - no way to complete this");
|
||||||
needAtLeastReferenceFee = true;
|
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) {
|
if (!req.emptyWallet) {
|
||||||
// This can throw InsufficientMoneyException.
|
// This can throw InsufficientMoneyException.
|
||||||
FeeCalculation feeCalculation;
|
FeeCalculation feeCalculation;
|
||||||
feeCalculation = calculateFee(req, value, originalInputs, needAtLeastReferenceFee, candidates);
|
feeCalculation = calculateFee(req, value, originalInputs, needAtLeastReferenceFee, candidates, txOutDustFeeCount);
|
||||||
bestCoinSelection = feeCalculation.bestCoinSelection;
|
bestCoinSelection = feeCalculation.bestCoinSelection;
|
||||||
bestChangeOutput = feeCalculation.bestChangeOutput;
|
bestChangeOutput = feeCalculation.bestChangeOutput;
|
||||||
} else {
|
} 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.
|
candidates = null; // Selector took ownership and might have changed candidates. Don't access again.
|
||||||
req.tx.getOutput(0).setValue(bestCoinSelection.valueGathered);
|
req.tx.getOutput(0).setValue(bestCoinSelection.valueGathered);
|
||||||
log.info(" emptying {}", bestCoinSelection.valueGathered.toFriendlyString());
|
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)
|
for (TransactionOutput output : bestCoinSelection.gathered)
|
||||||
@@ -3404,10 +3411,18 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
|||||||
Transaction tx = req.tx;
|
Transaction tx = req.tx;
|
||||||
if (!adjustOutputDownwardsForFee(tx, bestCoinSelection, baseFee, feePerKb))
|
if (!adjustOutputDownwardsForFee(tx, bestCoinSelection, baseFee, feePerKb))
|
||||||
throw new CouldNotAdjustDownwards();
|
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) {
|
if (bestChangeOutput != null) {
|
||||||
req.tx.addOutput(bestChangeOutput);
|
req.tx.addOutput(bestChangeOutput);
|
||||||
|
totalOutput = totalOutput.add(bestChangeOutput.getValue());
|
||||||
log.info(" with {} change", bestChangeOutput.getValue().toFriendlyString());
|
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)
|
if (size > Transaction.MAX_STANDARD_TX_SIZE)
|
||||||
throw new ExceededMaxTransactionSize();
|
throw new ExceededMaxTransactionSize();
|
||||||
|
|
||||||
final Coin calculatedFee = req.tx.getFee();
|
final Coin calculatedFee = totalInput.subtract(totalOutput);
|
||||||
if (calculatedFee != null) {
|
if (calculatedFee != null) {
|
||||||
log.info(" with a fee of {}", calculatedFee.toFriendlyString());
|
log.info(" with a fee of {}", calculatedFee.toFriendlyString());
|
||||||
}
|
}
|
||||||
@@ -3987,7 +4002,8 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
|||||||
//region Fee calculation code
|
//region Fee calculation code
|
||||||
|
|
||||||
public FeeCalculation calculateFee(SendRequest req, Coin value, List<TransactionInput> originalInputs,
|
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());
|
checkState(lock.isHeldByCurrentThread());
|
||||||
FeeCalculation result = new FeeCalculation();
|
FeeCalculation result = new FeeCalculation();
|
||||||
// There are 3 possibilities for what adding change might do:
|
// 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.
|
fees = fees.add(req.feePerKb); // First time around the loop.
|
||||||
}
|
}
|
||||||
if (needAtLeastReferenceFee && fees.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0)
|
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);
|
valueNeeded = value.add(fees);
|
||||||
if (additionalValueForNextCategory != null)
|
if (additionalValueForNextCategory != null)
|
||||||
@@ -4049,12 +4069,11 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
|
|||||||
if (additionalValueSelected != null)
|
if (additionalValueSelected != null)
|
||||||
change = change.add(additionalValueSelected);
|
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 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) &&
|
if (req.ensureMinRequiredFee && !change.equals(Coin.ZERO) && change.isLessThan(Coin.COIN)) {
|
||||||
change.compareTo(Coin.CENT) < 0 && fees.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) < 0) {
|
|
||||||
// This solution may fit into category 2, but it may also be category 3, we'll check that later
|
// This solution may fit into category 2, but it may also be category 3, we'll check that later
|
||||||
eitherCategory2Or3 = true;
|
eitherCategory2Or3 = true;
|
||||||
additionalValueForNextCategory = Coin.CENT;
|
additionalValueForNextCategory = Coin.COIN;
|
||||||
// If the change is smaller than the fee we want to add, this will be negative
|
// 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));
|
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();
|
changeAddress = getChangeAddress();
|
||||||
changeOutput = new TransactionOutput(params, req.tx, change, changeAddress);
|
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 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
|
// This solution definitely fits in category 3
|
||||||
isCategory3 = true;
|
isCategory3 = true;
|
||||||
additionalValueForNextCategory = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.add(
|
//additionalValueForNextCategory = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.add(
|
||||||
Transaction.MIN_NONDUST_OUTPUT.add(Coin.SATOSHI));
|
// 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 {
|
} else {
|
||||||
size += changeOutput.bitcoinSerialize().length + VarInt.sizeOf(req.tx.getOutputs().size()) - VarInt.sizeOf(req.tx.getOutputs().size() - 1);
|
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
|
// 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
|
// 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
|
// we can end up back here because CENT additional will always get us to 1
|
||||||
checkState(selection2 == null);
|
checkState(selection2 == null);
|
||||||
checkState(additionalValueForNextCategory.equals(Coin.CENT));
|
checkState(additionalValueForNextCategory.equals(Coin.COIN));
|
||||||
selection2 = selection;
|
selection2 = selection;
|
||||||
selection2Change = checkNotNull(changeOutput); // If we get no change in category 2, we are actually in category 3
|
selection2Change = checkNotNull(changeOutput); // If we get no change in category 2, we are actually in category 3
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -54,12 +54,12 @@ public final class MonetaryFormat {
|
|||||||
public static final MonetaryFormat UBTC = new MonetaryFormat().shift(6).minDecimals(0).optionalDecimals(2);
|
public static final MonetaryFormat UBTC = new MonetaryFormat().shift(6).minDecimals(0).optionalDecimals(2);
|
||||||
/** Standard format for fiat amounts. */
|
/** Standard format for fiat amounts. */
|
||||||
public static final MonetaryFormat FIAT = new MonetaryFormat().shift(0).minDecimals(2).repeatOptionalDecimals(2, 1);
|
public static final MonetaryFormat FIAT = new MonetaryFormat().shift(0).minDecimals(2).repeatOptionalDecimals(2, 1);
|
||||||
/** Currency code for base 1 Bitcoin. */
|
/** Currency code for base 1 Dogecoin. */
|
||||||
public static final String CODE_BTC = "BTC";
|
public static final String CODE_BTC = "DOGE";
|
||||||
/** Currency code for base 1/1000 Bitcoin. */
|
/** Currency code for base 1/1000 Dogecoin. */
|
||||||
public static final String CODE_MBTC = "mBTC";
|
public static final String CODE_MBTC = "mDOGE";
|
||||||
/** Currency code for base 1/1000000 Bitcoin. */
|
/** Currency code for base 1/1000000 Dogecoin. */
|
||||||
public static final String CODE_UBTC = "µBTC";
|
public static final String CODE_UBTC = "µDOGE";
|
||||||
|
|
||||||
private final char negativeSign;
|
private final char negativeSign;
|
||||||
private final char positiveSign;
|
private final char positiveSign;
|
||||||
|
|||||||
Reference in New Issue
Block a user