diff --git a/core/src/main/java/org/bitcoinj/script/ScriptPattern.java b/core/src/main/java/org/bitcoinj/script/ScriptPattern.java
index a2d06be84cd..aa9b8053c87 100644
--- a/core/src/main/java/org/bitcoinj/script/ScriptPattern.java
+++ b/core/src/main/java/org/bitcoinj/script/ScriptPattern.java
@@ -258,25 +258,23 @@ public static boolean isOpReturn(Script script) {
/**
* Returns whether this script matches the pattern for a segwit commitment (in an output of the coinbase
* transaction).
+ * See BIP141.
*/
public static boolean isWitnessCommitment(Script script) {
- List chunks = script.chunks;
- if (chunks.size() < 2)
- return false;
- if (!chunks.get(0).equalsOpCode(ScriptOpCodes.OP_RETURN))
- return false;
- byte[] chunkData = chunks.get(1).data;
- if (chunkData == null || chunkData.length != 36)
- return false;
- if (!Arrays.equals(Arrays.copyOfRange(chunkData, 0, 4), SEGWIT_COMMITMENT_HEADER))
- return false;
- return true;
+ byte[] bytes = script.getProgram();
+ return bytes.length >= 38
+ && bytes[0] == ScriptOpCodes.OP_RETURN
+ && bytes[1] == 36 // length byte
+ && Arrays.equals(Arrays.copyOfRange(bytes, 2, 6), SEGWIT_COMMITMENT_HEADER);
}
/**
* Retrieves the hash from a segwit commitment (in an output of the coinbase transaction).
+ * You will want to guard calls to this method with {@link #isWitnessCommitment(Script)}.
+ * See BIP141.
*/
public static Sha256Hash extractWitnessCommitmentHash(Script script) {
- return Sha256Hash.wrap(Arrays.copyOfRange(script.chunks.get(1).data, 4, 36));
+ byte[] hash = Arrays.copyOfRange(script.getProgram(), 6, 38);
+ return Sha256Hash.wrap(hash);
}
}
diff --git a/core/src/test/java/org/bitcoinj/script/ScriptPatternTest.java b/core/src/test/java/org/bitcoinj/script/ScriptPatternTest.java
index 2e078391764..2beb5eb1019 100644
--- a/core/src/test/java/org/bitcoinj/script/ScriptPatternTest.java
+++ b/core/src/test/java/org/bitcoinj/script/ScriptPatternTest.java
@@ -21,11 +21,14 @@
import com.google.common.collect.Lists;
import org.bitcoinj.core.ECKey;
+import org.bitcoinj.core.Sha256Hash;
+import org.bitcoinj.core.Utils;
import org.junit.Test;
import java.util.List;
import static org.bitcoinj.script.ScriptOpCodes.OP_CHECKMULTISIG;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -93,4 +96,55 @@ public void testCreateOpReturnScript() {
ScriptBuilder.createOpReturnScript(new byte[10])
));
}
+
+ @Test
+ public void isWitnessCommitment() {
+ // OP_RETURN <1-byte length 36> <4-byte commitment header> <32 bytes commitment>
+ String hex = "6a24aa21a9ed0000000000000000000000000000000000000000000000000000000000000000";
+ Script script = new Script(Utils.HEX.decode(hex));
+ assertTrue(ScriptPattern.isWitnessCommitment(script));
+ assertEquals(Sha256Hash.ZERO_HASH, ScriptPattern.extractWitnessCommitmentHash(script));
+ }
+
+ @Test
+ public void isWitnessCommitment_tooShort() {
+ // OP_RETURN <1-byte length 35> <4-byte commitment header> <31 bytes commitment>
+ String hex = "6a23aa21a9ed00000000000000000000000000000000000000000000000000000000000000";
+ Script script = new Script(Utils.HEX.decode(hex));
+ assertFalse(ScriptPattern.isWitnessCommitment(script));
+ }
+
+ @Test
+ public void isWitnessCommitment_tooLong() {
+ // OP_RETURN <1-byte length 37> <4-byte commitment header> <33 bytes commitment>
+ String hex = "6a25aa21a9ed000000000000000000000000000000000000000000000000000000000000000000";
+ Script script = new Script(Utils.HEX.decode(hex));
+ assertFalse(ScriptPattern.isWitnessCommitment(script));
+ }
+
+ @Test
+ public void isWitnessCommitment_noOpReturn() {
+ // OP_NOP <1-byte length 36> <4-byte commitment header> <32 bytes commitment>
+ String hex = "6124aa21a9ed0000000000000000000000000000000000000000000000000000000000000000";
+ Script script = new Script(Utils.HEX.decode(hex));
+ assertFalse(ScriptPattern.isWitnessCommitment(script));
+ }
+
+ @Test
+ public void isWitnessCommitment_wrongCommitmentHeader() {
+ // OP_RETURN <1-byte length 36> <4-byte commitment header> <32 bytes commitment>
+ String hex = "6a24ffffffff0000000000000000000000000000000000000000000000000000000000000000";
+ Script script = new Script(Utils.HEX.decode(hex));
+ assertFalse(ScriptPattern.isWitnessCommitment(script));
+ }
+
+ @Test
+ public void extractWitnessCommitmentHash() {
+ // OP_RETURN <1-byte length 36> <4-byte commitment header> <32 bytes commitment>
+ String hex = "6a24aa21a9ed0000000000000000000000000000000000000000000000000000000000000000";
+ Script script = new Script(Utils.HEX.decode(hex));
+ Sha256Hash hash = ScriptPattern.extractWitnessCommitmentHash(script);
+ assertEquals("0000000000000000000000000000000000000000000000000000000000000000",
+ Utils.HEX.encode(hash.getBytes()));
+ }
}