ScriptPattern: Add pattern matcher for segwit commitment (in an output of the coinbase transaction).

Adds a test for parsing block 481815, which contains a segwit commitment in its coinbase.
This commit is contained in:
Andreas Schildbach 2018-03-21 13:22:57 +01:00
parent d30d83c616
commit b976205716
3 changed files with 45 additions and 0 deletions

View file

@ -19,8 +19,11 @@ package org.bitcoinj.script;
import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.SegwitAddress;
import org.bitcoinj.core.Sha256Hash;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
import static org.bitcoinj.script.Script.decodeFromOpN;
@ -274,4 +277,31 @@ public class ScriptPattern {
List<ScriptChunk> chunks = script.chunks;
return chunks.size() > 0 && chunks.get(0).equalsOpCode(ScriptOpCodes.OP_RETURN);
}
private static final byte[] SEGWIT_COMMITMENT_HEADER = Hex.decode("aa21a9ed");
/**
* Returns whether this script matches the pattern for a segwit commitment (in an output of the coinbase
* transaction).
*/
public static boolean isSegwitCommitment(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;
}
/**
* Retrieves the hash from a segwit commitment (in an output of the coinbase transaction).
*/
public static Sha256Hash extractSegwitCommitmentHash(Script script) {
return Sha256Hash.wrap(Arrays.copyOfRange(script.chunks.get(1).data, 4, 36));
}
}

View file

@ -24,12 +24,15 @@ import org.bitcoinj.params.MainNetParams;
import org.bitcoinj.params.TestNet2Params;
import org.bitcoinj.params.TestNet3Params;
import org.bitcoinj.params.UnitTestParams;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptOpCodes;
import org.bitcoinj.script.ScriptPattern;
import org.bitcoinj.wallet.Wallet;
import org.bitcoinj.wallet.Wallet.BalanceType;
import org.junit.Before;
import org.junit.Test;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.EnumSet;
@ -242,6 +245,18 @@ public class BlockTest {
assertEquals(Coin.ZERO, wallet.getBalance(BalanceType.AVAILABLE));
}
@Test
public void testBlock481815_segwitCommitmentInCoinbase() throws Exception {
Block block481815 = MAINNET.getDefaultSerializer().makeBlock(ByteStreams.toByteArray(
getClass().getResourceAsStream("block481815.dat")));
assertEquals(2097, block481815.getTransactions().size());
Transaction coinbase = block481815.getTransactions().get(0);
final Script segwitCommitment = coinbase.getOutput(1).getScriptPubKey();
assertTrue(ScriptPattern.isSegwitCommitment(segwitCommitment));
assertEquals("3d03076733467c45b08ec503a0c5d406647b073e1914d35b5111960ed625f3b7",
ScriptPattern.extractSegwitCommitmentHash(segwitCommitment).toString());
}
@Test
public void isBIPs() throws Exception {
final Block genesis = MAINNET.getGenesisBlock();