/** * Skips saving state in the wallet for testing */ @VisibleForTesting synchronized void fakeSave() { try { wallet.commitTx(getContractInternal()); } catch (VerificationException e) { throw new RuntimeException(e); // We created it } stateMachine.transition(State.PROVIDE_MULTISIG_CONTRACT_TO_SERVER); }
/** * Skips saving state in the wallet for testing */ @VisibleForTesting synchronized void fakeSave() { try { wallet.commitTx(getContractInternal()); } catch (VerificationException e) { throw new RuntimeException(e); // We created it } stateMachine.transition(State.PROVIDE_MULTISIG_CONTRACT_TO_SERVER); }
/** * Skips saving state in the wallet for testing */ @VisibleForTesting synchronized void fakeSave() { try { wallet.commitTx(getContractInternal()); } catch (VerificationException e) { throw new RuntimeException(e); // We created it } stateMachine.transition(State.PROVIDE_MULTISIG_CONTRACT_TO_SERVER); }
/** * Skips saving state in the wallet for testing */ @VisibleForTesting synchronized void fakeSave() { try { wallet.commitTx(getContractInternal()); } catch (VerificationException e) { throw new RuntimeException(e); // We created it } stateMachine.transition(State.PROVIDE_MULTISIG_CONTRACT_TO_SERVER); }
/** * Sends coins to the given address but does not broadcast the resulting pending transaction. It is still stored * in the wallet, so when the wallet is added to a {@link PeerGroup} or {@link Peer} the transaction will be * announced to the network. The given {@link SendRequest} is completed first using * {@link Wallet#completeTx(Wallet.SendRequest)} to make it valid. * * @return the Transaction that was created * @throws InsufficientMoneyException if the request could not be completed due to not enough balance. * @throws IllegalArgumentException if you try and complete the same SendRequest twice * @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 Transaction sendCoinsOffline(SendRequest request) throws InsufficientMoneyException { lock.lock(); try { completeTx(request); commitTx(request.tx); return request.tx; } finally { lock.unlock(); } }
/** * Sends coins to the given address but does not broadcast the resulting pending transaction. It is still stored * in the wallet, so when the wallet is added to a {@link PeerGroup} or {@link Peer} the transaction will be * announced to the network. The given {@link SendRequest} is completed first using * {@link Wallet#completeTx(Wallet.SendRequest)} to make it valid. * * @return the Transaction that was created * @throws InsufficientMoneyException if the request could not be completed due to not enough balance. * @throws IllegalArgumentException if you try and complete the same SendRequest twice * @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 Transaction sendCoinsOffline(SendRequest request) throws InsufficientMoneyException { lock.lock(); try { completeTx(request); commitTx(request.tx); return request.tx; } finally { lock.unlock(); } }
@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 testEmptyRandomWallet() throws Exception { // Add a random set of outputs StoredBlock block = new StoredBlock(makeSolvedTestBlock(blockStore, OTHER_ADDRESS), BigInteger.ONE, 1); Random rng = new Random(); for (int i = 0; i < rng.nextInt(100) + 1; i++) { Transaction tx = createFakeTx(PARAMS, Coin.valueOf(rng.nextInt((int) COIN.value)), myAddress); wallet.receiveFromBlock(tx, block, AbstractBlockChain.NewBlockType.BEST_CHAIN, i); } SendRequest request = SendRequest.emptyWallet(OTHER_ADDRESS); wallet.completeTx(request); wallet.commitTx(request.tx); assertEquals(ZERO, wallet.getBalance()); }
private static void broadcastAndCommit(Wallet wallet, Transaction t) throws Exception { final LinkedList<Transaction> txns = Lists.newLinkedList(); wallet.addCoinsSentEventListener(new WalletCoinsSentEventListener() { @Override public void onCoinsSent(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) { txns.add(tx); } }); t.getConfidence().markBroadcastBy(new PeerAddress(PARAMS, InetAddress.getByAddress(new byte[]{1,2,3,4}))); t.getConfidence().markBroadcastBy(new PeerAddress(PARAMS, InetAddress.getByAddress(new byte[]{10,2,3,4}))); wallet.commitTx(t); Threading.waitForUserCode(); assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.PENDING)); assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.SPENT)); assertEquals(2, wallet.getTransactions(true).size()); assertEquals(t, txns.getFirst()); assertEquals(1, txns.size()); }
@Test public void ageMattersDuringSelection() throws Exception { // Test that we prefer older coins to newer coins when building spends. This reduces required fees and improves // time to confirmation as the transaction will appear less spammy. final int ITERATIONS = 10; Transaction[] txns = new Transaction[ITERATIONS]; for (int i = 0; i < ITERATIONS; i++) { txns[i] = sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, COIN); } // Check that we spend transactions in order of reception. for (int i = 0; i < ITERATIONS; i++) { Transaction spend = wallet.createSend(OTHER_ADDRESS, COIN); assertEquals(spend.getInputs().size(), 1); assertEquals("Failed on iteration " + i, spend.getInput(0).getOutpoint().getHash(), txns[i].getHash()); wallet.commitTx(spend); } }
@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 walletTest() throws Exception { NetworkParameters params = MainNetParams.get(); Context.propagate(new Context(params)); DumpedPrivateKey privKey = DumpedPrivateKey.fromBase58(params, "5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C"); Address addr = privKey.getKey().toAddress(params); assertTrue(addr.toString().equals("17Wx1GQfyPTNWpQMHrTwRSMTCAonSiZx9e")); KeyChainGroup group = new KeyChainGroup(params); // Add a random key which happens to have been used in a recent generation group.importKeys(ECKey.fromPublicOnly(privKey.getKey().getPubKeyPoint()), ECKey.fromPublicOnly(HEX.decode("03cb219f69f1b49468bd563239a86667e74a06fcba69ac50a08a5cbc42a5808e99"))); Wallet wallet = new Wallet(params, group); wallet.commitTx(new Transaction(params, HEX.decode("01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d038754030114062f503253482fffffffff01c05e559500000000232103cb219f69f1b49468bd563239a86667e74a06fcba69ac50a08a5cbc42a5808e99ac00000000"))); // We should have 2 per pubkey, and one for the pay-2-pubkey output we have assertEquals(5, wallet.getBloomFilterElementCount()); BloomFilter filter = wallet.getBloomFilter(wallet.getBloomFilterElementCount(), 0.001, 0); // Value generated by Bitcoin Core assertTrue(Arrays.equals(HEX.decode("082ae5edc8e51d4a03080000000000000002"), filter.unsafeBitcoinSerialize())); } }
@Test public void bounce() throws Exception { // This test covers bug 64 (False double spends). Check that if we create a spend and it's immediately sent // back to us, this isn't considered as a double spend. Coin coin1 = COIN; sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, coin1); // Send half to some other guy. Sending only half then waiting for a confirm is important to ensure the tx is // in the unspent pool, not pending or spent. Coin coinHalf = valueOf(0, 50); assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT)); assertEquals(1, wallet.getTransactions(true).size()); Transaction outbound1 = wallet.createSend(OTHER_ADDRESS, coinHalf); wallet.commitTx(outbound1); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, outbound1); assertTrue(outbound1.getWalletOutputs(wallet).size() <= 1); //the change address at most // That other guy gives us the coins right back. Transaction inbound2 = new Transaction(PARAMS); inbound2.addOutput(new TransactionOutput(PARAMS, inbound2, coinHalf, myAddress)); assertTrue(outbound1.getWalletOutputs(wallet).size() >= 1); inbound2.addInput(outbound1.getOutputs().get(0)); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, inbound2); assertEquals(coin1, wallet.getBalance()); }
@Test public void txSpendingDeadTx() throws Exception { CoinSelector originalCoinSelector = wallet.getCoinSelector(); try { wallet.allowSpendingUnconfirmedTransactions(); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, valueOf(2, 0)); Transaction send1 = checkNotNull(wallet.createSend(OTHER_ADDRESS, valueOf(1, 0))); Transaction send2 = checkNotNull(wallet.createSend(OTHER_ADDRESS, valueOf(1, 20))); wallet.commitTx(send1); assertPending(send1); Transaction send1b = checkNotNull(wallet.createSend(OTHER_ADDRESS, valueOf(0, 50))); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, send2); assertDead(send1); assertUnspent(send2); wallet.receivePending(send1b, null); assertDead(send1); assertUnspent(send2); assertDead(send1b); } finally { wallet.setCoinSelector(originalCoinSelector); } }
@Test public void spendToSameWallet() throws Exception { // Test that a spend to the same wallet is dealt with correctly. // It should appear in the wallet and confirm. // This is a bit of a silly thing to do in the real world as all it does is burn a fee but it is perfectly valid. Coin coin1 = COIN; Coin coinHalf = valueOf(0, 50); // Start by giving us 1 coin. sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, coin1); // Send half to ourselves. We should then have a balance available to spend of zero. assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT)); assertEquals(1, wallet.getTransactions(true).size()); Transaction outbound1 = wallet.createSend(myAddress, coinHalf); wallet.commitTx(outbound1); // We should have a zero available balance before the next block. assertEquals(ZERO, wallet.getBalance()); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, outbound1); // We should have a balance of 1 BTC after the block is received. assertEquals(coin1, wallet.getBalance()); }
@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 doubleSpendUnspendsOtherInputs() throws Exception { // Test another Finney attack, but this time the killed transaction was also spending some other outputs in // our wallet which were not themselves double spent. This test ensures the death of the pending transaction // frees up the other outputs and makes them spendable again. // Receive 1 coin and then 2 coins in separate transactions. sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, COIN); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, valueOf(2, 0)); // Create a send to a merchant of all our coins. Transaction send1 = wallet.createSend(OTHER_ADDRESS, valueOf(2, 90)); // Create a double spend of just the first one. Address BAD_GUY = new ECKey().toAddress(PARAMS); Transaction send2 = wallet.createSend(BAD_GUY, COIN); send2 = PARAMS.getDefaultSerializer().makeTransaction(send2.bitcoinSerialize()); // Broadcast send1, it's now pending. wallet.commitTx(send1); assertEquals(ZERO, wallet.getBalance()); // change of 10 cents is not yet mined so not included in the balance. // Receive a block that overrides the send1 using send2. sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, send2); // send1 got rolled back and replaced with a smaller send that only used one of our received coins, thus ... assertEquals(valueOf(2, 0), wallet.getBalance()); assertTrue(wallet.isConsistent()); }
@Test public void childPaysForParent() throws Exception { // Receive confirmed balance to play with. Transaction toMe = createFakeTxWithoutChangeAddress(PARAMS, COIN, myAddress); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, toMe); assertEquals(Coin.COIN, wallet.getBalance(BalanceType.ESTIMATED_SPENDABLE)); assertEquals(Coin.COIN, wallet.getBalance(BalanceType.AVAILABLE_SPENDABLE)); // Receive unconfirmed coin without fee. Transaction toMeWithoutFee = createFakeTxWithoutChangeAddress(PARAMS, COIN, myAddress); wallet.receivePending(toMeWithoutFee, null); assertEquals(Coin.COIN.multiply(2), wallet.getBalance(BalanceType.ESTIMATED_SPENDABLE)); assertEquals(Coin.COIN, wallet.getBalance(BalanceType.AVAILABLE_SPENDABLE)); // Craft a child-pays-for-parent transaction. final Coin feeRaise = MILLICOIN; final SendRequest sendRequest = SendRequest.childPaysForParent(wallet, toMeWithoutFee, feeRaise); wallet.signTransaction(sendRequest); wallet.commitTx(sendRequest.tx); assertEquals(Transaction.Purpose.RAISE_FEE, sendRequest.tx.getPurpose()); assertEquals(Coin.COIN.multiply(2).subtract(feeRaise), wallet.getBalance(BalanceType.ESTIMATED_SPENDABLE)); assertEquals(Coin.COIN, wallet.getBalance(BalanceType.AVAILABLE_SPENDABLE)); }