Commit 34521106 authored by bitcoinj-sv's avatar bitcoinj-sv

ported needed stuff for cash

parent b131cc77
...@@ -21,6 +21,9 @@ import com.google.common.base.*; ...@@ -21,6 +21,9 @@ import com.google.common.base.*;
import com.google.common.collect.*; import com.google.common.collect.*;
import com.google.common.util.concurrent.*; import com.google.common.util.concurrent.*;
import org.bitcoinj.core.listeners.*; import org.bitcoinj.core.listeners.*;
import org.bitcoinj.pow.AbstractPowRulesChecker;
import org.bitcoinj.pow.AbstractRuleCheckerFactory;
import org.bitcoinj.pow.factory.RuleCheckerFactory;
import org.bitcoinj.store.*; import org.bitcoinj.store.*;
import org.bitcoinj.utils.*; import org.bitcoinj.utils.*;
import org.bitcoinj.wallet.Wallet; import org.bitcoinj.wallet.Wallet;
...@@ -104,7 +107,7 @@ public abstract class AbstractBlockChain { ...@@ -104,7 +107,7 @@ public abstract class AbstractBlockChain {
private final CopyOnWriteArrayList<ListenerRegistration<NewBestBlockListener>> newBestBlockListeners; private final CopyOnWriteArrayList<ListenerRegistration<NewBestBlockListener>> newBestBlockListeners;
private final CopyOnWriteArrayList<ListenerRegistration<ReorganizeListener>> reorganizeListeners; private final CopyOnWriteArrayList<ListenerRegistration<ReorganizeListener>> reorganizeListeners;
private final CopyOnWriteArrayList<ListenerRegistration<TransactionReceivedInBlockListener>> transactionReceivedListeners; private final CopyOnWriteArrayList<ListenerRegistration<TransactionReceivedInBlockListener>> transactionReceivedListeners;
protected final AbstractRuleCheckerFactory ruleCheckerFactory;
// Holds a block header and, optionally, a list of tx hashes or block's transactions // Holds a block header and, optionally, a list of tx hashes or block's transactions
class OrphanBlock { class OrphanBlock {
final Block block; final Block block;
...@@ -149,7 +152,7 @@ public abstract class AbstractBlockChain { ...@@ -149,7 +152,7 @@ public abstract class AbstractBlockChain {
chainHead = blockStore.getChainHead(); chainHead = blockStore.getChainHead();
log.info("chain head is at height {}:\n{}", chainHead.getHeight(), chainHead.getHeader()); log.info("chain head is at height {}:\n{}", chainHead.getHeight(), chainHead.getHeader());
this.params = context.getParams(); this.params = context.getParams();
this.ruleCheckerFactory = RuleCheckerFactory.create(this.params);
this.newBestBlockListeners = new CopyOnWriteArrayList<ListenerRegistration<NewBestBlockListener>>(); this.newBestBlockListeners = new CopyOnWriteArrayList<ListenerRegistration<NewBestBlockListener>>();
this.reorganizeListeners = new CopyOnWriteArrayList<ListenerRegistration<ReorganizeListener>>(); this.reorganizeListeners = new CopyOnWriteArrayList<ListenerRegistration<ReorganizeListener>>();
this.transactionReceivedListeners = new CopyOnWriteArrayList<ListenerRegistration<TransactionReceivedInBlockListener>>(); this.transactionReceivedListeners = new CopyOnWriteArrayList<ListenerRegistration<TransactionReceivedInBlockListener>>();
...@@ -485,7 +488,8 @@ public abstract class AbstractBlockChain { ...@@ -485,7 +488,8 @@ public abstract class AbstractBlockChain {
} else { } else {
checkState(lock.isHeldByCurrentThread()); checkState(lock.isHeldByCurrentThread());
// It connects to somewhere on the chain. Not necessarily the top of the best known chain. // It connects to somewhere on the chain. Not necessarily the top of the best known chain.
params.checkDifficultyTransitions(storedPrev, block, blockStore); AbstractPowRulesChecker rulesChecker = ruleCheckerFactory.getRuleChecker(storedPrev, block);
rulesChecker.checkRules(storedPrev, block, blockStore, this);
connectBlock(block, storedPrev, shouldVerifyTransactions(), filteredTxHashList, filteredTxn); connectBlock(block, storedPrev, shouldVerifyTransactions(), filteredTxHashList, filteredTxn);
} }
...@@ -714,7 +718,7 @@ public abstract class AbstractBlockChain { ...@@ -714,7 +718,7 @@ public abstract class AbstractBlockChain {
/** /**
* Gets the median timestamp of the last 11 blocks * Gets the median timestamp of the last 11 blocks
*/ */
private static long getMedianTimestampOfRecentBlocks(StoredBlock storedBlock, public static long getMedianTimestampOfRecentBlocks(StoredBlock storedBlock,
BlockStore store) throws BlockStoreException { BlockStore store) throws BlockStoreException {
long[] timestamps = new long[11]; long[] timestamps = new long[11];
int unused = 9; int unused = 9;
......
...@@ -88,6 +88,13 @@ public abstract class NetworkParameters { ...@@ -88,6 +88,13 @@ public abstract class NetworkParameters {
protected int majorityRejectBlockOutdated; protected int majorityRejectBlockOutdated;
protected int majorityWindow; protected int majorityWindow;
// Aug, 1 2017 hard fork
protected int uahfHeight;
// Nov, 13 2017 hard fork
protected int daaUpdateHeight;
// May, 15 2018 hard fork
protected long monolithActivationTime = 1526400000L;
/** /**
* See getId(). This may be null for old deserialized wallets. In that case we derive it heuristically * See getId(). This may be null for old deserialized wallets. In that case we derive it heuristically
* by looking at the port number. * by looking at the port number.
...@@ -106,6 +113,7 @@ public abstract class NetworkParameters { ...@@ -106,6 +113,7 @@ public abstract class NetworkParameters {
protected HttpDiscovery.Details[] httpSeeds = {}; protected HttpDiscovery.Details[] httpSeeds = {};
protected Map<Integer, Sha256Hash> checkpoints = new HashMap<Integer, Sha256Hash>(); protected Map<Integer, Sha256Hash> checkpoints = new HashMap<Integer, Sha256Hash>();
protected transient MessageSerializer defaultSerializer = null; protected transient MessageSerializer defaultSerializer = null;
protected String cashAddrPrefix;
protected NetworkParameters() { protected NetworkParameters() {
alertSigningKey = SATOSHI_KEY; alertSigningKey = SATOSHI_KEY;
...@@ -253,9 +261,9 @@ public abstract class NetworkParameters { ...@@ -253,9 +261,9 @@ public abstract class NetworkParameters {
* Throws an exception if the block's difficulty is not correct. * Throws an exception if the block's difficulty is not correct.
* *
* @throws VerificationException if the block's difficulty is not correct. * @throws VerificationException if the block's difficulty is not correct.
*/
public abstract void checkDifficultyTransitions(StoredBlock storedPrev, Block next, final BlockStore blockStore) throws VerificationException, BlockStoreException;
public abstract void checkDifficultyTransitions(StoredBlock storedPrev, Block next, final BlockStore blockStore) throws VerificationException, BlockStoreException;
*/
/** /**
* Returns true if the block height is either not a checkpoint, or is a checkpoint and the hash matches. * Returns true if the block height is either not a checkpoint, or is a checkpoint and the hash matches.
*/ */
...@@ -495,6 +503,15 @@ public abstract class NetworkParameters { ...@@ -495,6 +503,15 @@ public abstract class NetworkParameters {
return flags; return flags;
} }
public int getDAAUpdateHeight(){
return daaUpdateHeight;
}
/** MTP activation time for May 15th, 2018 upgrade **/
public long getMonolithActivationTime() {
return monolithActivationTime;
}
/** /**
* The flags indicating which script validation tests should be applied to * The flags indicating which script validation tests should be applied to
* the given transaction. Enables support for alternative blockchains which enable * the given transaction. Enables support for alternative blockchains which enable
......
...@@ -33,6 +33,7 @@ import com.google.common.primitives.Ints; ...@@ -33,6 +33,7 @@ import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs; import com.google.common.primitives.Longs;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.spongycastle.crypto.params.KeyParameter;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.io.*; import java.io.*;
...@@ -117,6 +118,9 @@ public class Transaction extends ChildMessage { ...@@ -117,6 +118,9 @@ public class Transaction extends ChildMessage {
*/ */
public static final Coin MIN_NONDUST_OUTPUT = Coin.valueOf(546); // satoshis public static final Coin MIN_NONDUST_OUTPUT = Coin.valueOf(546); // satoshis
public static final int CURRENT_VERSION = 2;
public static final int FORKID_VERSION = 2; //Version 2 and above will require the new signature hash
/** /**
* Max initial size of inputs and outputs ArrayList. * Max initial size of inputs and outputs ArrayList.
*/ */
...@@ -197,8 +201,12 @@ public class Transaction extends ChildMessage { ...@@ -197,8 +201,12 @@ public class Transaction extends ChildMessage {
private String memo; private String memo;
public Transaction(NetworkParameters params) { public Transaction(NetworkParameters params) {
this(params, CURRENT_VERSION);
}
public Transaction(NetworkParameters params, int version) {
super(params); super(params);
version = 1; this.version = version;
inputs = new ArrayList<TransactionInput>(); inputs = new ArrayList<TransactionInput>();
outputs = new ArrayList<TransactionOutput>(); outputs = new ArrayList<TransactionOutput>();
// We don't initialize appearsIn deliberately as it's only useful for transactions stored in the wallet. // We don't initialize appearsIn deliberately as it's only useful for transactions stored in the wallet.
...@@ -225,9 +233,6 @@ public class Transaction extends ChildMessage { ...@@ -225,9 +233,6 @@ public class Transaction extends ChildMessage {
* @param params NetworkParameters object. * @param params NetworkParameters object.
* @param payload Bitcoin protocol formatted byte array containing message content. * @param payload Bitcoin protocol formatted byte array containing message content.
* @param offset The location of the first payload byte within the array. * @param offset The location of the first payload byte within the array.
* @param parseRetain Whether to retain the backing byte array for quick reserialization.
* If true and the backing byte array is invalidated due to modification of a field then
* the cached bytes may be repopulated and retained if the message is serialized again in the future.
* @param length The length of message if known. Usually this is provided when deserializing of the wire * @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 * as the length will be provided as part of the header. If unknown then set to Message.UNKNOWN_LENGTH
* @throws ProtocolException * @throws ProtocolException
...@@ -484,6 +489,7 @@ public class Transaction extends ChildMessage { ...@@ -484,6 +489,7 @@ public class Transaction extends ChildMessage {
ALL(1), ALL(1),
NONE(2), NONE(2),
SINGLE(3), SINGLE(3),
FORKID(0x40),
ANYONECANPAY(0x80), // Caution: Using this type in isolation is non-standard. Treated similar to ANYONECANPAY_ALL. ANYONECANPAY(0x80), // Caution: Using this type in isolation is non-standard. Treated similar to ANYONECANPAY_ALL.
ANYONECANPAY_ALL(0x81), ANYONECANPAY_ALL(0x81),
ANYONECANPAY_NONE(0x82), ANYONECANPAY_NONE(0x82),
...@@ -769,8 +775,8 @@ public class Transaction extends ChildMessage { ...@@ -769,8 +775,8 @@ public class Transaction extends ChildMessage {
/** /**
* Adds an input to this transaction that imports value from the given output. Note that this input is <i>not</i> * Adds an input to this transaction that imports value from the given output. Note that this input is <i>not</i>
* complete and after every input is added with {@link #addInput()} and every output is added with * complete and after every input is added with {@code addInput()} and every output is added with
* {@link #addOutput()}, a {@link TransactionSigner} must be used to finalize the transaction and finish the inputs * {@code addOutput()}, a {@link TransactionSigner} must be used to finalize the transaction and finish the inputs
* off. Otherwise it won't be accepted by the network. * off. Otherwise it won't be accepted by the network.
* @return the newly created input. * @return the newly created input.
*/ */
...@@ -814,7 +820,64 @@ public class Transaction extends ChildMessage { ...@@ -814,7 +820,64 @@ public class Transaction extends ChildMessage {
addInput(input); addInput(input);
Sha256Hash hash = hashForSignature(inputs.size() - 1, scriptPubKey, sigHash, anyoneCanPay); Sha256Hash hash = hashForSignature(inputs.size() - 1, scriptPubKey, sigHash, anyoneCanPay);
ECKey.ECDSASignature ecSig = sigKey.sign(hash); ECKey.ECDSASignature ecSig = sigKey.sign(hash);
TransactionSignature txSig = new TransactionSignature(ecSig, sigHash, anyoneCanPay); TransactionSignature txSig = new TransactionSignature(ecSig, sigHash, anyoneCanPay, false);
if (scriptPubKey.isSentToRawPubKey())
input.setScriptSig(ScriptBuilder.createInputScript(txSig));
else if (scriptPubKey.isSentToAddress())
input.setScriptSig(ScriptBuilder.createInputScript(txSig, sigKey));
else
throw new ScriptException("Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey);
return input;
}
/**
* Adds a new and fully signed input for the given parameters. Note that this method is <b>not</b> 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 a pay to address or pay to pubkey script.
*/
public TransactionInput addSignedInput(TransactionOutPoint prevOut, Script scriptPubKey, ECKey sigKey,
SigHash sigHash, boolean anyoneCanPay, boolean forkId) 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 = forkId ?
hashForSignatureWitness(inputs.size() -1, scriptPubKey, prevOut.getConnectedOutput().getValue(), sigHash, anyoneCanPay) :
hashForSignature(inputs.size() - 1, scriptPubKey, sigHash, anyoneCanPay);
ECKey.ECDSASignature ecSig = sigKey.sign(hash);
TransactionSignature txSig = new TransactionSignature(ecSig, sigHash, anyoneCanPay, forkId);
if (scriptPubKey.isSentToRawPubKey())
input.setScriptSig(ScriptBuilder.createInputScript(txSig));
else if (scriptPubKey.isSentToAddress())
input.setScriptSig(ScriptBuilder.createInputScript(txSig, sigKey));
else
throw new ScriptException("Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey);
return input;
}
/**
* Adds a new and fully signed input for the given parameters. Note that this method is <b>not</b> 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. The amount parameter is used instead of prevOut.getConnectedOutput().getValue().
*
* @throws ScriptException if the scriptPubKey is not a pay to address or pay to pubkey script.
*/
public TransactionInput addSignedInput(TransactionOutPoint prevOut, Coin amount, Script scriptPubKey, ECKey sigKey,
SigHash sigHash, boolean anyoneCanPay, boolean forkId) 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 = forkId ?
hashForSignatureWitness(inputs.size() -1, scriptPubKey, amount, sigHash, anyoneCanPay) :
hashForSignature(inputs.size() - 1, scriptPubKey, sigHash, anyoneCanPay);
ECKey.ECDSASignature ecSig = sigKey.sign(hash);
TransactionSignature txSig = new TransactionSignature(ecSig, sigHash, anyoneCanPay, forkId);
if (scriptPubKey.isSentToRawPubKey()) if (scriptPubKey.isSentToRawPubKey())
input.setScriptSig(ScriptBuilder.createInputScript(txSig)); input.setScriptSig(ScriptBuilder.createInputScript(txSig));
else if (scriptPubKey.isSentToAddress()) else if (scriptPubKey.isSentToAddress())
...@@ -832,6 +895,14 @@ public class Transaction extends ChildMessage { ...@@ -832,6 +895,14 @@ public class Transaction extends ChildMessage {
return addSignedInput(prevOut, scriptPubKey, sigKey, SigHash.ALL, false); return addSignedInput(prevOut, scriptPubKey, sigKey, SigHash.ALL, false);
} }
/**
* Same as {@link #addSignedInput(TransactionOutPoint, Coin, org.bitcoinj.script.Script, ECKey, org.bitcoinj.core.Transaction.SigHash, boolean, boolean)}
* but defaults to {@link SigHash#ALL} and "false" for the anyoneCanPay flag. This is normally what you want.
*/
public TransactionInput addSignedInput(TransactionOutPoint prevOut, Coin amount, Script scriptPubKey, ECKey sigKey) throws ScriptException {
return addSignedInput(prevOut, amount, scriptPubKey, sigKey, SigHash.ALL, false, true);
}
/** /**
* Adds an input that points to the given output and contains a valid signature for it, calculated using the * Adds an input that points to the given output and contains a valid signature for it, calculated using the
* signing key. * signing key.
...@@ -840,6 +911,14 @@ public class Transaction extends ChildMessage { ...@@ -840,6 +911,14 @@ public class Transaction extends ChildMessage {
return addSignedInput(output.getOutPointFor(), output.getScriptPubKey(), 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. Assumes forkId is true for {@link #addSignedInput(TransactionOutPoint, Coin, Script, ECKey)}
*/
public TransactionInput addSignedInput(TransactionOutput output, Coin amount, ECKey signingKey) {
return addSignedInput(output.getOutPointFor(), amount, output.getScriptPubKey(), signingKey);
}
/** /**
* Adds an input that points to the given output and contains a valid signature for it, calculated using the * Adds an input that points to the given output and contains a valid signature for it, calculated using the
* signing key. * signing key.
...@@ -848,6 +927,14 @@ public class Transaction extends ChildMessage { ...@@ -848,6 +927,14 @@ public class Transaction extends ChildMessage {
return addSignedInput(output.getOutPointFor(), output.getScriptPubKey(), signingKey, sigHash, anyoneCanPay); return addSignedInput(output.getOutPointFor(), output.getScriptPubKey(), signingKey, sigHash, anyoneCanPay);
} }
/**
* Adds an input that points to the given output and contains a valid signature for it, calculated using the
* signing key. Assumes forkId is true (sign using Bitcoin Cash Signature)
*/
public TransactionInput addSignedInput(TransactionOutput output, Coin amount, ECKey signingKey, SigHash sigHash, boolean anyoneCanPay) {
return addSignedInput(output.getOutPointFor(), amount, output.getScriptPubKey(), signingKey, sigHash, anyoneCanPay, true);
}
/** /**
* Removes all the outputs from this transaction. * Removes all the outputs from this transaction.
* Note that this also invalidates the length attribute * Note that this also invalidates the length attribute
...@@ -862,6 +949,7 @@ public class Transaction extends ChildMessage { ...@@ -862,6 +949,7 @@ public class Transaction extends ChildMessage {
this.length = this.unsafeBitcoinSerialize().length; this.length = this.unsafeBitcoinSerialize().length;
} }
/** /**
* Adds the given output to this transaction. The output must be completely initialized. Returns the given output. * Adds the given output to this transaction. The output must be completely initialized. Returns the given output.
*/ */
...@@ -917,6 +1005,18 @@ public class Transaction extends ChildMessage { ...@@ -917,6 +1005,18 @@ public class Transaction extends ChildMessage {
return new TransactionSignature(key.sign(hash), 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, true);
}
/** /**
* Calculates a signature that is valid for being inserted into the input at the given position. This is simply * 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)} * a wrapper around calling {@link Transaction#hashForSignature(int, byte[], org.bitcoinj.core.Transaction.SigHash, boolean)}
...@@ -936,6 +1036,18 @@ public class Transaction extends ChildMessage { ...@@ -936,6 +1036,18 @@ public class Transaction extends ChildMessage {
return new TransactionSignature(key.sign(hash), 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, true);
}
/** /**
* <p>Calculates a signature hash, that is, a hash of a simplified form of the transaction. How exactly the transaction * <p>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.</p> * is simplified is specified by the type and anyoneCanPay parameters.</p>
...@@ -1066,6 +1178,104 @@ public class Transaction extends ChildMessage { ...@@ -1066,6 +1178,104 @@ public class Transaction extends ChildMessage {
} }
} }
/**
* <p>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.</p>
*
* <p>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)</p>
*
* @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, true);
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 @Override
protected void bitcoinSerializeToStream(OutputStream stream) throws IOException { protected void bitcoinSerializeToStream(OutputStream stream) throws IOException {
uint32ToByteStreamLE(version, stream); uint32ToByteStreamLE(version, stream);
......
...@@ -53,7 +53,8 @@ public class Utils { ...@@ -53,7 +53,8 @@ public class Utils {
/** The string that prefixes all text messages signed using Bitcoin keys. */ /** The string that prefixes all text messages signed using Bitcoin keys. */
public static final String BITCOIN_SIGNED_MESSAGE_HEADER = "Bitcoin Signed Message:\n"; public static final String BITCOIN_SIGNED_MESSAGE_HEADER = "Bitcoin Signed Message:\n";
public static final byte[] BITCOIN_SIGNED_MESSAGE_HEADER_BYTES = BITCOIN_SIGNED_MESSAGE_HEADER.getBytes(Charsets.UTF_8); public static final byte[] BITCOIN_SIGNED_MESSAGE_HEADER_BYTES = BITCOIN_SIGNED_MESSAGE_HEADER.getBytes(Charsets.UTF_8);
// zero length arrays are immutable so we can save some object allocation by reusing the same instance.
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
private static final Joiner SPACE_JOINER = Joiner.on(" "); private static final Joiner SPACE_JOINER = Joiner.on(" ");
private static BlockingQueue<Boolean> mockSleepQueue; private static BlockingQueue<Boolean> mockSleepQueue;
...@@ -254,7 +255,102 @@ public class Utils { ...@@ -254,7 +255,102 @@ public class Utils {
BigInteger result = new BigInteger(buf); BigInteger result = new BigInteger(buf);
return isNegative ? result.negate() : result; return isNegative ? result.negate() : result;
} }
/**
* Returns a minimally encoded encoded version of the data. That is, a version will pass the check
* in checkMinimallyEncodedLE(byte[] bytesLE).
*
* If the data is already minimally encoded the original byte array will be returned.
*
* inspired by: https://reviews.bitcoinabc.org/D1219
*
* @param dataLE
* @return
*/
public static byte[] minimallyEncodeLE(byte[] dataLE) {
if (dataLE.length == 0) {
return dataLE;
}
// If the last byte is not 0x00 or 0x80, we are minimally encoded.
int last = dataLE[dataLE.length - 1];
if ((last & 0x7f) != 0) {
return dataLE;
}
// If the script is one byte long, then we have a zero, which encodes as an
// empty array.
if (dataLE.length == 1) {
return EMPTY_BYTE_ARRAY;
}
// If the next byte has it sign bit set, then we are minimaly encoded.
if ((dataLE[dataLE.length - 2] & 0x80) != 0) {
return dataLE;
}
//we might modify the array so clone it
dataLE = dataLE.clone();
// We are not minimally encoded, we need to figure out how much to trim.
// we are using i - 1 indexes here as we want to ignore the last byte (first byte in BE)
for (int i = dataLE.length - 1; i > 0; i--) {
// We found a non zero byte, time to encode.
if (dataLE[i - 1] != 0) {
if ((dataLE[i - 1] & 0x80) != 0) {
// We found a byte with it's sign bit set so we need one more
// byte.
dataLE[i++] = (byte) last;
} else {
// the sign bit is clear, we can use it.
// add the sign bit from the last byte
dataLE[i - 1] |= last;
}
return Arrays.copyOf(dataLE, i);
}
}
// If we the whole thing is zeros, then we have a zero.
return EMPTY_BYTE_ARRAY;
}
/**
* checks that LE encoded number is minimally represented. That is that there are no leading zero bytes except in
* the case: if there's more than one byte and the most significant bit of the second-most-significant-byte is set it
* would conflict with the sign bit.
* @param bytesLE
* @return
*/
public static boolean checkMinimallyEncodedLE(byte[] bytesLE, int maxNumSize) {
if (bytesLE.length > maxNumSize) {
return false;
}
if (bytesLE.length > 0) {
// Check that the number is encoded with the minimum possible number
// of bytes.
//
// If the most-significant-byte - excluding the sign bit - is zero
// then we're not minimal. Note how this test also rejects the
// negative-zero encoding, 0x80.
if ((bytesLE[bytesLE.length - 1] & 0x7f) == 0) {
// One exception: if there's more than one byte and the most
// significant bit of the second-most-significant-byte is set it
// would conflict with the sign bit. An example of this case is
// +-255, which encode to 0xff00 and 0xff80 respectively.
// (big-endian).
if (bytesLE.length <= 1 || (bytesLE[bytesLE.length - 2] & 0x80) == 0) {
return false;
}
}
}
return true;
}
/** /**
* MPI encoded numbers are produced by the OpenSSL BN_bn2mpi function. They consist of * MPI encoded numbers are produced by the OpenSSL BN_bn2mpi function. They consist of
* a 4 byte big endian length field, followed by the stated number of bytes representing * a 4 byte big endian length field, followed by the stated number of bytes representing
......
...@@ -48,6 +48,10 @@ public class TransactionSignature extends ECKey.ECDSASignature { ...@@ -48,6 +48,10 @@ public class TransactionSignature extends ECKey.ECDSASignature {
super(r, s); super(r, s);
this.sighashFlags = sighashFlags; this.sighashFlags = sighashFlags;
} }
public TransactionSignature(ECKey.ECDSASignature signature, Transaction.SigHash mode, boolean anyoneCanPay, boolean useForkId) {
super(signature.r, signature.s);
sighashFlags = calcSigHashValue(mode, anyoneCanPay, useForkId);
}
/** Constructs a transaction signature based on the ECDSA signature. */ /** Constructs a transaction signature based on the ECDSA signature. */
public TransactionSignature(ECKey.ECDSASignature signature, Transaction.SigHash mode, boolean anyoneCanPay) { public TransactionSignature(ECKey.ECDSASignature signature, Transaction.SigHash mode, boolean anyoneCanPay) {
...@@ -75,6 +79,16 @@ public class TransactionSignature extends ECKey.ECDSASignature { ...@@ -75,6 +79,16 @@ public class TransactionSignature extends ECKey.ECDSASignature {
return sighashFlags; return sighashFlags;
} }
public static int calcSigHashValue(Transaction.SigHash mode, boolean anyoneCanPay, boolean useForkId) {
Preconditions.checkArgument(SigHash.ALL == mode || SigHash.NONE == mode || SigHash.SINGLE == mode); // enforce compatibility since this code was made before the SigHash enum was updated
int sighashFlags = mode.value;
if (anyoneCanPay)
sighashFlags |= Transaction.SigHash.ANYONECANPAY.value;
if(useForkId)
sighashFlags |= SigHash.FORKID.value;
return sighashFlags;
}
/** /**
* Returns true if the given signature is has canonical encoding, and will thus be accepted as standard by * Returns true if the given signature is has canonical encoding, and will thus be accepted as standard by
* Bitcoin Core. DER and the SIGHASH encoding allow for quite some flexibility in how the same structures