diff --git a/core/src/main/java/org/bitcoinj/core/TransactionOutput.java b/core/src/main/java/org/bitcoinj/core/TransactionOutput.java
index c363b8e3..bcbfba4e 100644
--- a/core/src/main/java/org/bitcoinj/core/TransactionOutput.java
+++ b/core/src/main/java/org/bitcoinj/core/TransactionOutput.java
@@ -131,7 +131,7 @@ public class TransactionOutput extends ChildMessage {
@Nullable
public LegacyAddress getAddressFromP2PKHScript(NetworkParameters networkParameters) throws ScriptException{
if (ScriptPattern.isPayToPubKeyHash(getScriptPubKey()))
- return getScriptPubKey().getToAddress(networkParameters);
+ return (LegacyAddress) getScriptPubKey().getToAddress(networkParameters);
return null;
}
@@ -151,7 +151,7 @@ public class TransactionOutput extends ChildMessage {
@Nullable
public LegacyAddress getAddressFromP2SH(NetworkParameters networkParameters) throws ScriptException{
if (ScriptPattern.isPayToScriptHash(getScriptPubKey()))
- return getScriptPubKey().getToAddress(networkParameters);
+ return (LegacyAddress) getScriptPubKey().getToAddress(networkParameters);
return null;
}
diff --git a/core/src/main/java/org/bitcoinj/script/Script.java b/core/src/main/java/org/bitcoinj/script/Script.java
index a722c61d..b2f7e745 100644
--- a/core/src/main/java/org/bitcoinj/script/Script.java
+++ b/core/src/main/java/org/bitcoinj/script/Script.java
@@ -239,22 +239,17 @@ public class Script {
}
/**
- *
If a program matches the standard template DUP HASH160 <pubkey hash> EQUALVERIFY CHECKSIG
- * then this function retrieves the third element.
- * In this case, this is useful for fetching the destination address of a transaction.
+ * If the program somehow pays to a hash, returns the hash.
*
- * If a program matches the standard template HASH160 <script hash> EQUAL
- * then this function retrieves the second element.
- * In this case, this is useful for fetching the hash of the redeem script of a transaction.
- *
- * Otherwise it throws a ScriptException.
- *
+ * Otherwise this method throws a ScriptException.
*/
public byte[] getPubKeyHash() throws ScriptException {
if (ScriptPattern.isPayToPubKeyHash(this))
return ScriptPattern.extractHashFromPayToPubKeyHash(this);
else if (ScriptPattern.isPayToScriptHash(this))
return ScriptPattern.extractHashFromPayToScriptHash(this);
+ else if (ScriptPattern.isPayToWitnessHash(this))
+ return ScriptPattern.extractHashFromPayToWitnessHash(this);
else
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Script not in the standard scriptPubKey form");
}
@@ -283,7 +278,7 @@ public class Script {
/**
* Gets the destination address from this script, if it's in the required form.
*/
- public LegacyAddress getToAddress(NetworkParameters params) throws ScriptException {
+ public Address getToAddress(NetworkParameters params) throws ScriptException {
return getToAddress(params, false);
}
@@ -294,13 +289,15 @@ public class Script {
* If true, allow payToPubKey to be casted to the corresponding address. This is useful if you prefer
* showing addresses rather than pubkeys.
*/
- public LegacyAddress getToAddress(NetworkParameters params, boolean forcePayToPubKey) throws ScriptException {
+ public Address getToAddress(NetworkParameters params, boolean forcePayToPubKey) throws ScriptException {
if (ScriptPattern.isPayToPubKeyHash(this))
return LegacyAddress.fromPubKeyHash(params, ScriptPattern.extractHashFromPayToPubKeyHash(this));
else if (ScriptPattern.isPayToScriptHash(this))
return LegacyAddress.fromP2SHScript(params, this);
else if (forcePayToPubKey && ScriptPattern.isPayToPubKey(this))
return LegacyAddress.fromKey(params, ECKey.fromPublicOnly(ScriptPattern.extractKeyFromPayToPubKey(this)));
+ else if (ScriptPattern.isPayToWitnessHash(this))
+ return SegwitAddress.fromHash(params, ScriptPattern.extractHashFromPayToWitnessHash(this));
else
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Cannot cast this script to a pay-to-address type");
}
diff --git a/core/src/main/java/org/bitcoinj/script/ScriptPattern.java b/core/src/main/java/org/bitcoinj/script/ScriptPattern.java
index 7da1f9bc..165f1578 100644
--- a/core/src/main/java/org/bitcoinj/script/ScriptPattern.java
+++ b/core/src/main/java/org/bitcoinj/script/ScriptPattern.java
@@ -18,6 +18,7 @@
package org.bitcoinj.script;
import org.bitcoinj.core.LegacyAddress;
+import org.bitcoinj.core.SegwitAddress;
import java.math.BigInteger;
import java.util.List;
@@ -105,6 +106,34 @@ public class ScriptPattern {
return script.chunks.get(0).data;
}
+ /**
+ * Returns true if this script is of the form OP_0 . This can either be a P2WPKH or P2WSH scriptPubKey. These
+ * two script types were introduced with segwit.
+ */
+ public static boolean isPayToWitnessHash(Script script) {
+ List chunks = script.chunks;
+ if (chunks.size() != 2)
+ return false;
+ if (!chunks.get(0).equalsOpCode(OP_0))
+ return false;
+ byte[] chunk1data = chunks.get(1).data;
+ if (chunk1data == null)
+ return false;
+ if (chunk1data.length != SegwitAddress.WITNESS_PROGRAM_LENGTH_PKH
+ && chunk1data.length != SegwitAddress.WITNESS_PROGRAM_LENGTH_SH)
+ return false;
+ return true;
+ }
+
+ /**
+ * Extract the pubkey hash from a P2WPKH or the script hash from a P2WSH scriptPubKey. It's important that the
+ * script is in the correct form, so you will want to guard calls to this method with
+ * {@link #isPayToWitnessHash(Script)}.
+ */
+ public static byte[] extractHashFromPayToWitnessHash(Script script) {
+ return script.chunks.get(1).data;
+ }
+
/**
* Returns whether this script matches the format used for multisig outputs: [n] [keys...] [m] CHECKMULTISIG
*/
diff --git a/core/src/main/java/org/bitcoinj/store/LevelDBFullPrunedBlockStore.java b/core/src/main/java/org/bitcoinj/store/LevelDBFullPrunedBlockStore.java
index 7a54f9fc..1e3fd549 100644
--- a/core/src/main/java/org/bitcoinj/store/LevelDBFullPrunedBlockStore.java
+++ b/core/src/main/java/org/bitcoinj/store/LevelDBFullPrunedBlockStore.java
@@ -29,6 +29,7 @@ import java.io.*;
import java.nio.ByteBuffer;
import org.bitcoinj.core.LegacyAddress;
+import org.bitcoinj.core.Address;
import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.NetworkParameters;
@@ -461,7 +462,7 @@ public class LevelDBFullPrunedBlockStore implements FullPrunedBlockStore {
}
if (txout != null) {
Script sc = txout.getScript();
- LegacyAddress address = sc.getToAddress(params, true);
+ Address address = sc.getToAddress(params, true);
UTXO output = new UTXO(txout.getHash(), txout.getIndex(), txout.getValue(), txout.getHeight(),
txout.isCoinbase(), txout.getScript(), address.toString());
results.add(output);
@@ -887,7 +888,7 @@ public class LevelDBFullPrunedBlockStore implements FullPrunedBlockStore {
String address = out.getAddress();
if (address == null || address.equals("")) {
Script sc = out.getScript();
- a = sc.getToAddress(params);
+ a = (LegacyAddress) sc.getToAddress(params);
hashBytes = a.getHash();
} else {
a = LegacyAddress.fromBase58(params, out.getAddress());
diff --git a/core/src/main/java/org/bitcoinj/wallet/KeyChainGroup.java b/core/src/main/java/org/bitcoinj/wallet/KeyChainGroup.java
index 62c38f8c..d08716c0 100644
--- a/core/src/main/java/org/bitcoinj/wallet/KeyChainGroup.java
+++ b/core/src/main/java/org/bitcoinj/wallet/KeyChainGroup.java
@@ -124,8 +124,8 @@ public class KeyChainGroup implements KeyBag {
if (isMarried()) {
for (Map.Entry entry : this.currentKeys.entrySet()) {
- LegacyAddress address = makeP2SHOutputScript(entry.getValue(), getActiveKeyChain()).getToAddress(params);
- currentAddresses.put(entry.getKey(), address);
+ Address address = makeP2SHOutputScript(entry.getValue(), getActiveKeyChain()).getToAddress(params);
+ currentAddresses.put(entry.getKey(), (LegacyAddress) address);
}
}
}
diff --git a/core/src/main/java/org/bitcoinj/wallet/Wallet.java b/core/src/main/java/org/bitcoinj/wallet/Wallet.java
index c1664750..e9377995 100644
--- a/core/src/main/java/org/bitcoinj/wallet/Wallet.java
+++ b/core/src/main/java/org/bitcoinj/wallet/Wallet.java
@@ -1005,7 +1005,7 @@ public class Wallet extends BaseTaggableObject
List addresses = new LinkedList<>();
for (Script script : watchedScripts)
if (ScriptPattern.isPayToPubKeyHash(script))
- addresses.add(script.getToAddress(params));
+ addresses.add(((LegacyAddress) script.getToAddress(params)));
return addresses;
} finally {
keyChainGroupLock.unlock();
diff --git a/core/src/test/java/org/bitcoinj/core/SegwitAddressTest.java b/core/src/test/java/org/bitcoinj/core/SegwitAddressTest.java
index fbcd1234..c3ada243 100644
--- a/core/src/test/java/org/bitcoinj/core/SegwitAddressTest.java
+++ b/core/src/test/java/org/bitcoinj/core/SegwitAddressTest.java
@@ -28,8 +28,10 @@ import java.util.Locale;
import org.bitcoinj.params.MainNetParams;
import org.bitcoinj.params.TestNet3Params;
+import org.bitcoinj.script.Script;
import org.bitcoinj.script.Script.ScriptType;
import org.bitcoinj.script.ScriptBuilder;
+import org.bitcoinj.script.ScriptPattern;
import org.junit.Test;
import com.google.common.base.MoreObjects;
@@ -103,6 +105,11 @@ public class SegwitAddressTest {
assertEquals(valid.expectedScriptPubKey,
Utils.HEX.encode(ScriptBuilder.createOutputScript(address).getProgram()));
assertEquals(valid.address.toLowerCase(Locale.ROOT), address.toBech32());
+ if (valid.expectedWitnessVersion == 0) {
+ Script expectedScriptPubKey = new Script(Utils.HEX.decode(valid.expectedScriptPubKey));
+ assertEquals(address, SegwitAddress.fromHash(valid.expectedParams,
+ ScriptPattern.extractHashFromPayToWitnessHash(expectedScriptPubKey)));
+ }
assertEquals(valid.expectedWitnessVersion, address.getWitnessVersion());
}
}
diff --git a/core/src/test/java/org/bitcoinj/script/ScriptPatternTest.java b/core/src/test/java/org/bitcoinj/script/ScriptPatternTest.java
index 7e3b0243..8c334da4 100644
--- a/core/src/test/java/org/bitcoinj/script/ScriptPatternTest.java
+++ b/core/src/test/java/org/bitcoinj/script/ScriptPatternTest.java
@@ -20,8 +20,10 @@ package org.bitcoinj.script;
import com.google.common.collect.Lists;
import org.bitcoinj.core.LegacyAddress;
-import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.NetworkParameters;
+import org.bitcoinj.core.SegwitAddress;
+import org.bitcoinj.core.Sha256Hash;
+import org.bitcoinj.core.ECKey;
import org.bitcoinj.params.MainNetParams;
import org.junit.Test;
@@ -42,12 +44,18 @@ public class ScriptPatternTest {
assertTrue(ScriptPattern.isPayToScriptHash(
ScriptBuilder.createP2SHOutputScript(2, keys)
));
- assertTrue(ScriptPattern.isSentToMultisig(
- ScriptBuilder.createMultiSigOutputScript(2, keys)
- ));
assertTrue(ScriptPattern.isPayToPubKey(
ScriptBuilder.createOutputScript(keys.get(0))
));
+ assertTrue(ScriptPattern.isPayToWitnessHash(
+ ScriptBuilder.createOutputScript(SegwitAddress.fromHash(MAINNET, keys.get(0).getPubKeyHash()))
+ ));
+ assertTrue(ScriptPattern.isPayToWitnessHash(
+ ScriptBuilder.createOutputScript(SegwitAddress.fromHash(MAINNET, Sha256Hash.hash(new byte[0])))
+ ));
+ assertTrue(ScriptPattern.isSentToMultisig(
+ ScriptBuilder.createMultiSigOutputScript(2, keys)
+ ));
assertTrue(ScriptPattern.isSentToCltvPaymentChannel(
ScriptBuilder.createCLTVPaymentChannelOutput(BigInteger.ONE, keys.get(0), keys.get(1))
));
diff --git a/core/src/test/java/org/bitcoinj/wallet/WalletTest.java b/core/src/test/java/org/bitcoinj/wallet/WalletTest.java
index dc811a8c..a2747655 100644
--- a/core/src/test/java/org/bitcoinj/wallet/WalletTest.java
+++ b/core/src/test/java/org/bitcoinj/wallet/WalletTest.java
@@ -2928,7 +2928,7 @@ public class WalletTest extends TestWithWallet {
assertEquals(THREE_CENTS, tx.getValueSentFromMe(wallet));
assertEquals(THREE_CENTS.subtract(tx.getFee()), tx.getValueSentToMe(wallet));
// TX sends to one of our addresses (for now we ignore married wallets).
- final LegacyAddress toAddress = tx.getOutput(0).getScriptPubKey().getToAddress(UNITTEST);
+ final LegacyAddress toAddress = (LegacyAddress) tx.getOutput(0).getScriptPubKey().getToAddress(UNITTEST);
final ECKey rotatingToKey = wallet.findKeyFromPubHash(toAddress.getHash());
assertNotNull(rotatingToKey);
assertFalse(wallet.isKeyRotating(rotatingToKey));