From 0a21b586e865bce436edd738f508c12b2ab52565 Mon Sep 17 00:00:00 2001 From: Andreas Schildbach Date: Mon, 26 Feb 2018 00:00:16 +0100 Subject: [PATCH] Script: Extract the extraction of the P2PKH and P2SH hashes to ScriptPattern. --- core/src/main/java/org/bitcoinj/core/Address.java | 2 +- core/src/main/java/org/bitcoinj/core/Transaction.java | 7 +++++-- .../java/org/bitcoinj/core/TransactionOutPoint.java | 6 +++--- .../main/java/org/bitcoinj/core/TransactionOutput.java | 2 +- core/src/main/java/org/bitcoinj/script/Script.java | 6 +++--- .../main/java/org/bitcoinj/script/ScriptPattern.java | 8 ++++++++ .../java/org/bitcoinj/wallet/KeyTimeCoinSelector.java | 2 +- core/src/main/java/org/bitcoinj/wallet/Wallet.java | 10 +++++----- 8 files changed, 27 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/org/bitcoinj/core/Address.java b/core/src/main/java/org/bitcoinj/core/Address.java index 0e43293f..ef455299 100644 --- a/core/src/main/java/org/bitcoinj/core/Address.java +++ b/core/src/main/java/org/bitcoinj/core/Address.java @@ -76,7 +76,7 @@ public class Address extends VersionedChecksummedBytes { /** Returns an Address that represents the script hash extracted from the given scriptPubKey */ public static Address fromP2SHScript(NetworkParameters params, Script scriptPubKey) { checkArgument(ScriptPattern.isPayToScriptHash(scriptPubKey), "Not a P2SH script"); - return fromP2SHHash(params, scriptPubKey.getPubKeyHash()); + return fromP2SHHash(params, ScriptPattern.extractHashFromPayToScriptHash(scriptPubKey)); } /** diff --git a/core/src/main/java/org/bitcoinj/core/Transaction.java b/core/src/main/java/org/bitcoinj/core/Transaction.java index 15824cc1..acabca84 100644 --- a/core/src/main/java/org/bitcoinj/core/Transaction.java +++ b/core/src/main/java/org/bitcoinj/core/Transaction.java @@ -705,9 +705,12 @@ public class Transaction extends ChildMessage { final TransactionOutput connectedOutput = outpoint.getConnectedOutput(); if (connectedOutput != null) { Script scriptPubKey = connectedOutput.getScriptPubKey(); - if (ScriptPattern.isPayToPubKeyHash(scriptPubKey) || ScriptPattern.isPayToScriptHash(scriptPubKey)) { + try { + byte[] pubKeyHash = scriptPubKey.getPubKeyHash(); s.append(" hash160:"); - s.append(Utils.HEX.encode(scriptPubKey.getPubKeyHash())); + s.append(Utils.HEX.encode(pubKeyHash)); + } catch (ScriptException x) { + // ignore } } if (in.hasSequence()) { diff --git a/core/src/main/java/org/bitcoinj/core/TransactionOutPoint.java b/core/src/main/java/org/bitcoinj/core/TransactionOutPoint.java index dfcb7047..1fc6eb42 100644 --- a/core/src/main/java/org/bitcoinj/core/TransactionOutPoint.java +++ b/core/src/main/java/org/bitcoinj/core/TransactionOutPoint.java @@ -142,7 +142,7 @@ public class TransactionOutPoint extends ChildMessage { checkNotNull(connectedOutput, "Input is not connected so cannot retrieve key"); Script connectedScript = connectedOutput.getScriptPubKey(); if (ScriptPattern.isPayToPubKeyHash(connectedScript)) { - byte[] addressBytes = connectedScript.getPubKeyHash(); + byte[] addressBytes = ScriptPattern.extractHashFromPayToPubKeyHash(connectedScript); return keyBag.findKeyFromPubHash(addressBytes); } else if (ScriptPattern.isPayToPubKey(connectedScript)) { byte[] pubkeyBytes = connectedScript.getPubKey(); @@ -165,13 +165,13 @@ public class TransactionOutPoint extends ChildMessage { checkNotNull(connectedOutput, "Input is not connected so cannot retrieve key"); Script connectedScript = connectedOutput.getScriptPubKey(); if (ScriptPattern.isPayToPubKeyHash(connectedScript)) { - byte[] addressBytes = connectedScript.getPubKeyHash(); + byte[] addressBytes = ScriptPattern.extractHashFromPayToPubKeyHash(connectedScript); return RedeemData.of(keyBag.findKeyFromPubHash(addressBytes), connectedScript); } else if (ScriptPattern.isPayToPubKey(connectedScript)) { byte[] pubkeyBytes = connectedScript.getPubKey(); return RedeemData.of(keyBag.findKeyFromPubKey(pubkeyBytes), connectedScript); } else if (ScriptPattern.isPayToScriptHash(connectedScript)) { - byte[] scriptHash = connectedScript.getPubKeyHash(); + byte[] scriptHash = ScriptPattern.extractHashFromPayToScriptHash(connectedScript); return keyBag.findRedeemDataFromScriptHash(scriptHash); } else { throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Could not understand form of connected output script: " + connectedScript); diff --git a/core/src/main/java/org/bitcoinj/core/TransactionOutput.java b/core/src/main/java/org/bitcoinj/core/TransactionOutput.java index 5da8dbd4..3760cdcf 100644 --- a/core/src/main/java/org/bitcoinj/core/TransactionOutput.java +++ b/core/src/main/java/org/bitcoinj/core/TransactionOutput.java @@ -325,7 +325,7 @@ public class TransactionOutput extends ChildMessage { byte[] pubkey = script.getPubKey(); return transactionBag.isPubKeyMine(pubkey); } if (ScriptPattern.isPayToScriptHash(script)) { - return transactionBag.isPayToScriptHashMine(script.getPubKeyHash()); + return transactionBag.isPayToScriptHashMine(ScriptPattern.extractHashFromPayToScriptHash(script)); } else { byte[] pubkeyHash = script.getPubKeyHash(); return transactionBag.isPubKeyHashMine(pubkeyHash); diff --git a/core/src/main/java/org/bitcoinj/script/Script.java b/core/src/main/java/org/bitcoinj/script/Script.java index 7230040b..4597a98d 100644 --- a/core/src/main/java/org/bitcoinj/script/Script.java +++ b/core/src/main/java/org/bitcoinj/script/Script.java @@ -250,9 +250,9 @@ public class Script { */ public byte[] getPubKeyHash() throws ScriptException { if (ScriptPattern.isPayToPubKeyHash(this)) - return chunks.get(2).data; + return ScriptPattern.extractHashFromPayToPubKeyHash(this); else if (ScriptPattern.isPayToScriptHash(this)) - return chunks.get(1).data; + return ScriptPattern.extractHashFromPayToScriptHash(this); else throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Script not in the standard scriptPubKey form"); } @@ -341,7 +341,7 @@ public class Script { */ public Address getToAddress(NetworkParameters params, boolean forcePayToPubKey) throws ScriptException { if (ScriptPattern.isPayToPubKeyHash(this)) - return new Address(params, getPubKeyHash()); + return new Address(params, ScriptPattern.extractHashFromPayToPubKeyHash(this)); else if (ScriptPattern.isPayToScriptHash(this)) return Address.fromP2SHScript(params, this); else if (forcePayToPubKey && ScriptPattern.isPayToPubKey(this)) diff --git a/core/src/main/java/org/bitcoinj/script/ScriptPattern.java b/core/src/main/java/org/bitcoinj/script/ScriptPattern.java index 1aaa209c..48be7fbd 100644 --- a/core/src/main/java/org/bitcoinj/script/ScriptPattern.java +++ b/core/src/main/java/org/bitcoinj/script/ScriptPattern.java @@ -45,6 +45,10 @@ public class ScriptPattern { chunks.get(4).equalsOpCode(OP_CHECKSIG); } + public static byte[] extractHashFromPayToPubKeyHash(Script script) { + return script.chunks.get(2).data; + } + /** *

Whether or not this is a scriptPubKey representing a pay-to-script-hash output. In such outputs, the logic that * controls reclamation is not actually in the output at all. Instead there's just a hash, and it's up to the @@ -65,6 +69,10 @@ public class ScriptPattern { chunks.get(2).equalsOpCode(OP_EQUAL); } + public static byte[] extractHashFromPayToScriptHash(Script script) { + return script.chunks.get(1).data; + } + /** * Returns true if this script is of the form OP_CHECKSIG. This form was originally intended for transactions * where the peers talked to each other directly via TCP/IP, but has fallen out of favor with time due to that mode diff --git a/core/src/main/java/org/bitcoinj/wallet/KeyTimeCoinSelector.java b/core/src/main/java/org/bitcoinj/wallet/KeyTimeCoinSelector.java index f4069eb8..2b178a62 100644 --- a/core/src/main/java/org/bitcoinj/wallet/KeyTimeCoinSelector.java +++ b/core/src/main/java/org/bitcoinj/wallet/KeyTimeCoinSelector.java @@ -65,7 +65,7 @@ public class KeyTimeCoinSelector implements CoinSelector { if (ScriptPattern.isPayToPubKey(scriptPubKey)) { controllingKey = wallet.findKeyFromPubKey(scriptPubKey.getPubKey()); } else if (ScriptPattern.isPayToPubKeyHash(scriptPubKey)) { - controllingKey = wallet.findKeyFromPubHash(scriptPubKey.getPubKeyHash()); + controllingKey = wallet.findKeyFromPubHash(ScriptPattern.extractHashFromPayToPubKeyHash(scriptPubKey)); } else { log.info("Skipping tx output {} because it's not of simple form.", output); continue; diff --git a/core/src/main/java/org/bitcoinj/wallet/Wallet.java b/core/src/main/java/org/bitcoinj/wallet/Wallet.java index 615f34ce..2fc0b48f 100644 --- a/core/src/main/java/org/bitcoinj/wallet/Wallet.java +++ b/core/src/main/java/org/bitcoinj/wallet/Wallet.java @@ -1082,7 +1082,7 @@ public class Wallet extends BaseTaggableObject byte[] pubkey = script.getPubKey(); keyChainGroup.markPubKeyAsUsed(pubkey); } else if (ScriptPattern.isPayToPubKeyHash(script)) { - byte[] pubkeyHash = script.getPubKeyHash(); + byte[] pubkeyHash = ScriptPattern.extractHashFromPayToPubKeyHash(script); keyChainGroup.markPubKeyHashAsUsed(pubkeyHash); } else if (ScriptPattern.isPayToScriptHash(script)) { Address a = Address.fromP2SHScript(tx.getParams(), script); @@ -4192,10 +4192,10 @@ public class Wallet extends BaseTaggableObject ECKey key = findKeyFromPubKey(pubkey); return key != null && (key.isEncrypted() || key.hasPrivKey()); } if (ScriptPattern.isPayToScriptHash(script)) { - RedeemData data = findRedeemDataFromScriptHash(script.getPubKeyHash()); + RedeemData data = findRedeemDataFromScriptHash(ScriptPattern.extractHashFromPayToScriptHash(script)); return data != null && canSignFor(data.redeemScript); } else if (ScriptPattern.isPayToPubKeyHash(script)) { - ECKey key = findKeyFromPubHash(script.getPubKeyHash()); + ECKey key = findKeyFromPubHash(ScriptPattern.extractHashFromPayToPubKeyHash(script)); return key != null && (key.isEncrypted() || key.hasPrivKey()); } else if (ScriptPattern.isSentToMultisig(script)) { for (ECKey pubkey : script.getPubKeys()) { @@ -4982,10 +4982,10 @@ public class Wallet extends BaseTaggableObject ECKey key = null; Script redeemScript = null; if (ScriptPattern.isPayToPubKeyHash(script)) { - key = findKeyFromPubHash(script.getPubKeyHash()); + key = findKeyFromPubHash(ScriptPattern.extractHashFromPayToPubKeyHash(script)); checkNotNull(key, "Coin selection includes unspendable outputs"); } else if (ScriptPattern.isPayToScriptHash(script)) { - redeemScript = findRedeemDataFromScriptHash(script.getPubKeyHash()).redeemScript; + redeemScript = findRedeemDataFromScriptHash(ScriptPattern.extractHashFromPayToScriptHash(script)).redeemScript; checkNotNull(redeemScript, "Coin selection includes unspendable outputs"); } size += script.getNumberOfBytesRequiredToSpend(key, redeemScript);