use of com.sparrowwallet.drongo.address.Address in project drongo by sparrowwallet.
the class TransactionTask method checkWallet.
private void checkWallet(Transaction transaction) {
for (WatchWallet wallet : drongo.getWallets()) {
List<Address> fromAddresses = new ArrayList<>();
for (TransactionInput input : transaction.getInputs()) {
for (Address address : input.getOutpoint().getAddresses()) {
if (wallet.containsAddress(address)) {
fromAddresses.add(address);
}
}
}
Map<Address, Long> toAddresses = new HashMap<>();
for (TransactionOutput output : transaction.getOutputs()) {
for (Address address : output.getAddresses()) {
if (wallet.containsAddress(address)) {
toAddresses.put(address, output.getValue());
}
}
}
if (!fromAddresses.isEmpty()) {
StringBuilder builder = new StringBuilder();
builder.append("Wallet ").append(wallet.getName()).append(" sent from address").append(fromAddresses.size() == 1 ? " " : "es ");
StringJoiner fromJoiner = new StringJoiner(", ", "[", "]");
for (Address address : fromAddresses) {
fromJoiner.add(address.toString() + " [" + Utils.formatHDPath(wallet.getAddressPath(address)) + "]");
}
builder.append(fromJoiner.toString()).append(" in txid ").append(transaction.getTxId());
log.info(builder.toString());
}
if (!toAddresses.isEmpty()) {
StringBuilder builder = new StringBuilder();
builder.append("Wallet ").append(wallet.getName()).append(" received to address").append(toAddresses.size() == 1 ? " " : "es ");
StringJoiner toJoiner = new StringJoiner(", ", "[", "]");
for (Address address : toAddresses.keySet()) {
toJoiner.add(address.toString() + " [" + Utils.formatHDPath(wallet.getAddressPath(address)) + "]" + " (" + toAddresses.get(address) + " sats)");
}
builder.append(toJoiner.toString()).append(" in txid ").append(transaction.getTxId());
log.info(builder.toString());
}
}
}
use of com.sparrowwallet.drongo.address.Address in project drongo by sparrowwallet.
the class PaymentCodeTest method testNotificationAddress.
@Test
public void testNotificationAddress() throws InvalidPaymentCodeException, InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, MnemonicException {
PaymentCode alicePaymentCode = new PaymentCode("PM8TJTLJbPRGxSbc8EJi42Wrr6QbNSaSSVJ5Y3E4pbCYiTHUskHg13935Ubb7q8tx9GVbh2UuRnBc3WSyJHhUrw8KhprKnn9eDznYGieTzFcwQRya4GA");
Address aliceNotificationAddress = alicePaymentCode.getNotificationAddress();
Assert.assertEquals("1JDdmqFLhpzcUwPeinhJbUPw4Co3aWLyzW", aliceNotificationAddress.toString());
ECKey alicePrivKey = DumpedPrivateKey.fromBase58("Kx983SRhAZpAhj7Aac1wUXMJ6XZeyJKqCxJJ49dxEbYCT4a1ozRD").getKey();
byte[] alicePayload = alicePaymentCode.getPayload();
Assert.assertEquals("010002b85034fb08a8bfefd22848238257b252721454bbbfba2c3667f168837ea2cdad671af9f65904632e2dcc0c6ad314e11d53fc82fa4c4ea27a4a14eccecc478fee00000000000000000000000000", Utils.bytesToHex(alicePayload));
PaymentCode paymentCodeBob = new PaymentCode("PM8TJS2JxQ5ztXUpBBRnpTbcUXbUHy2T1abfrb3KkAAtMEGNbey4oumH7Hc578WgQJhPjBxteQ5GHHToTYHE3A1w6p7tU6KSoFmWBVbFGjKPisZDbP97");
ECKey bobNotificationPubKey = paymentCodeBob.getNotificationKey();
Assert.assertEquals("024ce8e3b04ea205ff49f529950616c3db615b1e37753858cc60c1ce64d17e2ad8", Utils.bytesToHex(bobNotificationPubKey.getPubKey()));
TransactionOutPoint transactionOutPoint = new TransactionOutPoint(Sha256Hash.wrapReversed(Utils.hexToBytes("86f411ab1c8e70ae8a0795ab7a6757aea6e4d5ae1826fc7b8f00c597d500609c")), 1);
Assert.assertEquals("86f411ab1c8e70ae8a0795ab7a6757aea6e4d5ae1826fc7b8f00c597d500609c01000000", Utils.bytesToHex(transactionOutPoint.bitcoinSerialize()));
SecretPoint secretPoint = new SecretPoint(alicePrivKey.getPrivKeyBytes(), bobNotificationPubKey.getPubKey());
Assert.assertEquals("736a25d9250238ad64ed5da03450c6a3f4f8f4dcdf0b58d1ed69029d76ead48d", Utils.bytesToHex(secretPoint.ECDHSecretAsBytes()));
byte[] blindingMask = PaymentCode.getMask(secretPoint.ECDHSecretAsBytes(), transactionOutPoint.bitcoinSerialize());
Assert.assertEquals("be6e7a4256cac6f4d4ed4639b8c39c4cb8bece40010908e70d17ea9d77b4dc57f1da36f2d6641ccb37cf2b9f3146686462e0fa3161ae74f88c0afd4e307adbd5", Utils.bytesToHex(blindingMask));
byte[] blindedPaymentCode = PaymentCode.blind(alicePayload, blindingMask);
Assert.assertEquals("010002063e4eb95e62791b06c50e1a3a942e1ecaaa9afbbeb324d16ae6821e091611fa96c0cf048f607fe51a0327f5e2528979311c78cb2de0d682c61e1180fc3d543b00000000000000000000000000", Utils.bytesToHex(blindedPaymentCode));
Transaction transaction = new Transaction();
List<ScriptChunk> inputChunks = List.of(ScriptChunk.fromData(Utils.hexToBytes("3045022100ac8c6dbc482c79e86c18928a8b364923c774bfdbd852059f6b3778f2319b59a7022029d7cc5724e2f41ab1fcfc0ba5a0d4f57ca76f72f19530ba97c860c70a6bf0a801")), ScriptChunk.fromData(alicePrivKey.getPubKey()));
transaction.addInput(transactionOutPoint.getHash(), transactionOutPoint.getIndex(), new Script(inputChunks));
transaction.addOutput(10000, paymentCodeBob.getNotificationAddress());
List<ScriptChunk> opReturnChunks = List.of(ScriptChunk.fromOpcode(ScriptOpCodes.OP_RETURN), ScriptChunk.fromData(blindedPaymentCode));
transaction.addOutput(10000, new Script(opReturnChunks));
Assert.assertEquals("010000000186f411ab1c8e70ae8a0795ab7a6757aea6e4d5ae1826fc7b8f00c597d500609c010000006b483045022100ac8c6dbc482c79e86c18928a8b364923c774bfdb" + "d852059f6b3778f2319b59a7022029d7cc5724e2f41ab1fcfc0ba5a0d4f57ca76f72f19530ba97c860c70a6bf0a801210272d83d8a1fa323feab1c085157a0791b46eba34afb8bfbfaeb3a3fcc3f2" + "c9ad8ffffffff0210270000000000001976a9148066a8e7ee82e5c5b9b7dc1765038340dc5420a988ac1027000000000000536a4c50010002063e4eb95e62791b06c50e1a3a942e1ecaaa9afbbeb3" + "24d16ae6821e091611fa96c0cf048f607fe51a0327f5e2528979311c78cb2de0d682c61e1180fc3d543b0000000000000000000000000000000000", Utils.bytesToHex(transaction.bitcoinSerialize()));
Assert.assertEquals("9414f1681fb1255bd168a806254321a837008dd4480c02226063183deb100204", transaction.getTxId().toString());
ECKey alicePubKey = ECKey.fromPublicOnly(transaction.getInputs().get(0).getScriptSig().getChunks().get(1).data);
Assert.assertArrayEquals(alicePubKey.getPubKey(), alicePrivKey.getPubKey());
DeterministicSeed bobSeed = new DeterministicSeed("reward upper indicate eight swift arch injury crystal super wrestle already dentist", "", 0, DeterministicSeed.Type.BIP39);
Keystore bobKeystore = Keystore.fromSeed(bobSeed, List.of(new ChildNumber(47, true), ChildNumber.ZERO_HARDENED, ChildNumber.ZERO_HARDENED));
ECKey bobNotificationPrivKey = bobKeystore.getBip47ExtendedPrivateKey().getKey(List.of(ChildNumber.ZERO_HARDENED, new ChildNumber(0)));
SecretPoint bobSecretPoint = new SecretPoint(bobNotificationPrivKey.getPrivKeyBytes(), alicePubKey.getPubKey());
Assert.assertEquals("736a25d9250238ad64ed5da03450c6a3f4f8f4dcdf0b58d1ed69029d76ead48d", Utils.bytesToHex(bobSecretPoint.ECDHSecretAsBytes()));
byte[] bobBlindingMask = PaymentCode.getMask(secretPoint.ECDHSecretAsBytes(), transaction.getInputs().get(0).getOutpoint().bitcoinSerialize());
Assert.assertEquals("be6e7a4256cac6f4d4ed4639b8c39c4cb8bece40010908e70d17ea9d77b4dc57f1da36f2d6641ccb37cf2b9f3146686462e0fa3161ae74f88c0afd4e307adbd5", Utils.bytesToHex(bobBlindingMask));
PaymentCode unblindedPaymentCode = new PaymentCode(PaymentCode.blind(transaction.getOutputs().get(1).getScript().getChunks().get(1).data, blindingMask));
Assert.assertEquals(alicePaymentCode, unblindedPaymentCode);
PaymentCode unblindedPaymentCode2 = PaymentCode.getPaymentCode(transaction, bobKeystore);
Assert.assertEquals(alicePaymentCode, unblindedPaymentCode2);
}
use of com.sparrowwallet.drongo.address.Address in project drongo by sparrowwallet.
the class Wallet method getMaxSpendable.
/**
* Determines the maximum total amount this wallet can send for the number and type of addresses at the given fee rate
*
* @param paymentAddresses the addresses to sent to (amounts are irrelevant)
* @param feeRate the fee rate in sats/vB
* @return the maximum spendable amount (can be negative if the fee is higher than the combined UTXO value)
*/
public long getMaxSpendable(List<Address> paymentAddresses, double feeRate, boolean includeSpentMempoolOutputs) {
long maxInputValue = 0;
Transaction transaction = new Transaction();
for (Map.Entry<BlockTransactionHashIndex, WalletNode> utxo : getWalletUtxos(includeSpentMempoolOutputs).entrySet()) {
int inputWeightUnits = utxo.getValue().getWallet().getInputWeightUnits();
long minInputValue = (long) Math.ceil(feeRate * inputWeightUnits / WITNESS_SCALE_FACTOR);
if (utxo.getKey().getValue() > minInputValue) {
Transaction prevTx = getWalletTransaction(utxo.getKey().getHash()).getTransaction();
TransactionOutput prevTxOut = prevTx.getOutputs().get((int) utxo.getKey().getIndex());
addDummySpendingInput(transaction, utxo.getValue(), prevTxOut);
maxInputValue += utxo.getKey().getValue();
}
}
for (Address address : paymentAddresses) {
transaction.addOutput(1L, address);
}
long fee = (long) Math.floor(transaction.getVirtualSize() * feeRate);
return maxInputValue - fee;
}
use of com.sparrowwallet.drongo.address.Address in project drongo by sparrowwallet.
the class TransactionTest method verifyConstructedTxLengthP2PKH.
@Test
public void verifyConstructedTxLengthP2PKH() throws NonStandardScriptException, IOException {
String hex = "0100000003c07f2ee6dd4e55c6eefdc53659d1fb340beb5eb824d13bc15ba5269ade8de446000000006b483045022100d3f7526a8d1e22233c1f193b63f55406b32010aefeecdc802c07829b583d53a002205f1b666f156433baf6e976b8c43702cfe098e6d6c3c90e4bf2d24eeb1724740a012102faea485f773dbc2f57fe8cf664781a58d499c1f10ad55d370d5b08b92b8ee0c4ffffffffcac7a96d74d8a2b9177c7e0ce735f366d717e759d1f07bbd8a6db55e4b21304e000000006b483045022100d11822be0768c78cdb28ce613051facfa68c6689199505e7d0c75e95b7bd210c02202c5a610ceab38fc6816f6b792c43a1a25ae8507e80cd657dbfecfbff804a455101210287571cbb133887664c47917df7192017906916f7ce470532699c00ae4f10a178ffffffff3b16c58d5d76e119d337a56751b62b60c614ceca73d8e6403476c9e5a74497ab000000006b483045022100cb865e7b13f61f5968a734e0d8257fca72ad6f6b37c80e409e7f986a94f1269d022025e28e140e8087f1804a79b072ae18f69064f53223f2baa169685fe951f16b72012103f23d4fb4ab152b5f6b5e4a0bf79cfcac071c1f2cf07211c8cd176469b2a00628ffffffff02b3070000000000001976a914c3a1a5b559ff4db7f9c92c3d10274a3a18dcea3788ac4be28a00000000001976a914fe0c8a170be39d30f5447e57556e7836ed29e49088ac00000000";
Transaction parsedTransaction = new Transaction(Utils.hexToBytes(hex));
Transaction transaction = new Transaction();
for (TransactionInput txInput : parsedTransaction.getInputs()) {
transaction.addInput(txInput.getOutpoint().getHash(), txInput.getOutpoint().getIndex(), txInput.getScriptSig());
}
for (TransactionOutput txOutput : parsedTransaction.getOutputs()) {
Address address = txOutput.getScript().getToAddresses()[0];
transaction.addOutput(txOutput.getValue(), address);
}
Assert.assertEquals(parsedTransaction.getLength(), transaction.getLength());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
transaction.bitcoinSerializeToStream(baos);
String constructedHex = Utils.bytesToHex(baos.toByteArray());
Assert.assertEquals(hex, constructedHex);
}
use of com.sparrowwallet.drongo.address.Address in project drongo by sparrowwallet.
the class TransactionTest method verifyConstructedTxLengthP2WPKHMulti.
@Test
public void verifyConstructedTxLengthP2WPKHMulti() throws NonStandardScriptException, IOException {
String hex = "02000000000102ba4dc5a4a14bfaa941b7d115b379b5e15f960635cf694c178b9116763cbd63b11600000017160014fc164cbcac023f5eacfcead2d17d8768c41949affeffffff074d44d2856beb68ba52e8832da60a1682768c2421c2d9a8109ef4e66babd1fd1e000000171600148c3098be6b430859115f5ee99c84c368afecd0481500400002305310000000000017a914ffaf369c2212b178c7a2c21c9ccdd5d126e74c4187327f0300000000001976a914a7cda2e06b102a143ab606937a01d152e300cd3e88ac02473044022006da0ca227f765179219e08a33026b94e7cacff77f87b8cd8eb1b46d6dda11d6022064faa7912924fd23406b6ed3328f1bbbc3760dc51109a49c1b38bf57029d304f012103c6a2fcd030270427d4abe1041c8af929a9e2dbab07b243673453847ab842ee1f024730440220786316a16095105a0af28dccac5cf80f449dea2ea810a9559a89ecb989c2cb3d02205cbd9913d1217ffec144ae4f2bd895f16d778c2ec49ae9c929fdc8bcc2a2b1db0121024d4985241609d072a59be6418d700e87688f6c4d99a51ad68e66078211f076ee38820900";
Transaction parsedTransaction = new Transaction(Utils.hexToBytes(hex));
Transaction transaction = new Transaction();
transaction.setVersion(parsedTransaction.getVersion());
transaction.setSegwitFlag(parsedTransaction.getSegwitFlag());
transaction.setLocktime(parsedTransaction.getLocktime());
for (TransactionInput txInput : parsedTransaction.getInputs()) {
TransactionInput newInput = transaction.addInput(txInput.getOutpoint().getHash(), txInput.getOutpoint().getIndex(), txInput.getScriptSig(), txInput.getWitness());
newInput.setSequenceNumber(txInput.getSequenceNumber());
}
for (TransactionOutput txOutput : parsedTransaction.getOutputs()) {
Address address = txOutput.getScript().getToAddresses()[0];
transaction.addOutput(txOutput.getValue(), address);
}
Assert.assertEquals(parsedTransaction.getLength(), transaction.getLength());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
transaction.bitcoinSerializeToStream(baos);
String constructedHex = Utils.bytesToHex(baos.toByteArray());
Assert.assertEquals(hex, constructedHex);
}
Aggregations