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 cc35ad1 commit 7aa3350
Show file tree
Hide file tree
Showing 2 changed files with 64 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 @@ -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 <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.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 <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.getProgram(), 6, 38);
return Sha256Hash.wrap(hash);
}
}
54 changes: 54 additions & 0 deletions core/src/test/java/org/bitcoinj/script/ScriptPatternTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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()));
}
}

0 comments on commit 7aa3350

Please sign in to comment.