@Override public TransactionDetails getTransactionDetails(Sha256Hash txid) { // Note that this method is not synchronized, and we might fetch the transaction history while // synchronizing // accounts. That should be ok as we write to the DB in a sane order. TransactionEx tex = _backing.getTransaction(txid); Transaction tx = TransactionEx.toTransaction(tex); if (tx == null) { throw new RuntimeException(); } List<TransactionDetails.Item> inputs = new ArrayList<TransactionDetails.Item>(tx.inputs.length); if (tx.isCoinbase()) { // We have a coinbase transaction. Create one input with the sum of the outputs as its value, // and make the address the null address long value = 0; for (TransactionOutput out : tx.outputs) { value += out.value; } inputs.add(new TransactionDetails.Item(Address.getNullAddress(_network), value, true)); } else { // Populate the inputs for (TransactionInput input : tx.inputs) { Sha256Hash parentHash = input.outPoint.hash; // Get the parent transaction TransactionOutputEx parentOutput = _backing.getParentTransactionOutput(input.outPoint); if (parentOutput == null) { // We never heard about the parent, skip continue; } // Determine the parent address Address parentAddress; ScriptOutput parentScript = ScriptOutput.fromScriptBytes(parentOutput.script); if (parentScript == null) { // Null address means we couldn't figure out the address, strange script parentAddress = Address.getNullAddress(_network); } else { parentAddress = parentScript.getAddress(_network); } inputs.add(new TransactionDetails.Item(parentAddress, parentOutput.value, false)); } } // Populate the outputs TransactionDetails.Item[] outputs = new TransactionDetails.Item[tx.outputs.length]; for (int i = 0; i < tx.outputs.length; i++) { Address address = tx.outputs[i].script.getAddress(_network); outputs[i] = new TransactionDetails.Item(address, tx.outputs[i].value, false); } return new TransactionDetails( txid, tex.height, tex.time, inputs.toArray(new TransactionDetails.Item[] {}), outputs); }
@Override public BitcoinSigner findSignerByPublicKey(PublicKey publicKey) { Address address = publicKey.toAddress(_network); InMemoryPrivateKey privateKey; try { privateKey = getPrivateKeyForAddress(address, _cipher); } catch (InvalidKeyCipher e) { throw new RuntimeException( "Unable to decrypt private key for address " + address.toString()); } if (privateKey != null) { return privateKey; } throw new RuntimeException("Unable to find private key for address " + address.toString()); }
@Override public PublicKey findPublicKeyByAddress(Address address) { PublicKey publicKey = getPublicKeyForAddress(address); if (publicKey != null) { return publicKey; } throw new RuntimeException("Unable to find public key for address " + address.toString()); }
@Override public synchronized long calculateMaxSpendableAmount(long minerFeeToUse) { checkNotArchived(); Collection<UnspentTransactionOutput> spendableOutputs = transform(getSpendableOutputs()); long satoshis = 0; for (UnspentTransactionOutput output : spendableOutputs) { satoshis += output.value; } // Iteratively figure out whether we can send everything by subtracting // the miner fee for every iteration while (true) { satoshis -= minerFeeToUse; if (satoshis <= 0) { return 0; } // Create transaction builder StandardTransactionBuilder stb = new StandardTransactionBuilder(_network); // Try and add the output try { // Note, null address used here, we just use it for measuring the // transaction size stb.addOutput(Address.getNullAddress(_network), satoshis); } catch (OutputTooSmallException e1) { // The amount we try to send is lower than what the network allows return 0; } // Try to create an unsigned transaction try { stb.createUnsignedTransaction( spendableOutputs, getChangeAddress(), new PublicKeyRing(), _network, minerFeeToUse); // We have enough to pay the fees, return the amount as the maximum return satoshis; } catch (InsufficientFundsException e) { // We cannot send this amount, try again with a little higher fee continue; } } }
@Override public List<TransactionOutputSummary> getUnspentTransactionOutputSummary() { // Note that this method is not synchronized, and we might fetch the transaction history while // synchronizing // accounts. That should be ok as we write to the DB in a sane order. // Get all unspent outputs for this account Collection<TransactionOutputEx> outputs = _backing.getAllUnspentOutputs(); // Transform it to a list of summaries List<TransactionOutputSummary> list = new ArrayList<TransactionOutputSummary>(); int blockChainHeight = getBlockChainHeight(); for (TransactionOutputEx output : outputs) { ScriptOutput script = ScriptOutput.fromScriptBytes(output.script); Address address; if (script == null) { address = Address.getNullAddress(_network); // This never happens as we have parsed this script before } else { address = script.getAddress(_network); } int confirmations; if (output.height == -1) { confirmations = 0; } else { confirmations = Math.max(0, blockChainHeight - output.height + 1); } TransactionOutputSummary summary = new TransactionOutputSummary( output.outPoint, output.value, output.height, confirmations, address); list.add(summary); } // Sort & return Collections.sort(list); return list; }
protected static UUID addressToUUID(Address address) { return new UUID( BitUtils.uint64ToLong(address.getAllAddressBytes(), 1), BitUtils.uint64ToLong(address.getAllAddressBytes(), 9)); }