3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-11 09:45:53 +00:00

Added network parameters for Namecoin (abstract and mainnet).

This commit is contained in:
JeremyRand 2016-06-29 23:52:58 +00:00
parent bacc348541
commit 0cb659ca21
2 changed files with 411 additions and 0 deletions

View File

@ -0,0 +1,291 @@
/*
* Copyright 2016 Jeremy Rand.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.libdohj.params;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.util.concurrent.TimeUnit;
import com.google.common.base.Stopwatch;
import org.bitcoinj.core.AltcoinBlock;
import org.bitcoinj.core.Block;
import org.bitcoinj.core.Coin;
import static org.bitcoinj.core.Coin.COIN;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.VerificationException;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptOpCodes;
import org.bitcoinj.store.BlockStore;
import org.bitcoinj.store.BlockStoreException;
import org.bitcoinj.utils.MonetaryFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.bitcoinj.core.Sha256Hash;
import org.bitcoinj.core.StoredBlock;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionInput;
import org.bitcoinj.core.TransactionOutput;
import org.bitcoinj.core.Utils;
import org.libdohj.core.AltcoinSerializer;
import org.libdohj.core.AuxPoWNetworkParameters;
// TODO: review this
/**
* Common parameters for Namecoin networks.
*/
public abstract class AbstractNamecoinParams extends NetworkParameters implements AuxPoWNetworkParameters {
/** Standard format for the NMC denomination. */
public static final MonetaryFormat NMC;
/** Standard format for the mNMC denomination. */
public static final MonetaryFormat MNMC;
/** Standard format for the uBTC denomination. */
public static final MonetaryFormat UNMC;
public static final int AUXPOW_CHAIN_ID = 0x0001; // 1
/** Currency code for base 1 Namecoin. */
public static final String CODE_NMC = "NMC";
/** Currency code for base 1/1,000 Namecoin. */
public static final String CODE_MNMC = "mNMC";
/** Currency code for base 1/1,000,000 Namecoin. */
public static final String CODE_UNMC = "µNMC";
protected int auxpowStartHeight;
private static final int BLOCK_VERSION_FLAG_AUXPOW = 0x00000100;
static {
NMC = MonetaryFormat.BTC.noCode()
.code(0, CODE_NMC)
.code(3, CODE_MNMC)
.code(6, CODE_UNMC);
MNMC = NMC.shift(3).minDecimals(2).optionalDecimals(2);
UNMC = NMC.shift(6).minDecimals(0).optionalDecimals(0);
}
/** The string returned by getId() for the main, production network where people trade things. */
public static final String ID_NMC_MAINNET = "org.namecoin.production";
/** The string returned by getId() for the testnet. */
public static final String ID_NMC_TESTNET = "org.namecoin.test";
protected Logger log = LoggerFactory.getLogger(AbstractNamecoinParams.class);
public static final int NAMECOIN_PROTOCOL_VERSION_GETHEADERS = 38000;
public AbstractNamecoinParams() {
super();
genesisBlock = createGenesis(this);
interval = INTERVAL;
targetTimespan = TARGET_TIMESPAN;
maxTarget = Utils.decodeCompactBits(0x1e0fffffL); // TODO: figure out the Namecoin value of this
// BIP 43 recommends using these values regardless of which blockchain is in use.
bip32HeaderPub = 0x0488B21E; //The 4 byte header that serializes in base58 to "xpub".
bip32HeaderPriv = 0x0488ADE4; //The 4 byte header that serializes in base58 to "xprv"
}
private static AltcoinBlock createGenesis(NetworkParameters params) {
AltcoinBlock genesisBlock = new AltcoinBlock(params, Block.BLOCK_VERSION_GENESIS);
Transaction t = new Transaction(params);
try {
// "... choose what comes next. Lives of your own, or a return to chains. -- V"
byte[] bytes = Utils.HEX.decode
("04ff7f001c020a024b2e2e2e2063686f6f7365207768617420636f6d6573206e6578742e20204c69766573206f6620796f7572206f776e2c206f7220612072657475726e20746f20636861696e732e202d2d2056");
t.addInput(new TransactionInput(params, t, bytes));
ByteArrayOutputStream scriptPubKeyBytes = new ByteArrayOutputStream();
Script.writeBytes(scriptPubKeyBytes, Utils.HEX.decode
("04b620369050cd899ffbbc4e8ee51e8c4534a855bb463439d63d235d4779685d8b6f4870a238cf365ac94fa13ef9a2a22cd99d0d5ee86dcabcafce36c7acf43ce5"));
scriptPubKeyBytes.write(ScriptOpCodes.OP_CHECKSIG);
t.addOutput(new TransactionOutput(params, t, COIN.multiply(50), scriptPubKeyBytes.toByteArray()));
} catch (Exception e) {
// Cannot happen.
throw new RuntimeException(e);
}
genesisBlock.addTransaction(t);
return genesisBlock;
}
@Override
public Coin getBlockSubsidy(final int height) {
return COIN.multiply(50).shiftRight(height / getSubsidyDecreaseBlockCount());
}
@Override
public MonetaryFormat getMonetaryFormat() {
return NMC;
}
@Override
public Coin getMaxMoney() {
return MAX_MONEY;
}
// TODO: this is Bitcoin, need to figure out if it's the same for Namecoin
@Override
public Coin getMinNonDustOutput() {
return Transaction.MIN_NONDUST_OUTPUT;
}
@Override
public String getUriScheme() {
return "namecoin";
}
@Override
public boolean hasMaxMoney() {
return true;
}
// This is copied from Bitcoin
/**
* Checks if we are at a difficulty transition point.
* @param storedPrev The previous stored block
* @return If this is a difficulty transition point
*/
protected boolean isDifficultyTransitionPoint(StoredBlock storedPrev) {
return ((storedPrev.getHeight() + 1) % this.getInterval()) == 0;
}
@Override
public void checkDifficultyTransitions(StoredBlock storedPrev, Block nextBlock, BlockStore blockStore)
throws VerificationException, BlockStoreException {
// This is copied verbatim from Bitcoin except for the Namecoin changes marked accordingly
Block prev = storedPrev.getHeader();
// Is this supposed to be a difficulty transition point?
if (!isDifficultyTransitionPoint(storedPrev)) {
// No ... so check the difficulty didn't actually change.
if (nextBlock.getDifficultyTarget() != prev.getDifficultyTarget())
throw new VerificationException("Unexpected change in difficulty at height " + storedPrev.getHeight() +
": " + Long.toHexString(nextBlock.getDifficultyTarget()) + " vs " +
Long.toHexString(prev.getDifficultyTarget()));
return;
}
// We need to find a block far back in the chain. It's OK that this is expensive because it only occurs every
// two weeks after the initial block chain download.
final Stopwatch watch = Stopwatch.createStarted();
StoredBlock cursor = blockStore.get(prev.getHash());
// Namecoin addition
int blocksBack = this.getInterval() - 1;
if (storedPrev.getHeight() >= this.getAuxpowStartHeight() && (storedPrev.getHeight() + 1 > this.getInterval())) {
blocksBack = this.getInterval();
}
// Namecoin modification
//for (int i = 0; i < this.getInterval() - 1; i++) {
for (int i = 0; i < blocksBack; i++) {
if (cursor == null) {
// This should never happen. If it does, it means we are following an incorrect or busted chain.
throw new VerificationException(
"Difficulty transition point but we did not find a way back to the genesis block.");
}
cursor = blockStore.get(cursor.getHeader().getPrevBlockHash());
}
watch.stop();
if (watch.elapsed(TimeUnit.MILLISECONDS) > 50)
log.info("Difficulty transition traversal took {}", watch);
Block blockIntervalAgo = cursor.getHeader();
int timespan = (int) (prev.getTimeSeconds() - blockIntervalAgo.getTimeSeconds());
// Limit the adjustment step.
final int targetTimespan = this.getTargetTimespan();
if (timespan < targetTimespan / 4)
timespan = targetTimespan / 4;
if (timespan > targetTimespan * 4)
timespan = targetTimespan * 4;
BigInteger newTarget = Utils.decodeCompactBits(prev.getDifficultyTarget());
newTarget = newTarget.multiply(BigInteger.valueOf(timespan));
newTarget = newTarget.divide(BigInteger.valueOf(targetTimespan));
if (newTarget.compareTo(this.getMaxTarget()) > 0) {
log.info("Difficulty hit proof of work limit: {}", newTarget.toString(16));
newTarget = this.getMaxTarget();
}
int accuracyBytes = (int) (nextBlock.getDifficultyTarget() >>> 24) - 3;
long receivedTargetCompact = nextBlock.getDifficultyTarget();
// The calculated difficulty is to a higher precision than received, so reduce here.
BigInteger mask = BigInteger.valueOf(0xFFFFFFL).shiftLeft(accuracyBytes * 8);
newTarget = newTarget.and(mask);
long newTargetCompact = Utils.encodeCompactBits(newTarget);
if (newTargetCompact != receivedTargetCompact)
throw new VerificationException("Network provided difficulty bits do not match what was calculated: " +
Long.toHexString(newTargetCompact) + " vs " + Long.toHexString(receivedTargetCompact));
}
@Override
public int getChainID() {
return AUXPOW_CHAIN_ID;
}
// TODO: re-add this when we introduce Testnet2
/**
* Whether this network has special rules to enable minimum difficulty blocks
* after a long interval between two blocks (i.e. testnet).
*/
//public abstract boolean allowMinDifficultyBlocks();
/**
* Get the hash to use for a block.
*/
@Override
public Sha256Hash getBlockDifficultyHash(Block block) {
return block.getHash();
}
@Override
public AltcoinSerializer getSerializer(boolean parseRetain) {
return new AltcoinSerializer(this, parseRetain);
}
// TODO: look into allowing PeerGroups that don't support GetHeaders (since for full block retrieval it's not needed)
@Override
public int getProtocolVersionNum(final ProtocolVersion version) {
switch (version) {
case MINIMUM:
return NAMECOIN_PROTOCOL_VERSION_GETHEADERS;
default:
return version.getBitcoinProtocolVersion();
}
}
@Override
public boolean isAuxPoWBlockVersion(long version) {
return (version & BLOCK_VERSION_FLAG_AUXPOW) > 0;
}
public int getAuxpowStartHeight() {
return auxpowStartHeight;
}
private static class CheckpointEncounteredException extends Exception {
private CheckpointEncounteredException() {
}
}
}

