Skip to content

Commit

Permalink
Ferrite - Another edit - Scrypt and DAA
Browse files Browse the repository at this point in the history
Scrypt is used as the Proof of Work hash, not SHA256

Changed minimum version requirement to version 3.0.0

Added Difficulty Adjustment Algorithm (DAA) switch function and DAA function for height 250,000.
  • Loading branch information
koh-gt committed Aug 14, 2024
1 parent 1e91e5c commit c3ec25f
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 6 deletions.
35 changes: 34 additions & 1 deletion core/src/main/java/org/bitcoinj/core/Block.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public enum VerifyFlag {

/** Stores the hash of the block. If null, getHash() will recalculate it. */
private Sha256Hash hash;
private Sha256Hash powHash;

protected boolean headerBytesValid;
protected boolean transactionBytesValid;
Expand Down Expand Up @@ -384,6 +385,7 @@ private void unCacheHeader() {
if (!transactionBytesValid)
payload = null;
hash = null;
powHash = null;
}

private void unCacheTransactions() {
Expand Down Expand Up @@ -412,6 +414,20 @@ private Sha256Hash calculateHash() {
}
}

/**
* Calculates the block POW hash by serializing the block and hashing the
* resulting bytes.
*/
private Sha256Hash calculatePowHash() {
try {
ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(HEADER_SIZE);
writeHeader(bos);
return Sha256Hash.wrapReversed(Sha256Hash.hashScrypt(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 mainnet chain
Expand All @@ -432,6 +448,16 @@ public Sha256Hash getHash() {
return hash;
}

/**
* Returns the POW hash of the block (which for a valid, solved block should be
* below the target). Big endian.
*/
public Sha256Hash getPowHash() {
if (powHash == null)
powHash = calculatePowHash();
return powHash;
}

/**
* The number that is one greater than the largest representable SHA-256
* hash.
Expand Down Expand Up @@ -468,6 +494,7 @@ protected final void copyBitcoinHeaderTo(final Block block) {
block.difficultyTarget = difficultyTarget;
block.transactions = null;
block.hash = getHash();
block.powHash = getPowHash();
}

/**
Expand Down Expand Up @@ -544,7 +571,7 @@ protected boolean checkProofOfWork(boolean throwException) throws VerificationEx
// field is of the right value. This requires us to have the preceeding blocks.
BigInteger target = getDifficultyTargetAsInteger();

BigInteger h = getHash().toBigInteger();
BigInteger h = getPowHash().toBigInteger();
if (h.compareTo(target) > 0) {
// Proof of work check failed!
if (throwException)
Expand Down Expand Up @@ -748,6 +775,7 @@ public void setMerkleRoot(Sha256Hash value) {
unCacheHeader();
merkleRoot = value;
hash = null;
powHash = null;
}

/** Adds a transaction to this block. The nonce and merkle root are invalid after this. */
Expand All @@ -771,6 +799,7 @@ else if (runSanityChecks && transactions.size() > 0 && t.isCoinBase())
// Force a recalculation next time the values are needed.
merkleRoot = null;
hash = null;
powHash = null;
}

/** Returns the version of the block data structure as defined by the Bitcoin protocol. */
Expand All @@ -789,6 +818,7 @@ void setPrevBlockHash(Sha256Hash prevBlockHash) {
unCacheHeader();
this.prevBlockHash = prevBlockHash;
this.hash = null;
this.powHash = null;
}

/**
Expand All @@ -810,6 +840,7 @@ public void setTime(long time) {
unCacheHeader();
this.time = time;
this.hash = null;
this.powHash = null;
}

/**
Expand All @@ -830,6 +861,7 @@ public void setDifficultyTarget(long compactForm) {
unCacheHeader();
this.difficultyTarget = compactForm;
this.hash = null;
this.powHash = null;
}

/**
Expand All @@ -845,6 +877,7 @@ public void setNonce(long nonce) {
unCacheHeader();
this.nonce = nonce;
this.hash = null;
this.powHash = null;
}

/** Returns an immutable list of transactions held in this block, or null if this object represents just a header. */
Expand Down
6 changes: 3 additions & 3 deletions core/src/main/java/org/bitcoinj/core/NetworkParameters.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public abstract class NetworkParameters {
* The alert signing key originally owned by Satoshi, and now passed on to Gavin along with a few others.
*/
public static final byte[] SATOSHI_KEY = Utils.HEX.decode("04fc9702847840aaf195de8442ebecedf5b095cdbb9bc716bda9110971b28a49e0ead8564ff0db22209e0374782c093bb899692d524e9d6a6956e7c5ecbcd68284");

// Alert: removed from Bitcoin Core since March 2016 - 1AGRxqDa5WjUKBwHB9XYEjmkv1ucoUUy1s
/** The string returned by getId() for the main, production network where people trade things. */
public static final String ID_MAINNET = "org.bitcoin.production";
/** The string returned by getId() for the testnet. */
Expand Down Expand Up @@ -524,10 +524,10 @@ public EnumSet<Script.VerifyFlag> getTransactionVerificationFlags(final Block bl
public abstract int getProtocolVersionNum(final ProtocolVersion version);

public static enum ProtocolVersion {
MINIMUM(70000),
MINIMUM(30000), // Minimum v3 after Ferrite DGWv3 DAA fork
PONG(60001),
BLOOM_FILTER(70000),
CURRENT(70001);
CURRENT(30104); // Latest is v3.1.4 as of 20240815

private final int bitcoinProtocol;

Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/bitcoinj/core/PeerGroup.java
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ public int compare(PeerAddress a, PeerAddress b) {
peerDiscoverers = new CopyOnWriteArraySet<PeerDiscovery>();
runningBroadcasts = Collections.synchronizedSet(new HashSet<TransactionBroadcast>());
bloomFilterMerger = new FilterMerger(DEFAULT_BLOOM_FILTER_FP_RATE);
vMinRequiredProtocolVersion = params.getProtocolVersionNum(NetworkParameters.ProtocolVersion.BLOOM_FILTER);
vMinRequiredProtocolVersion = isBloomFilteringEnabled() ? params.getProtocolVersionNum(NetworkParameters.ProtocolVersion.BLOOM_FILTER) : params.getProtocolVersionNum(NetworkParameters.ProtocolVersion.MINIMUM);
}

private CountDownLatch executorStartupLatch = new CountDownLatch(1);
Expand Down
9 changes: 9 additions & 0 deletions core/src/main/java/org/bitcoinj/core/Sha256Hash.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import com.google.common.io.ByteStreams;
import com.google.common.primitives.*;
import com.lambdaworks.crypto.SCrypt;

import java.io.File;
import java.io.FileInputStream;
Expand Down Expand Up @@ -279,4 +280,12 @@ public int compareTo(final Sha256Hash other) {
}
return 0;
}

public static byte[] hashScrypt(byte[] input) {
try {
return SCrypt.scrypt(input, input, 1024, 1, 1, 32);
} catch (Exception e) {
return null;
}
}
}
113 changes: 113 additions & 0 deletions core/src/main/java/org/bitcoinj/params/AbstractBitcoinNetParams.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
package org.bitcoinj.params;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

import org.bitcoinj.core.Block;
Expand All @@ -37,6 +39,8 @@

import org.bitcoinj.core.BitcoinSerializer;

import static com.google.common.base.Preconditions.checkState;

/**
* Parameters for Bitcoin-like networks.
*/
Expand All @@ -48,6 +52,11 @@ public abstract class AbstractBitcoinNetParams extends NetworkParameters {

private static final Logger log = LoggerFactory.getLogger(AbstractBitcoinNetParams.class);


protected int powDGWHeight;
protected boolean powAllowMinimumDifficulty;
protected boolean powNoRetargeting;

public AbstractBitcoinNetParams() {
super();
}
Expand All @@ -61,8 +70,24 @@ protected boolean isDifficultyTransitionPoint(StoredBlock storedPrev) {
return ((storedPrev.getHeight() + 1) % this.getInterval()) == 0;
}

protected boolean isDifficultyTransitionPoint(int height) {
return height >= powDGWHeight ? true :
((height + 1) % this.getInterval()) == 0;
}

// DAA selector function
@Override
public void checkDifficultyTransitions(final StoredBlock storedPrev, final Block nextBlock,
final BlockStore blockStore) throws VerificationException, BlockStoreException {
int height = storedPrev.getHeight() + 1;
if(height >= powDGWHeight) {
DarkGravityWave(storedPrev, nextBlock, blockStore);
} else {
checkDifficultyTransitions_btc(storedPrev, nextBlock, blockStore);
}
}

public void checkDifficultyTransitions_btc(final StoredBlock storedPrev, final Block nextBlock,
final BlockStore blockStore) throws VerificationException, BlockStoreException {
Block prev = storedPrev.getHeader();

Expand Down Expand Up @@ -124,6 +149,94 @@ public void checkDifficultyTransitions(final StoredBlock storedPrev, final Block
Long.toHexString(newTargetCompact) + " vs " + Long.toHexString(receivedTargetCompact));
}

protected long calculateNextDifficulty(StoredBlock storedBlock, Block nextBlock, BigInteger newTarget) {
if (newTarget.compareTo(this.getMaxTarget()) > 0) {
newTarget = this.getMaxTarget();
}

int accuracyBytes = (int) (nextBlock.getDifficultyTarget() >>> 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);
}

protected void verifyDifficulty(StoredBlock storedPrev, Block nextBlock, BigInteger newTarget) throws VerificationException {
long newTargetCompact = calculateNextDifficulty(storedPrev, nextBlock, newTarget);
long receivedTargetCompact = nextBlock.getDifficultyTarget();

if (newTargetCompact != receivedTargetCompact)
throw new VerificationException("Network provided difficulty bits do not match what was calculated: " +
Long.toHexString(newTargetCompact) + " vs " + Long.toHexString(receivedTargetCompact));
}

public void DarkGravityWave(StoredBlock storedPrev, Block nextBlock,
final BlockStore blockStore) throws VerificationException {
/* current difficulty formula, darkcoin - DarkGravity v3, written by Evan Duffield - [email protected] */
long pastBlocks = 24;

if (storedPrev == null || storedPrev.getHeight() == 0 || storedPrev.getHeight() < pastBlocks) {
verifyDifficulty(storedPrev, nextBlock, getMaxTarget());
return;
}

if(powAllowMinimumDifficulty)
{
// recent block is more than 2 hours old
if (nextBlock.getTimeSeconds() > storedPrev.getHeader().getTimeSeconds() + 2 * 60 * 60) {
verifyDifficulty(storedPrev, nextBlock, getMaxTarget());
return;
}
// recent block is more than 10 minutes old
if (nextBlock.getTimeSeconds() > storedPrev.getHeader().getTimeSeconds() + NetworkParameters.TARGET_SPACING*4) {
BigInteger newTarget = storedPrev.getHeader().getDifficultyTargetAsInteger().multiply(BigInteger.valueOf(10));
verifyDifficulty(storedPrev, nextBlock, newTarget);
return;
}
}

StoredBlock cursor = storedPrev;
BigInteger pastTargetAverage = BigInteger.ZERO;
for(int countBlocks = 1; countBlocks <= pastBlocks; countBlocks++) {
BigInteger target = cursor.getHeader().getDifficultyTargetAsInteger();
if(countBlocks == 1) {
pastTargetAverage = target;
} else {
pastTargetAverage = pastTargetAverage.multiply(BigInteger.valueOf(countBlocks)).add(target).divide(BigInteger.valueOf(countBlocks+1));
}
if(countBlocks != pastBlocks) {
try {
cursor = cursor.getPrev(blockStore);
if(cursor == null) {
//when using checkpoints, the previous block will not exist until 24 blocks are in the store.
return;
}
} catch (BlockStoreException x) {
//when using checkpoints, the previous block will not exist until 24 blocks are in the store.
return;
}
}
}


BigInteger newTarget = pastTargetAverage;

long timespan = storedPrev.getHeader().getTimeSeconds() - cursor.getHeader().getTimeSeconds();
long targetTimespan = pastBlocks*TARGET_SPACING;

if (timespan < targetTimespan/3)
timespan = targetTimespan/3;
if (timespan > targetTimespan*3)
timespan = targetTimespan*3;

// Retarget
newTarget = newTarget.multiply(BigInteger.valueOf(timespan));
newTarget = newTarget.divide(BigInteger.valueOf(targetTimespan));
verifyDifficulty(storedPrev, nextBlock, newTarget);

}

@Override
public Coin getMaxMoney() {
return MAX_MONEY;
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/java/org/bitcoinj/params/MainNetParams.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ public MainNetParams() {
subsidyDecreaseBlockCount = 301107;
spendableCoinbaseDepth = 100;

powDGWHeight = 250000;
powKGWHeight = 250000;
powAllowMinimumDifficulty = false;
powNoRetargeting = false;

genesisBlock.setMerkleRoot(Sha256Hash.wrap("3db2b5aa928b56b8f38dc404f5bdb9e76209906b91ba175361acdc2405b19592"));

String genesisHash = genesisBlock.getHashAsString();
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/java/org/bitcoinj/params/TestNet3Params.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ public TestNet3Params() {
spendableCoinbaseDepth = 100;
subsidyDecreaseBlockCount = 301107;

powDGWHeight = 26;
powKGWHeight = 26;
powAllowMinimumDifficulty = true;
powNoRetargeting = false;

genesisBlock.setMerkleRoot(Sha256Hash.wrap("3db2b5aa928b56b8f38dc404f5bdb9e76209906b91ba175361acdc2405b19592"));

String genesisHash = genesisBlock.getHashAsString();
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/bitcoinj/params/UnitTestParams.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public UnitTestParams() {
genesisBlock.setTime(System.currentTimeMillis() / 1000);
genesisBlock.setDifficultyTarget(Block.EASIEST_DIFFICULTY_TARGET);
genesisBlock.solve();
port = 18333;
port = 19574;
interval = 10;
dumpedPrivateKeyHeader = 239;
targetTimespan = 200000000; // 6 years. Just a very big number.
Expand Down

0 comments on commit c3ec25f

Please sign in to comment.