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

Added Digibyte mainnet params, based on Litecoin classes, but using constants from https://github.com/DigiByte-Core/digibytej

This implementation is incomplete and may need modifications before it is fully usable.
This commit is contained in:
CalDescent 2022-04-22 14:03:11 +01:00
parent dcbc42885e
commit 247eaabc22
2 changed files with 446 additions and 0 deletions

View File

@ -0,0 +1,320 @@
/*
* 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.
*/
/*
* Adapted for Digibyte in April 2022 by Qortal dev team
* Thanks to https://github.com/DigiByte-Core/digibytej for the references
*/
package org.libdohj.params;
import org.bitcoinj.core.*;
import org.bitcoinj.store.BlockStore;
import org.bitcoinj.store.BlockStoreException;
import org.bitcoinj.utils.MonetaryFormat;
import org.libdohj.core.AltcoinNetworkParameters;
import org.libdohj.core.AltcoinSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import static org.bitcoinj.core.Coin.COIN;
/**
* Common parameters for Digibyte networks.
*/
public abstract class AbstractDigibyteParams extends NetworkParameters implements AltcoinNetworkParameters {
/** Standard format for the DGB denomination. */
public static final MonetaryFormat DGB;
/** Standard format for the mDGB denomination. */
public static final MonetaryFormat MDGB;
/** Standard format for the "Digioshi" denomination (I have no idea what to call this). */
public static final MonetaryFormat DIGIOSHI;
public static final int DIGI_TARGET_TIMESPAN = (int)(0.10 * 24 * 60 * 60); // 72 minutes per difficulty cycle, on average.
public static final int DIGI_TARGET_SPACING = (int)(1 * 60); // 40 seconds per block.
public static final int DIGI_INTERVAL = TARGET_TIMESPAN / TARGET_SPACING; //108 blocks
/**
* The maximum number of coins to be generated
*/
public static final long MAX_COINS = 21000000;
/**
* The maximum money to be generated
*/
public static final Coin MAX_DIGIBYTE_MONEY = COIN.multiply(MAX_COINS);
/** Currency code for base 1 Digibyte. */
public static final String CODE_DGB = "DGB";
/** Currency code for base 1/1,000 Digibyte. */
public static final String CODE_MDGB = "mDGB";
/** Currency code for base 1/100,000,000 Digibyte. */
public static final String CODE_DIGIOSHI = "Digioshi";
static {
DGB = MonetaryFormat.BTC.noCode()
.code(0, CODE_DGB)
.code(3, CODE_MDGB)
.code(7, CODE_DIGIOSHI);
MDGB = DGB.shift(3).minDecimals(2).optionalDecimals(2);
DIGIOSHI = DGB.shift(7).minDecimals(0).optionalDecimals(2);
}
/** The string returned by getId() for the main, production network where people trade things. */
public static final String ID_DGB_MAINNET = "main";
/** The string returned by getId() for the testnet. */
public static final String ID_DGB_TESTNET = "test";
/** The string returned by getId() for regtest. */
public static final String ID_DGB_REGTEST = "regtest";
public static final int DIGIBYTE_PROTOCOL_VERSION_MINIMUM = 70000;
public static final int DIGIBYTE_PROTOCOL_VERSION_CURRENT = 70017;
private static final Coin BASE_SUBSIDY = COIN.multiply(50);
protected Logger log = LoggerFactory.getLogger(AbstractDigibyteParams.class);
public AbstractDigibyteParams() {
super();
interval = DIGI_INTERVAL;
targetTimespan = DIGI_TARGET_TIMESPAN;
maxTarget = Utils.decodeCompactBits(0x1e0fffffL);
packetMagic = 0xfbc0b6db;
bip32HeaderP2PKHpub = 0x0488C42E; //The 4 byte header that serializes in base58 to "xpub". (?)
bip32HeaderP2PKHpriv = 0x0488E1F4; //The 4 byte header that serializes in base58 to "xprv" (?)
}
@Override
public Coin getBlockSubsidy(final int height) {
return BASE_SUBSIDY.shiftRight(height / getSubsidyDecreaseBlockCount());
}
/**
* Get the hash to use for a block.
*/
@Override
public Sha256Hash getBlockDifficultyHash(Block block) {
return ((AltcoinBlock) block).getScryptHash();
}
public MonetaryFormat getMonetaryFormat() {
return DGB;
}
@Override
public Coin getMaxMoney() {
return MAX_DIGIBYTE_MONEY;
}
@Override
public Coin getMinNonDustOutput() {
return Coin.valueOf(546);
}
@Override
public String getUriScheme() {
return "digibyte";
}
@Override
public boolean hasMaxMoney() {
return true;
}
@Override
public void checkDifficultyTransitions(StoredBlock storedPrev, Block nextBlock, BlockStore blockStore)
throws VerificationException, BlockStoreException {
try {
final long newTargetCompact = calculateNewDifficultyTarget(storedPrev, nextBlock, blockStore);
final long receivedTargetCompact = nextBlock.getDifficultyTarget();
if (newTargetCompact != receivedTargetCompact)
throw new VerificationException("Network provided difficulty bits do not match what was calculated: " +
newTargetCompact + " vs " + receivedTargetCompact);
} catch (CheckpointEncounteredException ex) {
// Just have to take it on trust then
}
}
/**
* Get the difficulty target expected for the next block. This includes all
* the weird cases for Litecoin such as testnet blocks which can be maximum
* difficulty if the block interval is high enough.
* TODO: this may need updating for Digibyte; it is currently copied from Litecoin
*
* @throws CheckpointEncounteredException if a checkpoint is encountered while
* calculating difficulty target, and therefore no conclusive answer can
* be provided.
*/
public long calculateNewDifficultyTarget(StoredBlock storedPrev, Block nextBlock, BlockStore blockStore)
throws VerificationException, BlockStoreException, CheckpointEncounteredException {
final Block prev = storedPrev.getHeader();
final int previousHeight = storedPrev.getHeight();
final int retargetInterval = this.getInterval();
// Is this supposed to be a difficulty transition point?
if ((storedPrev.getHeight() + 1) % retargetInterval != 0) {
if (this.allowMinDifficultyBlocks()) {
// Special difficulty rule for testnet:
// If the new block's timestamp is more than 5 minutes
// then allow mining of a min-difficulty block.
if (nextBlock.getTimeSeconds() > prev.getTimeSeconds() + getTargetSpacing() * 2) {
return Utils.encodeCompactBits(maxTarget);
} else {
// Return the last non-special-min-difficulty-rules-block
StoredBlock cursor = storedPrev;
while (cursor.getHeight() % retargetInterval != 0
&& cursor.getHeader().getDifficultyTarget() == Utils.encodeCompactBits(this.getMaxTarget())) {
StoredBlock prevCursor = cursor.getPrev(blockStore);
if (prevCursor == null) {
break;
}
cursor = prevCursor;
}
return cursor.getHeader().getDifficultyTarget();
}
}
// No ... so check the difficulty didn't actually change.
return prev.getDifficultyTarget();
}
// 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.
StoredBlock cursor = storedPrev;
int goBack = retargetInterval - 1;
// Litecoin: This fixes an issue where a 51% attack can change difficulty at will.
// Go back the full period unless it's the first retarget after genesis.
// Code based on original by Art Forz
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(
"Difficulty transition point but we did not find a way back to the genesis block.");
}
cursor = blockStore.get(cursor.getHeader().getPrevBlockHash());
}
//We used checkpoints...
if (cursor == null) {
log.debug("Difficulty transition: Hit checkpoint!");
throw new CheckpointEncounteredException();
}
Block blockIntervalAgo = cursor.getHeader();
return this.calculateNewDifficultyTargetInner(previousHeight, prev.getTimeSeconds(),
prev.getDifficultyTarget(), blockIntervalAgo.getTimeSeconds(),
nextBlock.getDifficultyTarget());
}
/**
* Calculate the difficulty target expected for the next block after a normal
* recalculation interval. Does not handle special cases such as testnet blocks
* being setting the target to maximum for blocks after a long interval.
*
* @param previousHeight height of the block immediately before the retarget.
* @param prev the block immediately before the retarget block.
* @param nextBlock the block the retarget happens at.
* @param blockIntervalAgo The last retarget block.
* @return New difficulty target as compact bytes.
*/
protected long calculateNewDifficultyTargetInner(int previousHeight, final Block prev,
final Block nextBlock, final Block blockIntervalAgo) {
return this.calculateNewDifficultyTargetInner(previousHeight, prev.getTimeSeconds(),
prev.getDifficultyTarget(), blockIntervalAgo.getTimeSeconds(),
nextBlock.getDifficultyTarget());
}
/**
*
* @param previousHeight Height of the block immediately previous to the one we're calculating difficulty of.
* @param previousBlockTime Time of the block immediately previous to the one we're calculating difficulty of.
* @param lastDifficultyTarget Compact difficulty target of the last retarget block.
* @param lastRetargetTime Time of the last difficulty retarget.
* @param nextDifficultyTarget The expected difficulty target of the next
* block, used for determining precision of the result.
* @return New difficulty target as compact bytes.
*/
protected long calculateNewDifficultyTargetInner(int previousHeight, long previousBlockTime,
final long lastDifficultyTarget, final long lastRetargetTime,
final long nextDifficultyTarget) {
final int retargetTimespan = this.getTargetTimespan();
int actualTime = (int) (previousBlockTime - lastRetargetTime);
final int minTimespan = retargetTimespan / 4;
final int maxTimespan = retargetTimespan * 4;
actualTime = Math.min(maxTimespan, Math.max(minTimespan, actualTime));
BigInteger newTarget = Utils.decodeCompactBits(lastDifficultyTarget);
newTarget = newTarget.multiply(BigInteger.valueOf(actualTime));
newTarget = newTarget.divide(BigInteger.valueOf(retargetTimespan));
if (newTarget.compareTo(this.getMaxTarget()) > 0) {
log.info("Difficulty hit proof of work limit: {}", newTarget.toString(16));
newTarget = this.getMaxTarget();
}
int accuracyBytes = (int) (nextDifficultyTarget >>> 24) - 3;
// 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);
return Utils.encodeCompactBits(newTarget);
}
@Override
public AltcoinSerializer getSerializer(boolean parseRetain) {
return new AltcoinSerializer(this, parseRetain);
}
@Override
public int getProtocolVersionNum(final ProtocolVersion version) {
switch (version) {
case PONG:
case BLOOM_FILTER:
return version.getBitcoinProtocolVersion();
case CURRENT:
return DIGIBYTE_PROTOCOL_VERSION_CURRENT;
case MINIMUM:
default:
return DIGIBYTE_PROTOCOL_VERSION_MINIMUM;
}
}
/**
* Whether this network has special rules to enable minimum difficulty blocks
* after a long interval between two blocks (i.e. testnet).
*/
public boolean allowMinDifficultyBlocks() {
return this.isTestNet();
}
public int getTargetSpacing() {
return this.getTargetTimespan() / this.getInterval();
}
private static class CheckpointEncounteredException extends Exception { }
}

