org.bitcoinj.core.Transaction Maven / Gradle / Ivy
/*
* 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 org.bitcoinj.core;
import org.bitcoinj.core.TransactionConfidence.ConfidenceType;
import org.bitcoinj.crypto.TransactionSignature;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.script.ScriptError;
import org.bitcoinj.script.ScriptException;
import org.bitcoinj.script.ScriptOpCodes;
import org.bitcoinj.signers.TransactionSigner;
import org.bitcoinj.utils.ExchangeRate;
import org.bitcoinj.wallet.Wallet;
import org.bitcoinj.wallet.WalletTransaction.Pool;
import com.google.common.base.Strings;
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 org.spongycastle.crypto.params.KeyParameter;
import javax.annotation.Nullable;
import java.io.*;
import java.util.*;
import static org.bitcoinj.core.Utils.*;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import java.math.BigInteger;
/**
* 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.
*
* Instances of this class are not safe for use by multiple threads.
*/
public class Transaction extends ChildMessage {
/**
* 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 SORT_TX_BY_UPDATE_TIME = new Comparator() {
@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 SORT_TX_BY_HEIGHT = new Comparator() {
@Override
public int compare(final Transaction tx1, final Transaction tx2) {
final TransactionConfidence confidence1 = tx1.getConfidence();
final int height1 = confidence1.getConfidenceType() == ConfidenceType.BUILDING
? confidence1.getAppearedAtChainHeight() : Block.BLOCK_HEIGHT_UNKNOWN;
final TransactionConfidence confidence2 = tx2.getConfidence();
final int height2 = confidence2.getConfidenceType() == ConfidenceType.BUILDING
? confidence2.getAppearedAtChainHeight() : Block.BLOCK_HEIGHT_UNKNOWN;
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);
/** 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
/** Same but as a BigInteger for CHECKLOCKTIMEVERIFY */
public static final BigInteger LOCKTIME_THRESHOLD_BIG = BigInteger.valueOf(LOCKTIME_THRESHOLD);
/** 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 feePerKb is lower than this, Bitcoin Core will treat it as if there were no fee.
*/
public static final Coin REFERENCE_DEFAULT_MIN_TX_FEE = Coin.valueOf(5000); // 0.05 mBTC
/**
* If using this feePerKb, transactions will get confirmed within the next couple of blocks.
* This should be adjusted from time to time. Last adjustment: February 2017.
*/
public static final Coin DEFAULT_TX_FEE = Coin.valueOf(100000); // 1 mBTC
/**
* 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(Coin)}.
*/
public static final Coin MIN_NONDUST_OUTPUT = Coin.valueOf(2730); // satoshis
/**
* Segwit makes sigop limit four times higher and scales regular sigops by four.
*/
public static final int WITNESS_SCALE_FACTOR = 4;
// These are bitcoin serialized.
private long version;
private ArrayList inputs;
private ArrayList outputs;
private ArrayList witnesses;
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. It contains the transaction hash (aka txid), used as a reference by transaction
// inputs via outpoints.
private 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 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 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,
/** Raise fee, e.g. child-pays-for-parent. */
RAISE_FEE,
// In future: de/refragmentation, privacy boosting/mixing, etc.
// When adding a value, it also needs to be added to wallet.proto, WalletProtobufSerialize.makeTxProto()
// and WalletProtobufSerializer.readTransaction()!
}
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;
/* Below flags apply in the context of BIP 68 */
/* If this flag set, CTxIn::nSequence is NOT interpreted as a
* relative lock-time. */
public static final long SEQUENCE_LOCKTIME_DISABLE_FLAG = 1L << 31;
/* If CTxIn::nSequence encodes a relative lock-time and this flag
* is set, the relative lock-time has units of 512 seconds,
* otherwise it specifies blocks with a granularity of 1. */
public static final long SEQUENCE_LOCKTIME_TYPE_FLAG = 1L << 22;
/* If CTxIn::nSequence encodes a relative lock-time, this mask is
* applied to extract that lock-time from the sequence field. */
public static final long SEQUENCE_LOCKTIME_MASK = 0x0000ffff;
public Transaction(NetworkParameters params) {
super(params);
version = 1;
inputs = new ArrayList<>();
outputs = new ArrayList<>();
witnesses = 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
}
/**
* 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 Bitcoin protocol formatted byte array containing message content.
* @param offset The location of the first payload byte within the array.
* @param parent The parent of the transaction.
* @param setSerializer The serializer to use for this transaction.
* @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,
MessageSerializer setSerializer,
int length)
throws ProtocolException
{
super(params, payload, offset, parent, setSerializer, length);
}
/**
* Creates a transaction by reading payload. Length of a transaction is fixed.
*/
public Transaction(
NetworkParameters params,
byte[] payload,
@Nullable Message parent,
MessageSerializer setSerializer,
int length)
throws ProtocolException
{
super(params, payload, 0, parent, setSerializer, length);
}
/**
* Create a new version of this transaction that serializes using the pre-SegWit method,
* excluding witness data.
*/
public Transaction disableWitnessSerialization() {
if ((transactionOptions & TransactionOptions.WITNESS) == TransactionOptions.WITNESS) {
final Transaction tx = new Transaction(params);
tx.transactionOptions = transactionOptions & ~TransactionOptions.WITNESS;
for (TransactionInput i: getInputs()) {
tx.addInput(i);
}
for (TransactionOutput o: getOutputs()) {
tx.addOutput(o);
}
if (tx.hasWitness())
for (int i = 0; i < countWitnesses(); i++)
tx.setWitness(i, getWitness(i));
return tx;
} else {
return this;
}
}
/**
* Returns the transaction hash (aka txid) as you see them in block explorers. It is used as a reference by
* transaction inputs via outpoints.
*/
@Override
public Sha256Hash getHash() {
return getHash(false);
}
public Sha256Hash getHash(boolean segwit) {
if (segwit) {
if (isCoinBase()) return Sha256Hash.ZERO_HASH;
ByteArrayOutputStream stream = new UnsafeByteArrayOutputStream(length < 32 ? 32 : length + 32);
try {
bitcoinSerializeToStream(stream, TransactionOptions.WITNESS);
} catch (IOException e) {
// Cannot happen, we are serializing to a memory stream.
}
byte[] bits = stream.toByteArray();
return Sha256Hash.wrapReversed(Sha256Hash.hashTwice(bits));
} else if (hash == null) {
ByteArrayOutputStream stream = new UnsafeByteArrayOutputStream(length < 32 ? 32 : length + 32);
try {
bitcoinSerializeToStream(stream, TransactionOptions.NONE);
} catch (IOException e) {
// Cannot happen, we are serializing to a memory stream.
}
byte[] bits = stream.toByteArray();
hash = Sha256Hash.wrapReversed(Sha256Hash.hashTwice(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;
}
/**
* Returns the transaction hash (aka txid) as you see them in block explorers, as a hex string.
*/
public String getHashAsString() {
return getHash().toString();
}
/**
* Gets the sum of the inputs, regardless of who owns them.
*/
public Coin getInputSum() {
Coin inputTotal = Coin.ZERO;
for (TransactionInput input: inputs) {
Coin inputValue = input.getValue();
if (inputValue != null) {
inputTotal = inputTotal.add(inputValue);
}
}
return inputTotal;
}
/**
* Calculates the sum of the outputs that are sending coins to a key in the wallet.
*/
public Coin getValueSentToMe(TransactionBag transactionBag) {
// This is tested in WalletTest.
Coin v = Coin.ZERO;
for (TransactionOutput o : outputs) {
if (!o.isMineOrWatched(transactionBag)) continue;
v = v.add(o.getValue());
}
return v;
}
/**
* 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();
// 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 of the inputs that are spending coins with keys in the wallet
*/
public Coin getValueSentFromMe(TransactionBag wallet) throws ScriptException {
// 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;
}
/**
* Gets the sum of the outputs of the transaction. If the outputs are less than the inputs, it does not count the fee.
* @return the sum of the outputs regardless of who owns them.
*/
public Coin getOutputSum() {
Coin totalOut = Coin.ZERO;
for (TransactionOutput output: outputs) {
totalOut = totalOut.add(output.getValue());
}
return totalOut;
}
/**
* Get the transaction witness of an input
* @return the witness of the input
* @throws java.lang.IndexOutOfBoundsException
*/
public TransactionWitness getWitness(int inputIndex) {
if (!(0 <= inputIndex && inputIndex < inputs.size()))
throw new java.lang.IndexOutOfBoundsException();
if (inputIndex >= witnesses.size())
return TransactionWitness.getEmpty();
return witnesses.get(inputIndex);
}
/**
* Set the transaction witness of an input
* @throws java.lang.IndexOutOfBoundsException
*/
public void setWitness(int inputIndex, TransactionWitness witness) {
if (!(0 <= inputIndex && inputIndex < inputs.size()))
throw new java.lang.IndexOutOfBoundsException();
witness = witness == null ? TransactionWitness.getEmpty() : witness;
while (inputIndex >= witnesses.size()) {
witnesses.add(TransactionWitness.getEmpty());
}
witnesses.set(inputIndex, witness);
}
/**
* Returns true if the transaction has witnesses
* @return true if the transaction has witnesses
*/
public boolean hasWitness() {
for (int i = 0; i < inputs.size(); i++) {
if (i >= Math.min(inputs.size(), witnesses.size()))
break;
if (witnesses.get(i).getPushCount() != 0)
return true;
}
return false;
}
/**
* Count witnesses in this transaction.
*
* @return number of witnesses
*/
public int countWitnesses() {
if (witnesses == null) return 0;
else return witnesses.size();
}
@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;
if (inputs.isEmpty() || outputs.isEmpty()) // Incomplete transaction
return null;
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() {
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) {
for (TransactionOutput output : outputs) {
if (output.isAvailableForSpending() && output.isMineOrWatched(transactionBag))
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.
*/
public enum SigHash {
ALL(1),
NONE(2),
SINGLE(3),
BITCOINCASHBIP143(0x40),
ANYONECANPAY(0x80), // Caution: Using this type in isolation is non-standard. Treated similar to ANYONECANPAY_ALL.
ANYONECANPAY_ALL(0x81),
ANYONECANPAY_NONE(0x82),
ANYONECANPAY_SINGLE(0x83),
ALL_CASH(1+0x40),
ALL_CASH_ANYONE_CANPAY(1+0x40+0x80),
UNSET(0); // Caution: Using this type in isolation is non-standard. Treated similar to ALL.
public final int value;
/**
* @param value
*/
private SigHash(final int value) {
this.value = value;
}
/**
* @return the value as a byte
*/
public byte byteValue() {
return (byte) this.value;
}
}
/**
* @deprecated Instead use SigHash.ANYONECANPAY.value or SigHash.ANYONECANPAY.byteValue() as appropriate.
*/
public static final byte SIGHASH_ANYONECANPAY_VALUE = (byte) 0x80;
@Override
protected void unCache() {
super.unCache();
hash = null;
}
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;
}
@Override
protected void parse() throws ProtocolException {
boolean witSupported = (transactionOptions & TransactionOptions.WITNESS) != 0;
cursor = offset;
version = readUint32();
optimalEncodingMessageSize = 4;
// First come the inputs.
readInputs();
byte flags = 0;
if (inputs.size() == 0 && witSupported) {
flags = readBytes(1)[0];
optimalEncodingMessageSize += 1;
if (flags != 0) {
readInputs();
readOutputs();
} else {
outputs = new ArrayList<>(0);
}
} else {
readOutputs();
}
if (((flags & 1) != 0) && witSupported) {
flags ^= 1;
readWitness();
}
if (flags != 0) {
throw new ProtocolException("Unknown transaction optional data");
}
lockTime = readUint32();
optimalEncodingMessageSize += 4;
length = cursor - offset;
witnesses = witnesses == null ? new ArrayList() : witnesses;
}
private void readWitness() {
witnesses = new ArrayList(inputs.size());
for (int i = 0; i < inputs.size(); i++) {
long pushCount = readVarInt();
TransactionWitness witness = new TransactionWitness((int) pushCount);
setWitness(i, witness);
optimalEncodingMessageSize += VarInt.sizeOf(pushCount);
for (int y = 0; y < pushCount; y++) {
long pushSize = readVarInt();
optimalEncodingMessageSize += VarInt.sizeOf(pushSize) + pushSize;
byte[] push = readBytes((int) pushSize);
witness.setPush(y, push);
}
}
}
private void readOutputs() {
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, payload, cursor, serializer);
outputs.add(output);
long scriptLen = readVarInt(8);
optimalEncodingMessageSize += 8 + VarInt.sizeOf(scriptLen) + scriptLen;
cursor += scriptLen;
}
}
private void readInputs() {
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, payload, cursor, serializer);
inputs.add(input);
long scriptLen = readVarInt(TransactionOutPoint.MESSAGE_LENGTH);
optimalEncodingMessageSize += TransactionOutPoint.MESSAGE_LENGTH + VarInt.sizeOf(scriptLen) + scriptLen + 4;
cursor += scriptLen + 4;
}
}
public int getOptimalEncodingMessageSize() {
if (optimalEncodingMessageSize != 0)
return optimalEncodingMessageSize;
optimalEncodingMessageSize = getMessageSize();
return optimalEncodingMessageSize;
}
/**
* The priority (coin age) calculation doesn't use the regular message size, but rather one adjusted downwards
* for the number of inputs. The goal is to incentivise cleaning up the UTXO set with free transactions, if one
* can do so.
*/
public int getMessageSizeForPriorityCalc() {
int size = getMessageSize();
for (TransactionInput input : inputs) {
// 41: min size of an input
// 110: enough to cover a compressed pubkey p2sh redemption (somewhat arbitrary).
int benefit = 41 + Math.min(110, input.getScriptSig().getProgram().length);
if (size > benefit)
size -= benefit;
}
return size;
}
/**
* A coinbase transaction is one that creates a new coin. They are the first transaction in each block and their
* value is determined by a formula that all implementations of Bitcoin share. In 2011 the value of a coinbase
* transaction is 50 coins, but in future it will be less. A coinbase transaction is defined not only by its
* position in a block but by the data in the inputs.
*/
public boolean isCoinBase() {
return inputs.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) {
StringBuilder s = new StringBuilder();
s.append(" ").append(getHashAsString()).append('\n');
if (updatedAt != null)
s.append(" updated: ").append(Utils.dateTimeFormat(updatedAt)).append('\n');
if (version != 1)
s.append(" version ").append(version).append('\n');
if (isTimeLocked()) {
s.append(" time locked until ");
if (lockTime < LOCKTIME_THRESHOLD) {
s.append("block ").append(lockTime);
if (chain != null) {
s.append(" (estimated to be reached at ")
.append(Utils.dateTimeFormat(chain.estimateBlockTime((int) lockTime))).append(')');
}
} else {
s.append(Utils.dateTimeFormat(lockTime * 1000));
}
s.append('\n');
}
if (isOptInFullRBF()) {
s.append(" opts into full replace-by-fee\n");
}
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();
}
if (!inputs.isEmpty()) {
int i = 0;
for (TransactionInput in : inputs) {
s.append(" ");
s.append("in ");
try {
String scriptSigStr = in.getScriptSig().toString();
s.append(!Strings.isNullOrEmpty(scriptSigStr) ? scriptSigStr : "");
final Coin value = in.getValue();
if (value != null)
s.append(" ").append(value.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 sequence:").append(Long.toHexString(in.getSequenceNumber()));
if (in.isOptInFullRBF())
s.append(", opts into full RBF");
}
if (this.hasWitness() && witnesses.get(i).getPushCount() > 0) {
s.append("\n ");
s.append("witness:");
s.append(witnesses.get(i));
}
} catch (Exception e) {
s.append("[exception: ").append(e.getMessage()).append("]");
}
s.append('\n');
i++;
}
} else {
s.append(" ");
s.append("INCOMPLETE: No inputs!\n");
}
for (TransactionOutput out : outputs) {
s.append(" ");
s.append("out ");
try {
String scriptPubKeyStr = out.getScriptPubKey().toString();
s.append(!Strings.isNullOrEmpty(scriptPubKeyStr) ? scriptPubKeyStr : "");
s.append(" ");
s.append(out.getValue().toFriendlyString());
if (!out.isAvailableForSpending()) {
s.append(" Spent");
}
final TransactionInput spentBy = out.getSpentBy();
if (spentBy != null) {
s.append(" by ");
s.append(spentBy.getParentTransaction().getHashAsString());
}
} catch (Exception e) {
s.append("[exception: ").append(e.getMessage()).append("]");
}
s.append('\n');
}
final Coin fee = getFee();
if (fee != null) {
final int size = unsafeBitcoinSerialize().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('\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.unsafeBitcoinSerialize().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 {@link #addInput(TransactionInput)} and every output is added with
* {@link #addOutput(TransactionOutput)}, a {@link TransactionSigner} must be used to finalize the transaction and
* finish the inputs off. Otherwise it won't be accepted by the network.
* @return 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.
* @return the new input.
*/
public TransactionInput addInput(TransactionInput input) {
unCache();
input.setParent(this);
inputs.add(input);
adjustLength(inputs.size(), input.length);
return input;
}
/**
* Creates and adds an input to this transaction, with no checking that it's valid.
* @return the newly created 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 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 P2PKH, P2PK, P2WPKH, or P2WPKH wrapped in P2SH.
*/
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;
// P2WSH not supported
if ( scriptPubKey.isSentToP2WPKH()
|| scriptPubKey.isSentToP2WPKHP2SH(sigKey)
|| scriptPubKey.isSentToP2WSH()) {
if (prevOut.getConnectedOutput().getValue() == null)
throw new ScriptException(ScriptError.SCRIPT_ERR_WITNESS_UNEXPECTED, "Cannot sign segwit script without value of previous output");
Script scriptCode;
if (scriptPubKey.isSentToP2WPKHP2SH(sigKey))
scriptCode = ScriptBuilder.createP2WPKHOutputScript(sigKey).scriptCode();
else
scriptCode = scriptPubKey.scriptCode();
hash = hashForSignatureWitness(
inputs.size() - 1,
scriptCode,
prevOut.getConnectedOutput().getValue(),
sigHash,
anyoneCanPay);
} else {
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 if (scriptPubKey.isSentToP2WPKH()) {
TransactionWitness witness = TransactionWitness.createWitness(txSig, sigKey);
setWitness(inputs.size() - 1, witness);
} else if (scriptPubKey.isSentToP2WPKHP2SH(sigKey)) {
TransactionWitness witness = TransactionWitness.createWitness(txSig, sigKey);
setWitness(inputs.size() - 1, witness);
Script p2wpkhProgram = ScriptBuilder.createP2WPKHOutputScript(sigKey);
input.setScriptSig(ScriptBuilder.createSegwitP2SHSigScript(p2wpkhProgram));
} else {
throw new ScriptException(ScriptError.SCRIPT_ERR_UNKNOWN_ERROR, "Don't know how to sign this script with key provided: " + scriptPubKey);
}
return input;
}
/**
* Same as {@link #addSignedInput(TransactionOutPoint, org.bitcoinj.script.Script, ECKey, org.bitcoinj.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.unsafeBitcoinSerialize().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[], org.bitcoinj.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 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);
}
public TransactionSignature calculateWitnessSignature(
int inputIndex,
ECKey key,
byte[] redeemScript,
Coin value,
SigHash hashType,
boolean anyoneCanPay)
{
Sha256Hash hash = hashForSignatureWitness(inputIndex, redeemScript, value, 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[], org.bitcoinj.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 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);
}
public TransactionSignature calculateWitnessSignature(
int inputIndex,
ECKey key,
Script redeemScript,
Coin value,
SigHash hashType,
boolean anyoneCanPay)
{
Sha256Hash hash = hashForSignatureWitness(inputIndex, redeemScript.getProgram(), value, 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[], org.bitcoinj.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 aesKey The AES key to use for decryption of the private key. If null then no decryption is required.
* @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 TransactionSignature calculateSignature(
int inputIndex,
ECKey key,
@Nullable KeyParameter aesKey,
byte[] redeemScript,
SigHash hashType,
boolean anyoneCanPay)
{
Sha256Hash hash = hashForSignature(inputIndex, redeemScript, hashType, anyoneCanPay);
return new TransactionSignature(key.sign(hash, aesKey), hashType, anyoneCanPay);
}
public TransactionSignature calculateWitnessSignature(
int inputIndex,
ECKey key,
@Nullable KeyParameter aesKey,
byte[] redeemScript,
Coin value,
SigHash hashType,
boolean anyoneCanPay)
{
Sha256Hash hash = hashForSignatureWitness(inputIndex, redeemScript, value, 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[], org.bitcoinj.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 aesKey The AES key to use for decryption of the private key. If null then no decryption is required.
* @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 TransactionSignature calculateSignature(
int inputIndex,
ECKey key,
@Nullable KeyParameter aesKey,
Script redeemScript,
SigHash hashType,
boolean anyoneCanPay)
{
Sha256Hash hash = hashForSignature(inputIndex, redeemScript.getProgram(), hashType, anyoneCanPay);
return new TransactionSignature(key.sign(hash, aesKey), hashType, anyoneCanPay);
}
public TransactionSignature calculateWitnessSignature(
int inputIndex,
ECKey key,
@Nullable KeyParameter aesKey,
Script redeemScript,
Coin value,
SigHash hashType,
boolean anyoneCanPay)
{
Sha256Hash hash = hashForSignatureWitness(inputIndex, redeemScript.getProgram(), value, hashType, anyoneCanPay);
return new TransactionSignature(key.sign(hash, aesKey), 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.
*
* 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.
*
* @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 Sha256Hash hashForSignature(
int inputIndex,
byte[] redeemScript,
SigHash type,
boolean anyoneCanPay)
{
byte sigHashType = (byte) TransactionSignature.calcSigHashValue(type, anyoneCanPay);
return hashForSignature(inputIndex, redeemScript, 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.
*
* 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.
*
* @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 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 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 {
// Create a copy of this transaction to operate upon because we need make changes to the inputs and outputs.
// It would not be thread-safe to change the attributes of the transaction object itself.
Transaction tx = this.params.getDefaultSerializer().makeTransaction(this.bitcoinSerialize());
// Clear input scripts 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.
for (int i = 0; i < tx.inputs.size(); i++) {
tx.inputs.get(i).clearScriptBytes();
}
// This step has no purpose beyond being synchronized with Bitcoin Core's 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. Bitcoin Core 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 = tx.inputs.get(inputIndex);
input.setScriptBytes(connectedScript);
if ((sigHashType & 0x1f) == SigHash.NONE.value) {
// SIGHASH_NONE means no outputs are signed at all - the signature is effectively for a "blank cheque".
tx.outputs = new ArrayList<>(0);
// The signature isn't broken by new versions of the transaction issued by other parties.
for (int i = 0; i < tx.inputs.size(); i++)
if (i != inputIndex)
tx.inputs.get(i).setSequenceNumber(0);
} else if ((sigHashType & 0x1f) == SigHash.SINGLE.value) {
// SIGHASH_SINGLE means only sign the output at the same index as the input (ie, my output).
if (inputIndex >= tx.outputs.size()) {
// The input index is beyond the number of outputs, it's a buggy signature made by a broken
// Bitcoin implementation. Bitcoin Core 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).
// Bitcoin Core's 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.
tx.outputs = new ArrayList<>(tx.outputs.subList(0, inputIndex + 1));
for (int i = 0; i < inputIndex; i++)
tx.outputs.set(i, new TransactionOutput(tx.params, tx, 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 < tx.inputs.size(); i++)
if (i != inputIndex)
tx.inputs.get(i).setSequenceNumber(0);
}
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.
tx.inputs = new ArrayList();
tx.inputs.add(input);
}
ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(tx.length == UNKNOWN_LENGTH ? 256 : tx.length + 4);
tx.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 = Sha256Hash.twiceOf(bos.toByteArray());
bos.close();
return hash;
} catch (IOException e) {
throw new RuntimeException(e); // Cannot happen.
}
}
public static final int SIGHASH_FORKID = 0x40;
/**
* Bitcoin Cash (BCC) signature algorithm.
*
* Calculate a signature hash to support BUIP-HF Digest for replay protected signature verification.
* Reference: https://github.com/Bitcoin-ABC/bitcoin-abc/blob/master/doc/abc/replay-protected-sighash.md
*
* @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 sigHashType Should be SigHash.ALL
* @param anyoneCanPay should be false.
* @param inputValue the inputIndex-th input value. Unit: satoshi
* @return
*/
public Sha256Hash hashForSignature(int inputIndex, Script redeemScript, SigHash sigHashType, boolean anyoneCanPay, long inputValue) {
int sigHash = TransactionSignature.calcSigHashValue(sigHashType, anyoneCanPay);
sigHash |= SIGHASH_FORKID;
// Create a copy of this transaction to operate upon because we need make changes to the inputs and outputs.
// It would not be thread-safe to change the attributes of the transaction object itself.
Transaction tx = this.params.getDefaultSerializer().makeTransaction(this.bitcoinSerialize());
// Clear input scripts 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.
for (int i = 0; i < tx.inputs.size(); i++) {
tx.inputs.get(i).clearScriptBytes();
}
byte[] connectedScript = redeemScript.getProgram();
connectedScript = Script.removeAllInstancesOfOp(connectedScript, ScriptOpCodes.OP_CODESEPARATOR);
TransactionInput input = tx.inputs.get(inputIndex);
input.setScriptBytes(connectedScript);
Sha256Hash hashPrevouts = Sha256Hash.ZERO_HASH;
Sha256Hash hashSequence = Sha256Hash.ZERO_HASH;
Sha256Hash hashOutputs = Sha256Hash.ZERO_HASH;
try {
if ((sigHash & SigHash.ANYONECANPAY.value) == 0) {
hashPrevouts = getPrevoutHash(tx);
}
if ((sigHash & SigHash.ANYONECANPAY.value) == 0 && (sigHash & 0x1F) != SigHash.SINGLE.value && (sigHash & 0x1F) != SigHash.NONE.value) {
hashSequence = getSequenceHash(tx);
}
if ((sigHash & 0x1F) != SigHash.SINGLE.value && (sigHash & 0x1F) != SigHash.NONE.value) {
hashOutputs = getOutputsHash(tx);
} else if ((sigHash & 0x1F) == SigHash.SINGLE.value && inputIndex < tx.getOutputs().size()) {
hashOutputs = Sha256Hash.twiceOf(tx.getOutputs().get(inputIndex).bitcoinSerialize());
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
uint32ToByteStreamLE(tx.getVersion(), baos);
baos.write(hashPrevouts.getBytes());
baos.write(hashSequence.getBytes());
// The input being signed (replacing the scriptSig with scriptCode +
// amount). The prevout may already be contained in hashPrevout, and the
// nSequence may already be contain in hashSequence.
tx.getInput(inputIndex).getOutpoint().bitcoinSerializeToStream(baos);
baos.write(new VarInt(connectedScript.length).encode());
baos.write(connectedScript);
uint64ToByteStreamLE(BigInteger.valueOf(inputValue), baos);
uint32ToByteStreamLE(tx.inputs.get(inputIndex).getSequenceNumber(), baos);
// Outputs (none/one/all, depending on flags)
baos.write(hashOutputs.getBytes());
// Locktime
uint32ToByteStreamLE(tx.lockTime, baos);
uint32ToByteStreamLE(0x000000ff & sigHash, baos);
Sha256Hash hash = Sha256Hash.twiceOf(baos.toByteArray());
baos.close();
return hash;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private Sha256Hash getPrevoutHash(Transaction tx) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int i = 0; i < tx.inputs.size(); i++) {
tx.inputs.get(i).getOutpoint().bitcoinSerializeToStream(baos);
}
Sha256Hash hash = Sha256Hash.twiceOf(baos.toByteArray());
baos.close();
return hash;
}
private Sha256Hash getSequenceHash(Transaction tx) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int i = 0; i < tx.inputs.size(); i++) {
uint32ToByteStreamLE(tx.inputs.get(i).getSequenceNumber(), baos);
}
Sha256Hash hash = Sha256Hash.twiceOf(baos.toByteArray());
baos.close();
return hash;
}
private Sha256Hash getOutputsHash(Transaction tx) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int i = 0; i < tx.outputs.size(); i++) {
tx.outputs.get(i).bitcoinSerializeToStream(baos);
}
Sha256Hash hash = Sha256Hash.twiceOf(baos.toByteArray());
baos.close();
return hash;
}
/**
* 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.
*
* 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 Witness output
* the scriptCode should be the script encoded into the scriptSig field, for normal transactions, it's the
* scriptPubKey of the output you're signing for. (See BIP143: https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki)
*
* @param inputIndex input the signature is being calculated for. Tx signatures are always relative to an input.
* @param scriptCode the script that should be in the given input during signing.
* @param prevValue the value of the coin being spent
* @param type Should be SigHash.ALL
* @param anyoneCanPay should be false.
*/
public synchronized Sha256Hash hashForSignatureWitness(
int inputIndex,
Script scriptCode,
Coin prevValue,
SigHash type,
boolean anyoneCanPay)
{
byte[] connectedScript = scriptCode.getProgram();
return hashForSignatureWitness(inputIndex, connectedScript, prevValue, type, anyoneCanPay);
}
public synchronized Sha256Hash hashForSignatureWitness(
int inputIndex,
byte[] connectedScript,
Coin prevValue,
SigHash type,
boolean anyoneCanPay)
{
byte sigHashType = (byte) TransactionSignature.calcSigHashValue(type, anyoneCanPay);
ByteArrayOutputStream bos = new UnsafeByteArrayOutputStream(length == UNKNOWN_LENGTH ? 256 : length + 4);
try {
byte[] hashPrevouts = new byte[32];
byte[] hashSequence = new byte[32];
byte[] hashOutputs = new byte[32];
anyoneCanPay = (sigHashType & SIGHASH_ANYONECANPAY_VALUE) == SIGHASH_ANYONECANPAY_VALUE;
if (!anyoneCanPay) {
ByteArrayOutputStream bosHashPrevouts = new UnsafeByteArrayOutputStream(256);
for (int i = 0; i < this.inputs.size(); ++i) {
bosHashPrevouts.write(this.inputs.get(i).getOutpoint().getHash().getReversedBytes());
uint32ToByteStreamLE(this.inputs.get(i).getOutpoint().getIndex(), bosHashPrevouts);
}
hashPrevouts = Sha256Hash.hashTwice(bosHashPrevouts.toByteArray());
}
if (!anyoneCanPay && type != SigHash.SINGLE && type != SigHash.NONE) {
ByteArrayOutputStream bosSequence = new UnsafeByteArrayOutputStream(256);
for (int i = 0; i < this.inputs.size(); ++i) {
uint32ToByteStreamLE(this.inputs.get(i).getSequenceNumber(), bosSequence);
}
hashSequence = Sha256Hash.hashTwice(bosSequence.toByteArray());
}
if (type != SigHash.SINGLE && type != SigHash.NONE) {
ByteArrayOutputStream bosHashOutputs = new UnsafeByteArrayOutputStream(256);
for (int i = 0; i < this.outputs.size(); ++i) {
uint64ToByteStreamLE(
BigInteger.valueOf(this.outputs.get(i).getValue().getValue()),
bosHashOutputs
);
bosHashOutputs.write(new VarInt(this.outputs.get(i).getScriptBytes().length).encode());
bosHashOutputs.write(this.outputs.get(i).getScriptBytes());
}
hashOutputs = Sha256Hash.hashTwice(bosHashOutputs.toByteArray());
} else if (type == SigHash.SINGLE && inputIndex < outputs.size()) {
ByteArrayOutputStream bosHashOutputs = new UnsafeByteArrayOutputStream(256);
uint64ToByteStreamLE(
BigInteger.valueOf(this.outputs.get(inputIndex).getValue().getValue()),
bosHashOutputs
);
bosHashOutputs.write(new VarInt(this.outputs.get(inputIndex).getScriptBytes().length).encode());
bosHashOutputs.write(this.outputs.get(inputIndex).getScriptBytes());
hashOutputs = Sha256Hash.hashTwice(bosHashOutputs.toByteArray());
}
uint32ToByteStreamLE(version, bos);
bos.write(hashPrevouts);
bos.write(hashSequence);
bos.write(inputs.get(inputIndex).getOutpoint().getHash().getReversedBytes());
uint32ToByteStreamLE(inputs.get(inputIndex).getOutpoint().getIndex(), bos);
bos.write(new VarInt(connectedScript.length).encode());
bos.write(connectedScript);
uint64ToByteStreamLE(BigInteger.valueOf(prevValue.getValue()), bos);
uint32ToByteStreamLE(inputs.get(inputIndex).getSequenceNumber(), bos);
bos.write(hashOutputs);
uint32ToByteStreamLE(this.lockTime, bos);
uint32ToByteStreamLE(0x000000ff & sigHashType, bos);
} catch (IOException e) {
throw new RuntimeException(e); // Cannot happen.
}
return Sha256Hash.twiceOf(bos.toByteArray());
}
@Override
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
bitcoinSerializeToStream(stream, transactionOptions);
}
protected void bitcoinSerializeToStream(OutputStream stream, int transactionOptions) throws IOException {
boolean witSupported = (transactionOptions & TransactionOptions.WITNESS) != 0;
boolean serializeWit = hasWitness() && witSupported;
uint32ToByteStreamLE(version, stream);
if (serializeWit) {
stream.write(new byte[]{0, 1});
}
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);
if (serializeWit) {
for (int i = 0; i < inputs.size(); i++) {
TransactionWitness witness = getWitness(i);
stream.write(new VarInt(witness.getPushCount()).encode());
for (int y = 0; y < witness.getPushCount(); y++) {
byte[] push = witness.getPush(y);
stream.write(new VarInt(push.length).encode());
stream.write(push);
}
}
}
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() {
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();
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;
}
public long getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
unCache();
}
/** Returns an unmodifiable view of all inputs. */
public List getInputs() {
return Collections.unmodifiableList(inputs);
}
/** Returns an unmodifiable view of all outputs. */
public List getOutputs() {
return Collections.unmodifiableList(outputs);
}
/**
* 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.
*
* @param transactionBag The wallet that controls addresses and watches scripts.
* @return linked list of outputs relevant to the wallet in this transaction
*/
public List getWalletOutputs(TransactionBag transactionBag) {
List walletOutputs = new LinkedList<>();
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() {
Collections.shuffle(outputs);
}
/** Same as getInputs().get(index). */
public TransactionInput getInput(long index) {
return inputs.get((int)index);
}
/** Same as getOutputs().get(index) */
public TransactionOutput getOutput(long index) {
return outputs.get((int)index);
}
/**
* Returns the confidence object for this transaction from the {@link org.bitcoinj.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 org.bitcoinj.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 org.bitcoinj.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;
return getHash().equals(((Transaction)o).getHash());
}
@Override
public int hashCode() {
return getHash().hashCode();
}
private int getWitnessSigOpCount(int index, Script pkScript)
throws VerificationException
{
final TransactionInput in = inputs.get(index);
final Script program;
if (pkScript.isPayToScriptHash())
program = Script.getRedeemScript(in.getScriptBytes());
else
program = pkScript;
if (program.isSentToP2WSH()) {
if (witnesses.size() < index + 1)
throw new VerificationException("Expected witness not found");
final TransactionWitness witness = getWitness(index);
final byte[] scriptBytes = witness.getScriptBytes();
return Script.getSigOpCount(scriptBytes, true);
} else {
return 0;
}
}
/**
* Gets the count of regular SigOps in this transaction.
*
* @see Transaction#WITNESS_SCALE_FACTOR
*/
public int getSigOpCount() throws ScriptException {
int sigOps = 0;
for (TransactionInput input : inputs)
sigOps += input.countSigOps();
for (TransactionOutput output : outputs)
sigOps += output.countSigOps();
return sigOps;
}
/**
* Check block height is in coinbase input script, for use after BIP 34
* enforcement is enabled.
*/
public void checkCoinBaseHeight(final int height)
throws VerificationException {
checkArgument(height >= Block.BLOCK_HEIGHT_GENESIS);
checkState(isCoinBase());
// Check block height is in coinbase input script
final TransactionInput in = this.getInputs().get(0);
final ScriptBuilder builder = new ScriptBuilder();
builder.number(height);
final byte[] expected = builder.build().getProgram();
final byte[] actual = in.getScriptBytes();
if (actual.length < expected.length) {
throw new VerificationException.CoinbaseHeightMismatch("Block height mismatch in coinbase.");
}
for (int scriptIdx = 0; scriptIdx < expected.length; scriptIdx++) {
if (actual[scriptIdx] != expected[scriptIdx]) {
throw new VerificationException.CoinbaseHeightMismatch("Block height mismatch in coinbase.");
}
}
}
/**
* 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.
* Specifically this method verifies:
*
*
* - That there is at least one input and output.
* - That the serialized size is not larger than the max block size.
* - That no outputs have negative value.
* - That the outputs do not sum to larger than the max allowed quantity of coin in the system.
* - If the tx is a coinbase tx, the coinbase scriptSig size is within range. Otherwise that there are no
* coinbase inputs in the tx.
*
*
* @throws VerificationException
*/
public void verify() throws VerificationException {
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 outpoints = new HashSet<>();
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());
if (params.hasMaxMoney() && valueOut.compareTo(params.getMaxMoney()) > 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 < 2 || inputs.get(0).getScriptBytes().length > 100)
throw new VerificationException.CoinbaseScriptSizeOutOfRange();
} else {
for (TransactionInput input : inputs)
if (input.isCoinBase())
throw new VerificationException.UnexpectedCoinbaseInput();
}
}
/**
* 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 whether this transaction will opt into the
* full replace-by-fee semantics.
*/
public boolean isOptInFullRBF() {
for (TransactionInput input : getInputs())
if (input.isOptInFullRBF())
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 Bitcoin Core and will need to be
* re-activated before this functionality is useful.
*/
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;
}
/**
* Transaction weight is a segwit-related computation 3b+t where b is the size of a transaction serialized in the
* traditional manner without witness data, and t is the size of a transaction serialized in the segwit format
* with witness data.
*/
// TODO: write tests for this
public int getWeight() {
final int baseLength;
{
final ByteArrayOutputStream base = new UnsafeByteArrayOutputStream(length < 32 ? 32 : length + 32);
try {
bitcoinSerializeToStream(base, TransactionOptions.NONE);
} catch (IOException e) {
// Cannot happen, we are serializing to a memory stream
}
baseLength = base.size();
}
final int totalLength;
{
final ByteArrayOutputStream total = new UnsafeByteArrayOutputStream(length < 32 ? 32 : length + 32);
try {
bitcoinSerializeToStream(total, TransactionOptions.WITNESS);
} catch (IOException e) {
// Cannot happen, we are serializing to a memory stream
}
totalLength = total.size();
}
return baseLength * (WITNESS_SCALE_FACTOR - 1) + totalLength;
}
public int getVirtualTransactionSize() {
return (getWeight() + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR;
}
}