Commit 25d25efe authored by bitcoinj-sv's avatar bitcoinj-sv

correction after seeing commits from Danconnolly

parent 581eaa78
......@@ -136,10 +136,20 @@ public class TransactionSignature extends ECKey.ECDSASignature {
return true;
}
public static boolean hasForkId (byte[] signature) {
int forkId = (signature[signature.length-1] & 0xff) & SigHash.FORKID.value; // mask the byte to prevent sign-extension hurting us
return forkId == SigHash.FORKID.value;
}
public boolean anyoneCanPay() {
return (sighashFlags & Transaction.SigHash.ANYONECANPAY.value) != 0;
}
public boolean useForkId() {
return (sighashFlags & SigHash.FORKID.value) != 0;
}
public Transaction.SigHash sigHashMode() {
final int mode = sighashFlags & 0x1f;
if (mode == Transaction.SigHash.NONE.value)
......
/*
* Copyright 2018 the bitcoinj-cash developers
*
* 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 org.bitcoinj.script;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.ScriptException;
import org.bitcoinj.core.Transaction;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Set;
/**
* A ScriptStateListener can be passed during script execution to allow visibility into the internal state of the script at each stage of execution.
*
* Note that the stacks are set as local variables in the setup stage as unModifiable lists. However they are backed by the live list in the
* script engine and as such will update on each call to the listener. Also important to note that although the lists themselves cannot
* be modified the elements are byte arrays and as such could be modified. Here be dragons!
*
* This class is not recommended for real world use. Only for testing and debugging scripts.
*
* See the tools project for an example implementation InteractiveScriptStateListener
*
* Created by shadders on 7/02/18.
*/
public abstract class ScriptStateListener {
private Transaction txContainingThis;
private long index;
private Script script;
private List<byte[]> stack;
private List<byte[]> altstack;
private List<Boolean> ifStack;
private Coin value;
private Set<Script.VerifyFlag> verifyFlags;
private int chunkIndex;
private ScriptChunk currentChunk;
private List<ScriptChunk> scriptChunks;
void setInitialState(@Nullable Transaction txContainingThis, long index,
Script script, List<byte[]> stack, List<byte[]> altstack, List<Boolean> ifStack, Coin value, Set<Script.VerifyFlag> verifyFlags) {
this.chunkIndex = -1;
this.txContainingThis = txContainingThis;
this.index = index;
this.script = script;
this.stack = stack;
this.altstack = altstack;
this.ifStack = ifStack;
this.value = value;
this.verifyFlags = verifyFlags;
this.scriptChunks = script.chunks;
}
void _onBeforeOpCodeExecuted(ScriptChunk chunk, boolean willExecute) {
chunkIndex++;
currentChunk = chunk;
onBeforeOpCodeExecuted(willExecute);
}
/**
* Called for all operations in the script. The operation may not execute if it's inside a conditional branch.
* @param willExecute true if the script engine will attempt execution.
*/
public abstract void onBeforeOpCodeExecuted(boolean willExecute);
/**
* Called after execution of an op code and all internal state is updated. Note that this may not get called for all op codes in the script.
* This will only be called if the operation does not fail and the operation is inside an executed branch of code.
*/
public abstract void onAfterOpCodeExectuted();
/**
* Used in conjunction with Script.executeDebugScript(...). This will be called if any ScriptExceptions are thrown before rethrowing the exception.
* @param exception
*/
public abstract void onExceptionThrown(ScriptException exception);
/**
* Called at the very end of the script. If this method is called the script has completed successfuly.
*/
public abstract void onScriptComplete();
public Transaction getTxContainingThis() {
return txContainingThis;
}
public long getIndex() {
return index;
}
public Script getScript() {
return script;
}
public List<byte[]> getStack() {
return stack;
}
public List<byte[]> getAltstack() {
return altstack;
}
public List<Boolean> getIfStack() {
return ifStack;
}
public Coin getValue() {
return value;
}
public Set<Script.VerifyFlag> getVerifyFlags() {
return verifyFlags;
}
/**
* @return The internally tracked index of the currently executing ScriptChunk.
*/
public int getChunkIndex() {
return chunkIndex;
}
/**
*
* @return the currently executing ScriptChunk
*/
public ScriptChunk getCurrentChunk() {
return currentChunk;
}
public List<ScriptChunk> getScriptChunks() {
return scriptChunks;
}
}
......@@ -81,9 +81,11 @@ public abstract class CustomTransactionSigner extends StatelessTransactionSigner
continue;
}
Sha256Hash sighash = tx.hashForSignature(i, redeemData.redeemScript, Transaction.SigHash.ALL, false);
Sha256Hash sighash = propTx.useForkId ?
tx.hashForSignatureWitness(i, redeemData.redeemScript, tx.getInput(i).getConnectedOutput().getValue(), Transaction.SigHash.ALL, false) :
tx.hashForSignature(i, redeemData.redeemScript, Transaction.SigHash.ALL, false);
SignatureAndKey sigKey = getSignature(sighash, propTx.keyPaths.get(scriptPubKey));
TransactionSignature txSig = new TransactionSignature(sigKey.sig, Transaction.SigHash.ALL, false);
TransactionSignature txSig = new TransactionSignature(sigKey.sig, Transaction.SigHash.ALL, false, propTx.useForkId);
int sigIndex = inputScript.getSigInsertionIndex(sighash, sigKey.pubKey);
inputScript = scriptPubKey.getScriptSigWithSignature(inputScript, txSig.encodeToBitcoin(), sigIndex);
txIn.setScriptSig(inputScript);
......
......@@ -72,7 +72,8 @@ public class LocalTransactionSigner extends StatelessTransactionSigner {
// We assume if its already signed, its hopefully got a SIGHASH type that will not invalidate when
// we sign missing pieces (to check this would require either assuming any signatures are signing
// standard output types or a way to get processed signatures out of script execution)
txIn.getScriptSig().correctlySpends(tx, i, txIn.getConnectedOutput().getScriptPubKey(), MINIMUM_VERIFY_FLAGS);
txIn.getScriptSig().correctlySpends(tx, i, txIn.getConnectedOutput().getScriptPubKey(),
txIn.getConnectedOutput().getValue(), MINIMUM_VERIFY_FLAGS);
log.warn("Input {} already correctly spends output, assuming SIGHASH type used will be safe and skipping signing.", i);
continue;
} catch (ScriptException e) {
......@@ -104,7 +105,9 @@ public class LocalTransactionSigner extends StatelessTransactionSigner {
// a CHECKMULTISIG program for P2SH inputs
byte[] script = redeemData.redeemScript.getProgram();
try {
TransactionSignature signature = tx.calculateSignature(i, key, script, Transaction.SigHash.ALL, false);
TransactionSignature signature = propTx.useForkId ?
tx.calculateWitnessSignature(i, key, script, tx.getInput(i).getConnectedOutput().getValue(), Transaction.SigHash.ALL, false) :
tx.calculateSignature(i, key, script, Transaction.SigHash.ALL, false);
// at this point we have incomplete inputScript with OP_0 in place of one or more signatures. We already
// have calculated the signature using the local key and now need to insert it in the correct place
......
......@@ -56,6 +56,14 @@ public interface TransactionSigner {
this.partialTx = partialTx;
this.keyPaths = new HashMap<Script, List<ChildNumber>>();
}
public ProposedTransaction(Transaction partialTx, boolean useForkId) {
this.partialTx = partialTx;
this.keyPaths = new HashMap<Script, List<ChildNumber>>();
this.useForkId = useForkId;
}
boolean useForkId = false;
}
class MissingSignatureException extends RuntimeException {
......
......@@ -264,4 +264,17 @@ public class SendRequest {
helper.add("shuffleOutputs", shuffleOutputs);
return helper.toString();
}
/** Use Version 2 Transactions with forkid signatures **/
private boolean useForkId = false;
public void setUseForkId(boolean useForkId) {
this.useForkId = useForkId;
if(tx != null)
tx.setVersion(Transaction.CURRENT_VERSION);
}
public boolean getUseForkId() {
return useForkId;
}
}
\ No newline at end of file
......@@ -3798,6 +3798,12 @@ public class Wallet extends BaseTaggableObject
return sendCoins(broadcaster, request);
}
public SendResult sendCoins(TransactionBroadcaster broadcaster, Address to, Coin value, boolean useforkId) throws InsufficientMoneyException {
SendRequest request = SendRequest.to(to, value);
request.setUseForkId(useforkId);
return sendCoins(broadcaster, request);
}
/**
* <p>Sends coins according to the given request, via the given {@link TransactionBroadcaster}.</p>
*
......@@ -3915,9 +3921,13 @@ public class Wallet extends BaseTaggableObject
* @throws MultipleOpReturnRequested if there is more than one OP_RETURN output for the resultant transaction.
*/
public void completeTx(SendRequest req) throws InsufficientMoneyException {
req.setUseForkId(true); // this library will always send BCH transactions
lock.lock();
try {
checkArgument(!req.completed, "Given SendRequest has already been completed.");
// set version
if(req.getUseForkId())
req.tx.setVersion(Transaction.CURRENT_VERSION);
// Calculate the amount of value we need to import.
Coin value = Coin.ZERO;
for (TransactionOutput output : req.tx.getOutputs()) {
......@@ -4027,6 +4037,7 @@ public class Wallet extends BaseTaggableObject
* transaction will be complete in the end.</p>
*/
public void signTransaction(SendRequest req) {
req.setUseForkId(true);
lock.lock();
try {
Transaction tx = req.tx;
......@@ -4049,7 +4060,8 @@ public class Wallet extends BaseTaggableObject
// We assume if its already signed, its hopefully got a SIGHASH type that will not invalidate when
// we sign missing pieces (to check this would require either assuming any signatures are signing
// standard output types or a way to get processed signatures out of script execution)
txIn.getScriptSig().correctlySpends(tx, i, txIn.getConnectedOutput().getScriptPubKey());
txIn.getScriptSig().correctlySpends(tx, i, txIn.getConnectedOutput().getScriptPubKey(),
txIn.getConnectedOutput().getValue(), Script.ALL_VERIFY_FLAGS);
log.warn("Input {} already correctly spends output, assuming SIGHASH type used will be safe and skipping signing.", i);
continue;
} catch (ScriptException e) {
......@@ -4063,7 +4075,7 @@ public class Wallet extends BaseTaggableObject
txIn.setScriptSig(scriptPubKey.createEmptyInputScript(redeemData.keys.get(0), redeemData.redeemScript));
}
TransactionSigner.ProposedTransaction proposal = new TransactionSigner.ProposedTransaction(tx);
TransactionSigner.ProposedTransaction proposal = new TransactionSigner.ProposedTransaction(tx, req.getUseForkId());
for (TransactionSigner signer : signers) {
if (!signer.signInputs(proposal, maybeDecryptingKeyBag))
log.info("{} returned false for the tx", signer.getClass().getName());
......
......@@ -2817,7 +2817,7 @@ public class WalletTest extends TestWithWallet {
assertEquals(1, request2.tx.getOutputs().size());
assertEquals(CENT, request2.tx.getOutput(0).getValue());
// Make sure it was properly signed
request2.tx.getInput(0).getScriptSig().correctlySpends(request2.tx, 0, tx3.getOutput(0).getScriptPubKey());
request2.tx.getInput(0).getScriptSig().correctlySpends(request2.tx, 0, tx3.getOutput(0).getScriptPubKey(),tx3.getOutput(0).getValue(),Script.ALL_VERIFY_FLAGS);
// However, if there is no connected output, we will grab a COIN output anyway and add the CENT to fee
SendRequest request3 = SendRequest.to(OTHER_ADDRESS, CENT);
......@@ -3243,7 +3243,8 @@ public class WalletTest extends TestWithWallet {
} else if (input.getConnectedOutput().getParentTransaction().equals(t2)) {
assertArrayEquals(expectedSig, input.getScriptSig().getChunks().get(0).data);
} else if (input.getConnectedOutput().getParentTransaction().equals(t3)) {
input.getScriptSig().correctlySpends(req.tx, i, t3.getOutput(0).getScriptPubKey());
input.getScriptSig().correctlySpends(req.tx, i, t3.getOutput(0).getScriptPubKey(),
t3.getOutput(0).getValue(),Script.ALL_VERIFY_FLAGS);
}
}
assertTrue(TransactionSignature.isEncodingCanonical(dummySig));
......
......@@ -596,6 +596,7 @@ public class WalletTool {
}
}
SendRequest req = SendRequest.forTx(t);
req.setUseForkId(true);
if (t.getOutputs().size() == 1 && t.getOutput(0).getValue().equals(wallet.getBalance())) {
log.info("Emptying out wallet, recipient may get less than what you expect");
req.emptyWallet = true;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment