3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-01-30 23:02:15 +00:00

Rename TransactionInput get/setSequence -> get/setSequenceNumber. The previous name could be confusing and was inconsistent. Add lots of comments to Transaction.hashForSignature.

This commit is contained in:
Mike Hearn 2012-11-01 14:06:59 +01:00
parent 40237f0549
commit 8d1591183f
5 changed files with 63 additions and 35 deletions

View File

@ -759,61 +759,85 @@ public class Transaction extends ChildMessage implements Serializable {
*/
synchronized Sha256Hash hashTransactionForSignature(int inputIndex, byte[] connectedScript,
byte sigHashType) throws ScriptException {
// TODO: This whole separate method should be un-necessary if we fix how we deserialize sighash flags.
// 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 sequences in case we are clearing them with SigHash.NONE/SINGLE
//
// Also store the input sequence numbers in case we are clearing them with SigHash.NONE/SINGLE
byte[][] inputScripts = new byte[inputs.size()][];
long[] inputSequences = new long[inputs.size()];
long[] inputSequenceNumbers = new long[inputs.size()];
for (int i = 0; i < inputs.size(); i++) {
inputScripts[i] = inputs.get(i).getScriptBytes();
inputSequences[i] = inputs.get(i).getSequence();
inputSequenceNumbers[i] = inputs.get(i).getSequenceNumber();
inputs.get(i).setScriptBytes(TransactionInput.EMPTY_ARRAY);
}
// "In case concatenating two scripts ends up with two codeseparators, or an extra
// one at the end, this prevents all those possible incompatibilities." - reference client
// This step has no purpose beyond being synchronized with the reference clients bugs. OP_CODESEPARATOR
// is a legacy holdover from a previous, broken design of executing scripts that shipped in Bitcoin 0.1.
// It was seriously flawed and would have let anyone take anyone elses money. Later versions switched to
// the design we use today where scripts are executed independently but share a stack. This left the
// OP_CODESEPARATOR instruction having no purpose as it was only meant to be used internally, not actually
// ever put into scripts. Deleting OP_CODESEPARATOR is a step that should never be required but if we don't
// do it, we could split off the main chain.
connectedScript = Script.removeAllInstancesOfOp(connectedScript, Script.OP_CODESEPARATOR);
// Set the input to the script of its output.
// 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).setSequence(0);
}
else if ((sigHashType & 0x1f) == (SigHash.SINGLE.ordinal() + 1)) {
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()) {
// TODO: Only allow this to happen if we are checking a signature, not signing a transactions
// Any transaction ouptut that is signed in this case will result in both the signed output
// The input index is beyond the number of outputs, it's a buggy signature made by a broken
// Bitcoin implementation. The reference client also contains a bug in handling this case:
// any transaction output that is signed in this case will result in both the signed output
// and any future outputs to this public key being steal-able by anyone who has
// the resulting signature and the public key (both of which are part of the signed tx input)
// 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).setSequence(inputSequences[i]);
inputs.get(i).setSequenceNumber(inputSequenceNumbers[i]);
}
this.outputs = outputs;
// Satoshis bug is that SignatureHash was supposed to return a hash and on this codepath it
// actually returns the constant "1" to indicate an error, which is never checked for. Oops.
return new Sha256Hash("0100000000000000000000000000000000000000000000000000000000000000");
}
// In SIGHASH_SINGLE the outputs after the matching input index are deleted, and the outputs before
// that position are "nulled out". Unintuitively, the value in a "null" transaction is set to -1.
this.outputs = new ArrayList<TransactionOutput>(this.outputs.subList(0, inputIndex));
for (int i = 0; i < inputIndex; i++)
this.outputs.set(i, new TransactionOutput(params, this, BigInteger.valueOf(-1), 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).setSequence(0);
inputs.get(i).setSequenceNumber(0);
}
ArrayList<TransactionInput> inputs = this.inputs;
if ((sigHashType & 0x80) == 0x80) {
// 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);
}
@ -831,7 +855,7 @@ public class Transaction extends ChildMessage implements Serializable {
this.inputs = inputs;
for (int i = 0; i < inputs.size(); i++) {
inputs.get(i).setScriptBytes(inputScripts[i]);
inputs.get(i).setSequence(inputSequences[i]);
inputs.get(i).setSequenceNumber(inputSequenceNumbers[i]);
}
this.outputs = outputs;
return hash;

View File

@ -179,17 +179,25 @@ public class TransactionInput extends ChildMessage implements Serializable {
}
/**
* @return Transaction version as defined by the sender. Intended for "replacement" of transactions when information is updated before inclusion into a block.
* Sequence numbers allow participants in a multi-party transaction signing protocol to create new versions of the
* transaction independently of each other. Newer versions of a transaction can replace an existing version that's
* in nodes memory pools if the existing version is time locked. See the Contracts page on the Bitcoin wiki for
* examples of how you can use this feature to build contract protocols. Note that as of 2012 the tx replacement
* feature is disabled so sequence numbers are unusable.
*/
public long getSequence() {
public long getSequenceNumber() {
maybeParse();
return sequence;
}
/**
* @param sequence Transaction version as defined by the sender. Intended for "replacement" of transactions when information is updated before inclusion into a block.
* Sequence numbers allow participants in a multi-party transaction signing protocol to create new versions of the
* transaction independently of each other. Newer versions of a transaction can replace an existing version that's
* in nodes memory pools if the existing version is time locked. See the Contracts page on the Bitcoin wiki for
* examples of how you can use this feature to build contract protocols. Note that as of 2012 the tx replacement
* feature is disabled so sequence numbers are unusable.
*/
public void setSequence(long sequence) {
public void setSequenceNumber(long sequence) {
unCache();
this.sequence = sequence;
}

View File

@ -164,7 +164,7 @@ public class WalletProtobufSerializer {
.setTransactionOutPointHash(hashToByteString(input.getOutpoint().getHash()))
.setTransactionOutPointIndex((int) input.getOutpoint().getIndex());
if (input.hasSequence()) {
inputBuilder.setSequence((int)input.getSequence());
inputBuilder.setSequence((int)input.getSequenceNumber());
}
txBuilder.addTransactionInput(inputBuilder);
}
@ -332,7 +332,7 @@ public class WalletProtobufSerializer {
);
TransactionInput input = new TransactionInput(params, tx, scriptBytes, outpoint);
if (transactionInput.hasSequence()) {
input.setSequence(transactionInput.getSequence());
input.setSequenceNumber(transactionInput.getSequence());
}
tx.addInput(input);
}

View File

@ -150,7 +150,7 @@ public class BitcoinSerializerTest {
assertEquals(!lazy, tx.isParsed());
assertEquals(true, tx.isCached());
tx.getInputs().get(0).setSequence(1);
tx.getInputs().get(0).setSequenceNumber(1);
//parent should have been uncached
assertEquals(false, tx.isCached());
//so should child
@ -177,7 +177,7 @@ public class BitcoinSerializerTest {
assertEquals(!lazy, tx.isParsed());
assertEquals(true, tx.isCached());
tx.getInputs().get(0).setSequence(tx.getInputs().get(0).getSequence());
tx.getInputs().get(0).setSequenceNumber(tx.getInputs().get(0).getSequenceNumber());
bos = new ByteArrayOutputStream();
bs.serialize(tx, bos);

View File

@ -1,16 +1,12 @@
package com.google.bitcoin.core;
import com.google.bitcoin.core.Transaction.SigHash;
import com.google.common.base.Preconditions;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import com.google.bitcoin.core.Transaction.SigHash;
import com.google.common.base.Preconditions;
import java.util.*;
class BlockAndValidity {
Block block;
@ -1102,7 +1098,7 @@ public class FullBlockTestGenerator {
private void addOnlyInputToTransaction(Transaction t, TransactionOutPointWithValue prevOut, long sequence) throws ScriptException {
TransactionInput input = new TransactionInput(params, t, new byte[]{}, prevOut.outpoint);
input.setSequence(sequence);
input.setSequenceNumber(sequence);
t.addInput(input);
byte[] connectedPubKeyScript = prevOut.scriptPubKey.program;