View File

@ -0,0 +1,120 @@
/*
* Copyright 2013 Google Inc, 2016 Jeremy Rand.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.libdohj.params;
import org.bitcoinj.core.Sha256Hash;
import org.spongycastle.util.encoders.Hex;
import static com.google.common.base.Preconditions.checkState;
// TODO: review this
/**
* Parameters for the main Namecoin production network on which people trade
* goods and services.
*/
public class NamecoinMainNetParams extends AbstractNamecoinParams {
public static final int MAINNET_MAJORITY_WINDOW = 1000;
public static final int MAINNET_MAJORITY_REJECT_BLOCK_OUTDATED = 950;
public static final int MAINNET_MAJORITY_ENFORCE_BLOCK_UPGRADE = 750;
public NamecoinMainNetParams() {
super();
dumpedPrivateKeyHeader = 180; //This is always addressHeader + 128
addressHeader = 52;
p2shHeader = 13;
//acceptableAddressCodes = new int[] { addressHeader, p2shHeader };
acceptableAddressCodes = new int[] { addressHeader }; // Namecoin doesn't yet enforce P2SH, so we disable it for now.
port = 8334;
packetMagic = 0xf9beb4fe;
genesisBlock.setDifficultyTarget(0x1C007FFFL);
genesisBlock.setTime(1303000001L);
genesisBlock.setNonce(2719916434L);
id = ID_NMC_MAINNET;
subsidyDecreaseBlockCount = 210000;
spendableCoinbaseDepth = 100;
auxpowStartHeight = 19200;
String genesisHash = genesisBlock.getHashAsString();
checkState(genesisHash.equals("000000000062b72c5e2ceb45fbc8587e807c155b0da735e6483dfba2f0a9c770"),
genesisHash);
// TODO: remove alert key since it's removed from Bitcoin Core / Namecoin Core
alertSigningKey = Hex.decode("04ba207043c1575208f08ea6ac27ed2aedd4f84e70b874db129acb08e6109a3bbb7c479ae22565973ebf0ac0391514511a22cb9345bdb772be20cfbd38be578b0c");
majorityEnforceBlockUpgrade = MAINNET_MAJORITY_ENFORCE_BLOCK_UPGRADE;
majorityRejectBlockOutdated = MAINNET_MAJORITY_REJECT_BLOCK_OUTDATED;
majorityWindow = MAINNET_MAJORITY_WINDOW;
// TODO: check whether there are any non BIP30 blocks in Namecoin; add them here if they exist
// This contains (at a minimum) the blocks which are not BIP30 compliant. BIP30 changed how duplicate
// transactions are handled. Duplicated transactions could occur in the case where a coinbase had the same
// extraNonce and the same outputs but appeared at different heights, and greatly complicated re-org handling.
// Having these here simplifies block connection logic considerably.
checkpoints.put( 2016, Sha256Hash.wrap("0000000000660bad0d9fbde55ba7ee14ddf766ed5f527e3fbca523ac11460b92"));
checkpoints.put( 4032, Sha256Hash.wrap("0000000000493b5696ad482deb79da835fe2385304b841beef1938655ddbc411"));
checkpoints.put( 6048, Sha256Hash.wrap("000000000027939a2e1d8bb63f36c47da858e56d570f143e67e85068943470c9"));
checkpoints.put( 8064, Sha256Hash.wrap("000000000003a01f708da7396e54d081701ea406ed163e519589717d8b7c95a5"));
checkpoints.put( 10080, Sha256Hash.wrap("00000000000fed3899f818b2228b4f01b9a0a7eeee907abd172852df71c64b06"));
checkpoints.put( 12096, Sha256Hash.wrap("0000000000006c06988ff361f124314f9f4bb45b6997d90a7ee4cedf434c670f"));
checkpoints.put( 14112, Sha256Hash.wrap("00000000000045d95e0588c47c17d593c7b5cb4fb1e56213d1b3843c1773df2b"));
checkpoints.put( 16128, Sha256Hash.wrap("000000000001d9964f9483f9096cf9d6c6c2886ed1e5dec95ad2aeec3ce72fa9"));
checkpoints.put( 18940, Sha256Hash.wrap("00000000000087f7fc0c8085217503ba86f796fa4984f7e5a08b6c4c12906c05"));
checkpoints.put( 30240, Sha256Hash.wrap("e1c8c862ff342358384d4c22fa6ea5f669f3e1cdcf34111f8017371c3c0be1da"));
checkpoints.put( 57000, Sha256Hash.wrap("aa3ec60168a0200799e362e2b572ee01f3c3852030d07d036e0aa884ec61f203"));
checkpoints.put(112896, Sha256Hash.wrap("73f880e78a04dd6a31efc8abf7ca5db4e262c4ae130d559730d6ccb8808095bf"));
checkpoints.put(182000, Sha256Hash.wrap("d47b4a8fd282f635d66ce34ebbeb26ffd64c35b41f286646598abfd813cba6d9"));
checkpoints.put(193000, Sha256Hash.wrap("3b85e70ba7f5433049cfbcf0ae35ed869496dbedcd1c0fafadb0284ec81d7b58"));
dnsSeeds = new String[] {
"namecoindnsseed.digi-masters.com", // George Lloyd
"namecoindnsseed.digi-masters.uk", // George Lloyd
"seed.namecoin.domob.eu", // Daniel Kraft
"nmc.seed.quisquis.de", // Peter Conrad
"dnsseed.namecoin.webbtc.com", // Marius Hanne
};
// TODO: look into HTTP seeds or Addr seeds as is done for Bitcoin
}
private static NamecoinMainNetParams instance;
public static synchronized NamecoinMainNetParams get() {
if (instance == null) {
instance = new NamecoinMainNetParams();
}
return instance;
}
// TODO: re-add this when we introduce Testnet2
/*
@Override
public boolean allowMinDifficultyBlocks() {
return false;
}
*/
@Override
public String getPaymentProtocolId() {
// TODO: CHANGE THIS (comment from Dogecoin)
return ID_NMC_MAINNET;
}
@Override
public boolean isTestNet() {
return false;
}
}