From aa1d38b9645cfe246c82d05e23973782eecfa652 Mon Sep 17 00:00:00 2001 From: HashEngineering Date: Tue, 30 Apr 2024 13:35:15 -0700 Subject: [PATCH] feat: align coinjoin signing with Dash Core, add tests (#252) * feat: sign coinjoin mixing tx with anyone can pay * tests: add CoinJoinSignedInputsTest * test was made with anyoneCanPay = false --- .../coinjoin/CoinJoinClientSession.java | 4 +- .../signers/CoinJoinTransactionSigner.java | 7 +- .../coinjoin/CoinJoinSignedInputsTest.java | 164 ++++++++++++++++++ 3 files changed, 169 insertions(+), 6 deletions(-) create mode 100644 core/src/test/java/org/bitcoinj/coinjoin/CoinJoinSignedInputsTest.java diff --git a/core/src/main/java/org/bitcoinj/coinjoin/CoinJoinClientSession.java b/core/src/main/java/org/bitcoinj/coinjoin/CoinJoinClientSession.java index 5e5306b08..878522365 100644 --- a/core/src/main/java/org/bitcoinj/coinjoin/CoinJoinClientSession.java +++ b/core/src/main/java/org/bitcoinj/coinjoin/CoinJoinClientSession.java @@ -1056,10 +1056,8 @@ private boolean signFinalTransaction(Transaction finalTransactionNew, Peer node) } } - //KeyBag maybeDecryptingKeyBag = new DecryptingKeyBag(mixingWallet, req.aesKey); - TransactionSigner.ProposedTransaction proposal = new TransactionSigner.ProposedTransaction(finalMutableTransaction); - CoinJoinTransactionSigner signer = new CoinJoinTransactionSigner(sigs); + CoinJoinTransactionSigner signer = new CoinJoinTransactionSigner(sigs, true); if (!signer.signInputs(proposal, mixingWallet)) log.info("{} returned false for the tx", signer.getClass().getName()); diff --git a/core/src/main/java/org/bitcoinj/signers/CoinJoinTransactionSigner.java b/core/src/main/java/org/bitcoinj/signers/CoinJoinTransactionSigner.java index f6e9be3c7..2241f47be 100644 --- a/core/src/main/java/org/bitcoinj/signers/CoinJoinTransactionSigner.java +++ b/core/src/main/java/org/bitcoinj/signers/CoinJoinTransactionSigner.java @@ -57,9 +57,11 @@ public class CoinJoinTransactionSigner implements TransactionSigner { VerifyFlag.NULLDUMMY); private final List sigs; + private final boolean anyoneCanPay; - public CoinJoinTransactionSigner(List sigs) { + public CoinJoinTransactionSigner(List sigs, boolean anyoneCanPay) { this.sigs = sigs; + this.anyoneCanPay = anyoneCanPay; sigs.clear(); } @@ -121,8 +123,7 @@ public boolean signInputs(ProposedTransaction propTx, KeyBag keyBag) { if (key.isEncrypted()) { key = Context.get().coinJoinManager.requestDecryptKey(key); } - TransactionSignature signature = tx.calculateSignature(i, key, script, Transaction.SigHash.ALL, - false); + TransactionSignature signature = tx.calculateSignature(i, key, script, Transaction.SigHash.ALL, anyoneCanPay); // at this point we have incomplete inputScript with OP_0 in place of one or more signatures. We // already have calculated the signature using the local key and now need to insert it in the diff --git a/core/src/test/java/org/bitcoinj/coinjoin/CoinJoinSignedInputsTest.java b/core/src/test/java/org/bitcoinj/coinjoin/CoinJoinSignedInputsTest.java new file mode 100644 index 000000000..e8acc2440 --- /dev/null +++ b/core/src/test/java/org/bitcoinj/coinjoin/CoinJoinSignedInputsTest.java @@ -0,0 +1,164 @@ +package org.bitcoinj.coinjoin; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.bitcoinj.core.ECKey; +import org.bitcoinj.core.KeyId; +import org.bitcoinj.core.NetworkParameters; +import org.bitcoinj.core.Sha256Hash; +import org.bitcoinj.core.Transaction; +import org.bitcoinj.core.TransactionInput; +import org.bitcoinj.core.TransactionOutPoint; +import org.bitcoinj.core.TransactionOutput; +import org.bitcoinj.params.UnitTestParams; +import org.bitcoinj.script.Script; +import org.bitcoinj.script.ScriptBuilder; +import org.bitcoinj.script.ScriptPattern; +import org.bitcoinj.signers.CoinJoinTransactionSigner; +import org.bitcoinj.signers.TransactionSigner; +import org.bitcoinj.wallet.KeyBag; +import org.bitcoinj.wallet.RedeemData; +import org.junit.Test; + +import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +import static org.bitcoinj.core.Utils.HEX; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +public class CoinJoinSignedInputsTest { + + static NetworkParameters PARAMS = UnitTestParams.get(); + byte[] dssMessage = HEX.decode("01204578c8a6564fb12d78e304420ed8738514712ce219c4611cef6b0bdb36ed580d0000006b4830450221009dc078c6c523976413f335cde75f12f9d2a8f64251ba8705947e1de788a0b30e02201d26709b9e4f269f7c7e43542b9ea5663d8a107e72232683b6f9880d994fa10d012102d52ccc891ac43e661cd32a93c892cd668d0ef321448b1a1633efbb3b2e61b31cffffffff"); + // byte[] dsfMessage = HEX.decode("9d7f0a000200000003204578c8a6564fb12d78e304420ed8738514712ce219c4611cef6b0bdb36ed580d00000000ffffffffa2039615b2db923f71d49b8af451df47aaf16d1b1baac79a5a732cb53117cd850100000000ffffffffafab56a552e4d66d2c1263027e7f1292b19669415119bdb6faa03ba1e86b58c30100000000ffffffff034a420f00000000001976a9142b4e226c80a96466631ff2db097727a8b712b69488ac4a420f00000000001976a9143b19e3f79a7114e51e95f8c5606c55816c48908988ac4a420f00000000001976a9149439af1abe644548530e2f63e80736a8af91e59b88ac00000000"); + byte[] dssMessageTwo = HEX.decode("08c547b604a9682cd6d36b3352697df8661afbf0fff39abe318f739ab22374803f030000006b48304502210085e8bd03a92422fc8f656ded2c19ceb70c68707a1699dd54782c4395532022b602207730c221bcf818c0b0445f576b36d2ee0698edc4d3fe872aeaff772122e4251d0121030c467477c7d4aefb377cbe11c48765669676c556d0149c06da25fd0c2ade39ecffffffffc0c771b0ce74bf4fa4845c990f4ec4faff7374fd6344c0b25f41dd30ebbe5e46040000006a47304402204316f56e90d27505ecb938e52391cc9b0b77474b67283983a63463d0823a813a022058bcb6eab28c2f4f936ba85183e750d010808e1bb11201fab5bae6d5a25391ba0121023c2ff18948d68270f5e150456caa8c1fcb0e3ed33e68340b5e087fd6651cbe7dffffffff204578c8a6564fb12d78e304420ed8738514712ce219c4611cef6b0bdb36ed58010000006a47304402200b049725050b28a4a42f80db28bd8e48f266e2a3460cf6a798457420fa54bd0d02204edd66bf280cfda43019f48394439009cb234ee5871c2ec490d4d7ba84e6a461012102dde437be25a79dedc6dbf4cf73ac8e3eefd90112ddae827c9042b43d0e8d9bf6ffffffff86473250bdabf066fd57bad86de9f141849112e395ea777aceeb0e0fb8388d770a0000006b4830450221009048e55404378c68962bdb4c2acb4ea6592a040ac17be5f5cc2a957b087930620220694e165128a6c40c3bc4f77b369949d80606b9bdcf34e2dd8622107b9a87e3ff0121025064ef597ec1520e2e3b7836261457eb79522299314703a7beeea2bbf42b9175ffffffffe6cb88bd8c3418b3665cc2987e778762e58f461fa84737315622923453c61f95090000006a473044022059c43a7d1327d36adce6cfef92e5e2159c83dc9ebc6b28c5ac8c1ec46e6cdcb2022050dea4f95e4a8a6c4a06d71e6c7e5fc9b7249a967f09bd4283c2ed08a1225ed601210257f68289f77638b808706bced459895b349001a5d86641da619886474e9b417cffffffffc05155aa36cd041a8fa5e8a676877adccecae72f02561379275fb29d429fa79a090000006a47304402205b3398b3ea59a6ebe5cad2be234b64ddc852057197aaf7071c5621739e54ed5f02201330850ddd04f318cc5cffb8ce8464dc0d17dcc8e8d37c84db8bdef0b980363f01210297f200b4f0dc3967d77d822189276d60e6baad8075d954be933724dfa7ded74fffffffff697bccd54ca8c47eb9bf18d948c12226e44234ae1a4ea16c732533518e1074aa080000006a47304402203c3023ee03dee54982ef2176c6b26c81be06d8c09fb19e98ef98c257210f3c0e0220193b4f24aa9cc5720186dd02c05c9257a46383eed23586a3348d4b9af2d3f304012103e943423c41299842d5532a7800d0e7f6d4a4e89a68b992f071988e3325ca3e82ffffffffe2c1cea1e1532922532a2ec7bfa1cd33d202b5067e59decaa7025377ca2de4d6070000006b483045022100de784c07605de564af098672900651b901bbbfd05a9daa51b51d50f414073e16022020881559ada92c99777d1ec103dded571cbb8a9cfc0052262fbd3bcf14ac9dfe01210336d148cfc9a3a3603fa27aca926a73e2d47861194871f1473d9cc173c9ee5df0ffffffff"); + + @Test + public void deserializeTest() { + ECKey privateKey = ECKey.fromPrivate(HEX.decode("c13e443f57c4b22b28068723759094505b7c28fb7764935e8c015fd8481fe8fb")); + + CoinJoinSignedInputs dss = new CoinJoinSignedInputs(PARAMS, dssMessage); + assertEquals(1, dss.getInputs().size()); + assertArrayEquals( + HEX.decode("4830450221009dc078c6c523976413f335cde75f12f9d2a8f64251ba8705947e1de788a0b30e02201d26709b9e4f269f7c7e43542b9ea5663d8a107e72232683b6f9880d994fa10d012102d52ccc891ac43e661cd32a93c892cd668d0ef321448b1a1633efbb3b2e61b31c"), + dss.getInputs().get(0).getScriptSig().getProgram() + ); + + assertArrayEquals(privateKey.getPubKey(), dss.getInputs().get(0).getScriptSig().getChunks().get(1).data); + } + + @Test + public void signatureTest() { + byte[] dsfMessage = HEX.decode("80eb0100020000000b82c0158cb7847c69e7b8234692595d39ce8ea94dd9d77d8ae261bed1fa741c000000000000ffffffffc547b604a9682cd6d36b3352697df8661afbf0fff39abe318f739ab22374803f0300000000ffffffffc0c771b0ce74bf4fa4845c990f4ec4faff7374fd6344c0b25f41dd30ebbe5e460400000000ffffffff204578c8a6564fb12d78e304420ed8738514712ce219c4611cef6b0bdb36ed580100000000ffffffff86473250bdabf066fd57bad86de9f141849112e395ea777aceeb0e0fb8388d770a00000000ffffffffe6cb88bd8c3418b3665cc2987e778762e58f461fa84737315622923453c61f950900000000ffffffffa2b2b771242df3d263317b508549940c47e61ba5420d30820a403be5f0f381950700000000ffffffffc05155aa36cd041a8fa5e8a676877adccecae72f02561379275fb29d429fa79a0900000000ffffffff697bccd54ca8c47eb9bf18d948c12226e44234ae1a4ea16c732533518e1074aa0800000000ffffffff177af5a77194cd370d1dca2a24a40a116be1fe22cdfa33aa3ac0ae73d6be6cb70200000000ffffffffe2c1cea1e1532922532a2ec7bfa1cd33d202b5067e59decaa7025377ca2de4d60700000000ffffffff0ba1860100000000001976a9141e624bcdc64f72e4e68eacc59d40ab7a3400aeae88aca1860100000000001976a914335194d41dbc6c7e71b263711f0319a6bef5b45288aca1860100000000001976a91475922991d66988fd0defee70a60753728882d59b88aca1860100000000001976a9147c47c66f6c7a09e20be326692b42f43576a6249e88aca1860100000000001976a91493a314141f390ddd111f9be2b550e8e74b70304388aca1860100000000001976a914b705bfd8367fcacd8a4b0d6c08bd6862eaa96b2c88aca1860100000000001976a914cfd97c2953dbda72b66d1e37d8e8f8acef4e3d4988aca1860100000000001976a914d0ee85ec9e29373f29fd922b4b1aa049c8b8e11e88aca1860100000000001976a914df35875deec179fa1d39d7182fe2d746ff1a2e2188aca1860100000000001976a914e58de7fe595532146be33e08493ed2b26f18251c88aca1860100000000001976a914fdebd30c80543b3c72b5cfff7c788e63d63d29a688ac00000000"); + CoinJoinFinalTransaction dsf = new CoinJoinFinalTransaction(PARAMS, dsfMessage); + assertEquals(125824, dsf.getMsgSessionID()); + Transaction finalTx = dsf.getTransaction(); + + String[] inputs = new String[] { + "01000000019deb9810323745e1f0e5b1a8c8a26aaf5e002c953966400f5fcf3a73d698f332010000006a47304402201f30d28adcdc2f8530529a0672fe48afde66883285118ec14ecc26a29521459b02205ed73068649e5b48e1d8d239c10f859873769ae07a62ef28141702756ef0cee601210234aae28a8051f9d0612be74b6ec555e4863d8681630117989abbc6110cc7b9ecffffffff0a409c0000000000001976a914b8e767d1071caf455af5eb466f051f0003b89b8888ac7a340100000000001976a914f77246d5e3bf30f5add30895fa38aaa56045484888aca1860100000000001976a9140325e8003485c9393a3da3832a6800ff3ce4e37c88aca1860100000000001976a91437b00a500178dfb1bb95d66fe7ba10d9baf9d14e88aca1860100000000001976a9145122ccabe261581fbc02838a04cb6e98945fde8388aca1860100000000001976a9149671cf86a314c30f4d7500b6290f6c71f2af76b188aca1860100000000001976a914a6275f54bf342a828ba3a87420b6296f65993ee888aca1860100000000001976a914bf19b1184f377419a84a386486f0199322d5aa6e88aca1860100000000001976a914dd0420226a15fe3d33426cf78d8c5cb733b28e6c88aca1860100000000001976a914e65c326f16e62d42fb09bf8c3767216b035741d288ac00000000", + "0100000001a5c767ab6974f358b99a6c0569c3a46635c68e3ee53ae236ac3c6c451357451a010000006b483045022100b51b68694b105ca08c321f345f071370658e4c12aeabf5ada5eba917ece5986102200760d3bf9f6eb640163fae43c64c5b3598eb4d73e44f2a5ad5da0354c4c8cc7c01210277a8e12ec5af3e181c33f520cf6045b692d8bd48f79f67f1a6630e6e38af3a2cffffffff0a409c0000000000001976a9140b52aea72179ac23671001e83f2f5d92e577159988ac79340100000000001976a91499ad666866853fb7547037aac0815536f4699c4f88aca1860100000000001976a9140ffd894d959bc6eee6b89378eed9181ed4a348f988aca1860100000000001976a91454feaca5b9e07cc1ab1a472f114f5dfb501e5b4588aca1860100000000001976a914786381547e752eaa9a06f70bb1265145e685753988aca1860100000000001976a91480c182b3c5abe2851c81abc07d43d2609e2efd0188aca1860100000000001976a914c4cfe8c32dd03c9bd57bdefc74a908b276d7254388aca1860100000000001976a914d75853eedc838f4b9e2243f795364e7cd52507b688aca1860100000000001976a914f32481a957fd326702f8d6d79db04d70f0df7ba788aca1860100000000001976a914fefe3fd23b669e5544004182509d03519c86bf7c88ac00000000", + "0100000001c10eba3e4e627dcb8be354811ade25e6aaae1451d01ab620210f7afa3a7b61ef010000006b483045022100cabb4f7709556411b669a41b82c3f00181904d1a71a20b07f4de90805ea969690220610293357c640f97ed36fd675762637e9c3b593a188dcd4ad85284c2ba5beb5201210275c0b9fb21f16e75e8da6b69b0c99b0c46323e0f85bd0a59caf2173216357defffffffff0b014b0000000000001976a914ccacda17cb18e10620a1b98e27cd77e39523bd4588ac409c0000000000001976a91449d96057c163310923825223a094acdba4f44dca88aca1860100000000001976a9143320704f770efa850e0df3477c95eca44311fe6688aca1860100000000001976a9143bb68005496bcebdade341316a26a1a9fbbc149b88aca1860100000000001976a91463c5f9186a3850cc69cf9e01c00ab76e12e7ef6988aca1860100000000001976a9146be90f28fbb768b5cc35deef55f81cd94bc9420b88aca1860100000000001976a91470d9f9aed92dd72f90c7a1d9163ab45018dd2ec288aca1860100000000001976a914754f1b507b6f54c55f03d194d318ccb601b4a3ab88aca1860100000000001976a91499c8b8a2b302655520e42eda93d2988a297dcffe88aca1860100000000001976a914c6b1b2d5e2127784d33ff3e49ca3714aad9c92f288aca1860100000000001976a914ccf7c90b25ecf67011e160bd7ca99fadaa8d272888ac00000000", + "01000000013d7d713940f1ab9a31b7e124b43f72dbf30b1c0a4d0281106314bfcd938ee3cc010000006a47304402204e27e59f429117e558ea8f19f2b0492718c16c5cb36b063f585d37cf285047d602204463203a694fbec55dc9c11d104fc46c0f57e9c458a8d7886acf8be50f1398c4012103887267d7ddd4f1ba75797bd0ab6b37429f6b114e7046933cb47243d4a4410b5affffffff0a409c0000000000001976a9145cb40f3af265225f4db5c403506b54eeabf44aed88ac7b340100000000001976a914c8126087bcb3bcd1bdb774080999b3bcb3c238d988aca1860100000000001976a9141976f27fa25a5fc7d9e795739b1f9c7c5244b93288aca1860100000000001976a914508ca3fced2be7050df0ab56d19349769cef4e7b88aca1860100000000001976a9145b9b699e03b8acd5ec8180f4764d37dee7bfb11b88aca1860100000000001976a91481c7aba40027891f9bd49a0ee8e6f05d534d7ce788aca1860100000000001976a9148b3df359ab626dfcac23891ade50e68f541fa1d988aca1860100000000001976a9149386fdae4ef704e000717e6e9eccd343dc0960a288aca1860100000000001976a914b31aaada30c2e9e3ce88b331447df9799b8ccae388aca1860100000000001976a914ffe035d9e39100e9d179601ae3ee2f2223c864fd88ac00000000", + "01000000010483d98ae613b67144a78207c2f14f0bdbae1c59f73e37163283ffef6ddf63f9010000006b48304502210087ade9093fe746cbd041f57bd550023dfaea2ae9f32b7a4249f49c4e4b1d5367022016e31e3d4992e8681604c3d7362f0d131d7204659bd0689a9bfb3edb626ce8300121021ff63d7e900b88fda9491bea69af777a34bf6636844e1fd6cbbeeacdd6701672ffffffff0b014b0000000000001976a914f72ca2b85ea5f22ef4198b0a0bcd876088b43ff788ac409c0000000000001976a9142abe315985630879212566fc8592ce9a4cbc748b88aca1860100000000001976a9141aa59c3889b7bc989f933c8acf201588e22a444388aca1860100000000001976a914276a3b182540c67f6682212f89459a30ee46cf9888aca1860100000000001976a914292feb4e06a0e4006b3fcfc2c66a7e82bb51815988aca1860100000000001976a9144abe69ef9533312ed2b27da77a2bb521249c235088aca1860100000000001976a9147fb39b231019b51e121b7c781b6c4b6c36bc049988aca1860100000000001976a9149fe820398f763d2fa3efb3984e7f96b69a90230288aca1860100000000001976a914a15deaeeea797ba1b1e26a064a55de3fea33b5a688aca1860100000000001976a914b1c59bf3ce5bd944c5213259c9398a0edbaa724188aca1860100000000001976a914dd8def7719d93036b2d6df0c30c18f432dbd1ac988ac00000000", + "0200000003e4c606509b5536c0eead2316eb93c782e3c66f8cbbbda3d00cf8d3815e553e8c000000006a4730440220631ee6f2329d02b82afc8225a128e77e3ead41db967925b44981d590368e722902203682c8b98bd6bbdd6d91dbb2889a90a752855c8e700fa89f85822973e18742488121028201d137931ced0e8cf0e65db0506af68a3698442aa7b6698d1ae4f520d7372bfffffffff268ae09bd1f97d934b59af1f20b60a4dd6dbacf7fb3af2636c4d6871e0c4592040000006a47304402203523e6159b29a1d69c55a3c3a2d0ac9c9339cb2ecb5e3cf28e3d716bd15d43df02202305d6d9d2fa12db63c5261bc594990c4d263b9d91549174f47f1bc2a8c74ace012103703f0bd6abc032d6cf8c786c7217ad15d884e6d57a923a7b0faadc76b7710344ffffffff6a8131aa3c954ba96e06897ba55fce3e6ed68b64826a8fc51e06bc774db831bf000000006a47304402205d8c1299eb1a10f41315fb1dcd286bcc83083d96a2075bfa14dfd358187310cf02204f9aece0ac8feec376830671edd4148dc5bb95979744c5f8cdae01db756f3f688121035d2095f04ccc98df2beb3f7836a451f42b2a3384d7cfc93012706774175dd867ffffffff03a1860100000000001976a914041f5a396a9d414854db1117814a96cb20ad73a088aca1860100000000001976a914d0a72d5e8556416a69890380e96a55d25dbd425588aca1860100000000001976a914fb786e316b76a796b7f6089b025b0145a9cdc4b588ac00000000", + "0100000001aecd5d4a7a11683ffa117a7edb091b704557995a62ac00d0bb7388631489f675010000006b483045022100b0655e5d2f61694431ed253b58400a43e8456e1dab442ba9fb5d4f6aa5236698022075874d76c8aa3d0774756e68433c03e1e50f63e85ea47e072cba6e6aa0e3452f0121034bc873417bf0e5282bf48626a4724d9743e113d17aa500a7f0637476886bb05affffffff0a409c0000000000001976a914c239bc7067a8bf81226430fbdac3b9180e20d7d288ac7a340100000000001976a914fcc13c8b5aeb01387680db359e0631aea9163af588aca1860100000000001976a914011ff726378379174473addef8908e784950baba88aca1860100000000001976a91406b37552df6f149d89e757e117d51f605a10e96188aca1860100000000001976a91453c3e09e1519b0dad21728a226403179d88ebd0b88aca1860100000000001976a9146d1f1f5638ca06567678192d85073b31df0a2cb588aca1860100000000001976a9146fbd25b7ee55fd6e12ab46cf8363e2d34368c74388aca1860100000000001976a914903dda3fb6996c5788ae855b696f0c276a80a6dd88aca1860100000000001976a914c1f7a7be3ac0eaa6248df07604d6ccdd397c873088aca1860100000000001976a914fd5aec3549c3e2d9d639ac8f93528bc4a393eec788ac00000000", + "01000000018c49825036239f1aba205cda6d7e9e36348115ebc1a69f227ebe1013c13e8cff010000006a473044022018d7cb59ed548b90f6e9eba802b3d79c018675d9772f5b7520a7740180d566870220657a9668518a741c7d94fbd32fb4d650b73d5d3547cdb1065f3d9eadbb41d4ff0121036401ea9dc9485fb0d794631be3e93261a6fd93da96fcb84e80da6ee83e24ea8effffffff0b014b0000000000001976a9147bac62a47fd4b05d9cf9b95c36530b14570c285d88ac409c0000000000001976a914c6cbd11d5625295687571c45afe2905093f20a6488aca1860100000000001976a9142250f639929ee5decf06eecade44d4e8aa0677f788aca1860100000000001976a9142c3c359e85abfca99287c97c5d41ca799bd549fa88aca1860100000000001976a914416c2b66f59ad55932999318380e9df06ba1e26b88aca1860100000000001976a91455740f263ba7b94b54c00c03fec7caaaf67ce7cf88aca1860100000000001976a9146277b75350c9329e50517f68f4e0061e4f6d508488aca1860100000000001976a91476728e35d67304d89a41e42df421d4a9c2b0454b88aca1860100000000001976a91483b7f4eb6e8f004ae4c37dfca20c15b4daac746488aca1860100000000001976a914b833f17ec6c611bd5d35e4a26b30ebb8c643178588aca1860100000000001976a914d41253d94b7da3fa1fd78f9b4835314e1843d1ae88ac00000000", + "0100000001e075eddacd2e88cbc9c39d8518197d14914fd95284bcebfb4c63cd430abff17e010000006b483045022100852b5c8428835c60b0fcc69dab7527286f82b5917e03d8711fabbbfae0709e44022020582e0bba98bedbdb4fb9b2b8a01a6c4f502a71627c84a00797f3e2aeb1a8540121033b90697837e780e9fd437cd2d9a60aa19e4f88c0fa4b8bf8322feb6b0a0b643fffffffff0f409c0000000000001976a914ba281026b8fb911ba6bce4c1911c23f25aea4a2788aca1860100000000001976a91419c171c953f1766d325a9181909380b0a31595fd88aca1860100000000001976a9142b9a6ad10e962c324beb57de1cad2a749368f5b688aca1860100000000001976a9145e11773edd236d9929955f124efdc0594d59e33e88aca1860100000000001976a914abc20057cc91acd2bda24bf6d5c59ce2cd5d188f88ac9eeb0700000000001976a91414f505cbfece549892b5054ebde23fe87c9b828988ac4a420f00000000001976a9146ccfa0bd06e6363cfee10698a24d6d05da0d6f0888ac4a420f00000000001976a91471528f25d64fe6e653931dff5aefc8f71c81d7c188ac4a420f00000000001976a9147f565b033aaae26a03bb26d63a99b606756ef04688ac4a420f00000000001976a91480666f937179437a29308dd94545f20711ea5bc888ac4a420f00000000001976a914ae787c186a8f42a150e4127a652602718d5e502388ac4a420f00000000001976a914c97a06e648cc56403eedf601069e4227d70ec51988ac4a420f00000000001976a914cd2f6bc815fbf4179e5992e060bbc981296a330888ac4a420f00000000001976a914e4881679da92486ca641ee8eb3fe4e01806de4eb88ac4a420f00000000001976a914ffca97d6aea734a5ae4780eecbfd987fbfac64f888ac00000000" + }; + + HashMap mixingWallet = Maps.newHashMap(); + + for (String inputTx : inputs) { + Transaction tx = new Transaction(PARAMS, HEX.decode(inputTx)); + mixingWallet.put(tx.getTxId(), tx); + } + + List keyList = Lists.newArrayList( + "107da4635cb63a387ec4c17258d600c8813599b7ec72893d5bab0bf3aa514788", + "325c50fff4c6cb307ec5eda1310e2766d632091d984ff19ad3848e1e65db742a", + "b1ae60833f531c642dbda4d509462e6f5dde064d217c88547ceef18360b8bb54", + "5dc84baacdc67aea283c64270500b072d65b7aa2bced91998f9c2fe599378d17", + "33479550caf0a09153fd3d22e884581e2a0e28f397b5a56c02a846409e4e7bd7", + "9e38eadc76529b88f1cb983f894fea4dd93042e18f47717c3c90f0229abc0909", + "b8df0a2e512cad7a13dc83816eea7d35a2e972c9b11ed04e56fc619ca974cefe", + "0ff7f48db747dcdf5607186460d143dfb0233c083c1cbc6722bb32acaf9b54dd" + ); + HashMap keyMap = Maps.newHashMap(); + keyList.forEach(str -> { + ECKey key = ECKey.fromPrivate(HEX.decode(str)); + keyMap.put(KeyId.fromBytes(key.getPubKeyHash()), key); + }); + + KeyBag keyBag = new KeyBag() { + @Nullable + @Override + public ECKey findKeyFromPubKeyHash(byte[] pubKeyHash, @Nullable Script.ScriptType scriptType) { + return keyMap.get(KeyId.fromBytes(pubKeyHash)); + } + + @Nullable + @Override + public ECKey findKeyFromPubKey(byte[] pubKey) { + AtomicReference resultKey = new AtomicReference<>(null); + keyMap.values().forEach(key -> { + if (Arrays.equals(key.getPubKey(), pubKey)) + resultKey.set(key); + + }); + return resultKey.get(); + } + + @Nullable + @Override + public RedeemData findRedeemDataFromScriptHash(byte[] scriptHash) { + return null; + } + }; + + Transaction finalMutableTransaction = new Transaction(finalTx.getParams());//finalTransactionNew; + finalMutableTransaction.setVersionAndType(finalTx.getVersionShort(), finalTx.getType()); + finalMutableTransaction.setLockTime(finalTx.getLockTime()); + // we need to connect inputs + for (TransactionInput input : finalTx.getInputs()) { + Transaction tx = mixingWallet.get(input.getOutpoint().getHash()); + if (tx != null) { + // this is our input, so connect to our transaction + TransactionOutPoint outPoint = new TransactionOutPoint(input.getParams(), input.getOutpoint().getIndex(), tx); + byte[] pubKeyHash = ScriptPattern.extractHashFromP2PKH(tx.getOutput(outPoint.getIndex()).getScriptPubKey()); + ECKey key = keyBag.findKeyFromPubKeyHash(pubKeyHash, Script.ScriptType.P2PKH); + if (key != null) { + Script inputScript = ScriptBuilder.createInputScript(null, key); + TransactionInput connectedInput = new TransactionInput(input.getParams(), finalMutableTransaction, inputScript.getProgram(), outPoint); + finalMutableTransaction.addInput(connectedInput); + continue; // go to the next input + } + } + // this is not our input + finalMutableTransaction.addInput(input); + } + for (TransactionOutput output : finalTx.getOutputs()) { + finalMutableTransaction.addOutput(output); + } + + List sigs = Lists.newArrayList(); + CoinJoinTransactionSigner signer = new CoinJoinTransactionSigner(sigs, false); // anyoneCanPay = false + + TransactionSigner.ProposedTransaction proposedTransaction = new TransactionSigner.ProposedTransaction(finalMutableTransaction); + signer.signInputs(proposedTransaction, keyBag); + + CoinJoinSignedInputs dss = new CoinJoinSignedInputs(PARAMS, dssMessageTwo); + + assertEquals(dss.getInputs().get(0).getScriptSig(), finalMutableTransaction.getInputs().get(1).getScriptSig()); + dss.getInputs().forEach(dssInput -> { + finalMutableTransaction.getInputs().forEach(txInput -> { + if (dssInput.getOutpoint().equals(txInput.getOutpoint())) { + assertEquals("dss input script doesn't match the signed input script for " + dssInput.getOutpoint(), dssInput.getScriptSig(), txInput.getScriptSig()); + } + }); + }); + } +}