Browse Source

Added PirateChainHTLC.getFundingTxid(), to lookup the txid that funded a P2SH.

pirate-chain
CalDescent 2 years ago
parent
commit
08fab451d2
  1. 63
      src/main/java/org/qortal/crosschain/PirateChainHTLC.java
  2. 26
      src/test/java/org/qortal/test/crosschain/PirateChainTests.java

63
src/main/java/org/qortal/crosschain/PirateChainHTLC.java

@ -173,6 +173,69 @@ public class PirateChainHTLC {
return null;
}
/**
* Returns a string containing the txid of the transaction that funded supplied <tt>p2shAddress</tt>
* We have to do this in a bit of a roundabout way due to the Pirate Light Client server omitting
* transaction hashes from the raw transaction data.
* <p>
* @throws ForeignBlockchainException if error occurs
*/
public static String getFundingTxid(BitcoinyBlockchainProvider blockchain, String p2shAddress) throws ForeignBlockchainException {
byte[] ourScriptPubKey = addressToScriptPubKey(p2shAddress);
// HASH160(redeem script) for this p2shAddress
byte[] ourRedeemScriptHash = addressToRedeemScriptHash(p2shAddress);
// Firstly look for an unspent output
// Note: we can't include unconfirmed transactions here because the Pirate light wallet server requires a block range
List<UnspentOutput> unspentOutputs = blockchain.getUnspentOutputs(p2shAddress, false);
for (UnspentOutput unspentOutput : unspentOutputs) {
if (!Arrays.equals(ourScriptPubKey, unspentOutput.script)) {
continue;
}
return HashCode.fromBytes(unspentOutput.hash).toString();
}
// No valid unspent outputs, so must be already spent...
// Note: we can't include unconfirmed transactions here because the Pirate light wallet server requires a block range
List<BitcoinyTransaction> transactions = blockchain.getAddressBitcoinyTransactions(p2shAddress, BitcoinyBlockchainProvider.EXCLUDE_UNCONFIRMED);
// Sort by confirmed first, followed by ascending height
transactions.sort(BitcoinyTransaction.CONFIRMED_FIRST.thenComparing(BitcoinyTransaction::getHeight));
for (BitcoinyTransaction bitcoinyTransaction : transactions) {
// Acceptable funding is one transaction output, so we're expecting only one input
if (bitcoinyTransaction.inputs.size() != 1)
// Wrong number of inputs
continue;
String scriptSig = bitcoinyTransaction.inputs.get(0).scriptSig;
List<byte[]> scriptSigChunks = extractScriptSigChunks(HashCode.fromString(scriptSig).asBytes());
if (scriptSigChunks.size() < 3 || scriptSigChunks.size() > 4)
// Not valid chunks for our form of HTLC
continue;
// Last chunk is redeem script
byte[] redeemScriptBytes = scriptSigChunks.get(scriptSigChunks.size() - 1);
byte[] redeemScriptHash = Crypto.hash160(redeemScriptBytes);
if (!Arrays.equals(redeemScriptHash, ourRedeemScriptHash))
// Not spending our specific HTLC redeem script
continue;
return bitcoinyTransaction.inputs.get(0).outputTxHash;
}
return null;
}
/**
* Returns HTLC status, given P2SH address and expected redeem/refund amount
* <p>

26
src/test/java/org/qortal/test/crosschain/PirateChainTests.java

@ -111,6 +111,32 @@ public class PirateChainTests extends Common {
assertEquals(REFUNDED, htlcStatus);
}
@Test
public void testGetTxidForUnspentAddress() throws ForeignBlockchainException {
String p2shAddress = "ba6Q5HWrWtmfU2WZqQbrFdRYsafA45cUAt";
String txid = PirateChainHTLC.getFundingTxid(pirateChain.getBlockchainProvider(), p2shAddress);
// Reverse the byte order of the txid used by block explorers, to get to big-endian form
byte[] expectedTxidLE = HashCode.fromString("fea4b0c1abcf8f0f3ddc2fa2f9438501ee102aad62a9ff18a5ce7d08774755c0").asBytes();
Bytes.reverse(expectedTxidLE);
String expectedTxidBE = HashCode.fromBytes(expectedTxidLE).toString();
assertEquals(expectedTxidBE, txid);
}
@Test
public void testGetTxidForSpentAddress() throws ForeignBlockchainException {
String p2shAddress = "bE49izfVxz8odhu8c2BcUaVFUnt7NLFRgv"; //"t3KtVxeEb8srJofo6atMEpMpEP6TjEi8VqA";
String txid = PirateChainHTLC.getFundingTxid(pirateChain.getBlockchainProvider(), p2shAddress);
// Reverse the byte order of the txid used by block explorers, to get to big-endian form
byte[] expectedTxidLE = HashCode.fromString("fb386fc8eea0fbf3ea37047726b92c39441652b32d8d62a274331687f7a1eca8").asBytes();
Bytes.reverse(expectedTxidLE);
String expectedTxidBE = HashCode.fromBytes(expectedTxidLE).toString();
assertEquals(expectedTxidBE, txid);
}
@Test
public void testGetTransactionsForAddress() throws ForeignBlockchainException {
String p2shAddress = "bE49izfVxz8odhu8c2BcUaVFUnt7NLFRgv"; //"t3KtVxeEb8srJofo6atMEpMpEP6TjEi8VqA";

Loading…
Cancel
Save