diff --git a/core/src/main/java/com/google/bitcoin/core/Transaction.java b/core/src/main/java/com/google/bitcoin/core/Transaction.java index 7b6b1928..da5452bf 100644 --- a/core/src/main/java/com/google/bitcoin/core/Transaction.java +++ b/core/src/main/java/com/google/bitcoin/core/Transaction.java @@ -685,6 +685,38 @@ public class Transaction extends ChildMessage implements Serializable { return input; } + /** + * Adds a new and fully signed input for the given parameters. Note that this method is not thread safe + * and requires external synchronization. Please refer to general documentation on Bitcoin scripting and contracts + * to understand the values of sigHash and anyoneCanPay: otherwise you can use the other form of this method + * that sets them to typical defaults. + * + * @throws ScriptException if the scriptPubKey is not a pay to address or pay to pubkey script. + */ + public TransactionInput addSignedInput(TransactionOutPoint prevOut, Script scriptPubKey, ECKey sigKey, + SigHash sigHash, boolean anyoneCanPay) throws ScriptException { + TransactionInput input = new TransactionInput(params, this, new byte[]{}, prevOut); + addInput(input); + Sha256Hash hash = hashForSignature(inputs.size() - 1, scriptPubKey, sigHash, anyoneCanPay); + ECKey.ECDSASignature ecSig = sigKey.sign(hash); + TransactionSignature txSig = new TransactionSignature(ecSig, sigHash, anyoneCanPay); + if (scriptPubKey.isSentToRawPubKey()) + input.setScriptSig(ScriptBuilder.createInputScript(txSig)); + else if (scriptPubKey.isSentToAddress()) + input.setScriptSig(ScriptBuilder.createInputScript(txSig, sigKey)); + else + throw new ScriptException("Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey); + return input; + } + + /** + * Same as {@link #addSignedInput(TransactionOutPoint, com.google.bitcoin.script.Script, ECKey, com.google.bitcoin.core.Transaction.SigHash, boolean)} + * but defaults to {@link SigHash#ALL} and "false" for the anyoneCanPay flag. This is normally what you want. + */ + public TransactionInput addSignedInput(TransactionOutPoint prevOut, Script scriptPubKey, ECKey sigKey) throws ScriptException { + return addSignedInput(prevOut, scriptPubKey, sigKey, SigHash.ALL, false); + } + /** * Removes all the inputs from this transaction. * Note that this also invalidates the length attribute diff --git a/core/src/test/java/com/google/bitcoin/core/FullPrunedBlockChainTest.java b/core/src/test/java/com/google/bitcoin/core/FullPrunedBlockChainTest.java index 87f00ec8..0f0eb674 100644 --- a/core/src/test/java/com/google/bitcoin/core/FullPrunedBlockChainTest.java +++ b/core/src/test/java/com/google/bitcoin/core/FullPrunedBlockChainTest.java @@ -17,11 +17,9 @@ package com.google.bitcoin.core; -import com.google.bitcoin.core.Transaction.SigHash; import com.google.bitcoin.params.MainNetParams; import com.google.bitcoin.params.UnitTestParams; import com.google.bitcoin.script.Script; -import com.google.bitcoin.store.BlockStoreException; import com.google.bitcoin.store.FullPrunedBlockStore; import com.google.bitcoin.store.MemoryFullPrunedBlockStore; import com.google.bitcoin.utils.BlockFileLoader; @@ -31,9 +29,7 @@ import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.IOException; import java.lang.ref.WeakReference; import java.util.Arrays; @@ -104,7 +100,7 @@ public class FullPrunedBlockChainTest { } } } - + @Test public void testFinalizedBlocks() throws Exception { final int UNDOABLE_BLOCKS_STORED = 10; @@ -133,7 +129,7 @@ public class FullPrunedBlockChainTest { Transaction t = new Transaction(params); // Entirely invalid scriptPubKey t.addOutput(new TransactionOutput(params, t, Utils.toNanoCoins(50, 0), new byte[] {})); - addInputToTransaction(t, spendableOutput, spendableOutputScriptPubKey, outKey); + t.addSignedInput(spendableOutput, new Script(spendableOutputScriptPubKey), outKey); rollingBlock.addTransaction(t); rollingBlock.solve(); @@ -159,30 +155,11 @@ public class FullPrunedBlockChainTest { assertNull(out.get()); } - private void addInputToTransaction(Transaction t, TransactionOutPoint prevOut, byte[] prevOutScriptPubKey, ECKey sigKey) throws ScriptException { - TransactionInput input = new TransactionInput(params, t, new byte[]{}, prevOut); - t.addInput(input); - - Sha256Hash hash = t.hashForSignature(0, prevOutScriptPubKey, SigHash.ALL, false); - - // Sign input - try { - ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(73); - bos.write(sigKey.sign(hash).encodeToDER()); - bos.write(SigHash.ALL.ordinal() + 1); - byte[] signature = bos.toByteArray(); - - input.setScriptBytes(Script.createInputScript(signature)); - } catch (IOException e) { - throw new RuntimeException(e); // Cannot happen. - } - } - @Test - public void testFirst100KBlocks() throws BlockStoreException, VerificationException, PrunedException { + public void testFirst100KBlocks() throws Exception { NetworkParameters params = MainNetParams.get(); File blockFile = new File(getClass().getResource("first-100k-blocks.dat").getFile()); - BlockFileLoader loader = new BlockFileLoader(params, Arrays.asList(new File[] {blockFile})); + BlockFileLoader loader = new BlockFileLoader(params, Arrays.asList(blockFile)); store = new MemoryFullPrunedBlockStore(params, 10); chain = new FullPrunedBlockChain(params, store);