/** * Copyright 2011 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.bitcoin.core; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.Serializable; import java.math.BigInteger; import java.util.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static com.google.bitcoin.core.Utils.*; /** * A transaction represents the movement of coins from some addresses to some other addresses. It can also represent * the minting of new coins. A Transaction object corresponds to the equivalent in the BitCoin C++ implementation.<p> * * It implements TWO serialization protocols - the BitCoin proprietary format which is identical to the C++ * implementation and is used for reading/writing transactions to the wire and for hashing. It also implements Java * serialization which is used for the wallet. This allows us to easily add extra fields used for our own accounting * or UI purposes. */ public class Transaction extends Message implements Serializable { private static final Logger log = LoggerFactory.getLogger(Transaction.class); private static final long serialVersionUID = -8567546957352643140L; // These are serialized in both bitcoin and java serialization. long version; public ArrayList<TransactionInput> inputs; public ArrayList<TransactionOutput> outputs; long lockTime; // This is only serialized in java public Date updatedAt; // This is only stored in Java serialization. It records which blocks (and their height + work) the transaction // has been included in. For most transactions this set will have a single member. In the case of a chain split a // transaction may appear in multiple blocks but only one of them is part of the best chain. It's not valid to // have an identical transaction appear in two blocks in the same chain but this invariant is expensive to check, // so it's not directly enforced anywhere. // // If this transaction is not stored in the wallet, appearsIn is null. Set<StoredBlock> appearsIn; // This is an in memory helper only. transient Sha256Hash hash; Transaction(NetworkParameters params) { super(params); version = 1; inputs = new ArrayList<TransactionInput>(); outputs = new ArrayList<TransactionOutput>(); updatedAt = new Date(); // We don't initialize appearsIn deliberately as it's only useful for transactions stored in the wallet. } /** * Creates a transaction from the given serialized bytes, eg, from a block or a tx network message. */ public Transaction(NetworkParameters params, byte[] payloadBytes) throws ProtocolException { super(params, payloadBytes, 0); } /** * Creates a transaction by reading payload starting from offset bytes in. Length of a transaction is fixed. */ public Transaction(NetworkParameters params, byte[] payload, int offset) throws ProtocolException { super(params, payload, offset); // inputs/outputs will be created in parse() } /** * Returns a read-only list of the inputs of this transaction. */ public List<TransactionInput> getInputs() { return Collections.unmodifiableList(inputs); } /** * Returns the transaction hash as you see them in the block explorer. */ public Sha256Hash getHash() { if (hash == null) { byte[] bits = bitcoinSerialize(); hash = new Sha256Hash(reverseBytes(doubleDigest(bits))); } return hash; } public String getHashAsString() { return getHash().toString(); } void setFakeHashForTesting(Sha256Hash hash) { this.hash = hash; } public boolean sent(Wallet wallet) { boolean sent = false; for (TransactionInput in : inputs) { if (in.isMine(wallet)) { return true; } } return false; } public boolean received(Wallet wallet) { return !sent(wallet); } public BigInteger amount(Wallet wallet) throws ScriptException { if (sent(wallet)) { return getValueSentFromMe(wallet).subtract(getValueSentToMe(wallet)); } else { return getValueSentToMe(wallet); } } /** * Calculates the sum of the outputs that are sending coins to a key in the wallet. The flag controls whether to * include spent outputs or not. */ public BigInteger getValueSentToMe(Wallet wallet, boolean includeSpent) { // This is tested in WalletTest. BigInteger v = BigInteger.ZERO; for (TransactionOutput o : outputs) { if (!o.isMine(wallet)) continue; if (!includeSpent && !o.isAvailableForSpending()) continue; v = v.add(o.getValue()); } return v; } /** * Calculates the sum of the outputs that are sending coins to a key in the wallet. */ public BigInteger getValueSentToMe(Wallet wallet) { return getValueSentToMe(wallet, true); } /** * Returns a set of blocks which contain the transaction, or null if this transaction doesn't have that data * because it's not stored in the wallet or because it has never appeared in a block. */ public Set<StoredBlock> getAppearsIn() { return appearsIn; } /** * Adds the given block to the internal serializable set of blocks in which this transaction appears. This is * used by the wallet to ensure transactions that appear on side chains are recorded properly even though the * block stores do not save the transaction data at all. */ void addBlockAppearance(StoredBlock block) { if (appearsIn == null) { appearsIn = new HashSet<StoredBlock>(); } appearsIn.add(block); } /** * Calculates the sum of the inputs that are spending coins with keys in the wallet. This requires the * transactions sending coins to those keys to be in the wallet. This method will not attempt to download the * blocks containing the input transactions if the key is in the wallet but the transactions are not. * * @return sum in nanocoins. */ public BigInteger getValueSentFromMe(Wallet wallet) throws ScriptException { // This is tested in WalletTest. BigInteger v = BigInteger.ZERO; for (TransactionInput input : inputs) { // This input is taking value from an transaction in our wallet. To discover the value, // we must find the connected transaction. TransactionOutput connected = input.getConnectedOutput(wallet.unspent); if (connected == null) connected = input.getConnectedOutput(wallet.spent); if (connected == null) connected = input.getConnectedOutput(wallet.pending); if (connected == null) continue; v = v.add(connected.getValue()); } return v; } boolean disconnectInputs() { boolean disconnected = false; for (TransactionInput input : inputs) { disconnected |= input.disconnect(); } return disconnected; } /** * Connects all inputs using the provided transactions. If any input cannot be connected returns that input or * null on success. */ TransactionInput connectInputs(Map<Sha256Hash, Transaction> transactions, boolean disconnect) { for (TransactionInput input : inputs) { // Coinbase transactions, by definition, do not have connectable inputs. if (input.isCoinBase()) continue; if (input.connect(transactions, disconnect) != TransactionInput.ConnectionResult.SUCCESS) { // Could not connect this input, so return it and abort. return input; } } return null; } /** * These constants are a part of a scriptSig signature on the inputs. They define the details of how a * transaction can be redeemed, specifically, they control how the hash of the transaction is calculated. * * In the official client, this enum also has another flag, SIGHASH_ANYONECANPAY. In this implementation, * that's kept separate. Only SIGHASH_ALL is actually used in the official client today. The other flags * exist to allow for distributed contracts. */ public enum SigHash { ALL, // 1 NONE, // 2 SINGLE, // 3 } void parse() throws ProtocolException { version = readUint32(); // First come the inputs. long numInputs = readVarInt(); inputs = new ArrayList<TransactionInput>((int)numInputs); for (long i = 0; i < numInputs; i++) { TransactionInput input = new TransactionInput(params, this, bytes, cursor); inputs.add(input); cursor += input.getMessageSize(); } // Now the outputs long numOutputs = readVarInt(); outputs = new ArrayList<TransactionOutput>((int)numOutputs); for (long i = 0; i < numOutputs; i++) { TransactionOutput output = new TransactionOutput(params, this, bytes, cursor); outputs.add(output); cursor += output.getMessageSize(); } lockTime = readUint32(); // Store a hash, it may come in useful later (want to avoid reserialization costs). hash = new Sha256Hash(reverseBytes(doubleDigest(bytes, offset, cursor - offset))); } public boolean isMine(Wallet wallet) { try { for (TransactionOutput output : this.outputs) { // TODO: Handle more types of outputs, not just regular to address outputs. if (output.getScriptPubKey().isSentToIP()) continue; // This is not thread safe as a key could be removed between the call to isMine and receive. if (output.isMine(wallet)) { return true; } } for (TransactionInput input : this.inputs) { if (input.getScriptSig().isSentToIP()) continue; // This is not thread safe as a key could be removed between the call to isPubKeyMine and receive. if (input.isMine(wallet)) { return true; } } return false; } catch (ScriptException e) { return false; } } /** * A coinbase transaction is one that creates a new coin. They are the first transaction in each block and their * value is determined by a formula that all implementations of BitCoin share. In 2011 the value of a coinbase * transaction is 50 coins, but in future it will be less. A coinbase transaction is defined not only by its * position in a block but by the data in the inputs. */ public boolean isCoinBase() { return inputs.get(0).isCoinBase(); } /** * @return A human readable version of the transaction useful for debugging. */ public String toString() { StringBuffer s = new StringBuffer(); s.append(" "); s.append(getHashAsString()); s.append("\n"); if (isCoinBase()) { String script = "???"; String script2 = "???"; try { script = inputs.get(0).getScriptSig().toString(); script2 = outputs.get(0).getScriptPubKey().toString(); } catch (ScriptException e) {} return " == COINBASE TXN (scriptSig " + script + ") (scriptPubKey " + script2 + ")"; } for (TransactionInput in : inputs) { s.append(" "); s.append("from "); try { s.append(in.getScriptSig().getFromAddress().toString()); } catch (Exception e) { s.append("[exception: ").append(e.getMessage()).append("]"); throw new RuntimeException(e); } s.append("\n"); } for (TransactionOutput out : outputs) { s.append(" "); s.append("to "); try { Address toAddr = new Address(params, out.getScriptPubKey().getPubKeyHash()); s.append(toAddr.toString()); s.append(" "); s.append(bitcoinValueToFriendlyString(out.getValue())); s.append(" BTC"); } catch (Exception e) { s.append("[exception: ").append(e.getMessage()).append("]"); } s.append("\n"); } return s.toString(); } /** * Adds an input to this transaction that imports value from the given output. Note that this input is NOT * complete and after every input is added with addInput() and every output is added with addOutput(), * signInputs() must be called to finalize the transaction and finish the inputs off. Otherwise it won't be * accepted by the network. */ public void addInput(TransactionOutput from) { inputs.add(new TransactionInput(params, this, from)); } /** * Adds the given output to this transaction. The output must be completely initialized. */ public void addOutput(TransactionOutput to) { to.parentTransaction = this; outputs.add(to); } /** * Once a transaction has some inputs and outputs added, the signatures in the inputs can be calculated. The * signature is over the transaction itself, to prove the redeemer actually created that transaction, * so we have to do this step last.<p> * * This method is similar to SignatureHash in script.cpp * * @param hashType This should always be set to SigHash.ALL currently. Other types are unused. * @param wallet A wallet is required to fetch the keys needed for signing. */ @SuppressWarnings({"SameParameterValue"}) public void signInputs(SigHash hashType, Wallet wallet) throws ScriptException { assert inputs.size() > 0; assert outputs.size() > 0; // I don't currently have an easy way to test other modes work, as the official client does not use them. assert hashType == SigHash.ALL; // The transaction is signed with the input scripts empty except for the input we are signing. In the case // where addInput has been used to set up a new transaction, they are already all empty. The input being signed // has to have the connected OUTPUT program in it when the hash is calculated! // // Note that each input may be claiming an output sent to a different key. So we have to look at the outputs // to figure out which key to sign with. byte[][] signatures = new byte[inputs.size()][]; ECKey[] signingKeys = new ECKey[inputs.size()]; for (int i = 0; i < inputs.size(); i++) { TransactionInput input = inputs.get(i); assert input.scriptBytes.length == 0 : "Attempting to sign a non-fresh transaction"; // Set the input to the script of its output. input.scriptBytes = input.outpoint.getConnectedPubKeyScript(); // Find the signing key we'll need to use. byte[] connectedPubKeyHash = input.outpoint.getConnectedPubKeyHash(); ECKey key = wallet.findKeyFromPubHash(connectedPubKeyHash); // This assert should never fire. If it does, it means the wallet is inconsistent. assert key != null : "Transaction exists in wallet that we cannot redeem: " + Utils.bytesToHexString(connectedPubKeyHash); // Keep the key around for the script creation step below. signingKeys[i] = key; // The anyoneCanPay feature isn't used at the moment. boolean anyoneCanPay = false; byte[] hash = hashTransactionForSignature(hashType, anyoneCanPay); // Set the script to empty again for the next input. input.scriptBytes = TransactionInput.EMPTY_ARRAY; // Now sign for the output so we can redeem it. We use the keypair to sign the hash, // and then put the resulting signature in the script along with the public key (below). try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); bos.write(key.sign(hash)); bos.write((hashType.ordinal() + 1) | (anyoneCanPay ? 0x80 : 0)) ; signatures[i] = bos.toByteArray(); } catch (IOException e) { throw new RuntimeException(e); // Cannot happen. } } // Now we have calculated each signature, go through and create the scripts. Reminder: the script consists of // a signature (over a hash of the transaction) and the complete public key needed to sign for the connected // output. for (int i = 0; i < inputs.size(); i++) { TransactionInput input = inputs.get(i); assert input.scriptBytes.length == 0; ECKey key = signingKeys[i]; input.scriptBytes = Script.createInputScript(signatures[i], key.getPubKey()); } // Every input is now complete. } private byte[] hashTransactionForSignature(SigHash type, boolean anyoneCanPay) { try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); bitcoinSerializeToStream(bos); // We also have to write a hash type. int hashType = type.ordinal() + 1; if (anyoneCanPay) hashType |= 0x80; Utils.uint32ToByteStreamLE(hashType, bos); // Note that this is NOT reversed to ensure it will be signed correctly. If it were to be printed out // however then we would expect that it is IS reversed. return doubleDigest(bos.toByteArray()); } catch (IOException e) { throw new RuntimeException(e); // Cannot happen. } } /** * Given a named input and the transaction output it connects to, runs the script formed from the * concatenation of the input and output scripts, returning true if the link is valid. In * this way, we prove that the creator of this transaction is allowed to redeem the output * of the connectedTx and thus spend the money.<p> * * <b>WARNING: NOT FINISHED</b><p> * * @param inputIndex Which input to verify. * @param connectedTx The Transaction that the input is connected to. */ @SuppressWarnings("unused") public boolean verifyInput(int inputIndex, Transaction connectedTx) throws ScriptException { TransactionInput input = inputs.get(inputIndex); //int outputIndex = (int) input.outpoint.index; //assert outputIndex >= 0 && outputIndex < connectedTx.outputs.size(); //Script outScript = connectedTx.outputs.get(outputIndex).getScriptPubKey(); Script inScript = input.getScriptSig(); //Script script = Script.join(inScript, outScript); //if (script.run(this)) { // LOG("Transaction input successfully verified!"); // return true; //} byte[] pubkey = inScript.getPubKey(); return false; } @Override public void bitcoinSerializeToStream(OutputStream stream) throws IOException { uint32ToByteStreamLE(version, stream); stream.write(new VarInt(inputs.size()).encode()); for (TransactionInput in : inputs) in.bitcoinSerializeToStream(stream); stream.write(new VarInt(outputs.size()).encode()); for (TransactionOutput out : outputs) out.bitcoinSerializeToStream(stream); uint32ToByteStreamLE(lockTime, stream); } @Override public boolean equals(Object other) { if (!(other instanceof Transaction)) return false; Transaction t = (Transaction) other; return t.getHash().equals(getHash()); } @Override public int hashCode() { return Arrays.hashCode(getHash().hash); } }