Skip to content

Commit

Permalink
Added signing of P2SH and P2WSH inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
tomaszslabon committed Sep 19, 2023
1 parent 450579a commit 53a4bbb
Showing 1 changed file with 95 additions and 32 deletions.
127 changes: 95 additions & 32 deletions typescript/src/deposit-sweep.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import bcoin from "bcoin"
import { Transaction, address, networks } from "bitcoinjs-lib"
import { Transaction, Stack, address, script, networks } from "bitcoinjs-lib"
import { BigNumber } from "ethers"
import {
RawTransaction,
Expand Down Expand Up @@ -292,39 +292,38 @@ export async function assembleDepositSweepTransactionBitcoinJsLib(
// TODO: Pass appropriate network type (testnet vs mainnet).
const ecPair = ecPairApi.fromWIF(walletPrivateKey, networks.testnet)

const transaction = new Transaction()
// Calculate the value of transaction's output. Note that the value of fee
// needs to be subtracted from the sum.
let totalInputValue = BigNumber.from(0)
if (mainUtxo) {
totalInputValue = totalInputValue.add(mainUtxo.value)
}
for (const utxo of utxos) {
totalInputValue = totalInputValue.add(utxo.value)
}
totalInputValue = totalInputValue.sub(fee)

// Create the transaction.
const transaction = new Transaction()

// Add the transaction's inputs.
if (mainUtxo) {
const prevTx = Transaction.fromHex(mainUtxo.transactionHex)
const scriptSig = prevTx.outs[mainUtxo.outputIndex].script
transaction.addInput(
mainUtxo.transactionHash.toBuffer(),
mainUtxo.transactionHash.reverse().toBuffer(),
mainUtxo.outputIndex,

Check failure on line 313 in typescript/src/deposit-sweep.ts

View workflow job for this annotation

GitHub Actions / typescript-format

Delete `,`
undefined,
scriptSig
)
totalInputValue = totalInputValue.add(mainUtxo.value)
}

for (const utxo of utxos) {
// TODO: Validate that the utxo's value is the same as the value in deposit
const prevTx = Transaction.fromHex(utxo.transactionHex)
const scriptSig = prevTx.outs[utxo.outputIndex].script
transaction.addInput(
utxo.transactionHash.toBuffer(),
utxo.outputIndex,
undefined,
scriptSig
utxo.transactionHash.reverse().toBuffer(),
utxo.outputIndex
)
totalInputValue = totalInputValue.add(utxo.value)
}

// Subtract fee from the output
totalInputValue = totalInputValue.sub(fee)

// TODO: Verify that output script is properly created from both testnet
// and mainnet addresses.
// Add transaction output.
const scriptPubKey = address.toOutputScript(walletAddress)
transaction.addOutput(scriptPubKey, totalInputValue.toNumber())

Expand All @@ -338,36 +337,41 @@ export async function assembleDepositSweepTransactionBitcoinJsLib(
}))

