Transaction: Add findWitnessCommitment() to locate the witness commitment in a coinbase.

This commit is contained in:
Andreas Schildbach 2019-02-22 01:23:22 +01:00
parent 533487b97f
commit 11e4cb956e
3 changed files with 29 additions and 12 deletions

View file

@ -33,6 +33,7 @@ import org.bitcoinj.wallet.WalletTransaction.Pool;
import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs; import com.google.common.primitives.Longs;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -1584,6 +1585,17 @@ public class Transaction extends ChildMessage {
} }
} }
/** Loops the outputs of a coinbase transaction to locate the witness commitment. */
public Sha256Hash findWitnessCommitment() {
checkState(isCoinBase());
for (TransactionOutput out : Lists.reverse(outputs)) {
Script scriptPubKey = out.getScriptPubKey();
if (ScriptPattern.isWitnessCommitment(scriptPubKey))
return ScriptPattern.extractWitnessCommitmentHash(scriptPubKey);
}
return null;
}
/** /**
* <p>Checks the transaction contents for sanity, in ways that can be done in a standalone manner. * <p>Checks the transaction contents for sanity, in ways that can be done in a standalone manner.
* Does <b>not</b> perform all checks on a transaction such as whether the inputs are already spent. * Does <b>not</b> perform all checks on a transaction such as whether the inputs are already spent.

View file

@ -284,7 +284,7 @@ public class ScriptPattern {
* Returns whether this script matches the pattern for a segwit commitment (in an output of the coinbase * Returns whether this script matches the pattern for a segwit commitment (in an output of the coinbase
* transaction). * transaction).
*/ */
public static boolean isSegwitCommitment(Script script) { public static boolean isWitnessCommitment(Script script) {
List<ScriptChunk> chunks = script.chunks; List<ScriptChunk> chunks = script.chunks;
if (chunks.size() < 2) if (chunks.size() < 2)
return false; return false;
@ -301,7 +301,7 @@ public class ScriptPattern {
/** /**
* Retrieves the hash from a segwit commitment (in an output of the coinbase transaction). * Retrieves the hash from a segwit commitment (in an output of the coinbase transaction).
*/ */
public static Sha256Hash extractSegwitCommitmentHash(Script script) { public static Sha256Hash extractWitnessCommitmentHash(Script script) {
return Sha256Hash.wrap(Arrays.copyOfRange(script.chunks.get(1).data, 4, 36)); return Sha256Hash.wrap(Arrays.copyOfRange(script.chunks.get(1).data, 4, 36));
} }
} }

View file

@ -23,9 +23,7 @@ import org.bitcoinj.core.AbstractBlockChain.NewBlockType;
import org.bitcoinj.params.MainNetParams; import org.bitcoinj.params.MainNetParams;
import org.bitcoinj.params.TestNet3Params; import org.bitcoinj.params.TestNet3Params;
import org.bitcoinj.params.UnitTestParams; import org.bitcoinj.params.UnitTestParams;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptOpCodes; import org.bitcoinj.script.ScriptOpCodes;
import org.bitcoinj.script.ScriptPattern;
import org.bitcoinj.wallet.Wallet; import org.bitcoinj.wallet.Wallet;
import org.bitcoinj.wallet.Wallet.BalanceType; import org.bitcoinj.wallet.Wallet.BalanceType;
import org.junit.Before; import org.junit.Before;
@ -238,22 +236,29 @@ public class BlockTest {
} }
@Test @Test
public void testBlock481815_segwitCommitmentInCoinbase() throws Exception { public void testBlock481815_witnessCommitmentInCoinbase() throws Exception {
Block block481815 = MAINNET.getDefaultSerializer().makeBlock(ByteStreams.toByteArray( Block block481815 = MAINNET.getDefaultSerializer()
getClass().getResourceAsStream("block481815.dat"))); .makeBlock(ByteStreams.toByteArray(getClass().getResourceAsStream("block481815.dat")));
assertEquals(2097, block481815.getTransactions().size()); assertEquals(2097, block481815.getTransactions().size());
Transaction coinbase = block481815.getTransactions().get(0); Transaction coinbase = block481815.getTransactions().get(0);
final Script segwitCommitment = coinbase.getOutput(1).getScriptPubKey(); assertEquals("919a0df2253172a55bebcb9002dbe775b8511f84955b282ca6dae826fdd94f90", coinbase.getTxId().toString());
assertTrue(ScriptPattern.isSegwitCommitment(segwitCommitment)); assertEquals("919a0df2253172a55bebcb9002dbe775b8511f84955b282ca6dae826fdd94f90",
assertEquals("3d03076733467c45b08ec503a0c5d406647b073e1914d35b5111960ed625f3b7", coinbase.getWTxId().toString());
ScriptPattern.extractSegwitCommitmentHash(segwitCommitment).toString()); Sha256Hash witnessCommitment = coinbase.findWitnessCommitment();
assertEquals("3d03076733467c45b08ec503a0c5d406647b073e1914d35b5111960ed625f3b7", witnessCommitment.toString());
} }
@Test @Test
public void testBlock481829_segwitTransaction() throws Exception { public void testBlock481829_witnessTransactions() throws Exception {
Block block481829 = MAINNET.getDefaultSerializer() Block block481829 = MAINNET.getDefaultSerializer()
.makeBlock(ByteStreams.toByteArray(getClass().getResourceAsStream("block481829.dat"))); .makeBlock(ByteStreams.toByteArray(getClass().getResourceAsStream("block481829.dat")));
assertEquals(2020, block481829.getTransactions().size()); assertEquals(2020, block481829.getTransactions().size());
Transaction coinbase = block481829.getTransactions().get(0);
assertEquals("9c1ab453283035800c43eb6461eb46682b81be110a0cb89ee923882a5fd9daa4", coinbase.getTxId().toString());
assertEquals("2bbda73aa4e561e7f849703994cc5e563e4bcf103fb0f6fef5ae44c95c7b83a6",
coinbase.getWTxId().toString());
Sha256Hash witnessCommitment = coinbase.findWitnessCommitment();
assertEquals("c3c1145d8070a57e433238e42e4c022c1e51ca2a958094af243ae1ee252ca106", witnessCommitment.toString());
} }
@Test @Test