Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| /** | |
| * Copyright 2011 Google Inc. | |
| * Copyright 2014 Andreas Schildbach | |
| * | |
| * 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.matthewmitchell.nubitsj.core; | |
| import com.matthewmitchell.nubitsj.core.TransactionConfidence.ConfidenceType; | |
| import com.matthewmitchell.nubitsj.crypto.TransactionSignature; | |
| import com.matthewmitchell.nubitsj.script.Script; | |
| import com.matthewmitchell.nubitsj.script.ScriptBuilder; | |
| import com.matthewmitchell.nubitsj.script.ScriptOpCodes; | |
| import com.matthewmitchell.nubitsj.utils.ExchangeRate; | |
| import com.matthewmitchell.nubitsj.wallet.WalletTransaction.Pool; | |
| import com.google.common.base.Joiner; | |
| import com.google.common.collect.ImmutableMap; | |
| import com.google.common.primitives.Ints; | |
| import com.google.common.primitives.Longs; | |
| import org.slf4j.Logger; | |
| import org.slf4j.LoggerFactory; | |
| import javax.annotation.Nullable; | |
| import java.io.*; | |
| import java.text.ParseException; | |
| import java.text.SimpleDateFormat; | |
| import java.util.*; | |
| import static com.matthewmitchell.nubitsj.core.Utils.*; | |
| import static com.google.common.base.Preconditions.checkState; | |
| /** | |
| * <p>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 Nubits C++ implementation.</p> | |
| * | |
| * <p>Transactions are the fundamental atoms of Nubits and have many powerful features. Read | |
| * <a href="http://code.google.com/p/bitcoinj/wiki/WorkingWithTransactions">"Working with transactions"</a> in the | |
| * documentation to learn more about how to use this class.</p> | |
| * | |
| * <p>All Nubits transactions are at risk of being reversed, though the risk is much less than with traditional payment | |
| * systems. Transactions have <i>confidence levels</i>, 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.</p> | |
| */ | |
| public class Transaction extends ChildMessage implements Serializable { | |
| /** | |
| * A comparator that can be used to sort transactions by their updateTime field. The ordering goes from most recent | |
| * into the past. | |
| */ | |
| public static final Comparator<Transaction> SORT_TX_BY_UPDATE_TIME = new Comparator<Transaction>() { | |
| @Override | |
| public int compare(final Transaction tx1, final Transaction tx2) { | |
| final long time1 = tx1.getUpdateTime().getTime(); | |
| final long time2 = tx2.getUpdateTime().getTime(); | |
| final int updateTimeComparison = -(Longs.compare(time1, time2)); | |
| //If time1==time2, compare by tx hash to make comparator consistent with equals | |
| return updateTimeComparison != 0 ? updateTimeComparison : tx1.getHash().compareTo(tx2.getHash()); | |
| } | |
| }; | |
| /** A comparator that can be used to sort transactions by their chain height. */ | |
| public static final Comparator<Transaction> SORT_TX_BY_HEIGHT = new Comparator<Transaction>() { | |
| @Override | |
| public int compare(final Transaction tx1, final Transaction tx2) { | |
| final int height1 = tx1.getConfidence().getAppearedAtChainHeight(); | |
| final int height2 = tx2.getConfidence().getAppearedAtChainHeight(); | |
| final int heightComparison = -(Ints.compare(height1, height2)); | |
| //If height1==height2, compare by tx hash to make comparator consistent with equals | |
| return heightComparison != 0 ? heightComparison : tx1.getHash().compareTo(tx2.getHash()); | |
| } | |
| }; | |
| 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 = 100000; | |
| /** | |
| * If fee is lower than this value (in satoshis), a default reference client will treat it as if there were no fee. | |
| */ | |
| public static final Coin REFERENCE_DEFAULT_MIN_TX_FEE = Coin.valueOf(100); | |
| /** | |
| * Any standard (ie pay-to-address) output smaller than this value (in satoshis) will be rejected by the network. | |
| * Currently it's 100 satoshis. | |
| */ | |
| public static final Coin MIN_OUTPUT_VALUE = Coin.valueOf(100); | |
| public final static byte NUSHARES = 'S'; | |
| public final static byte NUBITS = 'B'; | |
| // These are serialized in both nubits and java serialization. | |
| private long version; | |
| private long time; | |
| private ArrayList<TransactionInput> inputs; | |
| private ArrayList<TransactionOutput> outputs; | |
| private byte coinType; | |
| 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. | |
| @Nullable 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<Sha256Hash, Integer> 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, | |
| /** Transaction that uses up pledges to an assurance contract */ | |
| ASSURANCE_CONTRACT_CLAIM, | |
| /** Transaction that makes a pledge to an assurance contract. */ | |
| ASSURANCE_CONTRACT_PLEDGE, | |
| /** Send-to-self transaction that exists just to create an output of the right size we can pledge. */ | |
| ASSURANCE_CONTRACT_STUB | |
| // In future: de/refragmentation, privacy boosting/mixing, child-pays-for-parent fees, etc. | |
| } | |
| private Purpose purpose = Purpose.UNKNOWN; | |
| /** | |
| * This field can be used by applications to record the exchange rate that was valid when the transaction happened. | |
| * It's optional. | |
| */ | |
| @Nullable | |
| private ExchangeRate exchangeRate; | |
| /** | |
| * This field can be used to record the memo of the payment request that initiated the transaction. It's optional. | |
| */ | |
| @Nullable | |
| private String memo; | |
| public Transaction(NetworkParameters params) { | |
| super(params); | |
| version = 1; | |
| time = Utils.currentTimeSeconds(); | |
| inputs = new ArrayList<TransactionInput>(); | |
| outputs = new ArrayList<TransactionOutput>(); | |
| coinType = NUBITS; | |
| // 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, byte coinType) { | |
| this(params); | |
| this.coinType = coinType; | |
| } | |
| /** | |
| * 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 payload Nubits protocol formatted byte array containing message content. | |
| * @param offset The location of the first payload 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[] payload, int offset, @Nullable Message parent, boolean parseLazy, boolean parseRetain, int length) | |
| throws ProtocolException { | |
| super(params, payload, offset, parent, parseLazy, parseRetain, length); | |
| } | |
| /** | |
| * Creates a transaction by reading payload. Length of a transaction is fixed. | |
| */ | |
| public Transaction(NetworkParameters params, byte[] payload, @Nullable Message parent, boolean parseLazy, boolean parseRetain, int length) | |
| throws ProtocolException { | |
| super(params, payload, 0, parent, parseLazy, parseRetain, length); | |
| } | |
| /** | |
| * Returns the transaction hash as you see them in the block explorer. | |
| */ | |
| @Override | |
| public Sha256Hash getHash() { | |
| if (hash == null) { | |
| byte[] bits = nubitsSerialize(); | |
| hash = Sha256Hash.wrapReversed(Sha256Hash.hashTwice(bits)); | |
| } | |
| return hash; | |
| } | |
| /** | |
| * Used by NubitsSerializer. 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. | |
| */ | |
| Coin getValueSentToMe(TransactionBag transactionBag, boolean includeSpent) { | |
| maybeParse(); | |
| // This is tested in WalletTest. | |
| Coin v = Coin.ZERO; | |
| for (TransactionOutput o : outputs) { | |
| if (!o.isMineOrWatched(transactionBag)) 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(TransactionBag transactionBag, boolean isSpent) { | |
| boolean isActuallySpent = true; | |
| for (TransactionOutput o : outputs) { | |
| if (o.isAvailableForSpending()) { | |
| if (o.isMineOrWatched(transactionBag)) 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 Coin getValueSentToMe(TransactionBag transactionBag) { | |
| return getValueSentToMe(transactionBag, 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<Sha256Hash, Integer> 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; | |
| } | |
| /** | |
| * <p>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.</p> | |
| * | |
| * <p>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.</p> | |
| * | |
| * <p>Sets updatedAt to be the earliest valid block time where this tx was seen.</p> | |
| * | |
| * @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(); | |
| // 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<Sha256Hash, Integer>(); | |
| } | |
| 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 of the inputs that are spending coins with keys in the wallet | |
| */ | |
| public Coin getValueSentFromMe(TransactionBag wallet) throws ScriptException { | |
| maybeParse(); | |
| // This is tested in WalletTest. | |
| Coin v = Coin.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.getTransactionPool(Pool.UNSPENT)); | |
| if (connected == null) | |
| connected = input.getConnectedOutput(wallet.getTransactionPool(Pool.SPENT)); | |
| if (connected == null) | |
| connected = input.getConnectedOutput(wallet.getTransactionPool(Pool.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; | |
| } | |
| @Nullable private Coin cachedValue; | |
| @Nullable private TransactionBag cachedForBag; | |
| /** | |
| * Returns the difference of {@link Transaction#getValueSentToMe(TransactionBag)} and {@link Transaction#getValueSentFromMe(TransactionBag)}. | |
| */ | |
| public Coin getValue(TransactionBag wallet) throws ScriptException { | |
| // FIXME: TEMP PERF HACK FOR ANDROID - this crap can go away once we have a real payments API. | |
| boolean isAndroid = Utils.isAndroidRuntime(); | |
| if (isAndroid && cachedValue != null && cachedForBag == wallet) | |
| return cachedValue; | |
| Coin result = getValueSentToMe(wallet).subtract(getValueSentFromMe(wallet)); | |
| if (isAndroid) { | |
| cachedValue = result; | |
| cachedForBag = wallet; | |
| } | |
| return result; | |
| } | |
| /** | |
| * The transaction fee is the difference of the value of all inputs and the value of all outputs. Currently, the fee | |
| * can only be determined for transactions created by us. | |
| * | |
| * @return fee, or null if it cannot be determined | |
| */ | |
| public Coin getFee() { | |
| Coin fee = Coin.ZERO; | |
| for (TransactionInput input : inputs) { | |
| if (input.getValue() == null) | |
| return null; | |
| fee = fee.add(input.getValue()); | |
| } | |
| for (TransactionOutput output : outputs) { | |
| fee = fee.subtract(output.getValue()); | |
| } | |
| return fee; | |
| } | |
| /** | |
| * 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(TransactionBag transactionBag) { | |
| maybeParse(); | |
| for (TransactionOutput output : outputs) { | |
| if (output.isAvailableForSpending() && output.isMineOrWatched(transactionBag)) | |
| return false; | |
| } | |
| return true; | |
| } | |
| public long getTime() { | |
| return time; | |
| } | |
| public void setTime(long nTime) { | |
| this.time = nTime; | |
| } | |
| /** | |
| * 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. | |
| * <p/> | |
| * 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; | |
| @Override | |
| protected void unCache() { | |
| super.unCache(); | |
| hash = null; | |
| } | |
| @Override | |
| 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(payload, offset); | |
| cursor = offset + length; | |
| } | |
| } | |
| protected static int calcLength(byte[] buf, int offset) { | |
| VarInt varint; | |
| // jump past version (uint32) and time (uint32) | |
| int cursor = offset + 8; | |
| 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(); | |
| } | |
| // 5 = length of lock_time field (uint32) plus coin type | |
| return cursor - offset + 5; | |
| } | |
| @Override | |
| void parse() throws ProtocolException { | |
| if (parsed) | |
| return; | |
| cursor = offset; | |
| version = readUint32(); | |
| time = readUint32(); | |
| optimalEncodingMessageSize = 8; | |
| // First come the inputs. | |
| long numInputs = readVarInt(); | |
| optimalEncodingMessageSize += VarInt.sizeOf(numInputs); | |
| inputs = new ArrayList<TransactionInput>((int) numInputs); | |
| for (long i = 0; i < numInputs; i++) { | |
| TransactionInput input = new TransactionInput(params, this, payload, 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<TransactionOutput>((int) numOutputs); | |
| for (long i = 0; i < numOutputs; i++) { | |
| TransactionOutput output = new TransactionOutput(params, this, payload, cursor, parseLazy, parseRetain); | |
| outputs.add(output); | |
| long scriptLen = readVarInt(8); | |
| optimalEncodingMessageSize += 8 + VarInt.sizeOf(scriptLen) + scriptLen; | |
| cursor += scriptLen; | |
| } | |
| lockTime = readUint32(); | |
| coinType = payload[cursor++]; | |
| optimalEncodingMessageSize += 5; | |
| 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 Nubits 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(); | |
| } | |
| @Override | |
| 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(" ").append(getHashAsString()).append('\n'); | |
| if (hasConfidence()) | |
| s.append(" confidence: ").append(getConfidence()).append('\n'); | |
| 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(Locale.US, " time locked until %s%n", time)); | |
| } | |
| if (inputs.size() == 0) { | |
| s.append(String.format(Locale.US, " 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 ").append(script) | |
| .append(") (scriptPubKey ").append(script2).append(")\n"); | |
| return s.toString(); | |
| } | |
| for (TransactionInput in : inputs) { | |
| s.append(" "); | |
| s.append("in "); | |
| try { | |
| Script scriptSig = in.getScriptSig(); | |
| s.append(scriptSig); | |
| if (in.getValue() != null) | |
| s.append(" ").append(in.getValue().toFriendlyString()); | |
| s.append("\n "); | |
| s.append("outpoint:"); | |
| final TransactionOutPoint outpoint = in.getOutpoint(); | |
| s.append(outpoint.toString()); | |
| final TransactionOutput connectedOutput = outpoint.getConnectedOutput(); | |
| if (connectedOutput != null) { | |
| Script scriptPubKey = connectedOutput.getScriptPubKey(); | |
| if (scriptPubKey.isSentToAddress() || scriptPubKey.isPayToScriptHash()) { | |
| s.append(" hash160:"); | |
| s.append(Utils.HEX.encode(scriptPubKey.getPubKeyHash())); | |
| } | |
| } | |
| if (in.hasSequence()) | |
| s.append("\n (has sequence)"); | |
| } catch (Exception e) { | |
| s.append("[exception: ").append(e.getMessage()).append("]"); | |
| } | |
| s.append(String.format(Locale.US, "%n")); | |
| } | |
| for (TransactionOutput out : outputs) { | |
| s.append(" "); | |
| s.append("out "); | |
| try { | |
| Script scriptPubKey = out.getScriptPubKey(); | |
| s.append(scriptPubKey); | |
| s.append(" "); | |
| s.append(out.getValue().toFriendlyString()); | |
| 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(Locale.US, "%n")); | |
| } | |
| final Coin fee = getFee(); | |
| if (fee != null) { | |
| final int size = unsafeNubitsSerialize().length; | |
| s.append(" fee ").append(fee.multiply(1000).divide(size).toFriendlyString()).append("/kB, ") | |
| .append(fee.toFriendlyString()).append(" for ").append(size).append(" bytes\n"); | |
| } | |
| if (purpose != null) | |
| s.append(" prps ").append(purpose).append(String.format(Locale.US, "%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.nubitsSerialize().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 an input directly, with no checking that it's valid. Returns the new input. */ | |
| public TransactionInput addInput(Sha256Hash spendTxHash, long outputIndex, Script script) { | |
| return addInput(new TransactionInput(params, this, script.getProgram(), new TransactionOutPoint(params, outputIndex, spendTxHash))); | |
| } | |
| /** | |
| * Adds a new and fully signed input for the given parameters. Note that this method is <b>not</b> thread safe | |
| * and requires external synchronization. Please refer to general documentation on Nubits 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 { | |
| // Verify the API user didn't try to do operations out of order. | |
| checkState(!outputs.isEmpty(), "Attempting to sign tx without outputs."); | |
| 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.matthewmitchell.nubitsj.script.Script, ECKey, com.matthewmitchell.nubitsj.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); | |
| } | |
| /** | |
| * Adds an input that points to the given output and contains a valid signature for it, calculated using the | |
| * signing key. | |
| */ | |
| public TransactionInput addSignedInput(TransactionOutput output, ECKey signingKey) { | |
| return addSignedInput(output.getOutPointFor(), output.getScriptPubKey(), signingKey); | |
| } | |
| /** | |
| * Adds an input that points to the given output and contains a valid signature for it, calculated using the | |
| * signing key. | |
| */ | |
| public TransactionInput addSignedInput(TransactionOutput output, ECKey signingKey, SigHash sigHash, boolean anyoneCanPay) { | |
| return addSignedInput(output.getOutPointFor(), output.getScriptPubKey(), signingKey, sigHash, anyoneCanPay); | |
| } | |
| /** | |
| * Removes all the outputs 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.nubitsSerialize().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(Coin 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(Coin 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(Coin value, Script script) { | |
| return addOutput(new TransactionOutput(params, this, value, script.getProgram())); | |
| } | |
| /** | |
| * 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.matthewmitchell.nubitsj.core.Transaction.SigHash, boolean)} | |
| * followed by {@link ECKey#sign(Sha256Hash)} and then returning a new {@link TransactionSignature}. The key | |
| * must be usable for signing as-is: if the key is encrypted it must be decrypted first external to this method. | |
| * | |
| * @param inputIndex Which input to calculate the signature for, as an index. | |
| * @param key The private key used to calculate the signature. | |
| * @param redeemScript Byte-exact contents of the scriptPubKey that is being satisified, or the P2SH redeem script. | |
| * @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, | |
| byte[] redeemScript, | |
| SigHash hashType, boolean anyoneCanPay) { | |
| Sha256Hash hash = hashForSignature(inputIndex, redeemScript, hashType, anyoneCanPay); | |
| return new TransactionSignature(key.sign(hash), 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.matthewmitchell.nubitsj.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 redeemScript The scriptPubKey that is being satisified, or the P2SH redeem script. | |
| * @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 redeemScript, | |
| SigHash hashType, boolean anyoneCanPay) { | |
| Sha256Hash hash = hashForSignature(inputIndex, redeemScript.getProgram(), hashType, anyoneCanPay); | |
| return new TransactionSignature(key.sign(hash), hashType, anyoneCanPay); | |
| } | |
| /** | |
| * <p>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.</p> | |
| * | |
| * <p>This is a low level API and when using the regular {@link Wallet} class you don't have to call this yourself. | |
| * When working with more complex transaction types and contracts, it can be necessary. When signing a P2SH output | |
| * the redeemScript should be the script encoded into the scriptSig field, for normal transactions, it's the | |
| * scriptPubKey of the output you're signing for.</p> | |
| * | |
| * @param inputIndex input the signature is being calculated for. Tx signatures are always relative to an input. | |
| * @param redeemScript 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[] redeemScript, | |
| SigHash type, boolean anyoneCanPay) { | |
| byte sigHashType = (byte) TransactionSignature.calcSigHashValue(type, anyoneCanPay); | |
| return hashForSignature(inputIndex, redeemScript, sigHashType); | |
| } | |
| /** | |
| * <p>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.</p> | |
| * | |
| * <p>This is a low level API and when using the regular {@link Wallet} class you don't have to call this yourself. | |
| * When working with more complex transaction types and contracts, it can be necessary. When signing a P2SH output | |
| * the redeemScript should be the script encoded into the scriptSig field, for normal transactions, it's the | |
| * scriptPubKey of the output you're signing for.</p> | |
| * | |
| * @param inputIndex input the signature is being calculated for. Tx signatures are always relative to an input. | |
| * @param redeemScript 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 redeemScript, | |
| SigHash type, boolean anyoneCanPay) { | |
| int sigHash = TransactionSignature.calcSigHashValue(type, anyoneCanPay); | |
| return hashForSignature(inputIndex, redeemScript.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 Nubits 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<TransactionOutput> 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<TransactionOutput>(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 | |
| // Nubits 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 Sha256Hash.wrap("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<TransactionOutput>(this.outputs.subList(0, inputIndex + 1)); | |
| for (int i = 0; i < inputIndex; i++) | |
| this.outputs.set(i, new TransactionOutput(params, this, Coin.NEGATIVE_SATOSHI, 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<TransactionInput> 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<TransactionInput>(); | |
| this.inputs.add(input); | |
| } | |
| ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(length == UNKNOWN_LENGTH ? 256 : length + 4); | |
| nubitsSerialize(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 = Sha256Hash.twiceOf(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 nubitsSerializeToStream(OutputStream stream) throws IOException { | |
| uint32ToByteStreamLE(version, stream); | |
| uint32ToByteStreamLE(time, stream); | |
| stream.write(new VarInt(inputs.size()).encode()); | |
| for (TransactionInput in : inputs) | |
| in.nubitsSerialize(stream); | |
| stream.write(new VarInt(outputs.size()).encode()); | |
| for (TransactionOutput out : outputs) | |
| out.nubitsSerialize(stream); | |
| uint32ToByteStreamLE(lockTime, stream); | |
| stream.write(coinType); | |
| } | |
| /** | |
| * 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 Nubits 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 Nubits 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(); | |
| boolean seqNumSet = false; | |
| for (TransactionInput input : inputs) { | |
| if (input.getSequenceNumber() != TransactionInput.NO_SEQUENCE) { | |
| seqNumSet = true; | |
| break; | |
| } | |
| } | |
| if (lockTime != 0 && (!seqNumSet || inputs.isEmpty())) { | |
| // At least one input must have a non-default sequence number for lock times to have any effect. | |
| // For instance one of them can be set to zero to make this feature work. | |
| log.warn("You are setting the lock time on a transaction but none of the inputs have non-default sequence numbers. This will not do what you expect!"); | |
| } | |
| this.lockTime = lockTime; | |
| } | |
| /** | |
| * @return the version | |
| */ | |
| public long getVersion() { | |
| maybeParse(); | |
| return version; | |
| } | |
| /** Returns an unmodifiable view of all inputs. */ | |
| public List<TransactionInput> getInputs() { | |
| maybeParse(); | |
| return Collections.unmodifiableList(inputs); | |
| } | |
| /** Returns an unmodifiable view of all outputs. */ | |
| public List<TransactionOutput> getOutputs() { | |
| maybeParse(); | |
| return Collections.unmodifiableList(outputs); | |
| } | |
| public byte getCoinType() { | |
| return coinType; | |
| } | |
| /** | |
| * <p>Returns the list of transacion outputs, whether spent or unspent, that match a wallet by address or that are | |
| * watched by a wallet, i.e., transaction outputs whose script's address is controlled by the wallet and transaction | |
| * outputs whose script is watched by the wallet.</p> | |
| * | |
| * @param transactionBag The wallet that controls addresses and watches scripts. | |
| * @return linked list of outputs relevant to the wallet in this transaction | |
| */ | |
| public List<TransactionOutput> getWalletOutputs(TransactionBag transactionBag){ | |
| maybeParse(); | |
| List<TransactionOutput> walletOutputs = new LinkedList<TransactionOutput>(); | |
| for (TransactionOutput o : outputs) { | |
| if (!o.isMineOrWatched(transactionBag)) continue; | |
| walletOutputs.add(o); | |
| } | |
| return walletOutputs; | |
| } | |
| /** Randomly re-orders the transaction outputs: good for privacy */ | |
| public void shuffleOutputs() { | |
| maybeParse(); | |
| Collections.shuffle(outputs); | |
| } | |
| /** Same as getInputs().get(index). */ | |
| public TransactionInput getInput(long index) { | |
| maybeParse(); | |
| return inputs.get((int)index); | |
| } | |
| /** Same as getOutputs().get(index) */ | |
| public TransactionOutput getOutput(long index) { | |
| maybeParse(); | |
| return outputs.get((int)index); | |
| } | |
| /** | |
| * Returns the confidence object for this transaction from the {@link com.matthewmitchell.nubitsj.core.TxConfidenceTable} | |
| * referenced by the implicit {@link Context}. | |
| */ | |
| public TransactionConfidence getConfidence() { | |
| return getConfidence(Context.get()); | |
| } | |
| /** | |
| * Returns the confidence object for this transaction from the {@link com.matthewmitchell.nubitsj.core.TxConfidenceTable} | |
| * referenced by the given {@link Context}. | |
| */ | |
| public TransactionConfidence getConfidence(Context context) { | |
| return getConfidence(context.getConfidenceTable()); | |
| } | |
| /** | |
| * Returns the confidence object for this transaction from the {@link com.matthewmitchell.nubitsj.core.TxConfidenceTable} | |
| */ | |
| public TransactionConfidence getConfidence(TxConfidenceTable table) { | |
| if (confidence == null) | |
| confidence = table.getOrCreate(getHash()) ; | |
| return confidence; | |
| } | |
| /** Check if the transaction has a known confidence */ | |
| public boolean hasConfidence() { | |
| return getConfidence().getConfidenceType() != TransactionConfidence.ConfidenceType.UNKNOWN; | |
| } | |
| @Override | |
| public boolean equals(Object o) { | |
| if (this == o) return true; | |
| if (o == null || getClass() != o.getClass()) return false; | |
| Transaction other = (Transaction) o; | |
| return getHash().equals(other.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; | |
| } | |
| /** | |
| * <p>Checks the transaction contents for sanity, in ways that can be done in a standalone manner. | |
| * Does <b>not</b> perform all checks on a transaction such as whether the inputs are already spent. | |
| * Specifically this method verifies:</p> | |
| * | |
| * <ul> | |
| * <li>That there is at least one input and output.</li> | |
| * <li>That the serialized size is not larger than the max block size.</li> | |
| * <li>That no outputs have negative value.</li> | |
| * <li>That the outputs do not sum to larger than the max allowed quantity of coin in the system.</li> | |
| * <li>If the tx is a coinbase tx, the coinbase scriptSig size is within range. Otherwise that there are no | |
| * coinbase inputs in the tx.</li> | |
| * </ul> | |
| * | |
| * @throws VerificationException | |
| */ | |
| public void verify() throws VerificationException { | |
| maybeParse(); | |
| if (inputs.size() == 0 || outputs.size() == 0) | |
| throw new VerificationException.EmptyInputsOrOutputs(); | |
| if (this.getMessageSize() > Block.MAX_BLOCK_SIZE) | |
| throw new VerificationException.LargerThanMaxBlockSize(); | |
| Coin valueOut = Coin.ZERO; | |
| HashSet<TransactionOutPoint> outpoints = new HashSet<TransactionOutPoint>(); | |
| for (TransactionInput input : inputs) { | |
| if (outpoints.contains(input.getOutpoint())) | |
| throw new VerificationException.DuplicatedOutPoint(); | |
| outpoints.add(input.getOutpoint()); | |
| } | |
| try { | |
| for (TransactionOutput output : outputs) { | |
| if (output.getValue().signum() < 0) // getValue() can throw IllegalStateException | |
| throw new VerificationException.NegativeValueOutput(); | |
| valueOut = valueOut.add(output.getValue()); | |
| // Duplicate the MAX_MONEY check from Coin.add() in case someone accidentally removes it. | |
| if (valueOut.compareTo(NetworkParameters.MAX_MONEY) > 0) | |
| throw new IllegalArgumentException(); | |
| } | |
| } catch (IllegalStateException e) { | |
| throw new VerificationException.ExcessiveValue(); | |
| } catch (IllegalArgumentException e) { | |
| throw new VerificationException.ExcessiveValue(); | |
| } | |
| if (isCoinBase()) { | |
| if (inputs.get(0).getScriptBytes().length > 100) | |
| throw new VerificationException.CoinbaseScriptSizeOutOfRange(); | |
| } else { | |
| for (TransactionInput input : inputs) | |
| if (input.isCoinBase()) | |
| throw new VerificationException.UnexpectedCoinbaseInput(); | |
| } | |
| } | |
| /** | |
| * <p>A transaction is time locked if at least one of its inputs is non-final and it has a lock time</p> | |
| * | |
| * <p>To check if this transaction is final at a given height and time, see {@link Transaction#isFinal(int, long)} | |
| * </p> | |
| */ | |
| public boolean isTimeLocked() { | |
| if (getLockTime() == 0) | |
| return false; | |
| for (TransactionInput input : getInputs()) | |
| if (input.hasSequence()) | |
| return true; | |
| return false; | |
| } | |
| /** | |
| * <p>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 <a href="http://en.bitcoin.it/wiki/Contracts">contracts</a>, such as | |
| * micropayment channels.</p> | |
| * | |
| * <p>Note that currently the replacement feature is disabled in the Satoshi client and will need to be | |
| * re-activated before this functionality is useful.</p> | |
| */ | |
| public boolean isFinal(int height, long blockTimeSeconds) { | |
| long time = getLockTime(); | |
| return time < (time < LOCKTIME_THRESHOLD ? height : blockTimeSeconds) || !isTimeLocked(); | |
| } | |
| /** | |
| * 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; | |
| } | |
| /** | |
| * Getter for {@link #exchangeRate}. | |
| */ | |
| @Nullable | |
| public ExchangeRate getExchangeRate() { | |
| return exchangeRate; | |
| } | |
| /** | |
| * Setter for {@link #exchangeRate}. | |
| */ | |
| public void setExchangeRate(ExchangeRate exchangeRate) { | |
| this.exchangeRate = exchangeRate; | |
| } | |
| /** | |
| * Returns the transaction {@link #memo}. | |
| */ | |
| public String getMemo() { | |
| return memo; | |
| } | |
| /** | |
| * Set the transaction {@link #memo}. It can be used to record the memo of the payment request that initiated the | |
| * transaction. | |
| */ | |
| public void setMemo(String memo) { | |
| this.memo = memo; | |
| } | |
| } |