for (let i = 0; i < transaction.ins.length; i++) {
const previousOutput = findPreviousOutput(
TransactionHash.from(transaction.ins[i].hash).reverse(),
transaction.ins[i].index,
utxos,
mainUtxo
)
const previousOutputScript = previousOutput.script

// P2(W)PKH (main UTXO)
if (
isP2PKH(transaction.ins[i].script) ||
isP2WPKH(transaction.ins[i].script)
) {
if (isP2PKH(previousOutputScript) || isP2WPKH(previousOutputScript)) {
signMainUtxoInputBitcoinJsLib(transaction, i, ecPair)
continue
}

const utxoWithDeposit = utxosWithDeposits.find(
(u) =>
u.transactionHash.toString() ===
u.transactionHash.reverse().toString() ===
transaction.ins[i].hash.toString("hex") &&
u.outputIndex == transaction.ins[i].index
)
if (!utxoWithDeposit) {
throw new Error("Unknown input")
}

if (isP2SH(transaction.ins[i].script)) {
if (isP2SH(previousOutputScript)) {
// P2SH (deposit UTXO)
signP2SHDepositInputBitcoinJsLib(
await signP2SHDepositInputBitcoinJsLib(
transaction,
i,
utxoWithDeposit,
ecPair
)
} else if (isP2WSH(transaction.ins[i].script)) {
} else if (isP2WSH(previousOutputScript)) {
// P2WSH (deposit UTXO)
signP2WSHDepositInputBitcoinJsLib(
await signP2WSHDepositInputBitcoinJsLib(
transaction,
i,
utxoWithDeposit,
Expand All @@ -393,6 +397,27 @@ export async function assembleDepositSweepTransactionBitcoinJsLib(
}
}

function findPreviousOutput(
inputHash: TransactionHash,
inputIndex: number,
utxos: (UnspentTransactionOutput & RawTransaction)[],
mainUtxo?: UnspentTransactionOutput & RawTransaction
) {
if (mainUtxo &&

Check failure on line 406 in typescript/src/deposit-sweep.ts

View workflow job for this annotation

GitHub Actions / typescript-format

Insert `⏎····`
mainUtxo.transactionHash.equals(inputHash) &&
mainUtxo.outputIndex === inputIndex) {

Check failure on line 408 in typescript/src/deposit-sweep.ts

View workflow job for this annotation

GitHub Actions / typescript-format

Insert `⏎··`
return Transaction.fromHex(mainUtxo.transactionHex).outs[mainUtxo.outputIndex]

Check failure on line 409 in typescript/src/deposit-sweep.ts

View workflow job for this annotation

GitHub Actions / typescript-format

Replace `mainUtxo.outputIndex` with `⏎······mainUtxo.outputIndex⏎····`
}

for (const utxo of utxos) {
if (utxo.transactionHash.equals(inputHash) && utxo.outputIndex === inputIndex) {

Check failure on line 413 in typescript/src/deposit-sweep.ts

View workflow job for this annotation

GitHub Actions / typescript-format

Replace `utxo.transactionHash.equals(inputHash)·&&·utxo.outputIndex·===·inputIndex` with `⏎······utxo.transactionHash.equals(inputHash)·&&⏎······utxo.outputIndex·===·inputIndex⏎····`
return Transaction.fromHex(utxo.transactionHex).outs[utxo.outputIndex]
}
}

throw new Error("Unknown input")
}

/**
* Creates script for the transaction input at the given index and signs the
* input.
Expand Down Expand Up @@ -505,17 +530,54 @@ async function signP2SHDepositInputBitcoinJsLib(
deposit: Deposit,
ecPair: ECPairInterface
) {
// TODO: Implement
const { walletPublicKey, depositScript } =
await prepareInputSignDataBitcoinIsLib(deposit, ecPair)

const sigHashType = Transaction.SIGHASH_ALL

const sigHash = transaction.hashForSignature(
inputIndex,
depositScript,
sigHashType
)

const signature = script.signature.encode(ecPair.sign(sigHash), sigHashType)

const scriptSig: Stack = []
scriptSig.push(signature)
scriptSig.push(Buffer.from(walletPublicKey, "hex"))
scriptSig.push(depositScript)

transaction.ins[inputIndex].script = script.compile(scriptSig)
}

// TODO: Rename once the function is implemented.
async function signP2WSHDepositInputBitcoinJsLib(
transaction: Transaction,
inputIndex: number,
deposit: Deposit,
walletKeyRing: any
ecPair: ECPairInterface
) {
// TODO: Implement
const { walletPublicKey, depositScript, previousOutputValue } =
await prepareInputSignDataBitcoinIsLib(deposit, ecPair)

Check failure on line 562 in typescript/src/deposit-sweep.ts

View workflow job for this annotation

GitHub Actions / typescript-format

Insert `··`

const sigHashType = Transaction.SIGHASH_ALL

const sigHash = transaction.hashForWitnessV0(
inputIndex,
depositScript,
previousOutputValue,
sigHashType
)

const signature = script.signature.encode(ecPair.sign(sigHash), sigHashType)

const witness: Buffer[] = []
witness.push(signature)
witness.push(Buffer.from(walletPublicKey, "hex"))
witness.push(depositScript)

transaction.ins[inputIndex].witness = witness
}

async function prepareInputSignDataBitcoinIsLib(
Expand Down Expand Up @@ -544,7 +606,8 @@ async function prepareInputSignDataBitcoinIsLib(
const { amount, vault, ...depositScriptParameters } = deposit

const depositScript = Buffer.from(
await assembleDepositScript(depositScriptParameters)
await assembleDepositScript(depositScriptParameters),
"hex"
)

return {
Expand Down

0 comments on commit 53a4bbb

Please sign in to comment.