Skip to content

Commit

Permalink
chore: merge new sign-psbt code
Browse files Browse the repository at this point in the history
  • Loading branch information
AricRedemption committed Feb 5, 2024
2 parents 88c76fb + a36d56a commit d8dc026
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 48 deletions.
6 changes: 4 additions & 2 deletions src/lib/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,12 @@ export async function getSigner(chain: Chain = 'mvc', treehash?: string) {
const mnemonic = await getCurrentAccount().then((account) => account!.mnemonic)
const path = await getAccountProperty(chain, 'path')
const node = deriveBtcPrivateKey(mnemonic, path, network)
let nodeXOnlyPubkey = node.publicKey.subarray(1)
const nodeXOnlyPubkey = node.publicKey.subarray(1)
if (treehash) {
nodeXOnlyPubkey = Buffer.concat([nodeXOnlyPubkey, Buffer.from(treehash)])
const withTreehash = Buffer.concat([nodeXOnlyPubkey, Buffer.from(treehash, 'hex')])
return node.tweak(crypto.taggedHash('TapTweak', withTreehash))
}

return node.tweak(crypto.taggedHash('TapTweak', nodeXOnlyPubkey))
}
const privateKey = await getPrivateKey(chain)
Expand Down
98 changes: 52 additions & 46 deletions src/lib/actions/btc/sign-psbt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ export interface ToSignInput {
}

interface BaseUserToSignInput {
index: number;
sighashTypes: number[] | undefined;
disableTweakSigner?: boolean;
index: number
sighashTypes: number[] | undefined
disableTweakSigner?: boolean
}

