3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-13 10:45:51 +00:00

Added extension points for altcoin support via subclassing.

This commit is contained in:
Ross Nicoll 2015-04-11 12:38:17 +01:00 committed by Mike Hearn
parent c2d3cec6b0
commit 7a3aa74c6e
15 changed files with 142 additions and 24 deletions

View File

@ -829,9 +829,11 @@ public abstract class AbstractBlockChain {
private static final Date testnetDiffDate = new Date(1329264000000L); private static final Date testnetDiffDate = new Date(1329264000000L);
/** /**
* Throws an exception if the blocks difficulty is not correct. * Throws an exception if the block's difficulty is not correct.
*
* @throws VerificationException if the block's difficulty is not correct.
*/ */
private void checkDifficultyTransitions(StoredBlock storedPrev, Block nextBlock) throws BlockStoreException, VerificationException { protected void checkDifficultyTransitions(StoredBlock storedPrev, Block nextBlock) throws BlockStoreException, VerificationException {
checkState(lock.isHeldByCurrentThread()); checkState(lock.isHeldByCurrentThread());
Block prev = storedPrev.getHeader(); Block prev = storedPrev.getHeader();

View File

@ -643,7 +643,7 @@ public class Block extends Message {
} }
/** Returns true if the hash of the block is OK (lower than difficulty target). */ /** Returns true if the hash of the block is OK (lower than difficulty target). */
private boolean checkProofOfWork(boolean throwException) throws VerificationException { protected boolean checkProofOfWork(boolean throwException) throws VerificationException {
// This part is key - it is what proves the block was as difficult to make as it claims // This part is key - it is what proves the block was as difficult to make as it claims
// to be. Note however that in the context of this function, the block can claim to be // to be. Note however that in the context of this function, the block can claim to be
// as difficult as it wants to be .... if somebody was able to take control of our network // as difficult as it wants to be .... if somebody was able to take control of our network

View File

@ -297,12 +297,12 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
} }
// All values were already checked for being non-negative (as it is verified in Transaction.verify()) // All values were already checked for being non-negative (as it is verified in Transaction.verify())
// but we check again here just for defence in depth. Transactions with zero output value are OK. // but we check again here just for defence in depth. Transactions with zero output value are OK.
if (valueOut.signum() < 0 || valueOut.compareTo(NetworkParameters.MAX_MONEY) > 0) if (valueOut.signum() < 0 || valueOut.compareTo(this.params.getMaxMoney()) > 0)
throw new VerificationException("Transaction output value out of range"); throw new VerificationException("Transaction output value out of range");
if (isCoinBase) { if (isCoinBase) {
coinbaseValue = valueOut; coinbaseValue = valueOut;
} else { } else {
if (valueIn.compareTo(valueOut) < 0 || valueIn.compareTo(NetworkParameters.MAX_MONEY) > 0) if (valueIn.compareTo(valueOut) < 0 || valueIn.compareTo(this.params.getMaxMoney()) > 0)
throw new VerificationException("Transaction input value out of range"); throw new VerificationException("Transaction input value out of range");
totalFees = totalFees.add(valueIn.subtract(valueOut)); totalFees = totalFees.add(valueIn.subtract(valueOut));
} }
@ -314,7 +314,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
listScriptVerificationResults.add(future); listScriptVerificationResults.add(future);
} }
} }
if (totalFees.compareTo(NetworkParameters.MAX_MONEY) > 0 || block.getBlockInflation(height).add(totalFees).compareTo(coinbaseValue) < 0) if (totalFees.compareTo(this.params.getMaxMoney()) > 0 || block.getBlockInflation(height).add(totalFees).compareTo(coinbaseValue) < 0)
throw new VerificationException("Transaction fees out of range"); throw new VerificationException("Transaction fees out of range");
for (Future<VerificationException> future : listScriptVerificationResults) { for (Future<VerificationException> future : listScriptVerificationResults) {
VerificationException e; VerificationException e;
@ -427,12 +427,12 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
} }
// All values were already checked for being non-negative (as it is verified in Transaction.verify()) // All values were already checked for being non-negative (as it is verified in Transaction.verify())
// but we check again here just for defence in depth. Transactions with zero output value are OK. // but we check again here just for defence in depth. Transactions with zero output value are OK.
if (valueOut.signum() < 0 || valueOut.compareTo(NetworkParameters.MAX_MONEY) > 0) if (valueOut.signum() < 0 || valueOut.compareTo(this.params.getMaxMoney()) > 0)
throw new VerificationException("Transaction output value out of range"); throw new VerificationException("Transaction output value out of range");
if (isCoinBase) { if (isCoinBase) {
coinbaseValue = valueOut; coinbaseValue = valueOut;
} else { } else {
if (valueIn.compareTo(valueOut) < 0 || valueIn.compareTo(NetworkParameters.MAX_MONEY) > 0) if (valueIn.compareTo(valueOut) < 0 || valueIn.compareTo(this.params.getMaxMoney()) > 0)
throw new VerificationException("Transaction input value out of range"); throw new VerificationException("Transaction input value out of range");
totalFees = totalFees.add(valueIn.subtract(valueOut)); totalFees = totalFees.add(valueIn.subtract(valueOut));
} }
@ -444,7 +444,7 @@ public class FullPrunedBlockChain extends AbstractBlockChain {
listScriptVerificationResults.add(future); listScriptVerificationResults.add(future);
} }
} }
if (totalFees.compareTo(NetworkParameters.MAX_MONEY) > 0 || if (totalFees.compareTo(this.params.getMaxMoney()) > 0 ||
newBlock.getHeader().getBlockInflation(newBlock.getHeight()).add(totalFees).compareTo(coinbaseValue) < 0) newBlock.getHeader().getBlockInflation(newBlock.getHeight()).add(totalFees).compareTo(coinbaseValue) < 0)
throw new VerificationException("Transaction fees out of range"); throw new VerificationException("Transaction fees out of range");
txOutChanges = new TransactionOutputChanges(txOutsCreated, txOutsSpent); txOutChanges = new TransactionOutputChanges(txOutsCreated, txOutsSpent);

