This commit is contained in:
Andreas Schildbach 2025-03-11 12:21:09 -05:00 committed by GitHub
commit e58b8894da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 25 additions and 92 deletions

View file

@ -32,7 +32,6 @@ import org.bitcoinj.crypto.ECKey;
import org.bitcoinj.params.BitcoinNetworkParams;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.script.ScriptOpCodes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -43,6 +42,7 @@ import java.io.OutputStream;
import java.math.BigInteger;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
@ -239,22 +239,19 @@ public class Block extends BaseMessage {
}
private static List<Transaction> genesisTransactions() {
Transaction tx = Transaction.coinbase(genesisTxInputScriptBytes);
tx.addOutput(new TransactionOutput(tx, FIFTY_COINS, genesisTxScriptPubKeyBytes));
byte[] messageBytes = GENESIS_MESSAGE.getBytes(StandardCharsets.US_ASCII);
Script scriptSig = // TODO find out what the pushdata(4) is supposed to mean
new ScriptBuilder().bigNum(STANDARD_MAX_DIFFICULTY_TARGET).bigNum(4).data(messageBytes).build();
Transaction tx = Transaction.coinbase(scriptSig.program());
tx.addOutput(new TransactionOutput(
tx, FIFTY_COINS, ScriptBuilder.createP2PKOutputScript(GENESIS_OUTPUT_PUBKEY).program()));
return Collections.singletonList(tx);
}
// A script containing the difficulty bits and the following message:
//
// "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"
private static final byte[] genesisTxInputScriptBytes = ByteUtils.parseHex
("04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73");
private static final byte[] genesisTxScriptPubKeyBytes = new ScriptBuilder()
.data(ByteUtils.parseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"))
.op(ScriptOpCodes.OP_CHECKSIG)
.build()
.program();
private static final String GENESIS_MESSAGE =
"The Times 03/Jan/2009 Chancellor on brink of second bailout for banks";
private static final byte[] GENESIS_OUTPUT_PUBKEY = ByteUtils.parseHex(
"04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f");
@Override
public int messageSize() {

View file

@ -93,33 +93,18 @@ public class ScriptBuilder {
/** Adds the given chunk to the end of the program */
public ScriptBuilder addChunk(ScriptChunk chunk) {
return addChunk(chunks.size(), chunk);
}
/** Adds the given chunk at the given index in the program */
public ScriptBuilder addChunk(int index, ScriptChunk chunk) {
chunks.add(index, chunk);
chunks.add(chunk);
return this;
}
/** Adds the given opcode to the end of the program. */
public ScriptBuilder op(int opcode) {
return op(chunks.size(), opcode);
}
/** Adds the given opcode to the given index in the program */
public ScriptBuilder op(int index, int opcode) {
checkArgument(opcode > OP_PUSHDATA4);
return addChunk(index, new ScriptChunk(opcode, null));
return addChunk(new ScriptChunk(opcode, null));
}
/** Adds a copy of the given byte array as a data element (i.e. PUSHDATA) at the end of the program. */
public ScriptBuilder data(byte[] data) {
return data(chunks.size(), data);
}
/** Adds a copy of the given byte array as a data element (i.e. PUSHDATA) at the given index in the program. */
public ScriptBuilder data(int index, byte[] data) {
// implements BIP62
byte[] copy = Arrays.copyOf(data, data.length);
int opcode;
@ -140,7 +125,7 @@ public class ScriptBuilder {
} else {
throw new RuntimeException("Unimplemented");
}
return addChunk(index, new ScriptChunk(opcode, copy));
return addChunk(new ScriptChunk(opcode, copy));
}
/**
@ -148,20 +133,12 @@ public class ScriptBuilder {
* shortest encoding possible.
*/
public ScriptBuilder number(long num) {
return number(chunks.size(), num);
}
/**
* Adds the given number to the given index in the program. Automatically
* uses shortest encoding possible.
*/
public ScriptBuilder number(int index, long num) {
if (num == -1) {
return op(index, OP_1NEGATE);
return op(OP_1NEGATE);
} else if (num >= 0 && num <= 16) {
return smallNum(index, (int) num);
return smallNum((int) num);
} else {
return bigNum(index, num);
return bigNum(num);
}
}
@ -172,7 +149,11 @@ public class ScriptBuilder {
* @see #number(long)
*/
public ScriptBuilder smallNum(int num) {
return smallNum(chunks.size(), num);
checkArgument(num >= 0, () ->
"cannot encode negative numbers with smallNum");
checkArgument(num <= 16, () ->
"cannot encode numbers larger than 16 with smallNum");
return addChunk(new ScriptChunk(Script.encodeToOpN(num), null));
}
/** Adds the given number as a push data chunk.
@ -182,33 +163,7 @@ public class ScriptBuilder {
*
* @see #number(long)
*/
protected ScriptBuilder bigNum(long num) {
return bigNum(chunks.size(), num);
}
/**
* Adds the given number as a OP_N opcode to the given index in the program.
* Only handles values 0-16 inclusive.
*
* @see #number(long)
*/
public ScriptBuilder smallNum(int index, int num) {
checkArgument(num >= 0, () ->
"cannot encode negative numbers with smallNum");
checkArgument(num <= 16, () ->
"cannot encode numbers larger than 16 with smallNum");
return addChunk(index, new ScriptChunk(Script.encodeToOpN(num), null));
}
/**
* Adds the given number as a push data chunk to the given index in the program.
* This is intended to use for negative numbers or values greater than 16, and although
* it will accept numbers in the range 0-16 inclusive, the encoding would be
* considered non-standard.
*
* @see #number(long)
*/
protected ScriptBuilder bigNum(int index, long num) {
public ScriptBuilder bigNum(long num) {
final byte[] data;
if (num == 0) {
@ -242,7 +197,7 @@ public class ScriptBuilder {
// At most the encoded value could take up to 8 bytes, so we don't need
// to use OP_PUSHDATA opcodes
return addChunk(index, new ScriptChunk(data.length, data));
return addChunk(new ScriptChunk(data.length, data));
}
/**
@ -253,15 +208,6 @@ public class ScriptBuilder {
return number(1); // it push OP_1/OP_TRUE
}
/**
* Adds true to the given index in the program.
* @param index at which insert true
* @return this
*/
public ScriptBuilder opTrue(int index) {
return number(index, 1); // push OP_1/OP_TRUE
}
/**
* Adds false to the end of the program.
* @return this
@ -270,15 +216,6 @@ public class ScriptBuilder {
return number(0); // push OP_0/OP_FALSE
}
/**
* Adds false to the given index in the program.
* @param index at which insert true
* @return this
*/
public ScriptBuilder opFalse(int index) {
return number(index, 0); // push OP_0/OP_FALSE
}
/** Creates a new immutable Script based on the state of the builder. */
public Script build() {
if (creationTime != null)

View file

@ -134,9 +134,8 @@ public class ScriptBuilderTest {
ScriptBuilder builder = new ScriptBuilder();
// Numbers greater than 16 must be encoded with PUSHDATA
builder.number(15).number(16).number(17);
builder.number(0, 17).number(1, 16).number(2, 15);
Script script = builder.build();
assertEquals("PUSHDATA(1)[11] 16 15 15 16 PUSHDATA(1)[11]", script.toString());
assertEquals("15 16 PUSHDATA(1)[11]", script.toString());
}
@Test