|
8 | 8 | import org.twostack.bitcoin4j.Coin;
|
9 | 9 | import org.twostack.bitcoin4j.PrivateKey;
|
10 | 10 | import org.twostack.bitcoin4j.Utils;
|
11 |
| -import org.twostack.bitcoin4j.exception.InvalidKeyException; |
12 |
| -import org.twostack.bitcoin4j.exception.SigHashException; |
13 |
| -import org.twostack.bitcoin4j.exception.SignatureDecodeException; |
14 |
| -import org.twostack.bitcoin4j.exception.TransactionException; |
| 11 | +import org.twostack.bitcoin4j.crypto.*; |
| 12 | +import org.twostack.bitcoin4j.exception.*; |
15 | 13 | import org.twostack.bitcoin4j.params.NetworkAddressType;
|
| 14 | +import org.twostack.bitcoin4j.params.NetworkType; |
16 | 15 | import org.twostack.bitcoin4j.script.Interpreter;
|
17 | 16 | import org.twostack.bitcoin4j.script.Script;
|
18 | 17 |
|
19 | 18 | import java.io.IOException;
|
20 | 19 | import java.io.InputStreamReader;
|
21 | 20 | import java.math.BigInteger;
|
22 | 21 | import java.nio.charset.StandardCharsets;
|
23 |
| -import java.util.Arrays; |
24 |
| -import java.util.HashMap; |
25 |
| -import java.util.HashSet; |
26 |
| -import java.util.Set; |
| 22 | +import java.util.*; |
| 23 | + |
| 24 | +import static org.twostack.bitcoin4j.Utils.WHITESPACE_SPLITTER; |
27 | 25 |
|
28 | 26 | public class TransactionBuilderTest {
|
29 | 27 |
|
@@ -270,7 +268,7 @@ public void builderCanSpendFromUtxoMap() throws InvalidKeyException, IOException
|
270 | 268 | UnlockingScriptBuilder unlocker = new P2PKHUnlockBuilder(privateKey.getPublicKey());
|
271 | 269 |
|
272 | 270 | TransactionSigner signer = new TransactionSigner(SigHashType.ALL.value | SigHashType.FORKID.value, privateKey);
|
273 |
| - builder.spendFromUtxoMap(signer, utxoMap, unlocker); |
| 271 | + builder.spendFromUtxoMap(signer, utxoMap, unlocker); |
274 | 272 |
|
275 | 273 | }
|
276 | 274 |
|
@@ -308,4 +306,70 @@ public void builderCanSpendFromUtxoMap() throws InvalidKeyException, IOException
|
308 | 306 | }
|
309 | 307 | }).doesNotThrowAnyException();
|
310 | 308 | }
|
| 309 | + |
| 310 | + @Test |
| 311 | + public void canSpendMultipleOutputsFromSameTx() throws MnemonicException, IOException, InvalidKeyException, TransactionException, SigHashException, SignatureDecodeException { |
| 312 | + |
| 313 | + PrivateKey pkOne = PrivateKey.fromWIF("L14C38sujYefjpj4Zj5HTwjaHAZKMSLZZAnzLNojC8CmU8Q4TpWE"); |
| 314 | + PrivateKey pkTwo = PrivateKey.fromWIF("L4YDdzJa5Ae2ukENpPqFBPCutM1xsM9WvKFy6Bugv4iyg6U4c2kE"); |
| 315 | + |
| 316 | + TransactionBuilder builder = new TransactionBuilder(); |
| 317 | + builder.withFeePerKb(512); |
| 318 | + |
| 319 | + String rawFundingTx = "0100000001c5d4b2f482627e7c46ad977cbacf9bfdf2197229952daa3a4b57d15fccfad92b000000006a47304402207927136ea0b51fc9a2cb883ac2a72410dec41bef98062b7845eac691cc2c9f6602202b8b370b588542941623379a046e5aea654b1edd5c9c074a426b1be45545ed5d412103c36ea9ccb9a332b415ebf9e9823e2b352f2ed2c4199ea382cf98ddbfcf4eed24ffffffff02204e0000000000001976a914eb6edc362ae7e5d2765e86a97741722b0f7e20d688ac260c0300000000001976a914cd1a22818d1f143b152276ee6a69486233405d3e88ac00000000"; |
| 320 | + Transaction fundingTx = Transaction.fromHex(rawFundingTx); |
| 321 | + |
| 322 | + P2PKHUnlockBuilder unlockOne = new P2PKHUnlockBuilder(pkOne.getPublicKey()); |
| 323 | + TransactionSigner signerOne = new TransactionSigner(SigHashType.ALL.value | SigHashType.FORKID.value, pkOne); |
| 324 | + builder.spendFromTransaction(signerOne, fundingTx, 0, TransactionInput.MAX_SEQ_NUMBER, unlockOne); |
| 325 | + |
| 326 | + P2PKHUnlockBuilder unlockTwo = new P2PKHUnlockBuilder(pkTwo.getPublicKey()); |
| 327 | + TransactionSigner signerTwo = new TransactionSigner(SigHashType.ALL.value | SigHashType.FORKID.value, pkTwo); |
| 328 | + builder.spendFromTransaction(signerTwo, fundingTx, 1, TransactionInput.MAX_SEQ_NUMBER, unlockTwo); |
| 329 | + |
| 330 | + //send back to the first key |
| 331 | + Address recipientAddress = Address.fromKey(NetworkAddressType.MAIN_PKH, pkOne.getPublicKey()); |
| 332 | + P2PKHLockBuilder lockBuilder = new P2PKHLockBuilder(recipientAddress); |
| 333 | + builder.spendTo(lockBuilder, BigInteger.valueOf(20000)); |
| 334 | + |
| 335 | + //send change to second key |
| 336 | + Address changeAddress = Address.fromKey(NetworkAddressType.MAIN_PKH, pkTwo.getPublicKey()); |
| 337 | + builder.sendChangeTo(changeAddress); |
| 338 | + |
| 339 | + Transaction broadcastTx = builder.build(true); |
| 340 | + |
| 341 | + Assertions.assertThatCode(() -> { |
| 342 | + |
| 343 | + //new Script Interpreter to help us verify our spending conditions |
| 344 | + Interpreter interp = new Interpreter(); |
| 345 | + Set<Script.VerifyFlag> verifyFlags = new HashSet<Script.VerifyFlag>(Arrays.asList(Script.VerifyFlag.SIGHASH_FORKID)); |
| 346 | + |
| 347 | + |
| 348 | +// ////FIRST UTXO |
| 349 | + Integer fundingOutputIndexOne = 0; |
| 350 | + Long fundingValueOne = 20000L; |
| 351 | + |
| 352 | + //lookup funding transaction corresponding to first output |
| 353 | + TransactionOutput fundingOutput = fundingTx.getOutputs().get(fundingOutputIndexOne); |
| 354 | + |
| 355 | + //assert that funding transaction is spending correctly |
| 356 | + interp.correctlySpends(broadcastTx.getInputs().get(0).getScriptSig(), fundingOutput.getScript(), broadcastTx, 0, verifyFlags, Coin.valueOf(fundingValueOne)); |
| 357 | + |
| 358 | + ////SECOND UTXO |
| 359 | +// TransactionInput spendingInputTwo = TransactionInput.fromByteArray(broadcastTx.getInputs().get(1).serialize()); |
| 360 | + Integer fundingOutputIndexTwo = 1; |
| 361 | + Long fundingValueTwo = 199718L; |
| 362 | + |
| 363 | + //lookup funding transaction corresponding to first output |
| 364 | + TransactionOutput fundingOutputTwo = fundingTx.getOutputs().get(fundingOutputIndexTwo); |
| 365 | + |
| 366 | + Transaction signedTxTwo = signerTwo.sign(broadcastTx, fundingOutputTwo, 1); |
| 367 | + |
| 368 | + //assert that funding transaction is spending correctly |
| 369 | + interp.correctlySpends(broadcastTx.getInputs().get(1).getScriptSig(), fundingOutputTwo.getScript(), broadcastTx, 1, verifyFlags, Coin.valueOf(fundingValueTwo)); |
| 370 | + |
| 371 | + }).doesNotThrowAnyException(); |
| 372 | + |
| 373 | + } |
| 374 | + |
311 | 375 | }
|
0 commit comments