View File

@ -22,6 +22,10 @@ import org.bitcoinj.net.discovery.*;
import org.bitcoinj.params.*; import org.bitcoinj.params.*;
import org.bitcoinj.script.*; import org.bitcoinj.script.*;
import org.bitcoinj.store.BlockStore;
import org.bitcoinj.store.BlockStoreException;
import org.bitcoinj.utils.MonetaryFormat;
import javax.annotation.*; import javax.annotation.*;
import java.io.*; import java.io.*;
import java.math.*; import java.math.*;
@ -374,4 +378,34 @@ public abstract class NetworkParameters implements Serializable {
public int getBip32HeaderPriv() { public int getBip32HeaderPriv() {
return bip32HeaderPriv; return bip32HeaderPriv;
} }
/**
* Returns the number of coins that will be produced in total, on this
* network. Where not applicable, a very large number of coins is returned
* instead (i.e. the main coin issue for Dogecoin).
*/
public abstract Coin getMaxMoney();
/**
* Any standard (ie pay-to-address) output smaller than this value will
* most likely be rejected by the network.
*/
public abstract Coin getMinNonDustOutput();
/**
* The monetary object for this currency.
*/
public abstract MonetaryFormat getMonetaryFormat();
/**
* Scheme part for URIs, for example "bitcoin".
*/
public abstract String getUriScheme();
/**
* Returns whether this network has a maximum number of coins (finite supply) or
* not. Always returns true for Bitcoin, but exists to be overriden for other
* networks.
*/
public abstract boolean hasMaxMoney();
} }

View File