View File

@ -0,0 +1,126 @@
/*
* Copyright 2013 Google Inc.
* Copyright 2014 Andreas Schildbach
*
* 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.
*/
/*
* Adapted for Digibyte in April 2022 by Qortal dev team
* Thanks to https://github.com/DigiByte-Core/digibytej for the references
*/
package org.libdohj.params;
import org.bitcoinj.core.*;
import org.bitcoinj.params.MainNetParams;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptOpCodes;
import org.bouncycastle.util.encoders.Hex;
import java.io.ByteArrayOutputStream;
import static com.google.common.base.Preconditions.checkState;
import static org.bitcoinj.core.Coin.COIN;
/**
* Parameters for the Digibyte main production network on which people trade
* goods and services.
*/
public class DigibyteMainNetParams extends AbstractDigibyteParams {
public static final int MAINNET_MAJORITY_WINDOW = MainNetParams.MAINNET_MAJORITY_WINDOW;
public static final int MAINNET_MAJORITY_REJECT_BLOCK_OUTDATED = MainNetParams.MAINNET_MAJORITY_REJECT_BLOCK_OUTDATED;
public static final int MAINNET_MAJORITY_ENFORCE_BLOCK_UPGRADE = MainNetParams.MAINNET_MAJORITY_ENFORCE_BLOCK_UPGRADE;
public DigibyteMainNetParams() {
super();
id = ID_DGB_MAINNET;
// Genesis hash is 7497ea1b465eb39f1c8f507bc877078fe016d6fcb6dfad3a64c98dcc6e1e8496
packetMagic = 0xfac3b6da;
maxTarget = Utils.decodeCompactBits(0x1e0fffffL);
port = 12024;
addressHeader = 30;
p2shHeader = 63;
segwitAddressHrp = "dgb";
// acceptableAddressCodes = new int[] { addressHeader, p2shHeader };
dumpedPrivateKeyHeader = 128;
this.genesisBlock = createGenesis(this);
spendableCoinbaseDepth = 100;
subsidyDecreaseBlockCount = 210000;
String genesisHash = genesisBlock.getHashAsString();
checkState(genesisHash.equals("7497ea1b465eb39f1c8f507bc877078fe016d6fcb6dfad3a64c98dcc6e1e8496"));
alertSigningKey = Hex.decode("040184710fa689ad5023690c80f3a49c8f13f8d45b8c857fbcbc8bc4a8e4d3eb4b10f4d4604fa08dce601aaf0f470216fe1b51850b4acf21b179c45070ac7b03a9");
majorityEnforceBlockUpgrade = MAINNET_MAJORITY_ENFORCE_BLOCK_UPGRADE;
majorityRejectBlockOutdated = MAINNET_MAJORITY_REJECT_BLOCK_OUTDATED;
majorityWindow = MAINNET_MAJORITY_WINDOW;
dnsSeeds = new String[] {
"seed1.digibyte.io",
"seed2.digibyte.io",
"seed3.digibyte.io",
"seed.digibyte.io",
"digihash.co",
"digiexplorer.info",
"seed.digibyteprojects.com"
};
bip32HeaderP2PKHpub = 0x0488b21e; // The 4 byte header that serializes in base58 to "xpub".
bip32HeaderP2PKHpriv = 0x0488ade4; // The 4 byte header that serializes in base58 to "xprv"
bip32HeaderP2WPKHpub = 0x04b24746; // The 4 byte header that serializes in base58 to "zpub".
bip32HeaderP2WPKHpriv = 0x04b2430c; // The 4 byte header that serializes in base58 to "zprv"
}
private static AltcoinBlock createGenesis(NetworkParameters params) {
AltcoinBlock genesisBlock = new AltcoinBlock(params, Block.BLOCK_VERSION_GENESIS);
Transaction t = new Transaction(params);
try {
byte[] bytes = Hex.decode
("04ffff001d0104404e592054696d65732030352f4f63742f32303131205374657665204a6f62732c204170706c65e280997320566973696f6e6172792c2044696573206174203536");
t.addInput(new TransactionInput(params, t, bytes));
ByteArrayOutputStream scriptPubKeyBytes = new ByteArrayOutputStream();
Script.writeBytes(scriptPubKeyBytes, Hex.decode
("040184710fa689ad5023690c80f3a49c8f13f8d45b8c857fbcbc8bc4a8e4d3eb4b10f4d4604fa08dce601aaf0f470216fe1b51850b4acf21b179c45070ac7b03a9"));
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);
genesisBlock.setTime(1317972665L);
genesisBlock.setDifficultyTarget(0x1e0ffff0L);
genesisBlock.setNonce(2084524493);
return genesisBlock;
}
private static DigibyteMainNetParams instance;
public static synchronized DigibyteMainNetParams get() {
if (instance == null) {
instance = new DigibyteMainNetParams();
}
return instance;
}
@Override
public String getPaymentProtocolId() {
return ID_DGB_MAINNET;
}
@Override
public boolean isTestNet() {
return false;
}
}