Skip to content

Commit 9dc30be

Browse files
committed
Pull latest bitcoinj upstream and reimplement PoW/SPV shit, as well as P2SH-P2WPKH
1 parent 7cb61b0 commit 9dc30be

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+4923
-1592
lines changed

core/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ dependencies {
1515
api 'com.google.protobuf:protobuf-javalite:3.22.3'
1616
implementation 'org.slf4j:slf4j-api:2.0.7'
1717
implementation 'net.jcip:jcip-annotations:1.0'
18+
implementation 'com.lambdaworks:scrypt:1.4.0'
1819
testImplementation 'junit:junit:4.13.2'
1920
testImplementation 'org.easymock:easymock:5.1.0'
2021
testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.14.0'

core/src/main/java/org/bitcoinj/base/BitcoinNetwork.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public enum BitcoinNetwork implements Network {
5555
/**
5656
* The maximum number of coins to be generated
5757
*/
58-
private static final long MAX_COINS = 21_000_000;
58+
private static final long MAX_COINS = 84_000_000;
5959

6060
/**
6161
* The maximum money to be generated
@@ -140,7 +140,12 @@ public int legacyAddressHeader() {
140140
* @see LegacyAddress.P2SHHeader
141141
*/
142142
public int legacyP2SHHeader() {
143-
return LegacyAddress.P2SHHeader.ofNetwork(this).headerByte();
143+
return this == MAINNET ? LegacyAddress.P2SHHeader.X5.headerByte() : LegacyAddress.P2SHHeader.X196.headerByte();
144+
}
145+
146+
@Override
147+
public int legacyP2SHHeader2() {
148+
return this == MAINNET ? LegacyAddress.P2SHHeader.X50.headerByte() : LegacyAddress.P2SHHeader.X58.headerByte();
144149
}
145150

146151
/**

core/src/main/java/org/bitcoinj/base/DefaultAddressParser.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ private LegacyAddress parseBase58AnyNetwork(String base58)
141141
throws AddressFormatException, AddressFormatException.WrongNetwork {
142142
int version = Base58.decodeChecked(base58)[0] & 0xFF;
143143
return base58Networks.stream()
144-
.filter(n -> version == n.legacyAddressHeader() || version == n.legacyP2SHHeader())
144+
.filter(n -> version == n.legacyAddressHeader() || version == n.legacyP2SHHeader() || version == n.legacyP2SHHeader2())
145145
.findFirst()
146146
.map(n -> LegacyAddress.fromBase58(base58, n))
147147
.orElseThrow(() -> new AddressFormatException.InvalidPrefix("No network found for " + base58));

core/src/main/java/org/bitcoinj/base/LegacyAddress.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ public static LegacyAddress fromBase58(String base58, @Nonnull Network network)
195195
byte[] bytes = Arrays.copyOfRange(versionAndDataBytes, 1, versionAndDataBytes.length);
196196
if (version == network.legacyAddressHeader())
197197
return new LegacyAddress(network, false, bytes);
198-
else if (version == network.legacyP2SHHeader())
198+
else if (version == network.legacyP2SHHeader() || version == network.legacyP2SHHeader2())
199199
return new LegacyAddress(network, true, bytes);
200200
throw new AddressFormatException.WrongNetwork(version);
201201
}
@@ -216,7 +216,7 @@ public Network network() {
216216
* @return version header as one byte
217217
*/
218218
public int getVersion() {
219-
return p2sh ? network.legacyP2SHHeader() : network.legacyAddressHeader();
219+
return p2sh ? network.legacyP2SHHeader2() : network.legacyAddressHeader();
220220
}
221221

222222
/**
@@ -331,7 +331,9 @@ public int headerByte() {
331331
*/
332332
public enum P2SHHeader {
333333
X5(5, MAINNET),
334-
X196(196, TESTNET, SIGNET, REGTEST);
334+
X196(196, TESTNET, SIGNET, REGTEST),
335+
X50(50, MAINNET),
336+
X58(58, TESTNET, SIGNET, REGTEST);
335337

336338
private final int headerByte;
337339
private final EnumSet<BitcoinNetwork> networks;

core/src/main/java/org/bitcoinj/base/Network.java

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ public interface Network {
4040
*/
4141
int legacyP2SHHeader();
4242

43+
int legacyP2SHHeader2();
44+
4345
/**
4446
* Human-readable part (HRP) of bech32 encoded segwit addresses for this network.
4547
* @return HRP (lowercase)

core/src/main/java/org/bitcoinj/base/ScriptType.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ public enum ScriptType {
3636
P2SH("sh", 3), // pay to script hash
3737
P2WPKH("wpkh", 4), // pay to witness pubkey hash
3838
P2WSH("wsh", 5), // pay to witness script hash
39-
P2TR("tr", 6); // pay to taproot
39+
P2TR("tr", 6), // pay to taproot
40+
P2SH_P2WPKH("p2sh-p2wpkh", 6);
4041

4142
private final String scriptIdentifierString;
4243

core/src/main/java/org/bitcoinj/base/internal/ByteUtils.java

+9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.bitcoinj.base.internal;
1818

1919
import com.google.common.io.BaseEncoding;
20+
import com.lambdaworks.crypto.SCrypt;
2021

2122
import java.io.IOException;
2223
import java.io.InputStream;
@@ -742,4 +743,12 @@ public static Comparator<byte[]> arrayUnsignedComparator() {
742743
private static int compareUnsigned(byte a, byte b) {
743744
return Byte.toUnsignedInt(a) - Byte.toUnsignedInt(b);
744745
}
746+
747+
public static byte[] scryptDigest(byte[] input) {
748+
try {
749+
return SCrypt.scrypt(input, input, 1024, 1, 1, 32);
750+
} catch (Exception e) {
751+
return null;
752+
}
753+
}
745754
}

core/src/main/java/org/bitcoinj/core/Block.java

+49-9
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ public enum VerifyFlag {
133133

134134
/** Stores the hash of the block. If null, getHash() will recalculate it. */
135135
private Sha256Hash hash;
136+
private Sha256Hash scryptHash;
136137

137138
/**
138139
* Deserialize this message from a given payload.
@@ -151,13 +152,20 @@ public static Block read(ByteBuffer payload) throws BufferUnderflowException, Pr
151152
long difficultyTarget = ByteUtils.readUint32(payload);
152153
long nonce = ByteUtils.readUint32(payload);
153154
payload.reset(); // read again from the mark for the hash
154-
Sha256Hash hash = Sha256Hash.wrapReversed(Sha256Hash.hashTwice(Buffers.readBytes(payload, HEADER_SIZE)));
155+
byte[] header = Buffers.readBytes(payload, HEADER_SIZE);
156+
Sha256Hash hash = Sha256Hash.wrapReversed(Sha256Hash.hashTwice(header));
157+
byte[] scryptDigest = ByteUtils.scryptDigest(header);
158+
if(scryptDigest == null) {
159+
throw new RuntimeException("Scrypt digest is null.");
160+
}
161+
Sha256Hash scryptHash = Sha256Hash.wrap(ByteUtils.reverseBytes(scryptDigest));
155162
// transactions
156163
List<Transaction> transactions = payload.hasRemaining() ? // otherwise this message is just a header
157164
readTransactions(payload) :
158165
null;
159166
Block block = new Block(version, prevBlockHash, merkleRoot, time, difficultyTarget, nonce, transactions);
160167
block.hash = hash;
168+
block.scryptHash = scryptHash;
161169
return block;
162170
}
163171

@@ -242,14 +250,14 @@ public static Block createGenesis() {
242250
//
243251
// "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"
244252
private static final byte[] genesisTxInputScriptBytes = ByteUtils.parseHex
245-
("04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73");
253+
("04ffff001d0104404e592054696d65732030352f4f63742f32303131205374657665204a6f62732c204170706c65e280997320566973696f6e6172792c2044696573206174203536");
246254

247255
private static final byte[] genesisTxScriptPubKeyBytes;
248256
static {
249257
ByteArrayOutputStream scriptPubKeyBytes = new ByteArrayOutputStream();
250258
try {
251259
Script.writeBytes(scriptPubKeyBytes, ByteUtils.parseHex
252-
("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"));
260+
("040184710fa689ad5023690c80f3a49c8f13f8d45b8c857fbcbc8bc4a8e4d3eb4b10f4d4604fa08dce601aaf0f470216fe1b51850b4acf21b179c45070ac7b03a9"));
253261
} catch (IOException e) {
254262
throw new RuntimeException(e); // Cannot happen.
255263
}
@@ -307,6 +315,7 @@ protected void unCache() {
307315

308316
private void unCacheHeader() {
309317
hash = null;
318+
scryptHash = null;
310319
}
311320

312321
private void unCacheTransactions() {
@@ -318,6 +327,10 @@ private void unCacheTransactions() {
318327
merkleRoot = null;
319328
}
320329

330+
/**
331+
* Calculates the block hash by serializing the block and hashing the
332+
* resulting bytes.
333+
*/
321334
/**
322335
* Calculates the block hash by serializing the block and hashing the
323336
* resulting bytes.
@@ -332,6 +345,20 @@ private Sha256Hash calculateHash() {
332345
}
333346
}
334347

348+
private Sha256Hash calculateScryptHash() {
349+
try {
350+
ByteArrayOutputStream bos = new ByteArrayOutputStream(HEADER_SIZE);
351+
writeHeader(bos);
352+
byte[] scryptDigest = ByteUtils.scryptDigest(bos.toByteArray());
353+
if(scryptDigest == null) {
354+
throw new RuntimeException("Scrypt digest is null.");
355+
}
356+
return Sha256Hash.wrap(ByteUtils.reverseBytes(scryptDigest));
357+
} catch (IOException e) {
358+
throw new RuntimeException(e); // Cannot happen.
359+
}
360+
}
361+
335362
/**
336363
* Returns the hash of the block (which for a valid, solved block should be below the target) in the form seen on
337364
* the block explorer. If you call this on block 1 in the mainnet chain
@@ -341,6 +368,10 @@ public String getHashAsString() {
341368
return getHash().toString();
342369
}
343370

371+
public String getScryptHashAsString() {
372+
return getScryptHash().toString();
373+
}
374+
344375
/**
345376
* Returns the hash of the block (which for a valid, solved block should be
346377
* below the target). Big endian.
@@ -351,6 +382,12 @@ public Sha256Hash getHash() {
351382
return hash;
352383
}
353384

385+
public Sha256Hash getScryptHash() {
386+
if (scryptHash == null)
387+
scryptHash = calculateScryptHash();
388+
return scryptHash;
389+
}
390+
354391
/**
355392
* The number that is one greater than the largest representable SHA-256
356393
* hash.
@@ -383,6 +420,7 @@ public Block cloneAsHeader() {
383420
block.merkleRoot = getMerkleRoot();
384421
block.hash = getHash();
385422
block.transactions = null;
423+
block.scryptHash = getScryptHash();
386424
return block;
387425
}
388426

@@ -456,10 +494,6 @@ public BigInteger getDifficultyTargetAsInteger() {
456494

457495
/** Returns true if the hash of the block is OK (lower than difficulty target). */
458496
protected boolean checkProofOfWork(boolean throwException) throws VerificationException {
459-
// shortcut for unit-testing
460-
if (Context.get().isRelaxProofOfWork())
461-
return true;
462-
463497
// This part is key - it is what proves the block was as difficult to make as it claims
464498
// to be. Note however that in the context of this function, the block can claim to be
465499
// as difficult as it wants to be .... if somebody was able to take control of our network
@@ -470,11 +504,11 @@ protected boolean checkProofOfWork(boolean throwException) throws VerificationEx
470504
// field is of the right value. This requires us to have the preceding blocks.
471505
BigInteger target = getDifficultyTargetAsInteger();
472506

473-
BigInteger h = getHash().toBigInteger();
507+
BigInteger h = getScryptHash().toBigInteger();
474508
if (h.compareTo(target) > 0) {
475509
// Proof of work check failed!
476510
if (throwException)
477-
throw new VerificationException("Hash is higher than target: " + getHashAsString() + " vs "
511+
throw new VerificationException("Hash is higher than target: " + getScryptHashAsString() + " vs "
478512
+ target.toString(16));
479513
else
480514
return false;
@@ -659,6 +693,7 @@ void setMerkleRoot(Sha256Hash value) {
659693
unCacheHeader();
660694
merkleRoot = value;
661695
hash = null;
696+
scryptHash = null;
662697
}
663698

664699
/**
@@ -689,6 +724,7 @@ else if (runSanityChecks && transactions.size() > 0 && t.isCoinBase())
689724
// Force a recalculation next time the values are needed.
690725
merkleRoot = null;
691726
hash = null;
727+
scryptHash = null;
692728
}
693729

694730
/** Returns the version of the block data structure as defined by the Bitcoin protocol. */
@@ -708,6 +744,7 @@ void setPrevBlockHash(Sha256Hash prevBlockHash) {
708744
unCacheHeader();
709745
this.prevBlockHash = prevBlockHash;
710746
this.hash = null;
747+
this.scryptHash = null;
711748
}
712749

713750
/**
@@ -741,6 +778,7 @@ public void setTime(Instant time) {
741778
unCacheHeader();
742779
this.time = time.truncatedTo(ChronoUnit.SECONDS); // convert to Bitcoin time
743780
this.hash = null;
781+
this.scryptHash = null;
744782
}
745783

746784
/**
@@ -762,6 +800,7 @@ public void setDifficultyTarget(long compactForm) {
762800
unCacheHeader();
763801
this.difficultyTarget = compactForm;
764802
this.hash = null;
803+
this.scryptHash = null;
765804
}
766805

767806
/**
@@ -778,6 +817,7 @@ public void setNonce(long nonce) {
778817
unCacheHeader();
779818
this.nonce = nonce;
780819
this.hash = null;
820+
this.scryptHash = null;
781821
}
782822

783823
/** Returns an unmodifiable list of transactions held in this block, or null if this object represents just a header. */

core/src/main/java/org/bitcoinj/core/CheckpointManager.java

+45-7
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,7 @@
4444
import java.security.MessageDigest;
4545
import java.time.Instant;
4646
import java.time.temporal.ChronoUnit;
47-
import java.util.Arrays;
48-
import java.util.Map;
49-
import java.util.Objects;
50-
import java.util.TreeMap;
47+
import java.util.*;
5148

5249
import static org.bitcoinj.base.internal.Preconditions.checkArgument;
5350
import static org.bitcoinj.base.internal.Preconditions.checkState;
@@ -216,6 +213,43 @@ public StoredBlock getCheckpointBefore(long timeSecs) {
216213
return getCheckpointBefore(Instant.ofEpochSecond(timeSecs));
217214
}
218215

216+
public List<StoredBlock> getCheckpointsBefore(Instant time) {
217+
try {
218+
ArrayList<StoredBlock> checkpointsBefore = new ArrayList<>();
219+
checkArgument(time.isAfter(params.getGenesisBlock().time()));
220+
// This is thread safe because the map never changes after creation.
221+
Map.Entry<Instant, StoredBlock> entry = checkpoints.floorEntry(time);
222+
if (entry != null) {
223+
StoredBlock mostRecentCheckpointBlock = entry.getValue();
224+
StoredBlock blockBefore = getBlockBefore(mostRecentCheckpointBlock, checkpoints);
225+
checkpointsBefore.add(blockBefore);
226+
checkpointsBefore.add(mostRecentCheckpointBlock);
227+
return checkpointsBefore;
228+
}
229+
Block genesis = params.getGenesisBlock().cloneAsHeader();
230+
checkpointsBefore.add(new StoredBlock(genesis, genesis.getWork(), 0));
231+
return checkpointsBefore;
232+
} catch (VerificationException e) {
233+
throw new RuntimeException(e); // Cannot happen.
234+
}
235+
}
236+
237+
public StoredBlock getBlockBefore(StoredBlock block, TreeMap<Instant, StoredBlock> checkpoints) {
238+
/*
239+
Litecoin does this weird thing with the difficulty adjustment algorithm.
240+
In Bitcoin, upon time for the difficulty to adjust (every 2016 blocks), it actually goes back 2015 blocks to calculate.
241+
In Litecoin, it goes a full 2016 blocks, so for the checkpoint manager to fully work in litecoinj, we need both the actual
242+
block where the difficulty changes, and the block before it.
243+
*/
244+
int heightToLookFor = block.getHeight()-1;
245+
for(StoredBlock checkpoint : checkpoints.values()) {
246+
if(checkpoint.getHeight() == heightToLookFor) {
247+
return checkpoint;
248+
}
249+
}
250+
return null;
251+
}
252+
219253
/** Returns the number of checkpoints that were loaded. */
220254
public int numCheckpoints() {
221255
return checkpoints.size();
@@ -247,9 +281,13 @@ public static void checkpoint(NetworkParameters params, InputStream checkpoints,
247281

248282
BufferedInputStream stream = new BufferedInputStream(checkpoints);
249283
CheckpointManager manager = new CheckpointManager(params, stream);
250-
StoredBlock checkpoint = manager.getCheckpointBefore(time);
251-
store.put(checkpoint);
252-
store.setChainHead(checkpoint);
284+
List<StoredBlock> checkpointsBefore = manager.getCheckpointsBefore(time);
285+
for(int i = 0; i < checkpointsBefore.size(); i++) {
286+
store.put(checkpointsBefore.get(i));
287+
if(i == checkpointsBefore.size()-1) {
288+
store.setChainHead(checkpointsBefore.get(i));
289+
}
290+
}
253291
}
254292

255293
/** @deprecated use {@link #checkpoint(NetworkParameters, InputStream, BlockStore, Instant)} */

0 commit comments

Comments
 (0)