Step 4: Core changes, including difficulty, auxpow and fee calculation

This commit is contained in:
langerhans
2014-10-18 21:52:53 +02:00
parent dc689369f8
commit 3070012f4e
15 changed files with 396 additions and 53 deletions

View File

@@ -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));

View File

@@ -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;
}
}

View File

@@ -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)

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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.

View File

@@ -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;

View File

@@ -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.

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);**/
}
/**

View File

@@ -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();

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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;