Skip to content

Commit

Permalink
Implement ProtocolCash's tx ordering fix.
Browse files Browse the repository at this point in the history
  • Loading branch information
pokkst committed May 25, 2019
1 parent d34df5b commit 501b33f
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 11 deletions.
29 changes: 19 additions & 10 deletions core/src/main/java/org/bitcoinj/core/AbstractBlockChain.java
Original file line number Diff line number Diff line change
Expand Up @@ -700,18 +700,27 @@ private static void informListenerForNewTransactions(Block block, NewBlockType n
checkNotNull(filteredTxn);
// not the prettiest sorter, but it is very fast in most cases ever encountered here
List<Sha256Hash> orderedTxHashList = new ArrayList<>(filteredTxHashList);
for (Sha256Hash hash : filteredTxHashList) {
Transaction tx = filteredTxn.get(hash);
if (tx != null)
for (TransactionInput input : tx.getInputs()) {
Sha256Hash inputTx = input.getOutpoint().getHash();
// does this transaction's input depend on a transaction in our list?
if (filteredTxHashList.contains(inputTx)) {
// yes; so, remove the parent and add it to the top of the list
orderedTxHashList.remove(inputTx);
orderedTxHashList.add(0, inputTx);
for (int i = 0; i < filteredTxHashList.size(); i++) {
Sha256Hash childHash = filteredTxHashList.get(i);
Transaction childTx = filteredTxn.get(childHash);
if (childTx == null)
continue;

for (TransactionInput input : childTx.getInputs()) {
Sha256Hash parentHash = input.getOutpoint().getHash();
// does this transaction's input depend on a transaction in our list?
if (filteredTxHashList.contains(parentHash)) {
// yes; so, does the parent need to be moved above the child?
int childIndex = orderedTxHashList.indexOf(childHash);
if (childIndex < orderedTxHashList.indexOf(parentHash)) {
// yes; so, remove the parent and place it directly above the child
orderedTxHashList.remove(parentHash);
orderedTxHashList.add(childIndex, parentHash);
// step back one array element for the next loop, checking this parent is checked for a parent
i--;
}
}
}
}
// all transactions now have their parents above them
filteredTxHashList = orderedTxHashList;
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/bitcoinj/core/Block.java
Original file line number Diff line number Diff line change
Expand Up @@ -1004,7 +1004,7 @@ boolean isTransactionBytesValid() {
* purely a header).
*/
public boolean hasTransactions() {
return !this.transactions.isEmpty();
return (this.transactions != null && !this.transactions.isEmpty());
}

/**
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/java/org/bitcoinj/core/Transaction.java
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,10 @@ public TransactionOutput addOutput(Coin value, Script script) {
return addOutput(new TransactionOutput(params, this, value, script.getProgram()));
}

public TransactionOutput addData(byte[] data) {
Script script = ScriptBuilder.createOpReturnScript(data);
return addOutput(new TransactionOutput(params, this, Coin.ZERO, script.getProgram()));
}

/**
* Calculates a signature that is valid for being inserted into the input at the given position. This is simply
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,12 @@ public static TransactionSignature decodeFromBitcoin(byte[] bytes,
// isEncodingCanonical to learn more about this. So we must store the exact byte found.
return new TransactionSignature(sig.r, sig.s, bytes[bytes.length - 1]);
}

public static boolean isValidHashType(byte[] signature) {
boolean result = true;
int hashType = (signature[signature.length-1] & 0xff) & ~(Transaction.SigHash.ANYONECANPAY.value| SigHash.FORKID.value); // mask the byte to prevent sign-extension hurting us
if (hashType < Transaction.SigHash.ALL.value || hashType > Transaction.SigHash.SINGLE.value)
result = false;
return result;
}
}
7 changes: 7 additions & 0 deletions core/src/main/java/org/bitcoinj/script/Script.java
Original file line number Diff line number Diff line change
Expand Up @@ -1930,6 +1930,13 @@ public void correctlySpends(Transaction txContainingThis, long scriptSigIndex, S
if (!castToBool(p2shStack.pollLast()))
throw new ScriptException("P2SH script execution resulted in a non-true stack");
}

// The CLEANSTACK check is only performed after potential P2SH evaluation,
// as the non-P2SH evaluation of a P2SH script will obviously not result in
// a clean stack (the P2SH inputs remain).
if (verifyFlags.contains(VerifyFlag.CLEANSTACK) && verifyFlags.contains(VerifyFlag.P2SH))
if (stack.size() != 1)
throw new ScriptException("CleanStack check failed. Stack size is not 1.");
}

// Utility that doesn't copy for internal use
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/java/org/bitcoinj/wallet/Wallet.java
Original file line number Diff line number Diff line change
Expand Up @@ -3784,6 +3784,14 @@ public SendResult sendCoins(TransactionBroadcaster broadcaster, Address to, Coin
request.setUseForkId(useforkId);
return sendCoins(broadcaster, request);
}

public SendResult sendData(TransactionBroadcaster broadcaster, byte[] data, boolean useforkId) throws InsufficientMoneyException {
Transaction tx = new Transaction(params);
tx.addData(data);
SendRequest request = SendRequest.forTx(tx);
request.setUseForkId(useforkId);
return sendCoins(broadcaster, request);
}
/**
* <p>Sends coins according to the given request, via the given {@link TransactionBroadcaster}.</p>
*
Expand Down

0 comments on commit 501b33f

Please sign in to comment.