/**
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.bitcoin.core;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.Map;
/**
* A transfer of coins from one address to another creates a transaction in which the outputs
* can be claimed by the recipient in the input of another transaction. You can imagine a
* transaction as being a module which is wired up to others, the inputs of one have to be wired
* to the outputs of another. The exceptions are coinbase transactions, which create new coins.
*/
public class TransactionInput extends Message implements Serializable {
private static final long serialVersionUID = 2;
public static final byte[] EMPTY_ARRAY = new byte[0];
public Boolean coinbase=null;
// Allows for altering transactions after they were broadcast. Tx replacement is currently disabled in the C++
// client so this is always the UINT_MAX.
// TODO: Document this in more detail and build features that use it.
long sequence;
// Data needed to connect to the output of the transaction we're gathering coins from.
TransactionOutPoint outpoint;
// The "script bytes" might not actually be a script. In coinbase transactions where new coins are minted there
// is no input transaction, so instead the scriptBytes contains some extra stuff (like a rollover nonce) that we
// don't care about much. The bytes are turned into a Script object (cached below) on demand via a getter.
byte[] scriptBytes;
// The Script object obtained from parsing scriptBytes. Only filled in on demand and if the transaction is not
// coinbase.
transient private Script scriptSig;
// A pointer to the transaction that owns this input.
Transaction parentTransaction;
/** Used only in creation of the genesis block. */
TransactionInput(NetworkParameters params, Transaction parentTransaction, byte[] scriptBytes) {
super(params);
this.scriptBytes = scriptBytes;
this.outpoint = new TransactionOutPoint(params, -1, null);
this.sequence = 0xFFFFFFFFL;
this.parentTransaction = parentTransaction;
}
TransactionInput(NetworkParameters params){
super(params);
this.scriptBytes = null;
this.outpoint = new TransactionOutPoint(params, -1, null);
this.sequence = 0xFFFFFFFFL;
this.parentTransaction = null;
}
/** Creates an UNSIGNED input that links to the given output */
TransactionInput(NetworkParameters params, Transaction parentTransaction, TransactionOutput output) {
super(params);
long outputIndex = output.getIndex();
outpoint = new TransactionOutPoint(params, outputIndex, output.parentTransaction);
scriptBytes = EMPTY_ARRAY;
sequence = 0xFFFFFFFFL;
this.parentTransaction = parentTransaction;
}
/** Deserializes an input message. This is usually part of a transaction message. */
public TransactionInput(NetworkParameters params, Transaction parentTransaction,
byte[] payload, int offset) throws ProtocolException {
super(params, payload, offset);
this.parentTransaction = parentTransaction;
}
void parse() throws ProtocolException {
outpoint = new TransactionOutPoint(params, bytes, cursor);
cursor += outpoint.getMessageSize();
int scriptLen = (int) readVarInt();
scriptBytes = readBytes(scriptLen);
sequence = readUint32();
}
@Override
public void bitcoinSerializeToStream(OutputStream stream) throws IOException {
outpoint.bitcoinSerializeToStream(stream);
stream.write(new VarInt(scriptBytes.length).encode());
stream.write(scriptBytes);
Utils.uint32ToByteStreamLE(sequence, stream);
}
/**
* Set parent transaction
*/
public void setParentTransaction(Transaction t){
parentTransaction=t;
}
/**
* Coinbase transactions have special inputs with hashes of zero. If this is such an input, returns true.
*/
public boolean isCoinBase() {
if(coinbase!=null){
return coinbase;
}
for (int i = 0; i < outpoint.hash.length; i++)
if (outpoint.hash[i] != 0) coinbase=false;
if(coinbase==null){
coinbase=true;
}
return coinbase;
}
/**
* Returns the input script.
*/
public Script getScriptSig() throws ScriptException {
// Transactions that generate new coins don't actually have a script. Instead this
// parameter is overloaded to be something totally different.
if (scriptSig == null) {
assert scriptBytes != null;
scriptSig = new Script(params, scriptBytes, 0, scriptBytes.length);
}
return scriptSig;
}
/**
* Convenience method that returns the from address of this input by parsing the scriptSig.
* @throws ScriptException if the scriptSig could not be understood (eg, if this is a coinbase transaction).
* @throws AddressFormatException
*/
public Address getFromAddress() throws ScriptException, AddressFormatException {
assert !isCoinBase();
return getScriptSig().getFromAddress();
}
/** Returns a human readable debug string. */
public String toString() {
if (isCoinBase())
return "TxIn: COINBASE";
try {
return "TxIn from " + Utils.bytesToHexString(getScriptSig().getPubKey()) + " script:" +
getScriptSig().toString();
} catch (ScriptException e) {
throw new RuntimeException(e);
}
}
enum ConnectionResult {
NO_SUCH_TX,
ALREADY_SPENT,
SUCCESS
}
// TODO: Clean all this up once TransactionOutPoint disappears.
/**
* Locates the referenced output from the given pool of transactions.
* @return The TransactionOutput or null if the transactions map doesn't contain the referenced tx.
*/
TransactionOutput getConnectedOutput(Map<Sha256Hash, Transaction> transactions) {
Sha256Hash h = new Sha256Hash(outpoint.hash);
Transaction tx = transactions.get(h);
if (tx == null)
return null;
TransactionOutput out = tx.outputs.get((int)outpoint.index);
return out;
}
/**
* Connects this input to the relevant output of the referenced transaction if it's in the given map.
* Connecting means updating the internal pointers and spent flags.
*
* @param transactions Map of txhash->transaction.
* @param disconnect Whether to abort if there's a pre-existing connection or not.
* @return true if connection took place, false if the referenced transaction was not in the list.
*/
ConnectionResult connect(Map<Sha256Hash, Transaction> transactions, boolean disconnect) {
Sha256Hash h = new Sha256Hash(outpoint.hash);
Transaction tx = transactions.get(h);
if (tx == null)
return TransactionInput.ConnectionResult.NO_SUCH_TX;
TransactionOutput out = tx.outputs.get((int)outpoint.index);
if (!out.isAvailableForSpending()) {
if (disconnect)
out.markAsUnspent();
else
return TransactionInput.ConnectionResult.ALREADY_SPENT;
}
outpoint.fromTx = tx;
out.markAsSpent(this);
return TransactionInput.ConnectionResult.SUCCESS;
}
/**
* Release the connected output, making it spendable once again.
*
* @return true if the disconnection took place, false if it was not connected.
*/
boolean disconnect() {
if (outpoint.fromTx == null) return false;
outpoint.fromTx.outputs.get((int)outpoint.index).markAsUnspent();
outpoint.fromTx = null;
return true;
}
}