mirror of
https://github.com/Qortal/altcoinj.git
synced 2025-02-07 14:54: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:
parent
40237f0549
commit
8d1591183f
@ -759,61 +759,85 @@ public class Transaction extends ChildMessage implements Serializable {
|
|||||||
*/
|
*/
|
||||||
synchronized Sha256Hash hashTransactionForSignature(int inputIndex, byte[] connectedScript,
|
synchronized Sha256Hash hashTransactionForSignature(int inputIndex, byte[] connectedScript,
|
||||||
byte sigHashType) throws ScriptException {
|
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 {
|
try {
|
||||||
// Store all the input scripts and clear them in preparation for signing. If we're signing a fresh
|
// 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
|
// 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.
|
// 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()][];
|
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++) {
|
for (int i = 0; i < inputs.size(); i++) {
|
||||||
inputScripts[i] = inputs.get(i).getScriptBytes();
|
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);
|
inputs.get(i).setScriptBytes(TransactionInput.EMPTY_ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
// "In case concatenating two scripts ends up with two codeseparators, or an extra
|
// This step has no purpose beyond being synchronized with the reference clients bugs. OP_CODESEPARATOR
|
||||||
// one at the end, this prevents all those possible incompatibilities." - reference client
|
// 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);
|
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);
|
TransactionInput input = inputs.get(inputIndex);
|
||||||
input.setScriptBytes(connectedScript);
|
input.setScriptBytes(connectedScript);
|
||||||
|
|
||||||
ArrayList<TransactionOutput> outputs = this.outputs;
|
ArrayList<TransactionOutput> outputs = this.outputs;
|
||||||
if ((sigHashType & 0x1f) == (SigHash.NONE.ordinal() + 1)) {
|
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);
|
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++)
|
for (int i = 0; i < inputs.size(); i++)
|
||||||
if (i != inputIndex)
|
if (i != inputIndex)
|
||||||
inputs.get(i).setSequence(0);
|
inputs.get(i).setSequenceNumber(0);
|
||||||
}
|
} else if ((sigHashType & 0x1f) == (SigHash.SINGLE.ordinal() + 1)) {
|
||||||
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()) {
|
if (inputIndex >= this.outputs.size()) {
|
||||||
// TODO: Only allow this to happen if we are checking a signature, not signing a transactions
|
// The input index is beyond the number of outputs, it's a buggy signature made by a broken
|
||||||
// Any transaction ouptut that is signed in this case will result in both the signed output
|
// 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
|
// 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.
|
// 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++) {
|
for (int i = 0; i < inputs.size(); i++) {
|
||||||
inputs.get(i).setScriptBytes(inputScripts[i]);
|
inputs.get(i).setScriptBytes(inputScripts[i]);
|
||||||
inputs.get(i).setSequence(inputSequences[i]);
|
inputs.get(i).setSequenceNumber(inputSequenceNumbers[i]);
|
||||||
}
|
}
|
||||||
this.outputs = outputs;
|
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");
|
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));
|
this.outputs = new ArrayList<TransactionOutput>(this.outputs.subList(0, inputIndex));
|
||||||
for (int i = 0; i < inputIndex; i++)
|
for (int i = 0; i < inputIndex; i++)
|
||||||
this.outputs.set(i, new TransactionOutput(params, this, BigInteger.valueOf(-1), new byte[] {}));
|
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++)
|
for (int i = 0; i < inputs.size(); i++)
|
||||||
if (i != inputIndex)
|
if (i != inputIndex)
|
||||||
inputs.get(i).setSequence(0);
|
inputs.get(i).setSequenceNumber(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<TransactionInput> inputs = this.inputs;
|
ArrayList<TransactionInput> inputs = this.inputs;
|
||||||
if ((sigHashType & 0x80) == 0x80) {
|
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 = new ArrayList<TransactionInput>();
|
||||||
this.inputs.add(input);
|
this.inputs.add(input);
|
||||||
}
|
}
|
||||||
@ -831,7 +855,7 @@ public class Transaction extends ChildMessage implements Serializable {
|
|||||||
this.inputs = inputs;
|
this.inputs = inputs;
|
||||||
for (int i = 0; i < inputs.size(); i++) {
|
for (int i = 0; i < inputs.size(); i++) {
|
||||||
inputs.get(i).setScriptBytes(inputScripts[i]);
|
inputs.get(i).setScriptBytes(inputScripts[i]);
|
||||||
inputs.get(i).setSequence(inputSequences[i]);
|
inputs.get(i).setSequenceNumber(inputSequenceNumbers[i]);
|
||||||
}
|
}
|
||||||
this.outputs = outputs;
|
this.outputs = outputs;
|
||||||
return hash;
|
return hash;
|
||||||
|
@ -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();
|
maybeParse();
|
||||||
return sequence;
|
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();
|
unCache();
|
||||||
this.sequence = sequence;
|
this.sequence = sequence;
|
||||||
}
|
}
|
||||||
|
@ -164,7 +164,7 @@ public class WalletProtobufSerializer {
|
|||||||
.setTransactionOutPointHash(hashToByteString(input.getOutpoint().getHash()))
|
.setTransactionOutPointHash(hashToByteString(input.getOutpoint().getHash()))
|
||||||
.setTransactionOutPointIndex((int) input.getOutpoint().getIndex());
|
.setTransactionOutPointIndex((int) input.getOutpoint().getIndex());
|
||||||
if (input.hasSequence()) {
|
if (input.hasSequence()) {
|
||||||
inputBuilder.setSequence((int)input.getSequence());
|
inputBuilder.setSequence((int)input.getSequenceNumber());
|
||||||
}
|
}
|
||||||
txBuilder.addTransactionInput(inputBuilder);
|
txBuilder.addTransactionInput(inputBuilder);
|
||||||
}
|
}
|
||||||
@ -332,7 +332,7 @@ public class WalletProtobufSerializer {
|
|||||||
);
|
);
|
||||||
TransactionInput input = new TransactionInput(params, tx, scriptBytes, outpoint);
|
TransactionInput input = new TransactionInput(params, tx, scriptBytes, outpoint);
|
||||||
if (transactionInput.hasSequence()) {
|
if (transactionInput.hasSequence()) {
|
||||||
input.setSequence(transactionInput.getSequence());
|
input.setSequenceNumber(transactionInput.getSequence());
|
||||||
}
|
}
|
||||||
tx.addInput(input);
|
tx.addInput(input);
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,7 @@ public class BitcoinSerializerTest {
|
|||||||
assertEquals(!lazy, tx.isParsed());
|
assertEquals(!lazy, tx.isParsed());
|
||||||
assertEquals(true, tx.isCached());
|
assertEquals(true, tx.isCached());
|
||||||
|
|
||||||
tx.getInputs().get(0).setSequence(1);
|
tx.getInputs().get(0).setSequenceNumber(1);
|
||||||
//parent should have been uncached
|
//parent should have been uncached
|
||||||
assertEquals(false, tx.isCached());
|
assertEquals(false, tx.isCached());
|
||||||
//so should child
|
//so should child
|
||||||
@ -177,7 +177,7 @@ public class BitcoinSerializerTest {
|
|||||||
assertEquals(!lazy, tx.isParsed());
|
assertEquals(!lazy, tx.isParsed());
|
||||||
assertEquals(true, tx.isCached());
|
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();
|
bos = new ByteArrayOutputStream();
|
||||||
bs.serialize(tx, bos);
|
bs.serialize(tx, bos);
|
||||||
|
@ -1,16 +1,12 @@
|
|||||||
package com.google.bitcoin.core;
|
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.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
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;
|
|
||||||
|
|
||||||
class BlockAndValidity {
|
class BlockAndValidity {
|
||||||
Block block;
|
Block block;
|
||||||
@ -1102,7 +1098,7 @@ public class FullBlockTestGenerator {
|
|||||||
|
|
||||||
private void addOnlyInputToTransaction(Transaction t, TransactionOutPointWithValue prevOut, long sequence) throws ScriptException {
|
private void addOnlyInputToTransaction(Transaction t, TransactionOutPointWithValue prevOut, long sequence) throws ScriptException {
|
||||||
TransactionInput input = new TransactionInput(params, t, new byte[]{}, prevOut.outpoint);
|
TransactionInput input = new TransactionInput(params, t, new byte[]{}, prevOut.outpoint);
|
||||||
input.setSequence(sequence);
|
input.setSequenceNumber(sequence);
|
||||||
t.addInput(input);
|
t.addInput(input);
|
||||||
|
|
||||||
byte[] connectedPubKeyScript = prevOut.scriptPubKey.program;
|
byte[] connectedPubKeyScript = prevOut.scriptPubKey.program;
|
||||||
|
Loading…
Reference in New Issue
Block a user