Skip to content

Commit

Permalink
ScriptPattern: simplify isWitnessCommitment() and extractWitnessCommi…
Browse files Browse the repository at this point in the history
…tmentHash()

It now operates on bytes, rather than chunks. This also adds a bit of JavaDoc
and a couple of tests.
  • Loading branch information
schildbach committed Dec 14, 2024
1 parent 2589c0d commit c88e973
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 12 deletions.
22 changes: 10 additions & 12 deletions core/src/main/java/org/bitcoinj/script/ScriptPattern.java
Original file line number Diff line number Diff line change
Expand Up @@ -266,25 +266,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 <a href="https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#commitment-structure">BIP141</a>.
*/
public static boolean isWitnessCommitment(Script script) {
List<ScriptChunk> 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.program();
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 <a href="https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#commitment-structure">BIP141</a>.
*/
public static Sha256Hash extractWitnessCommitmentHash(Script script) {
return Sha256Hash.wrap(Arrays.copyOfRange(script.chunks().get(1).data, 4, 36));
byte[] hash = Arrays.copyOfRange(script.program(), 6, 38);
return Sha256Hash.wrap(hash);
}
}
52 changes: 52 additions & 0 deletions core/src/test/java/org/bitcoinj/script/ScriptPatternTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package org.bitcoinj.script;

import com.google.common.collect.Lists;
import org.bitcoinj.base.Sha256Hash;
import org.bitcoinj.base.internal.ByteUtils;
import org.bitcoinj.crypto.DumpedPrivateKey;
import org.bitcoinj.crypto.ECKey;
Expand Down Expand Up @@ -112,4 +113,55 @@ public void p2shScriptHashFromKeys() {
byte[] p2shScriptHash = ScriptPattern.extractHashFromP2SH(p2shScript);
assertEquals("defdb71910720a2c854529019189228b4245eddd", ByteUtils.formatHex(p2shScriptHash));
}

@Test
public void isWitnessCommitment() {
// OP_RETURN <1-byte length 36> <4-byte commitment header> <32 bytes commitment>
String hex = "6a24aa21a9ed0000000000000000000000000000000000000000000000000000000000000000";
Script script = Script.parse(ByteUtils.parseHex(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 = Script.parse(ByteUtils.parseHex(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 = Script.parse(ByteUtils.parseHex(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 = Script.parse(ByteUtils.parseHex(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 = Script.parse(ByteUtils.parseHex(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 = Script.parse(ByteUtils.parseHex(hex));
Sha256Hash hash = ScriptPattern.extractWitnessCommitmentHash(script);
assertEquals("0000000000000000000000000000000000000000000000000000000000000000",
ByteUtils.formatHex(hash.getBytes()));
}
}

0 comments on commit c88e973

Please sign in to comment.