Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
pokkst committed Apr 11, 2021
2 parents 611ac18 + ee157a3 commit 35858f2
Show file tree
Hide file tree
Showing 29 changed files with 602 additions and 101 deletions.
4 changes: 2 additions & 2 deletions core/src/main/java/org/bitcoinj/core/Address.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ public static Address fromString(@Nullable NetworkParameters params, String str)
* @return constructed address
*/
public static Address fromKey(final NetworkParameters params, final ECKey key, final ScriptType outputScriptType) {
if (outputScriptType == Script.ScriptType.P2PKH)
return LegacyAddress.fromKey(params, key);
if (outputScriptType == Script.ScriptType.P2PKH || outputScriptType == ScriptType.P2SH_P2WPKH)
return LegacyAddress.fromKey(params, key, outputScriptType);
else if (outputScriptType == Script.ScriptType.P2WPKH)
return SegwitAddress.fromKey(params, key);
else
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/bitcoinj/core/ECKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -1295,7 +1295,7 @@ public void formatKeyWithAddress(boolean includePrivateKeys, @Nullable KeyParame
if (outputScriptType != null) {
builder.append(Address.fromKey(params, this, outputScriptType));
} else {
builder.append(LegacyAddress.fromKey(params, this));
builder.append(LegacyAddress.fromKey(params, this, Script.ScriptType.P2PKH));
if (isCompressed())
builder.append(',').append(SegwitAddress.fromKey(params, this));
}
Expand Down
16 changes: 13 additions & 3 deletions core/src/main/java/org/bitcoinj/core/LegacyAddress.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@

import com.google.common.primitives.UnsignedBytes;
import org.bitcoinj.params.Networks;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.Script.ScriptType;
import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.script.ScriptPattern;

/**
* <p>A Bitcoin address looks like 1MsScoe2fTJoq4ZPdQgqyhgWeoNamYPevy and is derived from an elliptic curve public key
Expand All @@ -50,7 +53,7 @@ public class LegacyAddress extends Address {
/**
* Private constructor. Use {@link #fromBase58(NetworkParameters, String)},
* {@link #fromPubKeyHash(NetworkParameters, byte[])}, {@link #fromScriptHash(NetworkParameters, byte[])} or
* {@link #fromKey(NetworkParameters, ECKey)}.
* {@link #fromKey(NetworkParameters, ECKey, ScriptType)}.
*
* @param params
* network this address is valid for
Expand Down Expand Up @@ -91,8 +94,15 @@ public static LegacyAddress fromPubKeyHash(NetworkParameters params, byte[] hash
* only the public part is used
* @return constructed address
*/
public static LegacyAddress fromKey(NetworkParameters params, ECKey key) {
return fromPubKeyHash(params, key.getPubKeyHash());
public static LegacyAddress fromKey(NetworkParameters params, ECKey key, ScriptType outputScriptType) {
if(outputScriptType == ScriptType.P2PKH) {
return fromPubKeyHash(params, key.getPubKeyHash());
} else if(outputScriptType == ScriptType.P2SH_P2WPKH) {
Script script = ScriptBuilder.createP2SHP2WPKHOutputScript(key);
return fromScriptHash(params, ScriptPattern.extractHashFromP2SH(script));
} else {
throw new IllegalArgumentException("Prohibited output script type: " + outputScriptType);
}
}

/**
Expand Down
13 changes: 13 additions & 0 deletions core/src/main/java/org/bitcoinj/core/NetworkParameters.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ public abstract class NetworkParameters {
protected int bip32HeaderP2PKHpriv;
protected int bip32HeaderP2WPKHpub;
protected int bip32HeaderP2WPKHpriv;
protected int bip32HeaderP2SHP2WPKHpub;
protected int bip32HeaderP2SHP2WPKHpriv;

/** Used to check majorities for block version upgrade */
protected int majorityEnforceBlockUpgrade;
Expand Down Expand Up @@ -347,6 +349,17 @@ public int getBip32HeaderP2WPKHpub() {
public int getBip32HeaderP2WPKHpriv() {
return bip32HeaderP2WPKHpriv;
}

/** Returns the 4 byte header for BIP32 wallet P2SH-P2WPKH - public key part. */
public int getBip32HeaderP2SHP2WPKHpub() {
return bip32HeaderP2SHP2WPKHpub;
}

/** Returns the 4 byte header for BIP32 wallet P2SH-P2WPKH - private key part. */
public int getBip32HeaderP2SHP2WPKHpriv() {
return bip32HeaderP2SHP2WPKHpriv;
}

/**
* 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
Expand Down
12 changes: 11 additions & 1 deletion core/src/main/java/org/bitcoinj/core/TransactionOutPoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,17 @@ public ECKey getConnectedKey(KeyBag keyBag) throws ScriptException {
} else if (ScriptPattern.isP2WPKH(connectedScript)) {
byte[] addressBytes = ScriptPattern.extractHashFromP2WH(connectedScript);
return keyBag.findKeyFromPubKeyHash(addressBytes, Script.ScriptType.P2WPKH);
} else if (ScriptPattern.isP2PK(connectedScript)) {
} else if (ScriptPattern.isP2SH(connectedScript)) {
byte[] addressBytes = ScriptPattern.extractHashFromP2SH(connectedScript);
RedeemData redeemData = keyBag.findRedeemDataFromScriptHash(addressBytes);
if(redeemData != null) {
Script redeemScript = redeemData.redeemScript;
byte[] witnessHash = ScriptPattern.extractHashFromP2WH(redeemScript);
return keyBag.findKeyFromPubKeyHash(witnessHash, Script.ScriptType.P2SH_P2WPKH);
} else {
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Could not understand form of connected output script: " + connectedScript);
}
}else if (ScriptPattern.isP2PK(connectedScript)) {
byte[] pubkeyBytes = ScriptPattern.extractKeyFromP2PK(connectedScript);
return keyBag.findKeyFromPubKey(pubkeyBytes);
} else {
Expand Down
3 changes: 2 additions & 1 deletion core/src/main/java/org/bitcoinj/crypto/BIP38PrivateKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.bitcoinj.crypto;

import org.bitcoinj.core.*;
import org.bitcoinj.script.Script;
import org.bouncycastle.crypto.generators.SCrypt;

import com.google.common.primitives.Bytes;
Expand Down Expand Up @@ -118,7 +119,7 @@ public String toBase58() {
public ECKey decrypt(String passphrase) throws BadPassphraseException {
String normalizedPassphrase = Normalizer.normalize(passphrase, Normalizer.Form.NFC);
ECKey key = ecMultiply ? decryptEC(normalizedPassphrase) : decryptNoEC(normalizedPassphrase);
Sha256Hash hash = Sha256Hash.twiceOf(LegacyAddress.fromKey(params, key).toString().getBytes(StandardCharsets.US_ASCII));
Sha256Hash hash = Sha256Hash.twiceOf(LegacyAddress.fromKey(params, key, Script.ScriptType.P2PKH).toString().getBytes(StandardCharsets.US_ASCII));
byte[] actualAddressHash = Arrays.copyOfRange(hash.getBytes(), 0, 4);
if (!Arrays.equals(actualAddressHash, addressHash))
throw new BadPassphraseException();
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/org/bitcoinj/crypto/DeterministicKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,8 @@ private byte[] serialize(NetworkParameters params, boolean pub, Script.ScriptTyp
ser.putInt(pub ? params.getBip32HeaderP2PKHpub() : params.getBip32HeaderP2PKHpriv());
else if (outputScriptType == Script.ScriptType.P2WPKH)
ser.putInt(pub ? params.getBip32HeaderP2WPKHpub() : params.getBip32HeaderP2WPKHpriv());
else if (outputScriptType == Script.ScriptType.P2SH_P2WPKH)
ser.putInt(pub ? params.getBip32HeaderP2SHP2WPKHpub() : params.getBip32HeaderP2SHP2WPKHpriv());
else
throw new IllegalStateException(outputScriptType.toString());
ser.put((byte) getDepth());
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/org/bitcoinj/params/MainNetParams.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public MainNetParams() {
bip32HeaderP2PKHpriv = 0x019d9cfe; // The 4 byte header that serializes in base58 to "Ltpv"
bip32HeaderP2WPKHpub = 0x04b24746; // The 4 byte header that serializes in base58 to "zpub".
bip32HeaderP2WPKHpriv = 0x04b2430c; // The 4 byte header that serializes in base58 to "zprv"
bip32HeaderP2SHP2WPKHpub = 0x049d7cb2; // The 4 byte header that serializes in base58 to "ypub"
bip32HeaderP2SHP2WPKHpriv = 0x049d7878; // The 4 byte header that serializes in base58 to "yprv"

majorityEnforceBlockUpgrade = MAINNET_MAJORITY_ENFORCE_BLOCK_UPGRADE;
majorityRejectBlockOutdated = MAINNET_MAJORITY_REJECT_BLOCK_OUTDATED;
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/org/bitcoinj/params/TestNet3Params.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ public TestNet3Params() {
bip32HeaderP2PKHpriv = 0x04358394; // The 4 byte header that serializes in base58 to "tprv"
bip32HeaderP2WPKHpub = 0x045f1cf6; // The 4 byte header that serializes in base58 to "vpub".
bip32HeaderP2WPKHpriv = 0x045f18bc; // The 4 byte header that serializes in base58 to "vprv"
bip32HeaderP2SHP2WPKHpub = 0x044a5262; // The 4 byte header that serializes in base58 to "upub".
bip32HeaderP2SHP2WPKHpriv = 0x044a4e28; // The 4 byte header that serializes in base58 to "uprv"

majorityEnforceBlockUpgrade = TESTNET_MAJORITY_ENFORCE_BLOCK_UPGRADE;
majorityRejectBlockOutdated = TESTNET_MAJORITY_REJECT_BLOCK_OUTDATED;
Expand Down
5 changes: 3 additions & 2 deletions core/src/main/java/org/bitcoinj/script/Script.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ public enum ScriptType {
P2PK(2), // pay to pubkey
P2SH(3), // pay to script hash
P2WPKH(4), // pay to witness pubkey hash
P2WSH(5); // pay to witness script hash
P2WSH(5), // pay to witness script hash
P2SH_P2WPKH(6);

public final int id;

Expand Down Expand Up @@ -280,7 +281,7 @@ public Address getToAddress(NetworkParameters params, boolean forcePayToPubKey)
else if (ScriptPattern.isP2SH(this))
return LegacyAddress.fromScriptHash(params, ScriptPattern.extractHashFromP2SH(this));
else if (forcePayToPubKey && ScriptPattern.isP2PK(this))
return LegacyAddress.fromKey(params, ECKey.fromPublicOnly(ScriptPattern.extractKeyFromP2PK(this)));
return LegacyAddress.fromKey(params, ECKey.fromPublicOnly(ScriptPattern.extractKeyFromP2PK(this)), ScriptType.P2PKH);
else if (ScriptPattern.isP2WH(this))
return SegwitAddress.fromHash(params, ScriptPattern.extractHashFromP2WH(this));
else
Expand Down
26 changes: 26 additions & 0 deletions core/src/main/java/org/bitcoinj/script/ScriptBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,32 @@ public static Script createP2WPKHOutputScript(ECKey key) {
return createP2WPKHOutputScript(key.getPubKeyHash());
}

/**
* Creates a segwit scriptPubKey that sends to the given public key hash.
*/
public static Script createP2SHP2WPKHOutputScript(byte[] hash) {
checkArgument(hash.length == SegwitAddress.WITNESS_PROGRAM_LENGTH_PKH);
Script p2wpkhRedeemScript = createP2WPKHOutputScript(hash);
return ScriptBuilder.createP2SHOutputScript(p2wpkhRedeemScript);
}

/**
* Creates a segwit scriptPubKey that sends to the given public key.
*/
public static Script createP2SHP2WPKHOutputScript(ECKey key) {
checkArgument(key.isCompressed());
Script p2wpkhRedeemScript = createP2WPKHOutputScript(key);
return ScriptBuilder.createP2SHOutputScript(p2wpkhRedeemScript);
}

/**
* Creates a segwit scriptPubKey that sends to the given public key.
*/
public static Script createP2SHP2WPKHRedeemScript(ECKey key) {
checkArgument(key.isCompressed());
return createP2WPKHOutputScript(key);
}

/**
* Creates a scriptPubKey that sends to the given script hash. Read
* <a href="https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki">BIP 16</a> to learn more about this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public boolean signInputs(ProposedTransaction propTx, KeyBag keyBag) {
byte[] script = redeemData.redeemScript.getProgram();
try {
if (ScriptPattern.isP2PK(scriptPubKey) || ScriptPattern.isP2PKH(scriptPubKey)
|| ScriptPattern.isP2SH(scriptPubKey)) {
|| ScriptPattern.isP2SH(scriptPubKey) && !ScriptPattern.isP2WPKH(redeemData.redeemScript)) {
TransactionSignature signature = tx.calculateSignature(i, key, script, Transaction.SigHash.ALL,
false);

Expand All @@ -140,6 +140,14 @@ public boolean signInputs(ProposedTransaction propTx, KeyBag keyBag) {
Transaction.SigHash.ALL, false);
txIn.setScriptSig(ScriptBuilder.createEmpty());
txIn.setWitness(TransactionWitness.redeemP2WPKH(signature, key));
} else if(ScriptPattern.isP2SH(scriptPubKey) && ScriptPattern.isP2WPKH(redeemData.redeemScript)) {
Script redeemScript = ScriptBuilder.createP2WPKHOutputScript(key);
Script witnessScript = ScriptBuilder.createP2PKHOutputScript(key);
Coin value = txIn.getValue();
TransactionSignature signature = tx.calculateWitnessSignature(i, key, witnessScript, value,
Transaction.SigHash.ALL, false);
txIn.setScriptSig(new ScriptBuilder().data(redeemScript.getProgram()).build());
txIn.setWitness(TransactionWitness.redeemP2WPKH(signature, key));
} else {
throw new IllegalStateException(script.toString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1133,7 +1133,7 @@ public List<UTXO> getOpenTransactionOutputs(List<ECKey> keys) throws UTXOProvide
s = conn.get().prepareStatement(getTransactionOutputSelectSQL());
for (ECKey key : keys) {
// TODO switch to pubKeyHash in order to support native segwit addresses
s.setString(1, LegacyAddress.fromKey(params, key).toString());
s.setString(1, LegacyAddress.fromKey(params, key, ScriptType.P2PKH).toString());
ResultSet rs = s.executeQuery();
while (rs.next()) {
Sha256Hash hash = Sha256Hash.wrap(rs.getBytes(1));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import org.bitcoinj.core.*;
import com.google.common.base.Preconditions;
import org.bitcoinj.script.Script;

import javax.annotation.Nullable;
import java.util.*;
Expand Down Expand Up @@ -416,7 +417,7 @@ public List<UTXO> getOpenTransactionOutputs(List<ECKey> keys) throws UTXOProvide
for (UTXO output : outputsList) {
for (ECKey key : keys) {
// TODO switch to pubKeyHash in order to support native segwit addresses
Address address = LegacyAddress.fromKey(params, key);
Address address = LegacyAddress.fromKey(params, key, Script.ScriptType.P2PKH);
if (output.getAddress().equals(address.toString())) {
foundOutputs.add(output);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public DeterministicKeyChain makeKeyChain(DeterministicSeed seed, KeyCrypter cry
DeterministicKeyChain chain;
if (isMarried)
chain = new MarriedKeyChain(seed, crypter, outputScriptType, accountPath);
else if(outputScriptType == Script.ScriptType.P2SH_P2WPKH)
chain = new NestedSegwitKeyChain(seed, crypter, outputScriptType, accountPath);
else
chain = new DeterministicKeyChain(seed, crypter, outputScriptType, accountPath);
return chain;
Expand All @@ -45,6 +47,8 @@ public DeterministicKeyChain makeWatchingKeyChain(DeterministicKey accountKey, b
chain = new MarriedKeyChain(accountKey, outputScriptType);
else if (isFollowingKey)
chain = DeterministicKeyChain.builder().watchAndFollow(accountKey).outputScriptType(outputScriptType).build();
else if(outputScriptType == Script.ScriptType.P2SH_P2WPKH)
chain = NestedSegwitKeyChain.builder().watch(accountKey).outputScriptType(outputScriptType).build();
else
chain = DeterministicKeyChain.builder().watch(accountKey).outputScriptType(outputScriptType).build();
return chain;
Expand All @@ -56,6 +60,8 @@ public DeterministicKeyChain makeSpendingKeyChain(DeterministicKey accountKey, b
DeterministicKeyChain chain;
if (isMarried)
chain = new MarriedKeyChain(accountKey, outputScriptType);
else if (outputScriptType == Script.ScriptType.P2SH_P2WPKH)
chain = NestedSegwitKeyChain.builder().spend(accountKey).outputScriptType(outputScriptType).build();
else
chain = DeterministicKeyChain.builder().spend(accountKey).outputScriptType(outputScriptType).build();
return chain;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ public class DeterministicKeyChain implements EncryptableKeyChain {
// m / 44' / 2' / 0'
public static final HDPath BIP44_ACCOUNT_ZERO_PATH = HDPath.M(new ChildNumber(44, true))
.extend(new ChildNumber(2, true), ChildNumber.ZERO_HARDENED);
// m / 49' / 2' / 0'
public static final HDPath BIP49_ACCOUNT_ZERO_PATH = HDPath.M(new ChildNumber(49, true))
.extend(new ChildNumber(2, true), ChildNumber.ZERO_HARDENED);
// m / 44' / 2' / 0'
public static final HDPath BIP84_ACCOUNT_ZERO_PATH = HDPath.M(new ChildNumber(84, true))
.extend(new ChildNumber(2, true), ChildNumber.ZERO_HARDENED);
Expand Down Expand Up @@ -363,7 +366,8 @@ public DeterministicKeyChain(DeterministicKey key, boolean isFollowing, boolean
protected DeterministicKeyChain(DeterministicSeed seed, @Nullable KeyCrypter crypter,
Script.ScriptType outputScriptType, List<ChildNumber> accountPath) {
checkArgument(outputScriptType == null || outputScriptType == Script.ScriptType.P2PKH
|| outputScriptType == Script.ScriptType.P2WPKH, "Only P2PKH or P2WPKH allowed.");
|| outputScriptType == Script.ScriptType.P2WPKH
|| outputScriptType == Script.ScriptType.P2SH_P2WPKH, "Only P2PKH or P2WPKH allowed.");
this.outputScriptType = outputScriptType != null ? outputScriptType : Script.ScriptType.P2PKH;
this.accountPath = HDPath.M(accountPath);
this.seed = seed;
Expand Down Expand Up @@ -1361,6 +1365,10 @@ public boolean isMarried() {
return false;
}

public boolean isNestedSegwit() {
return false;
}

/** Get redeem data for a key. Only applicable to married keychains. */
public RedeemData getRedeemData(DeterministicKey followedKey) {
throw new UnsupportedOperationException();
Expand Down
Loading

0 comments on commit 35858f2

Please sign in to comment.