From fdee6eb37bc4f0ac170c9d064f76757b3a4d0fb4 Mon Sep 17 00:00:00 2001 From: Leon Date: Tue, 30 Apr 2024 00:54:19 +0800 Subject: [PATCH] feat: Babylon staking transaction --- .../engine/src/vaults/impl/btc/provider.ts | 45 ++++++++++++++----- .../vaults/utils/btcForkChain/KeyringHd.ts | 6 ++- .../vaults/utils/btcForkChain/VaultBtcFork.ts | 3 ++ .../kit-bg/src/providers/ProviderApiBtc.ts | 18 ++++++-- packages/shared/src/engine/engineConsts.ts | 4 +- .../ProviderApiBtc/ProviderApiBtc.types.ts | 4 +- .../ProviderApiBtc/ProviderApiBtc.utils.ts | 16 ++++++- 7 files changed, 76 insertions(+), 20 deletions(-) diff --git a/packages/engine/src/vaults/impl/btc/provider.ts b/packages/engine/src/vaults/impl/btc/provider.ts index ca50b502a19..62a9c2a85b6 100644 --- a/packages/engine/src/vaults/impl/btc/provider.ts +++ b/packages/engine/src/vaults/impl/btc/provider.ts @@ -1,5 +1,5 @@ import * as BitcoinJS from 'bitcoinjs-lib'; -import { toXOnly } from 'bitcoinjs-lib/src/psbt/bip371'; +import { isTaprootInput, toXOnly } from 'bitcoinjs-lib/src/psbt/bip371'; import { memoizee } from '@onekeyhq/shared/src/utils/cacheUtils'; @@ -25,7 +25,9 @@ function tapTweakHash(pubKey: Buffer, h: Buffer | undefined): Buffer { export function tweakSigner( privKey: Buffer, publicKey: Buffer, - opts: { tweakHash?: Buffer; network?: Network } = {}, + opts: { tweakHash?: Buffer; network?: Network; needTweak: boolean } = { + needTweak: true, + }, ): BitcoinJS.Signer { let privateKey: Uint8Array | null = new Uint8Array(privKey.buffer); if (!privateKey) { @@ -43,13 +45,17 @@ export function tweakSigner( privateKey, tapTweakHash(toXOnly(publicKey), opts.tweakHash), ); + if (!tweakedPrivateKey) { throw new Error('Invalid tweaked private key!'); } - return getBitcoinECPair().fromPrivateKey(Buffer.from(tweakedPrivateKey), { - network: opts.network, - }); + return getBitcoinECPair().fromPrivateKey( + Buffer.from(opts.needTweak ? tweakedPrivateKey : privateKey), + { + network: opts.network, + }, + ); } export default class Provider extends BaseProvider { @@ -78,13 +84,28 @@ export default class Provider extends BaseProvider { const publicKey = await signer.getPubkey(true); // P2TR - if (input.tapInternalKey) { - const privateKey = await signer.getPrvkey(); - const tweakedSigner = tweakSigner(privateKey, publicKey, { - network: this.network, - }); - - return tweakedSigner; + if (isTaprootInput(input)) { + let needTweak = true; + // script path spend + if ( + input.tapLeafScript && + input.tapLeafScript?.length > 0 && + !input.tapMerkleRoot + ) { + input.tapLeafScript.forEach((e) => { + if (e.controlBlock && e.script) { + needTweak = false; + } + }); + } + if (input.tapInternalKey) { + const privateKey = await signer.getPrvkey(); + const tweakedSigner = tweakSigner(privateKey, publicKey, { + network: this.network, + needTweak, + }); + return tweakedSigner; + } } // For other encoding diff --git a/packages/engine/src/vaults/utils/btcForkChain/KeyringHd.ts b/packages/engine/src/vaults/utils/btcForkChain/KeyringHd.ts index bcd91326e34..46210c68658 100644 --- a/packages/engine/src/vaults/utils/btcForkChain/KeyringHd.ts +++ b/packages/engine/src/vaults/utils/btcForkChain/KeyringHd.ts @@ -45,10 +45,14 @@ export class KeyringHd extends KeyringHdBase { throw new OneKeyInternalError('Software signing requires a password.'); } + const dbAccount = (await this.getDbAccount()) as DBUTXOAccount; const { transferInfo } = unsignedTx.encodedTx as IEncodedTxBtc; const signers = await this.getSigners( password, - (inputsToSign || unsignedTx.inputs).map((input) => input.address), + [ + ...(inputsToSign || unsignedTx.inputs).map((input) => input.address), + ...Object.values(dbAccount.addresses), + ], transferInfo.useCustomAddressesBalance, ); debugLogger.engine.info('signTransaction', this.networkId, unsignedTx); diff --git a/packages/engine/src/vaults/utils/btcForkChain/VaultBtcFork.ts b/packages/engine/src/vaults/utils/btcForkChain/VaultBtcFork.ts index a20aad4693f..97514b7b356 100644 --- a/packages/engine/src/vaults/utils/btcForkChain/VaultBtcFork.ts +++ b/packages/engine/src/vaults/utils/btcForkChain/VaultBtcFork.ts @@ -444,6 +444,9 @@ export default class VaultBtcFork extends VaultBase { } decodedTxToLegacy(decodedTx: IDecodedTx): Promise { + if (!decodedTx.actions || decodedTx.actions.length === 0) { + return Promise.resolve({} as IDecodedTxLegacy); + } const { type, nativeTransfer, inscriptionInfo } = decodedTx.actions[0]; if (type === IDecodedTxActionType.NATIVE_TRANSFER && nativeTransfer) { return Promise.resolve({ diff --git a/packages/kit-bg/src/providers/ProviderApiBtc.ts b/packages/kit-bg/src/providers/ProviderApiBtc.ts index fb64c5a44a0..245fb6190b8 100644 --- a/packages/kit-bg/src/providers/ProviderApiBtc.ts +++ b/packages/kit-bg/src/providers/ProviderApiBtc.ts @@ -437,7 +437,15 @@ class ProviderApiBtc extends ProviderApiBase { ) { const { psbtHexs, options } = params; - const { network } = getActiveWalletAccount(); + const { network, wallet } = getActiveWalletAccount(); + if (wallet?.type === 'hw') { + throw web3Errors.provider.custom({ + code: 4003, + message: + 'Partially signed bitcoin transactions is not supported on hardware.', + }); + } + if (!network) return null; const psbtNetwork = toPsbtNetwork(network); @@ -537,13 +545,14 @@ class ProviderApiBtc extends ProviderApiBase { psbt, psbtNetwork, account, + isBtcWalletProvider: options.isBtcWalletProvider, }); const resp = (await this.backgroundApi.serviceDapp.openSignAndSendModal( request, { encodedTx: { - inputs: decodedPsbt.inputInfos.map((v) => ({ + inputs: (decodedPsbt.inputInfos ?? []).map((v) => ({ ...v, path: '', value: v.value.toString(), @@ -553,7 +562,7 @@ class ProviderApiBtc extends ProviderApiBase { ), ), })), - outputs: decodedPsbt.outputInfos.map((v) => ({ + outputs: (decodedPsbt.outputInfos ?? []).map((v) => ({ ...v, value: v.value.toString(), inscriptions: v.inscriptions.map((i) => @@ -589,6 +598,9 @@ class ProviderApiBtc extends ProviderApiBase { }); } + if (options.isBtcWalletProvider) { + return respPsbt.extractTransaction().toHex(); + } return respPsbt.toHex(); } diff --git a/packages/shared/src/engine/engineConsts.ts b/packages/shared/src/engine/engineConsts.ts index 6465a9ba27c..8667c1fd361 100644 --- a/packages/shared/src/engine/engineConsts.ts +++ b/packages/shared/src/engine/engineConsts.ts @@ -285,4 +285,6 @@ export const isLightningNetworkByNetworkId = (networkId?: string) => networkId === OnekeyNetwork.lightning || networkId === OnekeyNetwork.tlightning; export const isBTCNetwork = (networkId?: string) => - networkId === OnekeyNetwork.btc || networkId === OnekeyNetwork.tbtc; + networkId === OnekeyNetwork.btc || + networkId === OnekeyNetwork.tbtc || + networkId === OnekeyNetwork.sbtc; diff --git a/packages/shared/src/providerApis/ProviderApiBtc/ProviderApiBtc.types.ts b/packages/shared/src/providerApis/ProviderApiBtc/ProviderApiBtc.types.ts index 47b9ae3fde0..c067b622cc6 100644 --- a/packages/shared/src/providerApis/ProviderApiBtc/ProviderApiBtc.types.ts +++ b/packages/shared/src/providerApis/ProviderApiBtc/ProviderApiBtc.types.ts @@ -43,12 +43,12 @@ export type PushTxParams = { }; export type SignPsbtParams = { psbtHex: string; - options: { autoFinalized: boolean }; + options: { autoFinalized: boolean; isBtcWalletProvider: boolean }; }; export type SignPsbtsParams = { psbtHexs: string[]; - options: { autoFinalized: boolean }; + options: { autoFinalized: boolean; isBtcWalletProvider: boolean }; }; export type PushPsbtParams = { diff --git a/packages/shared/src/providerApis/ProviderApiBtc/ProviderApiBtc.utils.ts b/packages/shared/src/providerApis/ProviderApiBtc/ProviderApiBtc.utils.ts index 42b826f6794..f6a8f2d8777 100644 --- a/packages/shared/src/providerApis/ProviderApiBtc/ProviderApiBtc.utils.ts +++ b/packages/shared/src/providerApis/ProviderApiBtc/ProviderApiBtc.utils.ts @@ -15,7 +15,11 @@ import { isBTCNetwork } from '../../engine/engineConsts'; import { NETWORK_TYPES, NetworkTypeEnum } from './ProviderApiBtc.types'; -import type { InputToSign, Inscription } from './ProviderApiBtc.types'; +import type { + InputToSign, + Inscription, + SignPsbtParams, +} from './ProviderApiBtc.types'; export const OPENAPI_URL_MAINNET = 'https://wallet-api.unisat.io/v5'; export const OPENAPI_URL_TESTNET = 'https://wallet-api-testnet.unisat.io/v5'; @@ -130,10 +134,12 @@ export function getInputsToSignFromPsbt({ psbt, psbtNetwork, account, + isBtcWalletProvider, }: { account: Account; psbt: BitcoinJS.Psbt; psbtNetwork: BitcoinJS.networks.Network; + isBtcWalletProvider: SignPsbtParams['options']['isBtcWalletProvider']; }) { const inputsToSign: InputToSign[] = []; psbt.data.inputs.forEach((v, index) => { @@ -164,6 +170,14 @@ export function getInputsToSignFromPsbt({ Buffer.from(account.pubKey as string, 'hex'), ); } + } else if (isBtcWalletProvider) { + // handle babylon + inputsToSign.push({ + index, + publicKey: account.pubKey as string, + address: account.address, + sighashTypes: v.sighashType ? [v.sighashType] : undefined, + }); } } });