All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.bitcoin.core.Transaction Maven / Gradle / Ivy

There is a newer version: 0.11.3
Show newest version
/**
 * 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 com.google.bitcoin.core.TransactionConfidence.ConfidenceType;
import com.google.bitcoin.crypto.TransactionSignature;
import com.google.bitcoin.script.Script;
import com.google.bitcoin.script.ScriptBuilder;
import com.google.bitcoin.script.ScriptOpCodes;
import com.google.common.collect.ImmutableMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongycastle.crypto.params.KeyParameter;

import javax.annotation.Nullable;
import java.io.*;
import java.math.BigInteger;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

import static com.google.bitcoin.core.Utils.*;
import static com.google.common.base.Preconditions.*;

/**
 * 

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.

* *

Transactions are the fundamental atoms of Bitcoin and have many powerful features. Read * "Working with transactions" in the * documentation to learn more about how to use this class.

* *

All Bitcoin transactions are at risk of being reversed, though the risk is much less than with traditional payment * systems. Transactions have confidence levels, which help you decide whether to trust a transaction or not. * Whether to trust a transaction is something that needs to be decided on a case by case basis - a rule that makes * sense for selling MP3s might not make sense for selling cars, or accepting payments from a family member. If you * are building a wallet, how to present confidence to your users is something to consider carefully.

*/ public class Transaction extends ChildMessage implements Serializable { private static final Logger log = LoggerFactory.getLogger(Transaction.class); private static final long serialVersionUID = -8567546957352643140L; /** Threshold for lockTime: below this value it is interpreted as block number, otherwise as timestamp. **/ public static final int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC /** How many bytes a transaction can be before it won't be relayed anymore. Currently 100kb. */ public static final int MAX_STANDARD_TX_SIZE = 100 * 1024; /** * If fee is lower than this value (in satoshis), a default reference client will treat it as if there were no fee. * Currently this is 10000 satoshis. */ public static final BigInteger REFERENCE_DEFAULT_MIN_TX_FEE = BigInteger.valueOf(10000); /** * Any standard (ie pay-to-address) output smaller than this value (in satoshis) will most likely be rejected by the network. * This is calculated by assuming a standard output will be 34 bytes, and then using the formula used in * {@link TransactionOutput#getMinNonDustValue(BigInteger)}. Currently it's 5460 satoshis. */ public static final BigInteger MIN_NONDUST_OUTPUT = BigInteger.valueOf(5460); // These are serialized in both bitcoin and java serialization. private long version; private ArrayList inputs; private ArrayList outputs; private long lockTime; // This is either the time the transaction was broadcast as measured from the local clock, or the time from the // block in which it was included. Note that this can be changed by re-orgs so the wallet may update this field. // Old serialized transactions don't have this field, thus null is valid. It is used for returning an ordered // list of transactions from a wallet, which is helpful for presenting to users. private Date updatedAt; // This is an in memory helper only. private transient Sha256Hash hash; // Data about how confirmed this tx is. Serialized, may be null. private TransactionConfidence confidence; // Records a map of which blocks the transaction has appeared in (keys) to an index within that block (values). // The "index" is not a real index, instead the values are only meaningful relative to each other. For example, // consider two transactions that appear in the same block, t1 and t2, where t2 spends an output of t1. Both // will have the same block hash as a key in their appearsInHashes, but the counter would be 1 and 2 respectively // regardless of where they actually appeared in the block. // // If this transaction is not stored in the wallet, appearsInHashes is null. private Map appearsInHashes; // Transactions can be encoded in a way that will use more bytes than is optimal // (due to VarInts having multiple encodings) // MAX_BLOCK_SIZE must be compared to the optimal encoding, not the actual encoding, so when parsing, we keep track // of the size of the ideal encoding in addition to the actual message size (which Message needs) so that Blocks // can properly keep track of optimal encoded size private transient int optimalEncodingMessageSize; /** * This enum describes the underlying reason the transaction was created. It's useful for rendering wallet GUIs * more appropriately. */ public enum Purpose { /** Used when the purpose of a transaction is genuinely unknown. */ UNKNOWN, /** Transaction created to satisfy a user payment request. */ USER_PAYMENT, /** Transaction automatically created and broadcast in order to reallocate money from old to new keys. */ KEY_ROTATION, // In future: de/refragmentation, privacy boosting/mixing, child-pays-for-parent fees, etc. } private Purpose purpose = Purpose.UNKNOWN; public Transaction(NetworkParameters params) { super(params); version = 1; inputs = new ArrayList(); outputs = new ArrayList(); // We don't initialize appearsIn deliberately as it's only useful for transactions stored in the wallet. length = 8; // 8 for std fields } public Transaction(NetworkParameters params, int version, Sha256Hash hash) { super(params); this.version = version & ((1L<<32) - 1); // this field is unsigned - remove any sign extension inputs = new ArrayList(); outputs = new ArrayList(); this.hash = hash; // We don't initialize appearsIn deliberately as it's only useful for transactions stored in the wallet. length = 8; //8 for std fields } /** * 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() } /** * Creates a transaction by reading payload starting from offset bytes in. Length of a transaction is fixed. * @param params NetworkParameters object. * @param msg Bitcoin protocol formatted byte array containing message content. * @param offset The location of the first msg byte within the array. * @param parseLazy Whether to perform a full parse immediately or delay until a read is requested. * @param parseRetain Whether to retain the backing byte array for quick reserialization. * If true and the backing byte array is invalidated due to modification of a field then * the cached bytes may be repopulated and retained if the message is serialized again in the future. * @param length The length of message if known. Usually this is provided when deserializing of the wire * as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH * @throws ProtocolException */ public Transaction(NetworkParameters params, byte[] msg, int offset, @Nullable Message parent, boolean parseLazy, boolean parseRetain, int length) throws ProtocolException { super(params, msg, offset, parent, parseLazy, parseRetain, length); } /** * Creates a transaction by reading payload starting from offset bytes in. Length of a transaction is fixed. */ public Transaction(NetworkParameters params, byte[] msg, @Nullable Message parent, boolean parseLazy, boolean parseRetain, int length) throws ProtocolException { super(params, msg, 0, parent, parseLazy, parseRetain, length); } /** * 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; } /** * Used by BitcoinSerializer. The serializer has to calculate a hash for checksumming so to * avoid wasting the considerable effort a set method is provided so the serializer can set it. * * No verification is performed on this hash. */ void setHash(Sha256Hash hash) { this.hash = hash; } public String getHashAsString() { return getHash().toString(); } /** * 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. */ BigInteger getValueSentToMe(Wallet wallet, boolean includeSpent) { maybeParse(); // This is tested in WalletTest. BigInteger v = BigInteger.ZERO; for (TransactionOutput o : outputs) { if (!o.isMineOrWatched(wallet)) continue; if (!includeSpent && !o.isAvailableForSpending()) continue; v = v.add(o.getValue()); } return v; } /* * If isSpent - check that all my outputs spent, otherwise check that there at least * one unspent. */ boolean isConsistent(Wallet wallet, boolean isSpent) { boolean isActuallySpent = true; for (TransactionOutput o : outputs) { if (o.isAvailableForSpending()) { if (o.isMineOrWatched(wallet)) isActuallySpent = false; if (o.getSpentBy() != null) { log.error("isAvailableForSpending != spentBy"); return false; } } else { if (o.getSpentBy() == null) { log.error("isAvailableForSpending != spentBy"); return false; } } } return isActuallySpent == isSpent; } /** * 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 map of block [hashes] which contain the transaction mapped to relativity counters, 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. */ @Nullable public Map getAppearsInHashes() { return appearsInHashes != null ? ImmutableMap.copyOf(appearsInHashes) : null; } /** * Convenience wrapper around getConfidence().getConfidenceType() * @return true if this transaction hasn't been seen in any block yet. */ public boolean isPending() { return getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.PENDING; } /** *

Puts the given block in the internal 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.

* *

If there is a re-org this will be called once for each block that was previously seen, to update which block * is the best chain. The best chain block is guaranteed to be called last. So this must be idempotent.

* *

Sets updatedAt to be the earliest valid block time where this tx was seen.

* * @param block The {@link StoredBlock} in which the transaction has appeared. * @param bestChain whether to set the updatedAt timestamp from the block header (only if not already set) * @param relativityOffset A number that disambiguates the order of transactions within a block. */ public void setBlockAppearance(StoredBlock block, boolean bestChain, int relativityOffset) { long blockTime = block.getHeader().getTimeSeconds() * 1000; if (bestChain && (updatedAt == null || updatedAt.getTime() == 0 || updatedAt.getTime() > blockTime)) { updatedAt = new Date(blockTime); } addBlockAppearance(block.getHeader().getHash(), relativityOffset); if (bestChain) { TransactionConfidence transactionConfidence = getConfidence(); // Reset the work done. try { transactionConfidence.setWorkDone(block.getHeader().getWork()); } catch (VerificationException e) { throw new RuntimeException(e); // Cannot happen. } // This sets type to BUILDING and depth to one. transactionConfidence.setAppearedAtChainHeight(block.getHeight()); } } public void addBlockAppearance(final Sha256Hash blockHash, int relativityOffset) { if (appearsInHashes == null) { // TODO: This could be a lot more memory efficient as we'll typically only store one element. appearsInHashes = new TreeMap(); } appearsInHashes.put(blockHash, relativityOffset); } /** * 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 { maybeParse(); // This is tested in WalletTest. BigInteger v = BigInteger.ZERO; for (TransactionInput input : inputs) { // This input is taking value from a 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; // The connected output may be the change to the sender of a previous input sent to this wallet. In this // case we ignore it. if (!connected.isMineOrWatched(wallet)) continue; v = v.add(connected.getValue()); } return v; } /** * Returns the difference of {@link Transaction#getValueSentFromMe(Wallet)} and {@link Transaction#getValueSentToMe(Wallet)}. */ public BigInteger getValue(Wallet wallet) throws ScriptException { return getValueSentToMe(wallet).subtract(getValueSentFromMe(wallet)); } boolean disconnectInputs() { boolean disconnected = false; maybeParse(); for (TransactionInput input : inputs) { disconnected |= input.disconnect(); } return disconnected; } /** * Returns true if every output is marked as spent. */ public boolean isEveryOutputSpent() { maybeParse(); for (TransactionOutput output : outputs) { if (output.isAvailableForSpending()) return false; } return true; } /** * Returns true if any of the outputs is marked as spent. */ public boolean isAnyOutputSpent() { maybeParse(); for (TransactionOutput output : outputs) { if (!output.isAvailableForSpending()) return true; } return false; } /** * Returns false if this transaction has at least one output that is owned by the given wallet and unspent, true * otherwise. */ public boolean isEveryOwnedOutputSpent(Wallet wallet) { maybeParse(); for (TransactionOutput output : outputs) { if (output.isAvailableForSpending() && output.isMineOrWatched(wallet)) return false; } return true; } /** * Returns the earliest time at which the transaction was seen (broadcast or included into the chain), * or the epoch if that information isn't available. */ public Date getUpdateTime() { if (updatedAt == null) { // Older wallets did not store this field. Set to the epoch. updatedAt = new Date(0); } return updatedAt; } public void setUpdateTime(Date updatedAt) { this.updatedAt = updatedAt; } /** * 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 } public static final byte SIGHASH_ANYONECANPAY_VALUE = (byte) 0x80; protected void unCache() { super.unCache(); hash = null; } protected void parseLite() throws ProtocolException { //skip this if the length has been provided i.e. the tx is not part of a block if (parseLazy && length == UNKNOWN_LENGTH) { //If length hasn't been provided this tx is probably contained within a block. //In parseRetain mode the block needs to know how long the transaction is //unfortunately this requires a fairly deep (though not total) parse. //This is due to the fact that transactions in the block's list do not include a //size header and inputs/outputs are also variable length due the contained //script so each must be instantiated so the scriptlength varint can be read //to calculate total length of the transaction. //We will still persist will this semi-light parsing because getting the lengths //of the various components gains us the ability to cache the backing bytearrays //so that only those subcomponents that have changed will need to be reserialized. //parse(); //parsed = true; length = calcLength(bytes, offset); cursor = offset + length; } } protected static int calcLength(byte[] buf, int offset) { VarInt varint; // jump past version (uint32) int cursor = offset + 4; int i; long scriptLen; varint = new VarInt(buf, cursor); long txInCount = varint.value; cursor += varint.getOriginalSizeInBytes(); for (i = 0; i < txInCount; i++) { // 36 = length of previous_outpoint cursor += 36; varint = new VarInt(buf, cursor); scriptLen = varint.value; // 4 = length of sequence field (unint32) cursor += scriptLen + 4 + varint.getOriginalSizeInBytes(); } varint = new VarInt(buf, cursor); long txOutCount = varint.value; cursor += varint.getOriginalSizeInBytes(); for (i = 0; i < txOutCount; i++) { // 8 = length of tx value field (uint64) cursor += 8; varint = new VarInt(buf, cursor); scriptLen = varint.value; cursor += scriptLen + varint.getOriginalSizeInBytes(); } // 4 = length of lock_time field (uint32) return cursor - offset + 4; } void parse() throws ProtocolException { if (parsed) return; cursor = offset; version = readUint32(); optimalEncodingMessageSize = 4; // First come the inputs. long numInputs = readVarInt(); optimalEncodingMessageSize += VarInt.sizeOf(numInputs); inputs = new ArrayList((int) numInputs); for (long i = 0; i < numInputs; i++) { TransactionInput input = new TransactionInput(params, this, bytes, cursor, parseLazy, parseRetain); inputs.add(input); long scriptLen = readVarInt(TransactionOutPoint.MESSAGE_LENGTH); optimalEncodingMessageSize += TransactionOutPoint.MESSAGE_LENGTH + VarInt.sizeOf(scriptLen) + scriptLen + 4; cursor += scriptLen + 4; } // Now the outputs long numOutputs = readVarInt(); optimalEncodingMessageSize += VarInt.sizeOf(numOutputs); outputs = new ArrayList((int) numOutputs); for (long i = 0; i < numOutputs; i++) { TransactionOutput output = new TransactionOutput(params, this, bytes, cursor, parseLazy, parseRetain); outputs.add(output); long scriptLen = readVarInt(8); optimalEncodingMessageSize += 8 + VarInt.sizeOf(scriptLen) + scriptLen; cursor += scriptLen; } lockTime = readUint32(); optimalEncodingMessageSize += 4; length = cursor - offset; } public int getOptimalEncodingMessageSize() { if (optimalEncodingMessageSize != 0) return optimalEncodingMessageSize; maybeParse(); if (optimalEncodingMessageSize != 0) return optimalEncodingMessageSize; optimalEncodingMessageSize = getMessageSize(); return optimalEncodingMessageSize; } /** * 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() { maybeParse(); return inputs.size() == 1 && inputs.get(0).isCoinBase(); } /** * A transaction is mature if it is either a building coinbase tx that is as deep or deeper than the required coinbase depth, or a non-coinbase tx. */ public boolean isMature() { if (!isCoinBase()) return true; if (getConfidence().getConfidenceType() != ConfidenceType.BUILDING) return false; return getConfidence().getDepthInBlocks() >= params.getSpendableCoinbaseDepth(); } public String toString() { return toString(null); } /** * A human readable version of the transaction useful for debugging. The format is not guaranteed to be stable. * @param chain If provided, will be used to estimate lock times (if set). Can be null. */ public String toString(@Nullable AbstractBlockChain chain) { // Basic info about the tx. StringBuilder s = new StringBuilder(); s.append(String.format(" %s: %s%n", getHashAsString(), getConfidence())); if (isTimeLocked()) { String time; if (lockTime < LOCKTIME_THRESHOLD) { time = "block " + lockTime; if (chain != null) { time = time + " (estimated to be reached at " + chain.estimateBlockTime((int)lockTime).toString() + ")"; } } else { time = new Date(lockTime*1000).toString(); } s.append(String.format(" time locked until %s%n", time)); } if (inputs.size() == 0) { s.append(String.format(" INCOMPLETE: No inputs!%n")); return s.toString(); } if (isCoinBase()) { String script; String script2; try { script = inputs.get(0).getScriptSig().toString(); script2 = outputs.get(0).getScriptPubKey().toString(); } catch (ScriptException e) { script = "???"; script2 = "???"; } s.append(" == COINBASE TXN (scriptSig " + script + ") (scriptPubKey " + script2 + ")\n"); return s.toString(); } for (TransactionInput in : inputs) { s.append(" "); s.append("in "); try { Script scriptSig = in.getScriptSig(); s.append(scriptSig); s.append(" / "); s.append(in.getOutpoint().toString()); } catch (Exception e) { s.append("[exception: ").append(e.getMessage()).append("]"); } s.append(String.format("%n")); } for (TransactionOutput out : outputs) { s.append(" "); s.append("out "); try { Script scriptPubKey = out.getScriptPubKey(); s.append(scriptPubKey); s.append(" "); s.append(bitcoinValueToFriendlyString(out.getValue())); s.append(" BTC"); if (!out.isAvailableForSpending()) { s.append(" Spent"); } if (out.getSpentBy() != null) { s.append(" by "); s.append(out.getSpentBy().getParentTransaction().getHashAsString()); } } catch (Exception e) { s.append("[exception: ").append(e.getMessage()).append("]"); } s.append(String.format("%n")); } return s.toString(); } /** * Removes all the inputs from this transaction. * Note that this also invalidates the length attribute */ public void clearInputs() { unCache(); for (TransactionInput input : inputs) { input.setParent(null); } inputs.clear(); // You wanted to reserialize, right? this.length = this.bitcoinSerialize().length; } /** * 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. Returns the newly created input. */ public TransactionInput addInput(TransactionOutput from) { return addInput(new TransactionInput(params, this, from)); } /** * Adds an input directly, with no checking that it's valid. Returns the new input. */ public TransactionInput addInput(TransactionInput input) { unCache(); input.setParent(this); inputs.add(input); adjustLength(inputs.size(), input.length); return input; } /** * Adds a new and fully signed input for the given parameters. Note that this method is not thread safe * and requires external synchronization. Please refer to general documentation on Bitcoin scripting and contracts * to understand the values of sigHash and anyoneCanPay: otherwise you can use the other form of this method * that sets them to typical defaults. * * @throws ScriptException if the scriptPubKey is not a pay to address or pay to pubkey script. */ public TransactionInput addSignedInput(TransactionOutPoint prevOut, Script scriptPubKey, ECKey sigKey, SigHash sigHash, boolean anyoneCanPay) throws ScriptException { TransactionInput input = new TransactionInput(params, this, new byte[]{}, prevOut); addInput(input); Sha256Hash hash = hashForSignature(inputs.size() - 1, scriptPubKey, sigHash, anyoneCanPay); ECKey.ECDSASignature ecSig = sigKey.sign(hash); TransactionSignature txSig = new TransactionSignature(ecSig, sigHash, anyoneCanPay); if (scriptPubKey.isSentToRawPubKey()) input.setScriptSig(ScriptBuilder.createInputScript(txSig)); else if (scriptPubKey.isSentToAddress()) input.setScriptSig(ScriptBuilder.createInputScript(txSig, sigKey)); else throw new ScriptException("Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey); return input; } /** * Same as {@link #addSignedInput(TransactionOutPoint, com.google.bitcoin.script.Script, ECKey, com.google.bitcoin.core.Transaction.SigHash, boolean)} * but defaults to {@link SigHash#ALL} and "false" for the anyoneCanPay flag. This is normally what you want. */ public TransactionInput addSignedInput(TransactionOutPoint prevOut, Script scriptPubKey, ECKey sigKey) throws ScriptException { return addSignedInput(prevOut, scriptPubKey, sigKey, SigHash.ALL, false); } /** * Removes all the inputs from this transaction. * Note that this also invalidates the length attribute */ public void clearOutputs() { unCache(); for (TransactionOutput output : outputs) { output.setParent(null); } outputs.clear(); // You wanted to reserialize, right? this.length = this.bitcoinSerialize().length; } /** * Adds the given output to this transaction. The output must be completely initialized. Returns the given output. */ public TransactionOutput addOutput(TransactionOutput to) { unCache(); to.setParent(this); outputs.add(to); adjustLength(outputs.size(), to.length); return to; } /** * Creates an output based on the given address and value, adds it to this transaction, and returns the new output. */ public TransactionOutput addOutput(BigInteger value, Address address) { return addOutput(new TransactionOutput(params, this, value, address)); } /** * Creates an output that pays to the given pubkey directly (no address) with the given value, adds it to this * transaction, and returns the new output. */ public TransactionOutput addOutput(BigInteger value, ECKey pubkey) { return addOutput(new TransactionOutput(params, this, value, pubkey)); } /** * Creates an output that pays to the given script. The address and key forms are specialisations of this method, * you won't normally need to use it unless you're doing unusual things. */ public TransactionOutput addOutput(BigInteger value, Script script) { return addOutput(new TransactionOutput(params, this, value, script.getProgram())); } /** * 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.

*

* 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. */ public synchronized void signInputs(SigHash hashType, Wallet wallet) throws ScriptException { signInputs(hashType, wallet, null); } /** *

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.

* * @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. * @param aesKey The AES key to use to decrypt the key before signing. Null if no decryption is required. */ public synchronized void signInputs(SigHash hashType, Wallet wallet, @Nullable KeyParameter aesKey) throws ScriptException { checkState(inputs.size() > 0); checkState(outputs.size() > 0); // I don't currently have an easy way to test other modes work, as the official client does not use them. checkArgument(hashType == SigHash.ALL, "Only SIGHASH_ALL is currently supported"); // 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. TransactionSignature[] signatures = new TransactionSignature[inputs.size()]; ECKey[] signingKeys = new ECKey[inputs.size()]; for (int i = 0; i < inputs.size(); i++) { TransactionInput input = inputs.get(i); // We don't have the connected output, we assume it was signed already and move on if (input.getOutpoint().getConnectedOutput() == null) { log.warn("Missing connected output, assuming input {} is already signed.", i); continue; } try { // We assume if its already signed, its hopefully got a SIGHASH type that will not invalidate when // we sign missing pieces (to check this would require either assuming any signatures are signing // standard output types or a way to get processed signatures out of script execution) input.getScriptSig().correctlySpends(this, i, input.getOutpoint().getConnectedOutput().getScriptPubKey(), true); log.warn("Input {} already correctly spends output, assuming SIGHASH type used will be safe and skipping signing.", i); continue; } catch (ScriptException e) { // Expected. } if (input.getScriptBytes().length != 0) log.warn("Re-signing an already signed transaction! Be sure this is what you want."); // Find the signing key we'll need to use. ECKey key = input.getOutpoint().getConnectedKey(wallet); // This assert should never fire. If it does, it means the wallet is inconsistent. checkNotNull(key, "Transaction exists in wallet that we cannot redeem: %s", input.getOutpoint().getHash()); // 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[] connectedPubKeyScript = input.getOutpoint().getConnectedPubKeyScript(); if (key.hasPrivKey() || key.isEncrypted()) { signatures[i] = calculateSignature(i, key, aesKey, connectedPubKeyScript, hashType, anyoneCanPay); } else { // Create a dummy signature to ensure the transaction is of the correct size when we try to ensure // the right fee-per-kb is attached. If the wallet doesn't have the privkey, the user is assumed to // be doing something special and that they will replace the dummy signature with a real one later. signatures[i] = TransactionSignature.dummy(); } } // Now we have calculated each signature, go through and create the scripts. Reminder: the script consists: // 1) For pay-to-address outputs: a signature (over a hash of the simplified transaction) and the complete // public key needed to sign for the connected output. The output script checks the provided pubkey hashes // to the address and then checks the signature. // 2) For pay-to-key outputs: just a signature. for (int i = 0; i < inputs.size(); i++) { if (signatures[i] == null) continue; TransactionInput input = inputs.get(i); final TransactionOutput connectedOutput = input.getOutpoint().getConnectedOutput(); checkNotNull(connectedOutput); // Quiet static analysis: is never null here but cannot be statically proven Script scriptPubKey = connectedOutput.getScriptPubKey(); if (scriptPubKey.isSentToAddress()) { input.setScriptSig(ScriptBuilder.createInputScript(signatures[i], signingKeys[i])); } else if (scriptPubKey.isSentToRawPubKey()) { input.setScriptSig(ScriptBuilder.createInputScript(signatures[i])); } else { // Should be unreachable - if we don't recognize the type of script we're trying to sign for, we should // have failed above when fetching the key to sign with. throw new RuntimeException("Do not understand script type: " + scriptPubKey); } } // Every input is now complete. } /** * Calculates a signature that is valid for being inserted into the input at the given position. This is simply * a wrapper around calling {@link Transaction#hashForSignature(int, byte[], com.google.bitcoin.core.Transaction.SigHash, boolean)} * followed by {@link ECKey#sign(Sha256Hash, org.spongycastle.crypto.params.KeyParameter)} and then returning * a new {@link TransactionSignature}. * * @param inputIndex Which input to calculate the signature for, as an index. * @param key The private key used to calculate the signature. * @param aesKey If not null, this will be used to decrypt the key. * @param connectedPubKeyScript Byte-exact contents of the scriptPubKey that is being satisified. * @param hashType Signing mode, see the enum for documentation. * @param anyoneCanPay Signing mode, see the SigHash enum for documentation. * @return A newly calculated signature object that wraps the r, s and sighash components. */ public synchronized TransactionSignature calculateSignature(int inputIndex, ECKey key, @Nullable KeyParameter aesKey, byte[] connectedPubKeyScript, SigHash hashType, boolean anyoneCanPay) { Sha256Hash hash = hashForSignature(inputIndex, connectedPubKeyScript, hashType, anyoneCanPay); return new TransactionSignature(key.sign(hash, aesKey), hashType, anyoneCanPay); } /** * Calculates a signature that is valid for being inserted into the input at the given position. This is simply * a wrapper around calling {@link Transaction#hashForSignature(int, byte[], com.google.bitcoin.core.Transaction.SigHash, boolean)} * followed by {@link ECKey#sign(Sha256Hash)} and then returning a new {@link TransactionSignature}. * * @param inputIndex Which input to calculate the signature for, as an index. * @param key The private key used to calculate the signature. * @param connectedPubKeyScript The scriptPubKey that is being satisified. * @param hashType Signing mode, see the enum for documentation. * @param anyoneCanPay Signing mode, see the SigHash enum for documentation. * @return A newly calculated signature object that wraps the r, s and sighash components. */ public synchronized TransactionSignature calculateSignature(int inputIndex, ECKey key, Script connectedPubKeyScript, SigHash hashType, boolean anyoneCanPay) { Sha256Hash hash = hashForSignature(inputIndex, connectedPubKeyScript.getProgram(), hashType, anyoneCanPay); return new TransactionSignature(key.sign(hash), hashType, anyoneCanPay); } /** *

Calculates a signature hash, that is, a hash of a simplified form of the transaction. How exactly the transaction * is simplified is specified by the type and anyoneCanPay parameters.

* *

You don't normally ever need to call this yourself. It will become more useful in future as the contracts * features of Bitcoin are developed.

* * @param inputIndex input the signature is being calculated for. Tx signatures are always relative to an input. * @param connectedScript the bytes that should be in the given input during signing. * @param type Should be SigHash.ALL * @param anyoneCanPay should be false. */ public synchronized Sha256Hash hashForSignature(int inputIndex, byte[] connectedScript, SigHash type, boolean anyoneCanPay) { byte sigHashType = (byte) TransactionSignature.calcSigHashValue(type, anyoneCanPay); return hashForSignature(inputIndex, connectedScript, sigHashType); } /** *

Calculates a signature hash, that is, a hash of a simplified form of the transaction. How exactly the transaction * is simplified is specified by the type and anyoneCanPay parameters.

* *

You don't normally ever need to call this yourself. It will become more useful in future as the contracts * features of Bitcoin are developed.

* * @param inputIndex input the signature is being calculated for. Tx signatures are always relative to an input. * @param connectedScript the script that should be in the given input during signing. * @param type Should be SigHash.ALL * @param anyoneCanPay should be false. */ public synchronized Sha256Hash hashForSignature(int inputIndex, Script connectedScript, SigHash type, boolean anyoneCanPay) { int sigHash = TransactionSignature.calcSigHashValue(type, anyoneCanPay); return hashForSignature(inputIndex, connectedScript.getProgram(), (byte) sigHash); } /** * This is required for signatures which use a sigHashType which cannot be represented using SigHash and anyoneCanPay * See transaction c99c49da4c38af669dea436d3e73780dfdb6c1ecf9958baa52960e8baee30e73, which has sigHashType 0 */ public synchronized Sha256Hash hashForSignature(int inputIndex, byte[] connectedScript, byte sigHashType) { // The SIGHASH flags are used in the design of contracts, please see this page for a further understanding of // the purposes of the code in this method: // // https://en.bitcoin.it/wiki/Contracts try { // Store all the input scripts and clear them in preparation for signing. If we're signing a fresh // transaction that step isn't very helpful, but it doesn't add much cost relative to the actual // EC math so we'll do it anyway. // // Also store the input sequence numbers in case we are clearing them with SigHash.NONE/SINGLE byte[][] inputScripts = new byte[inputs.size()][]; long[] inputSequenceNumbers = new long[inputs.size()]; for (int i = 0; i < inputs.size(); i++) { inputScripts[i] = inputs.get(i).getScriptBytes(); inputSequenceNumbers[i] = inputs.get(i).getSequenceNumber(); inputs.get(i).setScriptBytes(TransactionInput.EMPTY_ARRAY); } // This step has no purpose beyond being synchronized with the reference clients bugs. OP_CODESEPARATOR // is a legacy holdover from a previous, broken design of executing scripts that shipped in Bitcoin 0.1. // It was seriously flawed and would have let anyone take anyone elses money. Later versions switched to // the design we use today where scripts are executed independently but share a stack. This left the // OP_CODESEPARATOR instruction having no purpose as it was only meant to be used internally, not actually // ever put into scripts. Deleting OP_CODESEPARATOR is a step that should never be required but if we don't // do it, we could split off the main chain. connectedScript = Script.removeAllInstancesOfOp(connectedScript, ScriptOpCodes.OP_CODESEPARATOR); // Set the input to the script of its output. Satoshi does this but the step has no obvious purpose as // the signature covers the hash of the prevout transaction which obviously includes the output script // already. Perhaps it felt safer to him in some way, or is another leftover from how the code was written. TransactionInput input = inputs.get(inputIndex); input.setScriptBytes(connectedScript); ArrayList outputs = this.outputs; if ((sigHashType & 0x1f) == (SigHash.NONE.ordinal() + 1)) { // SIGHASH_NONE means no outputs are signed at all - the signature is effectively for a "blank cheque". this.outputs = new ArrayList(0); // The signature isn't broken by new versions of the transaction issued by other parties. for (int i = 0; i < inputs.size(); i++) if (i != inputIndex) inputs.get(i).setSequenceNumber(0); } else if ((sigHashType & 0x1f) == (SigHash.SINGLE.ordinal() + 1)) { // SIGHASH_SINGLE means only sign the output at the same index as the input (ie, my output). if (inputIndex >= this.outputs.size()) { // The input index is beyond the number of outputs, it's a buggy signature made by a broken // Bitcoin implementation. The reference client also contains a bug in handling this case: // any transaction output that is signed in this case will result in both the signed output // and any future outputs to this public key being steal-able by anyone who has // the resulting signature and the public key (both of which are part of the signed tx input). // Put the transaction back to how we found it. // // TODO: Only allow this to happen if we are checking a signature, not signing a transactions for (int i = 0; i < inputs.size(); i++) { inputs.get(i).setScriptBytes(inputScripts[i]); inputs.get(i).setSequenceNumber(inputSequenceNumbers[i]); } this.outputs = outputs; // Satoshis bug is that SignatureHash was supposed to return a hash and on this codepath it // actually returns the constant "1" to indicate an error, which is never checked for. Oops. return new Sha256Hash("0100000000000000000000000000000000000000000000000000000000000000"); } // In SIGHASH_SINGLE the outputs after the matching input index are deleted, and the outputs before // that position are "nulled out". Unintuitively, the value in a "null" transaction is set to -1. this.outputs = new ArrayList(this.outputs.subList(0, inputIndex + 1)); for (int i = 0; i < inputIndex; i++) this.outputs.set(i, new TransactionOutput(params, this, NEGATIVE_ONE, new byte[] {})); // The signature isn't broken by new versions of the transaction issued by other parties. for (int i = 0; i < inputs.size(); i++) if (i != inputIndex) inputs.get(i).setSequenceNumber(0); } ArrayList inputs = this.inputs; if ((sigHashType & SIGHASH_ANYONECANPAY_VALUE) == SIGHASH_ANYONECANPAY_VALUE) { // SIGHASH_ANYONECANPAY means the signature in the input is not broken by changes/additions/removals // of other inputs. For example, this is useful for building assurance contracts. this.inputs = new ArrayList(); this.inputs.add(input); } ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(length == UNKNOWN_LENGTH ? 256 : length + 4); bitcoinSerialize(bos); // We also have to write a hash type (sigHashType is actually an unsigned char) uint32ToByteStreamLE(0x000000ff & sigHashType, 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. Sha256Hash hash = new Sha256Hash(doubleDigest(bos.toByteArray())); bos.close(); // Put the transaction back to how we found it. this.inputs = inputs; for (int i = 0; i < inputs.size(); i++) { inputs.get(i).setScriptBytes(inputScripts[i]); inputs.get(i).setSequenceNumber(inputSequenceNumbers[i]); } this.outputs = outputs; return hash; } catch (IOException e) { throw new RuntimeException(e); // Cannot happen. } } @Override protected void bitcoinSerializeToStream(OutputStream stream) throws IOException { uint32ToByteStreamLE(version, stream); stream.write(new VarInt(inputs.size()).encode()); for (TransactionInput in : inputs) in.bitcoinSerialize(stream); stream.write(new VarInt(outputs.size()).encode()); for (TransactionOutput out : outputs) out.bitcoinSerialize(stream); uint32ToByteStreamLE(lockTime, stream); } /** * Transactions can have an associated lock time, specified either as a block height or in seconds since the * UNIX epoch. A transaction is not allowed to be confirmed by miners until the lock time is reached, and * since Bitcoin 0.8+ a transaction that did not end its lock period (non final) is considered to be non * standard and won't be relayed or included in the memory pool either. */ public long getLockTime() { maybeParse(); return lockTime; } /** * Transactions can have an associated lock time, specified either as a block height or in seconds since the * UNIX epoch. A transaction is not allowed to be confirmed by miners until the lock time is reached, and * since Bitcoin 0.8+ a transaction that did not end its lock period (non final) is considered to be non * standard and won't be relayed or included in the memory pool either. */ public void setLockTime(long lockTime) { unCache(); // TODO: Consider checking that at least one input has a non-final sequence number. this.lockTime = lockTime; } /** * @return the version */ public long getVersion() { maybeParse(); return version; } /** Returns an unmodifiable view of all inputs. */ public List getInputs() { maybeParse(); return Collections.unmodifiableList(inputs); } /** Returns an unmodifiable view of all outputs. */ public List getOutputs() { maybeParse(); return Collections.unmodifiableList(outputs); } /** @return the given transaction: same as getInputs().get(index). */ public TransactionInput getInput(int index) { maybeParse(); return inputs.get(index); } public TransactionOutput getOutput(int index) { maybeParse(); return outputs.get(index); } public synchronized TransactionConfidence getConfidence() { if (confidence == null) { confidence = new TransactionConfidence(this); } return confidence; } /** Check if the transaction has a known confidence */ public synchronized boolean hasConfidence() { return confidence != null && confidence.getConfidenceType() != TransactionConfidence.ConfidenceType.UNKNOWN; } @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 getHash().hashCode(); } /** * Ensure object is fully parsed before invoking java serialization. The backing byte array * is transient so if the object has parseLazy = true and hasn't invoked checkParse yet * then data will be lost during serialization. */ private void writeObject(ObjectOutputStream out) throws IOException { maybeParse(); out.defaultWriteObject(); } /** * Gets the count of regular SigOps in this transactions */ public int getSigOpCount() throws ScriptException { maybeParse(); int sigOps = 0; for (TransactionInput input : inputs) sigOps += Script.getSigOpCount(input.getScriptBytes()); for (TransactionOutput output : outputs) sigOps += Script.getSigOpCount(output.getScriptBytes()); return sigOps; } /** * Checks the transaction contents for sanity, in ways that can be done in a standalone manner. * Does not perform all checks on a transaction such as whether the inputs are already spent. * * @throws VerificationException */ public void verify() throws VerificationException { maybeParse(); if (inputs.size() == 0 || outputs.size() == 0) throw new VerificationException("Transaction had no inputs or no outputs."); if (this.getMessageSize() > Block.MAX_BLOCK_SIZE) throw new VerificationException("Transaction larger than MAX_BLOCK_SIZE"); BigInteger valueOut = BigInteger.ZERO; for (TransactionOutput output : outputs) { if (output.getValue().compareTo(BigInteger.ZERO) < 0) throw new VerificationException("Transaction output negative"); valueOut = valueOut.add(output.getValue()); } if (valueOut.compareTo(params.MAX_MONEY) > 0) throw new VerificationException("Total transaction output value greater than possible"); if (isCoinBase()) { if (inputs.get(0).getScriptBytes().length < 2 || inputs.get(0).getScriptBytes().length > 100) throw new VerificationException("Coinbase script size out of range"); } else { for (TransactionInput input : inputs) if (input.isCoinBase()) throw new VerificationException("Coinbase input as input in non-coinbase transaction"); } } /** *

A transaction is time locked if at least one of its inputs is non-final and it has a lock time

* *

To check if this transaction is final at a given height and time, see {@link Transaction#isFinal(int, long)} *

*/ public boolean isTimeLocked() { if (getLockTime() == 0) return false; for (TransactionInput input : getInputs()) if (input.hasSequence()) return true; return false; } /** *

Returns true if this transaction is considered finalized and can be placed in a block. Non-finalized * transactions won't be included by miners and can be replaced with newer versions using sequence numbers. * This is useful in certain types of contracts, such as * micropayment channels.

* *

Note that currently the replacement feature is disabled in the Satoshi client and will need to be * re-activated before this functionality is useful.

*/ public boolean isFinal(int height, long blockTimeSeconds) { long time = getLockTime(); if (time < (time < LOCKTIME_THRESHOLD ? height : blockTimeSeconds)) return true; if (!isTimeLocked()) return true; return false; } /** * Parses the string either as a whole number of blocks, or if it contains slashes as a YYYY/MM/DD format date * and returns the lock time in wire format. */ public static long parseLockTimeStr(String lockTimeStr) throws ParseException { if (lockTimeStr.indexOf("/") != -1) { SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd"); Date date = format.parse(lockTimeStr); return date.getTime() / 1000; } return Long.parseLong(lockTimeStr); } /** * Returns either the lock time as a date, if it was specified in seconds, or an estimate based on the time in * the current head block if it was specified as a block time. */ public Date estimateLockTime(AbstractBlockChain chain) { if (lockTime < LOCKTIME_THRESHOLD) return chain.estimateBlockTime((int)getLockTime()); else return new Date(getLockTime()*1000); } /** * Returns the purpose for which this transaction was created. See the javadoc for {@link Purpose} for more * information on the point of this field and what it can be. */ public Purpose getPurpose() { return purpose; } /** * Marks the transaction as being created for the given purpose. See the javadoc for {@link Purpose} for more * information on the point of this field and what it can be. */ public void setPurpose(Purpose purpose) { this.purpose = purpose; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy