Wallet implements Serializable, BlockChainListener, PeerFilterProvider { public void completeTx(SendRequest req) throws InsufficientMoneyException { lock.lock(); try { checkArgument(!req.completed, "Given SendRequest has already been completed."); BigInteger value = BigInteger.ZERO; for (TransactionOutput output : req.tx.getOutputs()) { value = value.add(output.getValue()); } BigInteger totalOutput = value; log.info("Completing send tx with {} outputs totalling {} satoshis (not including fees)", req.tx.getOutputs().size(), value); BigInteger totalInput = BigInteger.ZERO; for (TransactionInput input : req.tx.getInputs()) if (input.getConnectedOutput() != null) totalInput = totalInput.add(input.getConnectedOutput().getValue()); else log.warn("SendRequest transaction already has inputs but we don't know how much they are worth - they will be added to fee."); value = value.subtract(totalInput); List<TransactionInput> originalInputs = new ArrayList<TransactionInput>(req.tx.getInputs()); boolean needAtLeastReferenceFee = false; if (req.ensureMinRequiredFee && !req.emptyWallet) { for (TransactionOutput output : req.tx.getOutputs()) if (output.getValue().compareTo(Utils.CENT) < 0) { if (output.getValue().compareTo(output.getMinNonDustValue()) < 0) throw new IllegalArgumentException("Tried to send dust with ensureMinRequiredFee set - no way to complete this"); needAtLeastReferenceFee = true; break; } } LinkedList<TransactionOutput> candidates = calculateAllSpendCandidates(true); CoinSelection bestCoinSelection; TransactionOutput bestChangeOutput = null; if (!req.emptyWallet) { FeeCalculation feeCalculation; feeCalculation = new FeeCalculation(req, value, originalInputs, needAtLeastReferenceFee, candidates); bestCoinSelection = feeCalculation.bestCoinSelection; bestChangeOutput = feeCalculation.bestChangeOutput; } else { checkState(req.tx.getOutputs().size() == 1, "Empty wallet TX must have a single output only."); CoinSelector selector = req.coinSelector == null ? coinSelector : req.coinSelector; bestCoinSelection = selector.select(NetworkParameters.MAX_MONEY, candidates); req.tx.getOutput(0).setValue(bestCoinSelection.valueGathered); totalOutput = bestCoinSelection.valueGathered; } for (TransactionOutput output : bestCoinSelection.gathered) req.tx.addInput(output); if (req.ensureMinRequiredFee && req.emptyWallet) { final BigInteger baseFee = req.fee == null ? BigInteger.ZERO : req.fee; final BigInteger feePerKb = req.feePerKb == null ? BigInteger.ZERO : req.feePerKb; Transaction tx = req.tx; if (!adjustOutputDownwardsForFee(tx, bestCoinSelection, baseFee, feePerKb)) throw new InsufficientMoneyException.CouldNotAdjustDownwards(); } totalInput = totalInput.add(bestCoinSelection.valueGathered); if (bestChangeOutput != null) { req.tx.addOutput(bestChangeOutput); totalOutput = totalOutput.add(bestChangeOutput.getValue()); log.info(" with {} coins change", bitcoinValueToFriendlyString(bestChangeOutput.getValue())); } final BigInteger calculatedFee = totalInput.subtract(totalOutput); if (calculatedFee.signum() > 0) { log.info(" with a fee of {}", bitcoinValueToFriendlyString(calculatedFee)); } req.tx.signInputs(Transaction.SigHash.ALL, this, req.aesKey); int size = req.tx.bitcoinSerialize().length; if (size > Transaction.MAX_STANDARD_TX_SIZE) { throw new IllegalArgumentException( String.format("Transaction could not be created without exceeding max size: %d vs %d", size, Transaction.MAX_STANDARD_TX_SIZE)); } req.tx.getConfidence().setSource(TransactionConfidence.Source.SELF); req.tx.setPurpose(Transaction.Purpose.USER_PAYMENT); req.completed = true; req.fee = calculatedFee; log.info(" completed: {}", req.tx); } finally { lock.unlock(); } } Wallet(NetworkParameters params);  Wallet(NetworkParameters params, KeyCrypter keyCrypter); NetworkParameters getNetworkParameters(); List<ECKey> getKeys(); List<Script> getWatchedScripts(); boolean removeKey(ECKey key); int getKeychainSize(); void saveToFile(File temp, File destFile); void saveToFile(File f); void setAcceptRiskyTransactions(boolean acceptRiskyTransactions); boolean doesAcceptRiskyTransactions(); void setRiskAnalyzer(RiskAnalysis.Analyzer analyzer); RiskAnalysis.Analyzer getRiskAnalyzer(); WalletFiles autosaveToFile(File f, long delayTime, TimeUnit timeUnit,
                                      @Nullable WalletFiles.Listener eventListener); void saveToFileStream(OutputStream f); NetworkParameters getParams(); static Wallet loadFromFile(File f); boolean isConsistent(); static Wallet loadFromFileStream(InputStream stream); void notifyTransactionIsInBlock(Sha256Hash txHash, StoredBlock block,
                                           BlockChain.NewBlockType blockType,
                                           int relativityOffset); void receivePending(Transaction tx, @Nullable List<Transaction> dependencies, boolean overrideIsRelevant); boolean isTransactionRisky(Transaction tx, @Nullable List<Transaction> dependencies); void receivePending(Transaction tx, @Nullable List<Transaction> dependencies); boolean isPendingTransactionRelevant(Transaction tx); boolean isTransactionRelevant(Transaction tx); @Override void receiveFromBlock(Transaction tx, StoredBlock block,
                                 BlockChain.NewBlockType blockType,
                                 int relativityOffset); void notifyNewBestBlock(StoredBlock block); void addEventListener(WalletEventListener listener); void addEventListener(WalletEventListener listener, Executor executor); boolean removeEventListener(WalletEventListener listener); boolean maybeCommitTx(Transaction tx); void commitTx(Transaction tx); Set<Transaction> getTransactions(boolean includeDead); Iterable<WalletTransaction> getWalletTransactions(); void addWalletTransaction(WalletTransaction wtx); List<Transaction> getTransactionsByTime(); List<Transaction> getRecentTransactions(int numTransactions, boolean includeDead); @Nullable Transaction getTransaction(Sha256Hash hash); void clearTransactions(int fromHeight); void cleanup(); Transaction createSend(Address address, BigInteger nanocoins); Transaction sendCoinsOffline(SendRequest request); SendResult sendCoins(TransactionBroadcaster broadcaster, Address to, BigInteger value); SendResult sendCoins(TransactionBroadcaster broadcaster, SendRequest request); SendResult sendCoins(SendRequest request); Transaction sendCoins(Peer peer, SendRequest request); void completeTx(SendRequest req); LinkedList<TransactionOutput> calculateAllSpendCandidates(boolean excludeImmatureCoinbases); LinkedList<TransactionOutput> getWatchedOutputs(boolean excludeImmatureCoinbases); Address getChangeAddress(); boolean addKey(final ECKey key); int addKeys(final List<ECKey> keys); boolean isAddressWatched(Address address); boolean addWatchedAddress(final Address address); boolean addWatchedAddress(final Address address, long creationTime); int addWatchedAddresses(final List<Address> addresses, long creationTime); int addWatchedScripts(final List<Script> scripts); @Nullable ECKey findKeyFromPubHash(byte[] pubkeyHash); boolean hasKey(ECKey key); boolean isPubKeyHashMine(byte[] pubkeyHash); boolean isWatchedScript(Script script); @Nullable ECKey findKeyFromPubKey(byte[] pubkey); boolean isPubKeyMine(byte[] pubkey); BigInteger getBalance(); BigInteger getBalance(BalanceType balanceType); BigInteger getBalance(CoinSelector selector); BigInteger getWatchedBalance(); BigInteger getWatchedBalance(CoinSelector selector); @Override String toString(); String toString(boolean includePrivateKeys, boolean includeTransactions, boolean includeExtensions,
                           @Nullable AbstractBlockChain chain); void reorganize(StoredBlock splitPoint, List<StoredBlock> oldBlocks, List<StoredBlock> newBlocks); Collection<Transaction> getPendingTransactions(); @Override long getEarliestKeyCreationTime(); @Nullable Sha256Hash getLastBlockSeenHash(); void setLastBlockSeenHash(@Nullable Sha256Hash lastBlockSeenHash); void setLastBlockSeenHeight(int lastBlockSeenHeight); void setLastBlockSeenTimeSecs(long timeSecs); long getLastBlockSeenTimeSecs(); @Nullable Date getLastBlockSeenTime(); int getLastBlockSeenHeight(); KeyParameter encrypt(CharSequence password); void encrypt(KeyCrypter keyCrypter, KeyParameter aesKey); void decrypt(KeyParameter aesKey); ECKey addNewEncryptedKey(KeyCrypter keyCrypter, KeyParameter aesKey); ECKey addNewEncryptedKey(CharSequence password); boolean checkPassword(CharSequence password); boolean checkAESKey(KeyParameter aesKey); KeyCrypter getKeyCrypter(); void setKeyCrypter(KeyCrypter keyCrypter); EncryptionType getEncryptionType(); boolean isEncrypted(); int getVersion(); void setVersion(int version); void setDescription(String description); String getDescription(); @Override int getBloomFilterElementCount(); @Override boolean isRequiringUpdateAllBloomFilter(); BloomFilter getBloomFilter(double falsePositiveRate); @Override BloomFilter getBloomFilter(int size, double falsePositiveRate, long nTweak); CoinSelector getCoinSelector(); void setCoinSelector(@Nonnull CoinSelector coinSelector); void allowSpendingUnconfirmedTransactions(); ListenableFuture<BigInteger> getBalanceFuture(final BigInteger value, final BalanceType type); void addExtension(WalletExtension extension); WalletExtension addOrGetExistingExtension(WalletExtension extension); void addOrUpdateExtension(WalletExtension extension); Map<String, WalletExtension> getExtensions(); void setTransactionBroadcaster(@Nullable com.google.bitcoin.core.TransactionBroadcaster broadcaster); void setKeyRotationTime(Date time); Date getKeyRotationTime(); void setKeyRotationTime(long unixTimeSeconds); void setKeyRotationEnabled(boolean enabled); boolean isKeyRotating(ECKey key);  }
@Test(expected = IllegalArgumentException.class) public void respectMaxStandardSize() throws Exception { sendMoneyToWallet(Utils.toNanoCoins(100, 0), AbstractBlockChain.NewBlockType.BEST_CHAIN); Transaction tx = new Transaction(params); byte[] bits = new byte[20]; new Random().nextBytes(bits); BigInteger v = Utils.toNanoCoins(0, 1); for (int i = 0; i < 3100; i++) { tx.addOutput(v, new Address(params, bits)); } Wallet.SendRequest req = Wallet.SendRequest.forTx(tx); wallet.completeTx(req); }