diff --git a/core/src/main/java/com/google/bitcoin/core/ChildMessage.java b/core/src/main/java/com/google/bitcoin/core/ChildMessage.java index ba06593a..54f21512 100644 --- a/core/src/main/java/com/google/bitcoin/core/ChildMessage.java +++ b/core/src/main/java/com/google/bitcoin/core/ChildMessage.java @@ -20,13 +20,11 @@ import javax.annotation.Nullable; /** * Represents a Message type that can be contained within another Message. ChildMessages that have a cached * backing byte array need to invalidate their parent's caches as well as their own if they are modified. - * - * @author git */ public abstract class ChildMessage extends Message { private static final long serialVersionUID = -7657113383624517931L; - @Nullable private Message parent; + @Nullable protected Message parent; protected ChildMessage() { } diff --git a/core/src/main/java/com/google/bitcoin/core/TransactionInput.java b/core/src/main/java/com/google/bitcoin/core/TransactionInput.java index 1d9844c3..77028c54 100644 --- a/core/src/main/java/com/google/bitcoin/core/TransactionInput.java +++ b/core/src/main/java/com/google/bitcoin/core/TransactionInput.java @@ -58,13 +58,11 @@ public class TransactionInput extends ChildMessage implements Serializable { /** Value of the output connected to the input, if known. This field does not participate in equals()/hashCode(). */ @Nullable private final Coin value; - // A pointer to the transaction that owns this input. - private final Transaction parentTransaction; /** * Creates an input that connects to nothing - used only in creation of coinbase transactions. */ - public TransactionInput(NetworkParameters params, Transaction parentTransaction, byte[] scriptBytes) { + public TransactionInput(NetworkParameters params, @Nullable Transaction parentTransaction, byte[] scriptBytes) { this(params, parentTransaction, scriptBytes, new TransactionOutPoint(params, NO_SEQUENCE, (Transaction) null)); } @@ -79,8 +77,8 @@ public class TransactionInput extends ChildMessage implements Serializable { this.scriptBytes = scriptBytes; this.outpoint = outpoint; this.sequence = NO_SEQUENCE; - this.parentTransaction = parentTransaction; this.value = value; + setParent(parentTransaction); length = 40 + (scriptBytes == null ? 1 : VarInt.sizeOf(scriptBytes.length) + scriptBytes.length); } @@ -90,10 +88,10 @@ public class TransactionInput extends ChildMessage implements Serializable { TransactionInput(NetworkParameters params, Transaction parentTransaction, TransactionOutput output) { super(params); long outputIndex = output.getIndex(); - outpoint = new TransactionOutPoint(params, outputIndex, output.parentTransaction); + outpoint = new TransactionOutPoint(params, outputIndex, output.getParentTransaction()); scriptBytes = EMPTY_ARRAY; sequence = NO_SEQUENCE; - this.parentTransaction = parentTransaction; + setParent(parentTransaction); this.value = output.getValue(); length = 41; } @@ -104,7 +102,7 @@ public class TransactionInput extends ChildMessage implements Serializable { public TransactionInput(NetworkParameters params, Transaction parentTransaction, byte[] payload, int offset) throws ProtocolException { super(params, payload, offset); - this.parentTransaction = parentTransaction; + setParent(checkNotNull(parentTransaction)); this.value = null; } @@ -124,7 +122,6 @@ public class TransactionInput extends ChildMessage implements Serializable { boolean parseLazy, boolean parseRetain) throws ProtocolException { super(params, payload, offset, parentTransaction, parseLazy, parseRetain, UNKNOWN_LENGTH); - this.parentTransaction = parentTransaction; this.value = null; } @@ -261,7 +258,7 @@ public class TransactionInput extends ChildMessage implements Serializable { * @return The Transaction that owns this input. */ public Transaction getParentTransaction() { - return parentTransaction; + return (Transaction) parent; } /** @@ -344,13 +341,13 @@ public class TransactionInput extends ChildMessage implements Serializable { checkElementIndex((int) outpoint.getIndex(), transaction.getOutputs().size(), "Corrupt transaction"); TransactionOutput out = transaction.getOutput((int) outpoint.getIndex()); if (!out.isAvailableForSpending()) { - if (out.parentTransaction.equals(outpoint.fromTx)) { + if (getParentTransaction().equals(outpoint.fromTx)) { // Already connected. return ConnectionResult.SUCCESS; } else if (mode == ConnectMode.DISCONNECT_ON_CONFLICT) { out.markAsUnspent(); } else if (mode == ConnectMode.ABORT_ON_CONFLICT) { - outpoint.fromTx = checkNotNull(out.parentTransaction); + outpoint.fromTx = out.getParentTransaction(); return TransactionInput.ConnectionResult.ALREADY_SPENT; } } @@ -360,7 +357,7 @@ public class TransactionInput extends ChildMessage implements Serializable { /** Internal use only: connects this TransactionInput to the given output (updates pointers and spent flags) */ public void connect(TransactionOutput out) { - outpoint.fromTx = checkNotNull(out.parentTransaction); + outpoint.fromTx = out.getParentTransaction(); out.markAsSpent(this); } @@ -421,15 +418,15 @@ public class TransactionInput extends ChildMessage implements Serializable { * @throws VerificationException If the outpoint doesn't match the given output. */ public void verify(TransactionOutput output) throws VerificationException { - if (output.parentTransaction != null) { - if (!getOutpoint().getHash().equals(output.parentTransaction.getHash())) + if (output.parent != null) { + if (!getOutpoint().getHash().equals(output.getParentTransaction().getHash())) throw new VerificationException("This input does not refer to the tx containing the output."); if (getOutpoint().getIndex() != output.getIndex()) throw new VerificationException("This input refers to a different output on the given tx."); } Script pubKey = output.getScriptPubKey(); - int myIndex = parentTransaction.getInputs().indexOf(this); - getScriptSig().correctlySpends(parentTransaction, myIndex, pubKey, true); + int myIndex = getParentTransaction().getInputs().indexOf(this); + getScriptSig().correctlySpends(getParentTransaction(), myIndex, pubKey, true); } /** @@ -458,7 +455,7 @@ public class TransactionInput extends ChildMessage implements Serializable { if (!outpoint.equals(input.outpoint)) return false; if (!Arrays.equals(scriptBytes, input.scriptBytes)) return false; if (scriptSig != null ? !scriptSig.equals(input.scriptSig) : input.scriptSig != null) return false; - if (parentTransaction != input.parentTransaction) return false; + if (parent != input.parent) return false; return true; } diff --git a/core/src/main/java/com/google/bitcoin/core/TransactionOutput.java b/core/src/main/java/com/google/bitcoin/core/TransactionOutput.java index eef3ae03..dd77f4ff 100644 --- a/core/src/main/java/com/google/bitcoin/core/TransactionOutput.java +++ b/core/src/main/java/com/google/bitcoin/core/TransactionOutput.java @@ -59,8 +59,6 @@ public class TransactionOutput extends ChildMessage implements Serializable { private boolean availableForSpending; @Nullable private TransactionInput spentBy; - // A reference to the transaction which holds this output, if any. - @Nullable Transaction parentTransaction; private transient int scriptLen; /** @@ -69,7 +67,7 @@ public class TransactionOutput extends ChildMessage implements Serializable { public TransactionOutput(NetworkParameters params, @Nullable Transaction parent, byte[] payload, int offset) throws ProtocolException { super(params, payload, offset); - parentTransaction = parent; + setParent(parent); availableForSpending = true; } @@ -88,7 +86,6 @@ public class TransactionOutput extends ChildMessage implements Serializable { public TransactionOutput(NetworkParameters params, @Nullable Transaction parent, byte[] payload, int offset, boolean parseLazy, boolean parseRetain) throws ProtocolException { super(params, payload, offset, parent, parseLazy, parseRetain, UNKNOWN_LENGTH); - parentTransaction = parent; availableForSpending = true; } @@ -118,7 +115,7 @@ public class TransactionOutput extends ChildMessage implements Serializable { checkArgument(value.compareTo(NetworkParameters.MAX_MONEY) < 0, "Values larger than MAX_MONEY not allowed"); this.value = value.value; this.scriptBytes = scriptBytes; - parentTransaction = parent; + setParent(parent); availableForSpending = true; length = 8 + VarInt.sizeOf(scriptBytes.length) + scriptBytes.length; } @@ -218,14 +215,16 @@ public class TransactionOutput extends ChildMessage implements Serializable { this.value = value.value; } - int getIndex() { - checkNotNull(parentTransaction, "This output is not attached to a parent transaction."); - for (int i = 0; i < parentTransaction.getOutputs().size(); i++) { - if (parentTransaction.getOutputs().get(i) == this) + /** + * Gets the index of this output in the parent transaction, or throws if this output is free standing. Iterates + * over the parents list to discover this. + */ + public int getIndex() { + for (int i = 0; i < getParentTransaction().getOutputs().size(); i++) { + if (getParentTransaction().getOutputs().get(i) == this) return i; } - // Should never happen. - throw new RuntimeException("Output linked to wrong parent transaction?"); + throw new IllegalStateException("Output linked to wrong parent transaction?"); } /** @@ -270,8 +269,8 @@ public class TransactionOutput extends ChildMessage implements Serializable { checkState(availableForSpending); availableForSpending = false; spentBy = input; - if (parentTransaction != null) - log.info("Marked {}:{} as spent by {}", parentTransaction.getHash(), getIndex(), input); + if (parent != null) + log.info("Marked {}:{} as spent by {}", getParentTransaction().getHash(), getIndex(), input); else log.info("Marked floating output as spent by {}", input); } @@ -280,8 +279,8 @@ public class TransactionOutput extends ChildMessage implements Serializable { * Resets the spent pointer / availableForSpending flag to null. */ public void markAsUnspent() { - if (parentTransaction != null) - log.info("Un-marked {}:{} as spent by {}", parentTransaction.getHash(), getIndex(), spentBy); + if (parent != null) + log.info("Un-marked {}:{} as spent by {}", getParentTransaction().getHash(), getIndex(), spentBy); else log.info("Un-marked floating output as spent by {}", spentBy); availableForSpending = true; @@ -388,7 +387,7 @@ public class TransactionOutput extends ChildMessage implements Serializable { * Returns the transaction that owns this output, or throws NullPointerException if unowned. */ public Transaction getParentTransaction() { - return checkNotNull(parentTransaction, "Free-standing TransactionOutput"); + return checkNotNull((Transaction) parent, "Free-standing TransactionOutput"); } /** @@ -423,14 +422,13 @@ public class TransactionOutput extends ChildMessage implements Serializable { if (!Arrays.equals(scriptBytes, other.scriptBytes)) return false; if (value != other.value) return false; - if (parentTransaction != null && parentTransaction != other.parentTransaction) return false; + if (parent != null && parent != other.parent) return false; return true; } @Override public int hashCode() { - int result = 31 * (int) value + (scriptBytes != null ? Arrays.hashCode(scriptBytes) : 0); - return result; + return 31 * (int) value + (scriptBytes != null ? Arrays.hashCode(scriptBytes) : 0); } }