@Test public void sideChain() throws Exception { // The wallet receives a coin on the main chain, then on a side chain. Balance is equal to both added together // as we assume the side chain tx is pending and will be included shortly. Coin v1 = COIN; sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, v1); assertEquals(v1, wallet.getBalance()); assertEquals(1, wallet.getPoolSize(WalletTransaction.Pool.UNSPENT)); assertEquals(1, wallet.getTransactions(true).size()); Coin v2 = valueOf(0, 50); sendMoneyToWallet(AbstractBlockChain.NewBlockType.SIDE_CHAIN, v2); assertEquals(2, wallet.getTransactions(true).size()); assertEquals(v1, wallet.getBalance()); assertEquals(v1.add(v2), wallet.getBalance(Wallet.BalanceType.ESTIMATED)); }
@Override public void receiveFromBlock(Transaction tx, StoredBlock block, AbstractBlockChain.NewBlockType blockType, int relativityOffset) throws VerificationException { //Log.d("TransactionIsInBlock",block.toString()); synchronized (transactions) { countTransactions++; transactions.add(tx); if (transactions.size() >= 30) { transactions.remove(10); } } if(System.currentTimeMillis()-lastTimestamp>1000) { refreshUI(); lastTimestamp = System.currentTimeMillis(); } }
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 transactions() throws Exception { // This test covers a bug in which Transaction.getValueSentFromMe was calculating incorrectly. Transaction tx = createFakeTx(PARAMS, COIN, myAddress); // Now add another output (ie, change) that goes to some other address. TransactionOutput output = new TransactionOutput(PARAMS, tx, valueOf(0, 5), OTHER_ADDRESS); tx.addOutput(output); // Note that tx is no longer valid: it spends more than it imports. However checking transactions balance // correctly isn't possible in SPV mode because value is a property of outputs not inputs. Without all // transactions you can't check they add up. sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, tx); // Now the other guy creates a transaction which spends that change. Transaction tx2 = new Transaction(PARAMS); tx2.addInput(output); tx2.addOutput(new TransactionOutput(PARAMS, tx2, valueOf(0, 5), myAddress)); // tx2 doesn't send any coins from us, even though the output is in the wallet. assertEquals(ZERO, tx2.getValueSentFromMe(wallet)); }
@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 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 doubleSpendForBuildingTx() 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))); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, send1); assertUnspent(send1); wallet.receivePending(send2, null); assertUnspent(send1); assertDead(send2); } finally { wallet.setCoinSelector(originalCoinSelector); } }
@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 testAddTransactionsDependingOn() 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); Transaction send1b = checkNotNull(wallet.createSend(OTHER_ADDRESS, valueOf(0, 50))); wallet.commitTx(send1b); Transaction send1c = checkNotNull(wallet.createSend(OTHER_ADDRESS, valueOf(0, 25))); wallet.commitTx(send1c); wallet.commitTx(send2); Set<Transaction> txns = new HashSet<Transaction>(); txns.add(send1); wallet.addTransactionsDependingOn(txns, wallet.getTransactions(true)); assertEquals(3, txns.size()); assertTrue(txns.contains(send1)); assertTrue(txns.contains(send1b)); assertTrue(txns.contains(send1c)); } 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 pubkeyOnlyScripts() throws Exception { // Verify that we support outputs like OP_PUBKEY and the corresponding inputs. ECKey key1 = wallet.freshReceiveKey(); Coin value = valueOf(5, 0); Transaction t1 = createFakeTx(PARAMS, value, key1); if (wallet.isPendingTransactionRelevant(t1)) wallet.receivePending(t1, null); // TX should have been seen as relevant. assertEquals(value, wallet.getBalance(Wallet.BalanceType.ESTIMATED)); assertEquals(ZERO, wallet.getBalance(Wallet.BalanceType.AVAILABLE)); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, t1); // TX should have been seen as relevant, extracted and processed. assertEquals(value, wallet.getBalance(Wallet.BalanceType.AVAILABLE)); // Spend it and ensure we can spend the <key> OP_CHECKSIG output correctly. Transaction t2 = wallet.createSend(OTHER_ADDRESS, value); assertNotNull(t2); // TODO: This code is messy, improve the Script class and fixinate! assertEquals(t2.toString(), 1, t2.getInputs().get(0).getScriptSig().getChunks().size()); assertTrue(t2.getInputs().get(0).getScriptSig().getChunks().get(0).data.length > 50); }
@Test public void watchingScriptsSentFrom() throws Exception { int baseElements = wallet.getBloomFilterElementCount(); Address watchedAddress = new ECKey().toAddress(PARAMS); wallet.addWatchedAddress(watchedAddress); assertEquals(baseElements + 1, wallet.getBloomFilterElementCount()); Transaction t1 = createFakeTx(PARAMS, CENT, watchedAddress); Transaction t2 = createFakeTx(PARAMS, COIN, OTHER_ADDRESS); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, t1); assertEquals(baseElements + 2, wallet.getBloomFilterElementCount()); Transaction st2 = new Transaction(PARAMS); st2.addOutput(CENT, OTHER_ADDRESS); st2.addOutput(COIN, OTHER_ADDRESS); st2.addInput(t1.getOutput(0)); st2.addInput(t2.getOutput(0)); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, st2); assertEquals(baseElements + 2, wallet.getBloomFilterElementCount()); assertEquals(CENT, st2.getValueSentFromMe(wallet)); }
@Test public void replayWhilstPending() throws Exception { // Check that if a pending transaction spends outputs of chain-included transactions, we mark them as spent. // See bug 345. This can happen if there is a pending transaction floating around and then you replay the // chain without emptying the memory pool (or refilling it from a peer). Coin value = COIN; Transaction tx1 = createFakeTx(PARAMS, value, myAddress); Transaction tx2 = new Transaction(PARAMS); tx2.addInput(tx1.getOutput(0)); tx2.addOutput(valueOf(0, 9), OTHER_ADDRESS); // Add a change address to ensure this tx is relevant. tx2.addOutput(CENT, wallet.currentChangeAddress()); wallet.receivePending(tx2, null); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, tx1); assertEquals(ZERO, wallet.getBalance()); assertEquals(1, wallet.getPoolSize(Pool.SPENT)); assertEquals(1, wallet.getPoolSize(Pool.PENDING)); assertEquals(0, wallet.getPoolSize(Pool.UNSPENT)); }
@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 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 }
@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)); }
@Test public void keyRotationHD() throws Exception { // Test that if we rotate an HD chain, a new one is created and all arrivals on the old keys are moved. Utils.setMockClock(); wallet = new Wallet(PARAMS); ECKey key1 = wallet.freshReceiveKey(); ECKey key2 = wallet.freshReceiveKey(); sendMoneyToWallet(wallet, AbstractBlockChain.NewBlockType.BEST_CHAIN, CENT, key1.toAddress(PARAMS)); sendMoneyToWallet(wallet, AbstractBlockChain.NewBlockType.BEST_CHAIN, CENT, key2.toAddress(PARAMS)); DeterministicKey watchKey1 = wallet.getWatchingKey(); // A day later, we get compromised. Utils.rollMockClock(86400); wallet.setKeyRotationTime(Utils.currentTimeSeconds()); List<Transaction> txns = wallet.doMaintenance(null, false).get(); assertEquals(1, txns.size()); DeterministicKey watchKey2 = wallet.getWatchingKey(); assertNotEquals(watchKey1, watchKey2); }
public void completeTxPartiallySignedMarried(Wallet.MissingSigsMode missSigMode, byte[] expectedSig) throws Exception { // create married wallet without signer createMarriedWallet(2, 2, false); myAddress = wallet.currentAddress(KeyChain.KeyPurpose.RECEIVE_FUNDS); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, COIN, myAddress); SendRequest req = SendRequest.emptyWallet(OTHER_ADDRESS); req.missingSigsMode = missSigMode; wallet.completeTx(req); TransactionInput input = req.tx.getInput(0); boolean firstSigIsMissing = Arrays.equals(expectedSig, input.getScriptSig().getChunks().get(1).data); boolean secondSigIsMissing = Arrays.equals(expectedSig, input.getScriptSig().getChunks().get(2).data); assertTrue("Only one of the signatures should be missing/dummy", firstSigIsMissing ^ secondSigIsMissing); int localSigIndex = firstSigIsMissing ? 2 : 1; int length = input.getScriptSig().getChunks().get(localSigIndex).data.length; assertTrue("Local sig should be present: " + length, length >= 70); }
@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 }
@Test public void pubkeyOnlyScripts() throws Exception { // Verify that we support outputs like OP_PUBKEY and the corresponding inputs. ECKey key1 = wallet.freshReceiveKey(); Coin value = valueOf(5, 0); Transaction t1 = createFakeTx(PARAMS, value, key1); if (wallet.isPendingTransactionRelevant(t1)) wallet.receivePending(t1, null); // TX should have been seen as relevant. assertEquals(value, wallet.getBalance(Wallet.BalanceType.ESTIMATED)); assertEquals(ZERO, wallet.getBalance(Wallet.BalanceType.AVAILABLE)); sendMoneyToWallet(AbstractBlockChain.NewBlockType.BEST_CHAIN, t1); // TX should have been seen as relevant, extracted and processed. assertEquals(value, wallet.getBalance(Wallet.BalanceType.AVAILABLE)); // Spend it and ensure we can spend the <key> OP_CHECKSIG output correctly. Transaction t2 = wallet.createSend(OTHER_ADDRESS, value); assertNotNull(t2); // TODO: This code is messy, improve the Script class and fixinate! assertEquals(t2.toString(), 1, t2.getInputs().get(0).getScriptSig().getChunks().size()); assertTrue(t2.getInputs().get(0).getScriptSig().getChunks().get(0).data.length > 50); log.info(t2.toString(chain)); }
@Before public void setUp() throws Exception { super.setUp(); //setup key material commonPublicKey = convertPointToPubKEy( convertPubKeyToPoint(desktopKeyShare).multiply(convertPrivKeyToBigInt(phoneKeyShare))); //add money to the common addresses and common keys to wallet for(int i= 0; i < 3; i++){ ECKey key = commonPublicKey; wallet.addKey(key); sendMoneyToWallet(this.wallet, Coin.valueOf(0,1), key.toAddress(params), AbstractBlockChain.NewBlockType.BEST_CHAIN); } desktopBCParameters = BCParameters.generateBCParameters(); phoneBCParameters = BCParameters.generateBCParameters2(); }