mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2025-01-19 05:33:44 +01:00
Transaction, TransactionInput, TransactionOutPoint: add static constructors for the elements of a coinbase
This should reduce misuse of the standard constructors.
This commit is contained in:
parent
820b671dbc
commit
81fb0c5acb
@ -253,18 +253,12 @@ public class Block extends Message {
|
||||
|
||||
public static Block createGenesis(NetworkParameters n) {
|
||||
Block genesisBlock = new Block(n, BLOCK_VERSION_GENESIS);
|
||||
Transaction t = createGenesisTransaction(n, genesisTxInputScriptBytes, FIFTY_COINS, genesisTxScriptPubKeyBytes);
|
||||
genesisBlock.addTransaction(t);
|
||||
Transaction tx = Transaction.coinbase(n, genesisTxInputScriptBytes);
|
||||
tx.addOutput(new TransactionOutput(tx, FIFTY_COINS, genesisTxScriptPubKeyBytes));
|
||||
genesisBlock.addTransaction(tx);
|
||||
return genesisBlock;
|
||||
}
|
||||
|
||||
private static Transaction createGenesisTransaction(NetworkParameters n, byte[] inputScriptBytes, Coin amount, byte[] scriptPubKeyBytes) {
|
||||
Transaction t = new Transaction(n);
|
||||
t.addInput(new TransactionInput(t, inputScriptBytes));
|
||||
t.addOutput(new TransactionOutput(t, amount, scriptPubKeyBytes));
|
||||
return t;
|
||||
}
|
||||
|
||||
// A script containing the difficulty bits and the following message:
|
||||
//
|
||||
// "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"
|
||||
@ -891,7 +885,7 @@ public class Block extends Message {
|
||||
//
|
||||
// Here we will do things a bit differently so a new address isn't needed every time. We'll put a simple
|
||||
// counter in the scriptSig so every transaction has a different hash.
|
||||
coinbase.addInput(new TransactionInput(coinbase,
|
||||
coinbase.addInput(TransactionInput.coinbaseInput(coinbase,
|
||||
inputBuilder.build().getProgram()));
|
||||
coinbase.addOutput(new TransactionOutput(coinbase, value,
|
||||
ScriptBuilder.createP2PKOutputScript(ECKey.fromPublicOnly(pubKeyTo)).getProgram()));
|
||||
|
@ -222,6 +222,31 @@ public class Transaction extends Message {
|
||||
@Nullable
|
||||
private String memo;
|
||||
|
||||
/**
|
||||
* Constructs an incomplete coinbase transaction with a minimal input script and no outputs.
|
||||
*
|
||||
* @param params network to use
|
||||
* @return coinbase transaction
|
||||
*/
|
||||
public static Transaction coinbase(NetworkParameters params) {
|
||||
Transaction tx = new Transaction(params);
|
||||
tx.addInput(TransactionInput.coinbaseInput(tx, new byte[2])); // 2 is minimum
|
||||
return tx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an incomplete coinbase transaction with given bytes for the input script and no outputs.
|
||||
*
|
||||
* @param params network to use
|
||||
* @param inputScriptBytes arbitrary bytes for the coinbase input
|
||||
* @return coinbase transaction
|
||||
*/
|
||||
public static Transaction coinbase(NetworkParameters params, byte[] inputScriptBytes) {
|
||||
Transaction tx = new Transaction(params);
|
||||
tx.addInput(TransactionInput.coinbaseInput(tx, inputScriptBytes));
|
||||
return tx;
|
||||
}
|
||||
|
||||
public Transaction(NetworkParameters params) {
|
||||
super(params);
|
||||
version = 1;
|
||||
|
@ -92,9 +92,15 @@ public class TransactionInput extends Message {
|
||||
|
||||
/**
|
||||
* Creates an input that connects to nothing - used only in creation of coinbase transactions.
|
||||
*
|
||||
* @param parentTransaction parent transaction
|
||||
* @param scriptBytes arbitrary bytes in the script
|
||||
*/
|
||||
public TransactionInput(@Nullable Transaction parentTransaction, byte[] scriptBytes) {
|
||||
this(parentTransaction, scriptBytes, new TransactionOutPoint(UNCONNECTED, (Transaction) null));
|
||||
public static TransactionInput coinbaseInput(Transaction parentTransaction, byte[] scriptBytes) {
|
||||
Objects.requireNonNull(parentTransaction);
|
||||
checkArgument(scriptBytes.length >= 2 && scriptBytes.length <= 100, () ->
|
||||
"script must be between 2 and 100 bytes: " + scriptBytes.length);
|
||||
return new TransactionInput(parentTransaction, scriptBytes, TransactionOutPoint.UNCONNECTED);
|
||||
}
|
||||
|
||||
public TransactionInput(@Nullable Transaction parentTransaction, byte[] scriptBytes,
|
||||
|
@ -47,6 +47,10 @@ public class TransactionOutPoint extends Message {
|
||||
|
||||
static final int MESSAGE_LENGTH = 36;
|
||||
|
||||
/** Special outpoint that normally marks a coinbase input. It's also used as a test dummy. */
|
||||
public static final TransactionOutPoint UNCONNECTED =
|
||||
new TransactionOutPoint(ByteUtils.MAX_UNSIGNED_INTEGER, Sha256Hash.ZERO_HASH);
|
||||
|
||||
/** Hash of the transaction to which we refer. */
|
||||
private Sha256Hash hash;
|
||||
/** Which output of that transaction we are talking about. */
|
||||
@ -58,18 +62,13 @@ public class TransactionOutPoint extends Message {
|
||||
// The connected output.
|
||||
TransactionOutput connectedOutput;
|
||||
|
||||
public TransactionOutPoint(long index, @Nullable Transaction fromTx) {
|
||||
public TransactionOutPoint(long index, Transaction fromTx) {
|
||||
super();
|
||||
checkArgument(index >= 0 && index <= ByteUtils.MAX_UNSIGNED_INTEGER, () ->
|
||||
"index out of range: " + index);
|
||||
this.index = index;
|
||||
if (fromTx != null) {
|
||||
this.hash = fromTx.getTxId();
|
||||
this.fromTx = fromTx;
|
||||
} else {
|
||||
// This happens when constructing the genesis block.
|
||||
hash = Sha256Hash.ZERO_HASH;
|
||||
}
|
||||
}
|
||||
|
||||
public TransactionOutPoint(long index, Sha256Hash hash) {
|
||||
|
@ -74,14 +74,10 @@ public class FakeTxBuilder {
|
||||
|
||||
/** Create a fake coinbase transaction. */
|
||||
public static Transaction createFakeCoinbaseTx(final NetworkParameters params) {
|
||||
TransactionOutPoint outpoint = new TransactionOutPoint(ByteUtils.MAX_UNSIGNED_INTEGER, Sha256Hash.ZERO_HASH);
|
||||
TransactionInput input = new TransactionInput(null, new byte[0], outpoint);
|
||||
Transaction tx = new Transaction(params);
|
||||
tx.addInput(input);
|
||||
Transaction tx = Transaction.coinbase(params);
|
||||
TransactionOutput outputToMe = new TransactionOutput(tx, Coin.FIFTY_COINS, randomAddress(params));
|
||||
tx.addOutput(outputToMe);
|
||||
|
||||
checkState(tx.isCoinBase());
|
||||
tx.addOutput(outputToMe);
|
||||
return tx;
|
||||
}
|
||||
|
||||
|
@ -1007,7 +1007,7 @@ public class FullBlockTestGenerator {
|
||||
NewBlock b51 = createNextBlock(b44, chainHeadHeight + 16, out15, null);
|
||||
{
|
||||
Transaction coinbase = new Transaction(params);
|
||||
coinbase.addInput(new TransactionInput(coinbase, new byte[]{(byte) 0xff, 110, 1}));
|
||||
coinbase.addInput(TransactionInput.coinbaseInput(coinbase, new byte[]{(byte) 0xff, 110, 1}));
|
||||
coinbase.addOutput(new TransactionOutput(coinbase, SATOSHI, outScriptBytes));
|
||||
b51.block.addTransaction(coinbase, false);
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class TransactionInputTest {
|
||||
private static final NetworkParameters TESTNET = TestNet3Params.get();
|
||||
@ -105,4 +106,10 @@ public class TransactionInputTest {
|
||||
assertNull(txInToDisconnect.getOutpoint().fromTx);
|
||||
assertNull(txInToDisconnect.getOutpoint().connectedOutput);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void coinbaseInput() {
|
||||
TransactionInput coinbaseInput = TransactionInput.coinbaseInput(new Transaction(TESTNET), new byte[2]);
|
||||
assertTrue(coinbaseInput.isCoinBase());
|
||||
}
|
||||
}
|
||||
|
@ -489,7 +489,7 @@ public class TransactionTest {
|
||||
@Test
|
||||
public void testToStringWhenIteratingOverAnInputCatchesAnException() {
|
||||
Transaction tx = FakeTxBuilder.createFakeTx(TESTNET);
|
||||
TransactionInput ti = new TransactionInput(tx, new byte[0]) {
|
||||
TransactionInput ti = new TransactionInput(tx, new byte[0], TransactionOutPoint.UNCONNECTED) {
|
||||
@Override
|
||||
public Script getScriptSig() throws ScriptException {
|
||||
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "");
|
||||
|
@ -25,6 +25,7 @@ import org.bitcoinj.base.Address;
|
||||
import org.bitcoinj.base.Coin;
|
||||
import org.bitcoinj.base.internal.TimeUtils;
|
||||
import org.bitcoinj.core.Context;
|
||||
import org.bitcoinj.core.TransactionOutPoint;
|
||||
import org.bitcoinj.crypto.ECKey;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
@ -90,7 +91,7 @@ public class PaymentSessionTest {
|
||||
|
||||
// Send the payment and verify that the correct information is sent.
|
||||
// Add a dummy input to tx so it is considered valid.
|
||||
tx.addInput(new TransactionInput(tx, outputToMe.getScriptBytes()));
|
||||
tx.addInput(new TransactionInput(tx, outputToMe.getScriptBytes(), TransactionOutPoint.UNCONNECTED));
|
||||
ArrayList<Transaction> txns = new ArrayList<>();
|
||||
txns.add(tx);
|
||||
Address refundAddr = serverKey.toAddress(ScriptType.P2PKH, BitcoinNetwork.TESTNET);
|
||||
@ -130,7 +131,7 @@ public class PaymentSessionTest {
|
||||
assertTrue(paymentSession.isExpired());
|
||||
// Send the payment and verify that an exception is thrown.
|
||||
// Add a dummy input to tx so it is considered valid.
|
||||
tx.addInput(new TransactionInput(tx, outputToMe.getScriptBytes()));
|
||||
tx.addInput(new TransactionInput(tx, outputToMe.getScriptBytes(), TransactionOutPoint.UNCONNECTED));
|
||||
ArrayList<Transaction> txns = new ArrayList<>();
|
||||
txns.add(tx);
|
||||
|
||||
@ -169,7 +170,7 @@ public class PaymentSessionTest {
|
||||
|
||||
// Send the payment and verify that the correct information is sent.
|
||||
// Add a dummy input to tx so it is considered valid.
|
||||
tx.addInput(new TransactionInput(tx, outputToMe.getScriptBytes()));
|
||||
tx.addInput(new TransactionInput(tx, outputToMe.getScriptBytes(), TransactionOutPoint.UNCONNECTED));
|
||||
ArrayList<Transaction> txns = new ArrayList<>();
|
||||
txns.add(tx);
|
||||
Address refundAddr = serverKey.toAddress(ScriptType.P2PKH, BitcoinNetwork.TESTNET);
|
||||
|
@ -248,7 +248,7 @@ public class ScriptTest {
|
||||
public void testOp0() {
|
||||
// Check that OP_0 doesn't NPE and pushes an empty stack frame.
|
||||
Transaction tx = new Transaction(TESTNET);
|
||||
tx.addInput(new TransactionInput(tx, new byte[] {}));
|
||||
tx.addInput(new TransactionInput(tx, new byte[0], TransactionOutPoint.UNCONNECTED));
|
||||
Script script = new ScriptBuilder().smallNum(0).build();
|
||||
|
||||
LinkedList<byte[]> stack = new LinkedList<>();
|
||||
@ -354,7 +354,7 @@ public class ScriptTest {
|
||||
tx.setLockTime(0);
|
||||
|
||||
TransactionInput txInput = new TransactionInput(null,
|
||||
new ScriptBuilder().number(0).number(0).build().getProgram());
|
||||
new ScriptBuilder().number(0).number(0).build().getProgram(), TransactionOutPoint.UNCONNECTED);
|
||||
txInput.setSequenceNumber(TransactionInput.NO_SEQUENCE);
|
||||
tx.addInput(txInput);
|
||||
|
||||
@ -369,7 +369,8 @@ public class ScriptTest {
|
||||
tx.setVersion(1);
|
||||
tx.setLockTime(0);
|
||||
|
||||
TransactionInput txInput = new TransactionInput(creditingTransaction, scriptSig.getProgram());
|
||||
TransactionInput txInput = new TransactionInput(creditingTransaction, scriptSig.getProgram(),
|
||||
TransactionOutPoint.UNCONNECTED);
|
||||
txInput.setSequenceNumber(TransactionInput.NO_SEQUENCE);
|
||||
tx.addInput(txInput);
|
||||
|
||||
|
@ -21,6 +21,7 @@ import org.bitcoinj.base.Coin;
|
||||
import org.bitcoinj.base.ScriptType;
|
||||
import org.bitcoinj.base.internal.ByteUtils;
|
||||
import org.bitcoinj.core.Context;
|
||||
import org.bitcoinj.core.TransactionOutPoint;
|
||||
import org.bitcoinj.crypto.ECKey;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
@ -161,7 +162,7 @@ public class DefaultRiskAnalysisTest {
|
||||
// Test non-standard script as an input.
|
||||
Transaction tx = new Transaction(MAINNET);
|
||||
assertEquals(DefaultRiskAnalysis.RuleViolation.NONE, DefaultRiskAnalysis.isStandard(tx));
|
||||
tx.addInput(new TransactionInput(null, nonStandardScript));
|
||||
tx.addInput(new TransactionInput(null, nonStandardScript, TransactionOutPoint.UNCONNECTED));
|
||||
assertEquals(DefaultRiskAnalysis.RuleViolation.SHORTEST_POSSIBLE_PUSHDATA, DefaultRiskAnalysis.isStandard(tx));
|
||||
// Test non-standard script as an output.
|
||||
tx.clearInputs();
|
||||
@ -175,15 +176,14 @@ public class DefaultRiskAnalysisTest {
|
||||
TransactionSignature sig = TransactionSignature.dummy();
|
||||
Script scriptOk = ScriptBuilder.createInputScript(sig);
|
||||
assertEquals(RuleViolation.NONE,
|
||||
DefaultRiskAnalysis.isInputStandard(new TransactionInput(null, scriptOk.getProgram())));
|
||||
DefaultRiskAnalysis.isInputStandard(new TransactionInput(null, scriptOk.getProgram(), TransactionOutPoint.UNCONNECTED)));
|
||||
|
||||
byte[] sigBytes = sig.encodeToBitcoin();
|
||||
// Appending a zero byte makes the signature uncanonical without violating DER encoding.
|
||||
Script scriptUncanonicalEncoding = new ScriptBuilder().data(Arrays.copyOf(sigBytes, sigBytes.length + 1))
|
||||
.build();
|
||||
assertEquals(RuleViolation.SIGNATURE_CANONICAL_ENCODING,
|
||||
DefaultRiskAnalysis.isInputStandard(new TransactionInput(null, scriptUncanonicalEncoding
|
||||
.getProgram())));
|
||||
assertEquals(RuleViolation.SIGNATURE_CANONICAL_ENCODING, DefaultRiskAnalysis.isInputStandard(
|
||||
new TransactionInput(null, scriptUncanonicalEncoding.getProgram(), TransactionOutPoint.UNCONNECTED)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -192,8 +192,8 @@ public class DefaultRiskAnalysisTest {
|
||||
TransactionSignature sig = TransactionSignature.dummy();
|
||||
Script scriptHighS = ScriptBuilder
|
||||
.createInputScript(new TransactionSignature(sig.r, ECKey.CURVE.getN().subtract(sig.s)));
|
||||
assertEquals(RuleViolation.SIGNATURE_CANONICAL_ENCODING,
|
||||
DefaultRiskAnalysis.isInputStandard(new TransactionInput(null, scriptHighS.getProgram())));
|
||||
assertEquals(RuleViolation.SIGNATURE_CANONICAL_ENCODING, DefaultRiskAnalysis.isInputStandard(
|
||||
new TransactionInput(null, scriptHighS.getProgram(), TransactionOutPoint.UNCONNECTED)));
|
||||
|
||||
// This is a real transaction. Its signatures S component is "low".
|
||||
Transaction tx1 = new Transaction(MAINNET, ByteBuffer.wrap(ByteUtils.parseHex(
|
||||
|
@ -751,7 +751,8 @@ public class WalletTest extends TestWithWallet {
|
||||
TransactionOutput to = createMock(TransactionOutput.class);
|
||||
EasyMock.expect(to.isAvailableForSpending()).andReturn(true);
|
||||
EasyMock.expect(to.isMineOrWatched(wallet)).andReturn(true);
|
||||
EasyMock.expect(to.getSpentBy()).andReturn(new TransactionInput(null, new byte[0]));
|
||||
EasyMock.expect(to.getSpentBy()).andReturn(
|
||||
new TransactionInput(null, new byte[0], TransactionOutPoint.UNCONNECTED));
|
||||
|
||||
Transaction tx = FakeTxBuilder.createFakeTxWithoutChange(TESTNET, to);
|
||||
|
||||
|
@ -824,7 +824,7 @@ public class PeerTest extends TestWithNetworkConnections {
|
||||
});
|
||||
connect();
|
||||
Transaction t1 = new Transaction(TESTNET);
|
||||
t1.addInput(new TransactionInput(t1, new byte[]{}));
|
||||
t1.addInput(new TransactionInput(t1, new byte[0], TransactionOutPoint.UNCONNECTED));
|
||||
t1.addOutput(COIN, new ECKey().toAddress(ScriptType.P2PKH, BitcoinNetwork.TESTNET));
|
||||
Transaction t2 = new Transaction(TESTNET);
|
||||
t2.addInput(t1.getOutput(0));
|
||||
|
Loading…
Reference in New Issue
Block a user