All files / src/signer/methods signAction.ts

88.37% Statements 38/43
64.28% Branches 9/14
100% Functions 3/3
88.37% Lines 38/43

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91    57x 57x   57x   57x     2x 2x   2x     2x   2x   2x           2x     57x 1x 1x 1x 1x     57x                     2x 1x 1x 1x 1x   1x   1x 1x       2x             2x 2x         2x 2x 2x 2x 2x 2x 2x 2x           2x   2x    
/* eslint-disable @typescript-eslint/no-unused-vars */
 
import { Beef, Transaction as BsvTransaction, SignActionResult, SignActionSpend } from '@bsv/sdk'
import { asBsvSdkScript, ScriptTemplateSABPPP, sdk } from "../../index.client"
import { PendingSignAction, WalletSigner } from "../WalletSigner"
import { processAction } from './createAction'
 
export async function signAction(signer: WalletSigner, auth: sdk.AuthId, vargs: sdk.ValidSignActionArgs)
: Promise<SignActionResult>
{
  const prior = signer.pendingSignActions[vargs.reference]
  Iif (!prior)
    throw new sdk.WERR_NOT_IMPLEMENTED('recovery of out-of-session signAction reference data is not yet implemented.')
  Iif (!prior.dcr.inputBeef)
    throw new sdk.WERR_INTERNAL('prior.dcr.inputBeef must be valid')
 
  prior.tx = await completeSignedTransaction(prior, vargs.spends, signer)
 
  const sendWithResults = await processAction(prior, signer, auth, vargs)
 
  const r: SignActionResult = {
    txid: prior.tx.id('hex'),
    tx: vargs.options.returnTXIDOnly ? undefined : makeAtomicBeef(prior.tx, prior.dcr.inputBeef),
    sendWithResults
  }
 
  return r
}
 
export function makeAtomicBeef(tx: BsvTransaction, beef: number[] | Beef) : number[] {
  if (Array.isArray(beef))
    beef = Beef.fromBinary(beef)
  beef.mergeTransaction(tx)
  return beef.toBinaryAtomic(tx.id('hex'))
}
 
export async function completeSignedTransaction(
  prior: PendingSignAction,
  spends: Record<number, SignActionSpend>,
  ninja: WalletSigner,
)
: Promise<BsvTransaction>
{
 
  /////////////////////
  // Insert the user provided unlocking scripts from "spends" arg
  /////////////////////
  for (const [key, spend] of Object.entries(spends)) {
    const vin = Number(key)
    const createInput = prior.args.inputs[vin]
    const input = prior.tx.inputs[vin]
    Iif (!createInput || !input || createInput.unlockingScript || !Number.isInteger(createInput.unlockingScriptLength))
      throw new sdk.WERR_INVALID_PARAMETER('args', `spend does not correspond to prior input with valid unlockingScriptLength.`)
    Iif (spend.unlockingScript.length / 2 > createInput.unlockingScriptLength!)
      throw new sdk.WERR_INVALID_PARAMETER('args', `spend unlockingScript length ${spend.unlockingScript.length} exceeds expected length ${createInput.unlockingScriptLength}`)
    input.unlockingScript = asBsvSdkScript(spend.unlockingScript)
    Iif (spend.sequenceNumber !== undefined)
      input.sequence = spend.sequenceNumber
  }
 
  const results = {
    sdk: <SignActionResult>{}
  }
 
  /////////////////////
  // Insert SABPPP unlock templates for dojo signed inputs
  /////////////////////
  for (const pdi of prior.pdi) {
    const sabppp = new ScriptTemplateSABPPP({
      derivationPrefix: pdi.derivationPrefix,
      derivationSuffix: pdi.derivationSuffix,
      keyDeriver: ninja.keyDeriver
    })
    const keys = ninja.getClientChangeKeyPair()
    const lockerPrivKey = keys.privateKey
    const unlockerPubKey = pdi.unlockerPubKey || keys.publicKey
    const sourceSatoshis = pdi.sourceSatoshis
    const lockingScript = asBsvSdkScript(pdi.lockingScript)
    const unlockTemplate = sabppp.unlock(lockerPrivKey, unlockerPubKey, sourceSatoshis, lockingScript)
    const input = prior.tx.inputs[pdi.vin]
    input.unlockingScriptTemplate = unlockTemplate
  }
 
  /////////////////////
  // Sign dojo signed inputs making transaction fully valid.
  /////////////////////
  await prior.tx.sign()
  
  return prior.tx
}