Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions core/src/main/java/org/bitcoinj/core/VersionMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@

package org.bitcoinj.core;

import com.google.common.net.InetAddresses;
import org.bitcoinj.base.internal.Buffers;
import org.bitcoinj.base.internal.TimeUtils;
import org.bitcoinj.base.internal.ByteUtils;

import org.jspecify.annotations.Nullable;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
Expand Down Expand Up @@ -149,14 +149,21 @@ public VersionMessage(NetworkParameters params, int bestHeight) {
this.clientVersion = ProtocolVersion.CURRENT.intValue();
this.localServices = Services.none();
this.time = TimeUtils.currentTime().truncatedTo(ChronoUnit.SECONDS);
InetAddress localhost = InetAddresses.forString("127.0.0.1");
this.receivingServices = Services.none();
this.receivingAddr = new InetSocketAddress(localhost, params.getPort());
this.receivingAddr = new InetSocketAddress(getLocalhostAddr(), params.getPort());
this.subVer = LIBRARY_SUBVER;
this.bestHeight = bestHeight;
this.relayTxesBeforeFilter = true;
}

private InetAddress getLocalhostAddr() {
try {
return InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}

private VersionMessage(int clientVersion, Services localServices, Instant time, Services receivingServices,
InetSocketAddress receivingAddr, String subVer, long bestHeight,
boolean relayTxesBeforeFilter) {
Expand Down
15 changes: 9 additions & 6 deletions core/src/main/java/org/bitcoinj/script/Script.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public static Script of(List<ScriptChunk> chunks) {
* @param creationTime creation time to associate the script with
* @return script that wraps the chunks
*/
public static Script of(List<ScriptChunk> chunks, Instant creationTime) {
public static Script of(List<ScriptChunk> chunks, @Nullable Instant creationTime) {
return new Script(chunks, creationTime);
}

Expand All @@ -139,7 +139,7 @@ public static Script parse(byte[] program) throws ScriptException {
* @return parsed program
* @throws ScriptException if the program could not be parsed
*/
public static Script parse(byte[] program, Instant creationTime) throws ScriptException {
public static Script parse(byte[] program, @Nullable Instant creationTime) throws ScriptException {
return new Script(program, creationTime);
}

Expand Down Expand Up @@ -476,8 +476,10 @@ public List<ECKey> getPubKeys() {

ArrayList<ECKey> result = new ArrayList<>();
int numKeys = Script.decodeFromOpN(chunks.get(chunks.size() - 2).opcode);
for (int i = 0 ; i < numKeys ; i++)
result.add(ECKey.fromPublicOnly(chunks.get(1 + i).data));
for (int i = 0 ; i < numKeys ; i++) {
byte[] data = Objects.requireNonNull(chunks.get(1 + i).data);
result.add(ECKey.fromPublicOnly(data));
}
return result;
}

Expand All @@ -486,7 +488,8 @@ private int findSigInRedeem(byte[] signatureBytes, Sha256Hash hash) throws Signa
int numKeys = Script.decodeFromOpN(chunks.get(chunks.size() - 2).opcode);
TransactionSignature signature = TransactionSignature.decodeFromBitcoin(signatureBytes, true, false);
for (int i = 0 ; i < numKeys ; i++) {
if (ECKey.fromPublicOnly(chunks.get(i + 1).data).verify(hash, signature)) {
byte[] data = Objects.requireNonNull(chunks.get(1 + i).data);
if (ECKey.fromPublicOnly(data).verify(hash, signature)) {
return i;
}
}
Expand Down Expand Up @@ -570,7 +573,7 @@ public static long getP2SHSigOpCount(byte[] scriptSig) throws ScriptException {
Collections.reverse(chunks);
for (ScriptChunk chunk : chunks) {
if (!chunk.isOpCode()) {
Script subScript = parse(chunk.data);
Script subScript = parse(Objects.requireNonNull(chunk.data));
return getSigOpCount(subScript.chunks, true);
}
}
Expand Down
13 changes: 10 additions & 3 deletions core/src/main/java/org/bitcoinj/script/ScriptExecution.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import static org.bitcoinj.script.ScriptOpCodes.OP_0;
Expand Down Expand Up @@ -718,6 +719,7 @@ public static void executeScript(@Nullable Transaction txContainingThis, long in
}
break;
}
Objects.requireNonNull(txContainingThis);
executeCheckLockTimeVerify(txContainingThis, (int) index, stack, verifyFlags);
break;
case OP_CHECKSEQUENCEVERIFY:
Expand All @@ -728,6 +730,7 @@ public static void executeScript(@Nullable Transaction txContainingThis, long in
}
break;
}
Objects.requireNonNull(txContainingThis);
executeCheckSequenceVerify(txContainingThis, (int) index, stack, verifyFlags);
break;
case OP_NOP1:
Expand Down Expand Up @@ -1018,6 +1021,7 @@ public static void correctlySpends(Script script, Transaction txContainingThis,
Script scriptPubKey, Set<VerifyFlag> verifyFlags) throws ScriptException {
List<ScriptChunk> chunks = script.chunks();
if (ScriptPattern.isP2WPKH(scriptPubKey)) {
Objects.requireNonNull(witness);
// For segwit, full validation isn't implemented. So we simply check the signature. P2SH_P2WPKH is handled
// by the P2SH code for now.
if (witness.getPushCount() < 2)
Expand All @@ -1040,11 +1044,12 @@ public static void correctlySpends(Script script, Transaction txContainingThis,
throw new ScriptException(ScriptError.SCRIPT_ERR_SCRIPT_SIZE, "Invalid size: " + chunks.size());
TransactionSignature signature;
try {
signature = TransactionSignature.decodeFromBitcoin(chunks.get(0).data, true, true);
byte[] data = Objects.requireNonNull(chunks.get(0).data);
signature = TransactionSignature.decodeFromBitcoin(data, true, true);
} catch (SignatureDecodeException x) {
throw new ScriptException(ScriptError.SCRIPT_ERR_SIG_DER, "Cannot decode", x);
}
ECKey pubkey = ECKey.fromPublicOnly(chunks.get(1).data);
ECKey pubkey = ECKey.fromPublicOnly(Objects.requireNonNull(chunks.get(1).data));
Sha256Hash sigHash = txContainingThis.hashForSignature(scriptSigIndex, scriptPubKey,
signature.sigHashMode(), false);
boolean validSig = pubkey.verify(sigHash, signature);
Expand All @@ -1055,7 +1060,8 @@ public static void correctlySpends(Script script, Transaction txContainingThis,
throw new ScriptException(ScriptError.SCRIPT_ERR_SCRIPT_SIZE, "Invalid size: " + chunks.size());
TransactionSignature signature;
try {
signature = TransactionSignature.decodeFromBitcoin(chunks.get(0).data, false, false);
byte[] data = Objects.requireNonNull(chunks.get(0).data);
signature = TransactionSignature.decodeFromBitcoin(data, false, false);
} catch (SignatureDecodeException x) {
throw new ScriptException(ScriptError.SCRIPT_ERR_SIG_DER, "Cannot decode", x);
}
Expand Down Expand Up @@ -1121,6 +1127,7 @@ public static void correctlySpends(Script script, Transaction txContainingThis,

// TODO: Check if we can take out enforceP2SH if there's a checkpoint at the enforcement block.
if (verifyFlags.contains(VerifyFlag.P2SH) && ScriptPattern.isP2SH(scriptPubKey)) {
Objects.requireNonNull(p2shStack);
for (ScriptChunk chunk : script.chunks())
if (!chunk.isPushData())
throw new ScriptException(ScriptError.SCRIPT_ERR_SIG_PUSHONLY, "Attempted to spend a P2SH scriptPubKey with a script that contained the script op " + chunk);
Expand Down
11 changes: 6 additions & 5 deletions core/src/main/java/org/bitcoinj/script/ScriptPattern.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import static org.bitcoinj.script.Script.decodeFromOpN;
import static org.bitcoinj.script.ScriptOpCodes.OP_0;
Expand Down Expand Up @@ -72,7 +73,7 @@ public static boolean isP2PKH(Script script) {
* will want to guard calls to this method with {@link #isP2PKH(Script)}.
*/
public static byte[] extractHashFromP2PKH(Script script) {
return script.chunks().get(2).data;
return Objects.requireNonNull(script.chunks().get(2).data);
}

/**
Expand Down Expand Up @@ -114,7 +115,7 @@ public static boolean isP2SH(Script script) {
* will want to guard calls to this method with {@link #isP2SH(Script)}.
*/
public static byte[] extractHashFromP2SH(Script script) {
return script.chunks().get(1).data;
return Objects.requireNonNull(script.chunks().get(1).data);
}

/**
Expand Down Expand Up @@ -145,7 +146,7 @@ public static boolean isP2PK(Script script) {
* want to guard calls to this method with {@link #isP2PK(Script)}.
*/
public static byte[] extractKeyFromP2PK(Script script) {
return script.chunks().get(0).data;
return Objects.requireNonNull(script.chunks().get(0).data);
}

/**
Expand Down Expand Up @@ -201,7 +202,7 @@ public static boolean isP2WSH(Script script) {
* {@link #isP2WH(Script)}.
*/
public static byte[] extractHashFromP2WH(Script script) {
return script.chunks().get(1).data;
return Objects.requireNonNull(script.chunks().get(1).data);
}

/**
Expand All @@ -227,7 +228,7 @@ public static boolean isP2TR(Script script) {
* form, so you will want to guard calls to this method with {@link #isP2TR(Script)}.
*/
public static byte[] extractOutputKeyFromP2TR(Script script) {
return script.chunks().get(1).data;
return Objects.requireNonNull(script.chunks().get(1).data);
}

/**
Expand Down
5 changes: 4 additions & 1 deletion core/src/main/java/org/bitcoinj/script/package-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@
/**
* Classes for working with and executing Bitcoin script programs, as embedded in inputs and outputs.
*/
package org.bitcoinj.script;
@NullMarked
package org.bitcoinj.script;

import org.jspecify.annotations.NullMarked;
19 changes: 19 additions & 0 deletions core/src/test/java/org/bitcoinj/core/VersionMessageTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
import org.bitcoinj.params.TestNet3Params;
import org.junit.Test;

import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.time.Instant;

Expand All @@ -32,6 +34,23 @@

public class VersionMessageTest {
private static final NetworkParameters TESTNET = TestNet3Params.get();
private static final Inet4Address LOCALHOST_IPV4ADDR = getLocalhostAddr();

private static Inet4Address getLocalhostAddr() {
try {
return (Inet4Address) InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}

@Test
public void testConstructor() {
VersionMessage versionMessage = new VersionMessage(TESTNET, 0);
// Receiving address is currently hardcoded to 127.0.0.1 (ipv4 localhost)
assertEquals(LOCALHOST_IPV4ADDR, versionMessage.receivingAddr.getAddress());
assertEquals(TESTNET.getPort(), versionMessage.receivingAddr.getPort());
}

@Test
public void decode_noRelay_bestHeight_subVer() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package org.bitcoinj.script;

import org.jspecify.annotations.Nullable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
Expand All @@ -24,6 +25,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Random;

import static org.bitcoinj.script.ScriptOpCodes.OP_NOP;
Expand All @@ -42,7 +44,7 @@ public class ScriptChunkSizeTest {
private static final Random RANDOM = new Random(42);

@Parameterized.Parameter
public ScriptChunk scriptChunk;
@Nullable public ScriptChunk scriptChunk;

@Parameterized.Parameters
public static Collection<ScriptChunk> data() {
Expand Down Expand Up @@ -83,6 +85,7 @@ private static byte[] randomBytes(int size) {

@Test
public void testSize() {
Objects.requireNonNull(scriptChunk);
assertEquals(scriptChunk.toByteArray().length, scriptChunk.size());
}
}
11 changes: 7 additions & 4 deletions core/src/test/java/org/bitcoinj/script/ScriptTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

Expand Down Expand Up @@ -393,9 +394,10 @@ public void dataDrivenValidTransactions() throws Exception {

for (int i = 0; i < transaction.getInputs().size(); i++) {
TransactionInput input = transaction.getInput(i);
assertTrue(scriptPubKeys.containsKey(input.getOutpoint()));
Script script = scriptPubKeys.get(input.getOutpoint());
assertNotNull(script);
ScriptExecution.correctlySpends(input.getScriptSig(), transaction, i, null, null,
scriptPubKeys.get(input.getOutpoint()), verifyFlags);
script, verifyFlags);
}
} catch (Exception e) {
System.err.println(test);
Expand Down Expand Up @@ -442,10 +444,11 @@ public void dataDrivenInvalidTransactions() throws Exception {

for (int i = 0; i < transaction.getInputs().size() && valid; i++) {
TransactionInput input = transaction.getInput(i);
assertTrue(scriptPubKeys.containsKey(input.getOutpoint()));
Script script = scriptPubKeys.get(input.getOutpoint());
assertNotNull(script);
try {
ScriptExecution.correctlySpends(input.getScriptSig(), transaction, i, null, null,
scriptPubKeys.get(input.getOutpoint()), verifyFlags);
script, verifyFlags);
} catch (VerificationException e) {
valid = false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public RedeemData findRedeemDataFromScriptHash(byte[] scriptHash) {
EnumSet.of(ScriptExecution.VerifyFlag.DERSIG, ScriptExecution.VerifyFlag.P2SH));

final Script scriptSig = input.getScriptSig();
final TransactionSignature signature = TransactionSignature.decodeFromBitcoin(scriptSig.chunks().get(0).pushData(), true, false);
final TransactionSignature signature = TransactionSignature.decodeFromBitcoin(Objects.requireNonNull(scriptSig.chunks().get(0).pushData()), true, false);

// First output a conventional low-S transaction with the LOW_S flag, for the tx_valid.json set
System.out.println("[\"A transaction with a low-S signature.\"],");
Expand All @@ -119,7 +119,7 @@ public RedeemData findRedeemDataFromScriptHash(byte[] scriptHash) {

final BigInteger highS = HIGH_S_DIFFERENCE.subtract(signature.s);
final TransactionSignature highSig = new TransactionSignature(signature.r, highS);
input = input.withScriptSig(new ScriptBuilder().data(highSig.encodeToBitcoin()).data(scriptSig.chunks().get(1).pushData()).build());
input = input.withScriptSig(new ScriptBuilder().data(highSig.encodeToBitcoin()).data(Objects.requireNonNull(scriptSig.chunks().get(1).pushData())).build());
ScriptExecution.correctlySpends(input.getScriptSig(), outputTransaction, 0, null, null, output.getScriptPubKey(),
EnumSet.of(ScriptExecution.VerifyFlag.P2SH));

Expand Down
Loading