/** * <p>Sends coins to the given address, via the given {@link PeerGroup}. Change is returned to * {@link Wallet#currentChangeAddress()}. Note that a fee may be automatically added if one may be required for the * transaction to be confirmed.</p> * * <p>The returned object provides both the transaction, and a future that can be used to learn when the broadcast * is complete. Complete means, if the PeerGroup is limited to only one connection, when it was written out to * the socket. Otherwise when the transaction is written out and we heard it back from a different peer.</p> * * <p>Note that the sending transaction is committed to the wallet immediately, not when the transaction is * successfully broadcast. This means that even if the network hasn't heard about your transaction you won't be * able to spend those same coins again.</p> * * <p>You MUST ensure that value is not smaller than {@link Transaction#MIN_NONDUST_OUTPUT} or the transaction will * almost certainly be rejected by the network as dust.</p> * * @param broadcaster a {@link TransactionBroadcaster} to use to send the transactions out. * @param to Which address to send coins to. * @param value How much value to send. * @return An object containing the transaction that was created, and a future for the broadcast of it. * @throws InsufficientMoneyException if the request could not be completed due to not enough balance. * @throws DustySendRequested if the resultant transaction would violate the dust rules. * @throws CouldNotAdjustDownwards if emptying the wallet was requested and the output can't be shrunk for fees without violating a protocol rule. * @throws ExceededMaxTransactionSize if the resultant transaction is too big for Bitcoin to process. * @throws MultipleOpReturnRequested if there is more than one OP_RETURN output for the resultant transaction. */ public SendResult sendCoins(TransactionBroadcaster broadcaster, Address to, Coin value) throws InsufficientMoneyException { SendRequest request = SendRequest.to(to, value); return sendCoins(broadcaster, request); }
/** * <p>Sends coins to the given address, via the given {@link PeerGroup}. Change is returned to * {@link Wallet#currentChangeAddress()}. Note that a fee may be automatically added if one may be required for the * transaction to be confirmed.</p> * * <p>The returned object provides both the transaction, and a future that can be used to learn when the broadcast * is complete. Complete means, if the PeerGroup is limited to only one connection, when it was written out to * the socket. Otherwise when the transaction is written out and we heard it back from a different peer.</p> * * <p>Note that the sending transaction is committed to the wallet immediately, not when the transaction is * successfully broadcast. This means that even if the network hasn't heard about your transaction you won't be * able to spend those same coins again.</p> * * <p>You MUST ensure that value is not smaller than {@link Transaction#MIN_NONDUST_OUTPUT} or the transaction will * almost certainly be rejected by the network as dust.</p> * * @param broadcaster a {@link TransactionBroadcaster} to use to send the transactions out. * @param to Which address to send coins to. * @param value How much value to send. * @return An object containing the transaction that was created, and a future for the broadcast of it. * @throws InsufficientMoneyException if the request could not be completed due to not enough balance. * @throws DustySendRequested if the resultant transaction would violate the dust rules. * @throws CouldNotAdjustDownwards if emptying the wallet was requested and the output can't be shrunk for fees without violating a protocol rule. * @throws ExceededMaxTransactionSize if the resultant transaction is too big for Bitcoin to process. * @throws MultipleOpReturnRequested if there is more than one OP_RETURN output for the resultant transaction. */ public SendResult sendCoins(TransactionBroadcaster broadcaster, Address to, Coin value) throws InsufficientMoneyException { SendRequest request = SendRequest.to(to, value); return sendCoins(broadcaster, request); }
public SendResult sendCoins(TransactionBroadcaster broadcaster, Address to, Coin value, boolean useforkId) throws InsufficientMoneyException { SendRequest request = SendRequest.to(to, value); request.setUseForkId(useforkId); return sendCoins(broadcaster, request); } /**
public void onSendBitcoin(View view) { String to = mToAddressEdit.getText().toString(); String amount = mAmountEdit.getText().toString(); if (TextUtils.isEmpty(to) || TextUtils.isEmpty(amount)) { return; } Address address = Address.fromBase58(Constants.NETWORK_PARAMETERS, to); Coin coin = MonetaryFormat.MBTC.parse(amount); SendRequest sendRequest = SendRequest.to(address, coin); try { Transaction transaction = wallet.sendCoinsOffline(sendRequest); BlockChainService.broadcastTransaction(BitcoinWalletActivity.this, transaction); } catch (InsufficientMoneyException e) { Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show(); e.printStackTrace(); } }
@Test public void sendRequestP2PKHTest() { SendRequest req = SendRequest.to(OTHER_ADDRESS, SATOSHI.multiply(12)); assertEquals(OTHER_ADDRESS, req.tx.getOutputs().get(0).getScriptPubKey().getToAddress(PARAMS)); }
@Test public void sendRequestMemo() throws Exception { receiveATransaction(wallet, myAddress); SendRequest sendRequest = SendRequest.to(myAddress, Coin.COIN); sendRequest.memo = "memo"; wallet.completeTx(sendRequest); assertEquals(sendRequest.memo, sendRequest.tx.getMemo()); }
@Test(expected = java.lang.IllegalStateException.class) public void sendCoinsNoBroadcasterTest() throws InsufficientMoneyException { ECKey key = ECKey.fromPrivate(BigInteger.TEN); SendRequest req = SendRequest.to(OTHER_ADDRESS.getParameters(), key, SATOSHI.multiply(12)); wallet.sendCoins(req); }
@Override public void send() { String recipientAddress = view.getRecipient(); String amount = view.getAmount(); if(TextUtils.isEmpty(recipientAddress) || recipientAddress.equals("Scan recipient QR")) { view.showToastMessage("Select recipient"); return; } if(TextUtils.isEmpty(amount) | Double.parseDouble(amount) <= 0) { view.showToastMessage("Select valid amount"); return; } if(walletAppKit.wallet().getBalance().isLessThan(Coin.parseCoin(amount))) { view.showToastMessage("You got not enough coins"); view.clearAmount(); return; } SendRequest request = SendRequest.to(Address.fromBase58(parameters, recipientAddress), Coin.parseCoin(amount)); try { walletAppKit.wallet().completeTx(request); walletAppKit.wallet().commitTx(request.tx); walletAppKit.peerGroup().broadcastTransaction(request.tx).broadcast(); } catch (InsufficientMoneyException e) { e.printStackTrace(); view.showToastMessage(e.getMessage()); } }
@Test public void sendRequestP2PKTest() { ECKey key = new ECKey(); SendRequest req = SendRequest.to(PARAMS, key, SATOSHI.multiply(12)); assertArrayEquals(key.getPubKey(), req.tx.getOutputs().get(0).getScriptPubKey().getPubKey()); }
@Test public void sendRequestExchangeRate() throws Exception { receiveATransaction(wallet, myAddress); SendRequest sendRequest = SendRequest.to(myAddress, Coin.COIN); sendRequest.exchangeRate = new ExchangeRate(Fiat.parseFiat("EUR", "500")); wallet.completeTx(sendRequest); assertEquals(sendRequest.exchangeRate, sendRequest.tx.getExchangeRate()); }
@Test public void sendCoinsWithBroadcasterTest() throws InsufficientMoneyException { ECKey key = ECKey.fromPrivate(BigInteger.TEN); receiveATransactionAmount(wallet, myAddress, Coin.COIN); MockTransactionBroadcaster broadcaster = new MockTransactionBroadcaster(wallet); wallet.setTransactionBroadcaster(broadcaster); SendRequest req = SendRequest.to(OTHER_ADDRESS.getParameters(), key, Coin.CENT); wallet.sendCoins(req); }
@Test public void transactionGetFeeTest() throws Exception { // Prepare wallet to spend StoredBlock block = new StoredBlock(makeSolvedTestBlock(blockStore, OTHER_ADDRESS), BigInteger.ONE, 1); Transaction tx = createFakeTx(PARAMS, COIN, myAddress); wallet.receiveFromBlock(tx, block, AbstractBlockChain.NewBlockType.BEST_CHAIN, 0); // Create a transaction SendRequest request = SendRequest.to(OTHER_ADDRESS, CENT); request.feePerKb = Transaction.DEFAULT_TX_FEE; wallet.completeTx(request); assertEquals(Coin.valueOf(11350), request.tx.getFee()); }
@Test public void feeSolverAndCoinSelectionTest_dustySendRequested() throws Exception { // Generate a few outputs to us that are far too small to spend reasonably Transaction tx1 = createFakeTx(PARAMS, SATOSHI, myAddress); Transaction tx2 = createFakeTx(PARAMS, SATOSHI, myAddress); assertNotEquals(tx1.getHash(), tx2.getHash()); Transaction tx3 = createFakeTx(PARAMS, SATOSHI.multiply(10), myAddress); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, tx1, tx2, tx3); // Not allowed to send dust. try { SendRequest request = SendRequest.to(OTHER_ADDRESS, SATOSHI); request.ensureMinRequiredFee = true; wallet.completeTx(request); fail(); } catch (Wallet.DustySendRequested e) { // Expected. } // Spend it all without fee enforcement SendRequest req = SendRequest.to(OTHER_ADDRESS, SATOSHI.multiply(12)); assertNotNull(wallet.sendCoinsOffline(req)); assertEquals(ZERO, wallet.getBalance()); }
@Test public void testCategory2WithChange() throws Exception { // Specifically target case 2 with significant change // Generate a ton of small outputs StoredBlock block = new StoredBlock(makeSolvedTestBlock(blockStore, OTHER_ADDRESS), BigInteger.ONE, 1); int i = 0; while (i <= CENT.divide(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.multiply(10))) { Transaction tx = createFakeTxWithChangeAddress(PARAMS, Transaction.REFERENCE_DEFAULT_MIN_TX_FEE.multiply(10), myAddress, OTHER_ADDRESS); tx.getInput(0).setSequenceNumber(i++); // Keep every transaction unique wallet.receiveFromBlock(tx, block, AbstractBlockChain.NewBlockType.BEST_CHAIN, i); } // The selector will choose 2 with MIN_TX_FEE fee SendRequest request1 = SendRequest.to(OTHER_ADDRESS, CENT.add(SATOSHI)); request1.ensureMinRequiredFee = true; wallet.completeTx(request1); assertEquals(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE, request1.tx.getFee()); assertEquals(request1.tx.getInputs().size(), i); // We should have spent all inputs assertEquals(2, request1.tx.getOutputs().size()); // and gotten change back }
private Transaction cleanupCommon(Address destination) throws Exception { receiveATransaction(wallet, myAddress); Coin v2 = valueOf(0, 50); SendRequest req = SendRequest.to(destination, v2); wallet.completeTx(req); Transaction t2 = req.tx; // Broadcast the transaction and commit. broadcastAndCommit(wallet, t2); // At this point we have one pending and one spent Coin v1 = valueOf(0, 10); Transaction t = sendMoneyToWallet(null, v1, myAddress); Threading.waitForUserCode(); sendMoneyToWallet(null, t); assertEquals("Wrong number of PENDING", 2, wallet.getPoolSize(Pool.PENDING)); assertEquals("Wrong number of UNSPENT", 0, wallet.getPoolSize(Pool.UNSPENT)); assertEquals("Wrong number of ALL", 3, wallet.getTransactions(true).size()); assertEquals(valueOf(0, 60), wallet.getBalance(Wallet.BalanceType.ESTIMATED)); // Now we have another incoming pending return t; }
@Test public void cleanupFailsDueToSpend() throws Exception { Transaction t = cleanupCommon(OTHER_ADDRESS); // Now we have another incoming pending. Spend everything. Coin v3 = valueOf(0, 60); SendRequest req = SendRequest.to(OTHER_ADDRESS, v3); // Force selection of the incoming coin so that we can spend it req.coinSelector = new TestCoinSelector(); wallet.completeTx(req); wallet.commitTx(req.tx); assertEquals("Wrong number of PENDING", 3, wallet.getPoolSize(WalletTransaction.Pool.PENDING)); assertEquals("Wrong number of UNSPENT", 0, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT)); assertEquals("Wrong number of ALL", 4, wallet.getTransactions(true).size()); // Consider the new pending as risky and try to remove it from the wallet wallet.setRiskAnalyzer(new TestRiskAnalysis.Analyzer(t)); wallet.cleanup(); assertTrue(wallet.isConsistent()); // The removal should have failed assertEquals("Wrong number of PENDING", 3, wallet.getPoolSize(WalletTransaction.Pool.PENDING)); assertEquals("Wrong number of UNSPENT", 0, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT)); assertEquals("Wrong number of ALL", 4, wallet.getTransactions(true).size()); assertEquals(ZERO, wallet.getBalance(Wallet.BalanceType.ESTIMATED)); }
private Wallet spendUnconfirmedChange(Wallet wallet, Transaction t2, KeyParameter aesKey) throws Exception { if (wallet.getTransactionSigners().size() == 1) // don't bother reconfiguring the p2sh wallet wallet = roundTrip(wallet); Coin v3 = valueOf(0, 50); assertEquals(v3, wallet.getBalance()); SendRequest req = SendRequest.to(OTHER_ADDRESS, valueOf(0, 48)); req.aesKey = aesKey; req.shuffleOutputs = false; wallet.completeTx(req); Transaction t3 = req.tx; assertNotEquals(t2.getOutput(1).getScriptPubKey().getToAddress(PARAMS), t3.getOutput(1).getScriptPubKey().getToAddress(PARAMS)); assertNotNull(t3); wallet.commitTx(t3); assertTrue(wallet.isConsistent()); // t2 and t3 gets confirmed in the same block. sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, t2, t3); assertTrue(wallet.isConsistent()); return wallet; }
@Test public void lowerThanDefaultFee() throws InsufficientMoneyException { int feeFactor = 10; Coin fee = Transaction.DEFAULT_TX_FEE.divide(feeFactor); receiveATransactionAmount(wallet, myAddress, Coin.COIN); SendRequest req = SendRequest.to(myAddress, Coin.CENT); req.feePerKb = fee; wallet.completeTx(req); assertEquals(Coin.valueOf(11350).divide(feeFactor), req.tx.getFee()); wallet.commitTx(req.tx); SendRequest emptyReq = SendRequest.emptyWallet(myAddress); emptyReq.feePerKb = fee; emptyReq.ensureMinRequiredFee = true; emptyReq.emptyWallet = true; emptyReq.coinSelector = AllowUnconfirmedCoinSelector.get(); wallet.completeTx(emptyReq); assertEquals(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE, emptyReq.tx.getFee()); wallet.commitTx(emptyReq.tx); }
@Test public void higherThanDefaultFee() throws InsufficientMoneyException { int feeFactor = 10; Coin fee = Transaction.DEFAULT_TX_FEE.multiply(feeFactor); receiveATransactionAmount(wallet, myAddress, Coin.COIN); SendRequest req = SendRequest.to(myAddress, Coin.CENT); req.feePerKb = fee; wallet.completeTx(req); assertEquals(Coin.valueOf(11350).multiply(feeFactor), req.tx.getFee()); wallet.commitTx(req.tx); SendRequest emptyReq = SendRequest.emptyWallet(myAddress); emptyReq.feePerKb = fee; emptyReq.emptyWallet = true; emptyReq.coinSelector = AllowUnconfirmedCoinSelector.get(); wallet.completeTx(emptyReq); assertEquals(Coin.valueOf(171000), emptyReq.tx.getFee()); wallet.commitTx(emptyReq.tx); }
@Test public void totalReceivedSent() throws Exception { // Receive 4 BTC in 2 separate transactions Transaction toMe1 = createFakeTxWithoutChangeAddress(PARAMS, COIN.multiply(2), myAddress); Transaction toMe2 = createFakeTxWithoutChangeAddress(PARAMS, COIN.multiply(2), myAddress); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, toMe1, toMe2); // Check we calculate the total received correctly assertEquals(Coin.COIN.multiply(4), wallet.getTotalReceived()); // Send 3 BTC in a single transaction SendRequest req = SendRequest.to(OTHER_ADDRESS, Coin.COIN.multiply(3)); wallet.completeTx(req); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, req.tx); // Check that we still have the same totalReceived, since the above tx will have sent us change back assertEquals(Coin.COIN.multiply(4),wallet.getTotalReceived()); assertEquals(Coin.COIN.multiply(3),wallet.getTotalSent()); // TODO: test shared wallet calculation here } }