@ -3080,7 +3080,7 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
try { try {
checkNotNull(selector); checkNotNull(selector);
List<TransactionOutput> candidates = calculateAllSpendCandidates(true, false); List<TransactionOutput> candidates = calculateAllSpendCandidates(true, false);
CoinSelection selection = selector.select(NetworkParameters.MAX_MONEY, candidates); CoinSelection selection = selector.select(params.getMaxMoney(), candidates);
return selection.valueGathered; return selection.valueGathered;
} finally { } finally {
lock.unlock(); lock.unlock();
@ -3663,7 +3663,7 @@ public class Wallet extends BaseTaggableObject implements Serializable, BlockCha
// of the total value we can currently spend as determined by the selector, and then subtracting the fee. // of the total value we can currently spend as determined by the selector, and then subtracting the fee.
checkState(req.tx.getOutputs().size() == 1, "Empty wallet TX must have a single output only."); checkState(req.tx.getOutputs().size() == 1, "Empty wallet TX must have a single output only.");
CoinSelector selector = req.coinSelector == null ? coinSelector : req.coinSelector; CoinSelector selector = req.coinSelector == null ? coinSelector : req.coinSelector;
bestCoinSelection = selector.select(NetworkParameters.MAX_MONEY, candidates); bestCoinSelection = selector.select(params.getMaxMoney(), candidates);
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());

View File

@ -0,0 +1,69 @@
/*
* Copyright 2013 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 org.bitcoinj.params;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.Utils;
import org.bitcoinj.utils.MonetaryFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.checkState;
/**
* Parameters for Bitcoin-like networks.
*/
public abstract class AbstractBitcoinNetParams extends NetworkParameters {
/**
* Scheme part for Bitcoin URIs.
*/
public static final String BITCOIN_SCHEME = "bitcoin";
private static final Logger log = LoggerFactory.getLogger(AbstractBitcoinNetParams.class);
public AbstractBitcoinNetParams() {
super();
}
@Override
public Coin getMaxMoney() {
return MAX_MONEY;
}
@Override
public Coin getMinNonDustOutput() {
return Transaction.MIN_NONDUST_OUTPUT;
}
@Override
public MonetaryFormat getMonetaryFormat() {
return new MonetaryFormat();
}
@Override
public String getUriScheme() {
return BITCOIN_SCHEME;
}
@Override
public boolean hasMaxMoney() {
return true;
}
}

View File

@ -27,7 +27,7 @@ import static com.google.common.base.Preconditions.*;
/** /**
* Parameters for the main production network on which people trade goods and services. * Parameters for the main production network on which people trade goods and services.
*/ */
public class MainNetParams extends NetworkParameters { public class MainNetParams extends AbstractBitcoinNetParams {
public MainNetParams() { public MainNetParams() {
super(); super();
interval = INTERVAL; interval = INTERVAL;

View File

@ -31,9 +31,9 @@ import java.util.Set;
*/ */
public class Networks { public class Networks {
/** Registered networks */ /** Registered networks */
private static Set<NetworkParameters> networks = ImmutableSet.of(TestNet3Params.get(), MainNetParams.get()); private static Set<? extends NetworkParameters> networks = ImmutableSet.of(TestNet3Params.get(), MainNetParams.get());
public static Set<NetworkParameters> get() { public static Set<? extends NetworkParameters> get() {
return networks; return networks;
} }

View File

@ -16,7 +16,6 @@
package org.bitcoinj.params; package org.bitcoinj.params;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.Utils; import org.bitcoinj.core.Utils;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
@ -25,7 +24,7 @@ import static com.google.common.base.Preconditions.checkState;
* Parameters for the old version 2 testnet. This is not useful to you - it exists only because some unit tests are * Parameters for the old version 2 testnet. This is not useful to you - it exists only because some unit tests are
* based on it. * based on it.
*/ */
public class TestNet2Params extends NetworkParameters { public class TestNet2Params extends AbstractBitcoinNetParams {
public TestNet2Params() { public TestNet2Params() {
super(); super();
id = ID_TESTNET; id = ID_TESTNET;

View File

@ -17,7 +17,6 @@
package org.bitcoinj.params; package org.bitcoinj.params;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.Utils; import org.bitcoinj.core.Utils;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
@ -26,7 +25,7 @@ import static com.google.common.base.Preconditions.checkState;
* Parameters for the testnet, a separate public instance of Bitcoin that has relaxed rules suitable for development * Parameters for the testnet, a separate public instance of Bitcoin that has relaxed rules suitable for development
* and testing of applications and new Bitcoin versions. * and testing of applications and new Bitcoin versions.
*/ */
public class TestNet3Params extends NetworkParameters { public class TestNet3Params extends AbstractBitcoinNetParams {
public TestNet3Params() { public TestNet3Params() {
super(); super();
id = ID_TESTNET; id = ID_TESTNET;

View File

@ -24,7 +24,7 @@ import java.math.BigInteger;
* Network parameters used by the bitcoinj unit tests (and potentially your own). This lets you solve a block using * Network parameters used by the bitcoinj unit tests (and potentially your own). This lets you solve a block using
* {@link org.bitcoinj.core.Block#solve()} by setting difficulty to the easiest possible. * {@link org.bitcoinj.core.Block#solve()} by setting difficulty to the easiest possible.
*/ */
public class UnitTestParams extends NetworkParameters { public class UnitTestParams extends AbstractBitcoinNetParams {
// A simple static key/address for re-use in unit tests, to speed things up. // A simple static key/address for re-use in unit tests, to speed things up.
public static ECKey TEST_KEY = new ECKey(); public static ECKey TEST_KEY = new ECKey();
public static Address TEST_ADDRESS; public static Address TEST_ADDRESS;

View File

@ -193,6 +193,7 @@ public class StoredPaymentChannelServerStates implements WalletExtension {
ServerState.StoredServerPaymentChannels.Builder builder = ServerState.StoredServerPaymentChannels.newBuilder(); ServerState.StoredServerPaymentChannels.Builder builder = ServerState.StoredServerPaymentChannels.newBuilder();
for (StoredServerChannel channel : mapChannels.values()) { for (StoredServerChannel channel : mapChannels.values()) {
// First a few asserts to make sure things won't break // First a few asserts to make sure things won't break
// TODO: Pull MAX_MONEY from network parameters
checkState(channel.bestValueToMe.signum() >= 0 && channel.bestValueToMe.compareTo(NetworkParameters.MAX_MONEY) < 0); checkState(channel.bestValueToMe.signum() >= 0 && channel.bestValueToMe.compareTo(NetworkParameters.MAX_MONEY) < 0);
checkState(channel.refundTransactionUnlockTimeSecs > 0); checkState(channel.refundTransactionUnlockTimeSecs > 0);
checkNotNull(channel.myKey.getPrivKeyBytes()); checkNotNull(channel.myKey.getPrivKeyBytes());

View File

@ -400,7 +400,7 @@ public class PaymentSession {
} }
// This won't ever happen in practice. It would only happen if the user provided outputs // This won't ever happen in practice. It would only happen if the user provided outputs
// that are obviously invalid. Still, we don't want to silently overflow. // that are obviously invalid. Still, we don't want to silently overflow.
if (totalValue.compareTo(NetworkParameters.MAX_MONEY) > 0) if (params.hasMaxMoney() && totalValue.compareTo(params.getMaxMoney()) > 0)
throw new PaymentProtocolException.InvalidOutputs("The outputs are way too big."); throw new PaymentProtocolException.InvalidOutputs("The outputs are way too big.");
} catch (InvalidProtocolBufferException e) { } catch (InvalidProtocolBufferException e) {
throw new PaymentProtocolException(e); throw new PaymentProtocolException(e);

View File

@ -20,6 +20,7 @@ import org.bitcoinj.core.Address;
import org.bitcoinj.core.AddressFormatException; import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.params.AbstractBitcoinNetParams;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -91,6 +92,12 @@ public class BitcoinURI {
public static final String FIELD_ADDRESS = "address"; public static final String FIELD_ADDRESS = "address";
public static final String FIELD_PAYMENT_REQUEST_URL = "r"; public static final String FIELD_PAYMENT_REQUEST_URL = "r";
/**
* URI for Bitcoin network. Use {@link org.bitcoinj.params.AbstractBitcoinNetParams.BITCOIN_SCHEME} if you specifically
* need Bitcoin, or use {@link org.bitcoinj.core.NetworkParams.getUriScheme()} to get the scheme
* from network parameters.
*/
@Deprecated
public static final String BITCOIN_SCHEME = "bitcoin"; public static final String BITCOIN_SCHEME = "bitcoin";
private static final String ENCODED_SPACE_CHARACTER = "%20"; private static final String ENCODED_SPACE_CHARACTER = "%20";
private static final String AMPERSAND_SEPARATOR = "&"; private static final String AMPERSAND_SEPARATOR = "&";
@ -124,6 +131,10 @@ public class BitcoinURI {
checkNotNull(input); checkNotNull(input);
log.debug("Attempting to parse '{}' for {}", input, params == null ? "any" : params.getId()); log.debug("Attempting to parse '{}' for {}", input, params == null ? "any" : params.getId());
String scheme = null == params
? AbstractBitcoinNetParams.BITCOIN_SCHEME
: params.getUriScheme();
// Attempt to form the URI (fail fast syntax checking to official standards). // Attempt to form the URI (fail fast syntax checking to official standards).
URI uri; URI uri;
try { try {
@ -141,11 +152,13 @@ public class BitcoinURI {
// For instance with : bitcoin:129mVqKUmJ9uwPxKJBnNdABbuaaNfho4Ha?amount=0.06&label=Tom%20%26%20Jerry // For instance with : bitcoin:129mVqKUmJ9uwPxKJBnNdABbuaaNfho4Ha?amount=0.06&label=Tom%20%26%20Jerry
// the & (%26) in Tom and Jerry gets interpreted as a separator and the label then gets parsed // the & (%26) in Tom and Jerry gets interpreted as a separator and the label then gets parsed
// as 'Tom ' instead of 'Tom & Jerry') // as 'Tom ' instead of 'Tom & Jerry')
String blockchainInfoScheme = scheme + "://";
String correctScheme = scheme + ":";
String schemeSpecificPart; String schemeSpecificPart;
if (input.startsWith("bitcoin://")) { if (input.startsWith(blockchainInfoScheme)) {
schemeSpecificPart = input.substring("bitcoin://".length()); schemeSpecificPart = input.substring(blockchainInfoScheme.length());
} else if (input.startsWith("bitcoin:")) { } else if (input.startsWith(correctScheme)) {
schemeSpecificPart = input.substring("bitcoin:".length()); schemeSpecificPart = input.substring(correctScheme.length());
} else { } else {
throw new BitcoinURIParseException("Unsupported URI scheme: " + uri.getScheme()); throw new BitcoinURIParseException("Unsupported URI scheme: " + uri.getScheme());
} }

View File

@ -25,6 +25,7 @@ public class DefaultCoinSelector implements CoinSelector {
ArrayList<TransactionOutput> sortedOutputs = new ArrayList<TransactionOutput>(candidates); ArrayList<TransactionOutput> sortedOutputs = new ArrayList<TransactionOutput>(candidates);
// When calculating the wallet balance, we may be asked to select all possible coins, if so, avoid sorting // When calculating the wallet balance, we may be asked to select all possible coins, if so, avoid sorting
// them in order to improve performance. // them in order to improve performance.
// TODO: Take in network parameters when instanatiated, and then test against the current network. Or just have a boolean parameter for "give me everything"
if (!target.equals(NetworkParameters.MAX_MONEY)) { if (!target.equals(NetworkParameters.MAX_MONEY)) {
sortOutputs(sortedOutputs); sortOutputs(sortedOutputs);
} }