export interface AddressUserToSignInput extends BaseUserToSignInput {
Expand All @@ -37,16 +37,20 @@ export interface SignPsbtOptions {
toSignInputs?: UserToSignInput[]
}

export async function process({ psbtHex, options }: {
psbtHex: string,
export async function process({
psbtHex,
options,
}: {
psbtHex: string
options?: { toSignInputs?: ToSignInput[]; autoFinalized: boolean }
}): Promise<string> {
const psbtNetwork = await getBtcNetwork()
const addressType = await getAddressType('btc')
const pubkey = await getPublicKey('btc')

if (!options) [
if (!options) {
options = { toSignInputs: undefined, autoFinalized: true }
]
}
if (!options?.toSignInputs) {
// Compatibility with legacy code.
options.toSignInputs = await formatOptionsToSignInputs(psbtHex)
Expand All @@ -56,26 +60,31 @@ export async function process({ psbtHex, options }: {
const psbt = Psbt.fromHex(psbtHex, { network: psbtNetwork })

psbt.data.inputs.forEach((v, index) => {
const isNotSigned = !(v.finalScriptSig || v.finalScriptWitness);
const isNotSigned = !(v.finalScriptSig || v.finalScriptWitness)
const isP2TR = addressType === 'P2TR'
const lostInternalPubkey = !v.tapInternalKey;
const lostInternalPubkey = !v.tapInternalKey
// Special measures taken for compatibility with certain applications.
if (isNotSigned && isP2TR && lostInternalPubkey) {
const tapInternalKey = toXOnly(Buffer.from(options!.toSignInputs![index].publicKey, 'hex'));
const tapInternalKey = toXOnly(Buffer.from(pubkey, 'hex'))
const { output } = payments.p2tr({
internalPubkey: tapInternalKey,
network: psbtNetwork
});
network: psbtNetwork,
})
if (v.witnessUtxo?.script.toString('hex') == output?.toString('hex')) {
v.tapInternalKey = tapInternalKey;
v.tapInternalKey = tapInternalKey
}
}
})

options.toSignInputs.forEach(async (v, index) => {
const keyPair = await getSigner('btc', options!.toSignInputs![index].treehash)
// options.toSignInputs.forEach(async (v, index) => {
// const keyPair = await getSigner('btc', options!.toSignInputs![index].treehash)
// psbt.signInput(v.index, keyPair, v.sighashTypes)
// })
for (let i = 0; i < options.toSignInputs.length; i++) {
const keyPair = await getSigner('btc', options!.toSignInputs![i].treehash)
const v = options.toSignInputs[i]
psbt.signInput(v.index, keyPair, v.sighashTypes)
})
}

if (options.autoFinalized) {
options.toSignInputs.forEach((v) => {
Expand All @@ -91,72 +100,69 @@ const formatOptionsToSignInputs = async (_psbt: string | Psbt, options?: SignPsb
const btcAddress = await getAddress('btc')
const account = { pubkey, address: btcAddress }

let toSignInputs: ToSignInput[] = [];
let toSignInputs: ToSignInput[] = []
if (options && options.toSignInputs) {
// We expect userToSignInputs objects to be similar to ToSignInput interface,
// but we allow address to be specified in addition to publicKey for convenience.
toSignInputs = options.toSignInputs.map((input) => {
const index = Number(input.index);
if (isNaN(index)) throw new Error('invalid index in toSignInput');
const index = Number(input.index)
if (isNaN(index)) throw new Error('invalid index in toSignInput')

if (!(input as AddressUserToSignInput).address && !(input as PublicKeyUserToSignInput).publicKey) {
throw new Error('no address or public key in toSignInput');
throw new Error('no address or public key in toSignInput')
}

if ((input as AddressUserToSignInput).address && (input as AddressUserToSignInput).address != account.address) {
throw new Error('invalid address in toSignInput');
throw new Error('invalid address in toSignInput')
}

if (
(input as PublicKeyUserToSignInput).publicKey &&
(input as PublicKeyUserToSignInput).publicKey != account.pubkey
) {
throw new Error('invalid public key in toSignInput');
throw new Error('invalid public key in toSignInput')
}

const sighashTypes = input.sighashTypes?.map(Number);
const sighashTypes = input.sighashTypes?.map(Number)

if (sighashTypes?.some(isNaN)) throw new Error('invalid sighash type in toSignInput');
if (sighashTypes?.some(isNaN)) throw new Error('invalid sighash type in toSignInput')

return {
index,
publicKey: account.pubkey,
sighashTypes,
disableTweakSigner: input.disableTweakSigner
};
});
disableTweakSigner: input.disableTweakSigner,
}
})
} else {
const networkType = await getNetwork()
const psbtNetwork = networkType === 'mainnet' ? networks.bitcoin : networks.testnet

const psbt =
typeof _psbt === 'string'
? Psbt.fromHex(_psbt as string, { network: psbtNetwork })
: (_psbt as Psbt);
const psbt = typeof _psbt === 'string' ? Psbt.fromHex(_psbt as string, { network: psbtNetwork }) : (_psbt as Psbt)
psbt.data.inputs.forEach((v, index) => {
let script: any = null;
let value = 0;
let script: any = null
let value = 0
if (v.witnessUtxo) {
script = v.witnessUtxo.script;
value = v.witnessUtxo.value;
script = v.witnessUtxo.script
value = v.witnessUtxo.value
} else if (v.nonWitnessUtxo) {
const tx = Transaction.fromBuffer(v.nonWitnessUtxo);
const output = tx.outs[psbt.txInputs[index].index];
script = output.script;
value = output.value;
const tx = Transaction.fromBuffer(v.nonWitnessUtxo)
const output = tx.outs[psbt.txInputs[index].index]
script = output.script
value = output.value
}
const isSigned = v.finalScriptSig || v.finalScriptWitness;
const isSigned = v.finalScriptSig || v.finalScriptWitness
if (script && !isSigned) {
const address = btcjs.address.fromOutputScript(script, psbtNetwork);
const address = btcjs.address.fromOutputScript(script, psbtNetwork)
if (account.address === address) {
toSignInputs.push({
index,
publicKey: account.pubkey,
sighashTypes: v.sighashType ? [v.sighashType] : undefined
});
sighashTypes: v.sighashType ? [v.sighashType] : undefined,
})
}
}
});
})
}
return toSignInputs;
};
return toSignInputs
}

0 comments on commit d8dc026

Please sign in to comment.