Skip to content

Commit b322360

Browse files
authored
Merge pull request #7841 from BitGo/BTC-0-revert-v1-for-now
Revert "Merge pull request #7792 from BitGo/BTC-2894-v1.psbt"
2 parents 9423f1d + 6db28d3 commit b322360

File tree

3 files changed

+3
-439
lines changed

3 files changed

+3
-439
lines changed

modules/sdk-api/src/v1/transactionBuilder.ts

Lines changed: 1 addition & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -21,46 +21,6 @@ import { common, getAddressP2PKH, getNetwork, sanitizeLegacyPath } from '@bitgo/
2121
import { verifyAddress } from './verifyAddress';
2222
import { tryPromise } from '../util';
2323

24-
type Triple<T> = [T, T, T];
25-
26-
interface V1Keychain {
27-
xpub: string;
28-
path?: string;
29-
walletSubPath?: string;
30-
}
31-
32-
/**
33-
* Parse chainPath like "/0/13" into { chain: 0, index: 13 }
34-
*/
35-
function parseChainPath(chainPath: string): { chain: number; index: number } {
36-
const parts = chainPath.split('/').filter((p) => p.length > 0);
37-
if (parts.length !== 2) {
38-
throw new Error(`Invalid chainPath: ${chainPath}`);
39-
}
40-
return { chain: parseInt(parts[0], 10), index: parseInt(parts[1], 10) };
41-
}
42-
43-
/**
44-
* Create RootWalletKeys from v1 wallet keychains.
45-
* v1 keychains have a structure like { xpub, path: 'm', walletSubPath: '/0/0' }
46-
*/
47-
function createRootWalletKeysFromV1Keychains(keychains: V1Keychain[]): utxolib.bitgo.RootWalletKeys {
48-
if (keychains.length !== 3) {
49-
throw new Error('Expected 3 keychains for v1 wallet');
50-
}
51-
52-
const bip32Keys = keychains.map((k) => bip32.fromBase58(k.xpub)) as Triple<utxolib.BIP32Interface>;
53-
54-
// v1 wallets typically have walletSubPath like '/0/0' which we convert to derivation prefixes like '0/0'
55-
const derivationPrefixes = keychains.map((k) => {
56-
const walletSubPath = k.walletSubPath || '/0/0';
57-
// Remove leading slash if present
58-
return walletSubPath.startsWith('/') ? walletSubPath.slice(1) : walletSubPath;
59-
}) as Triple<string>;
60-
61-
return new utxolib.bitgo.RootWalletKeys(bip32Keys, derivationPrefixes);
62-
}
63-
6424
interface BaseOutput {
6525
amount: number;
6626
travelInfo?: any;
@@ -275,9 +235,6 @@ exports.createTransaction = function (params) {
275235

276236
let changeOutputs: Output[] = [];
277237

278-
// All outputs for the transaction (recipients, OP_RETURNs, change, fees)
279-
let outputs: Output[] = [];
280-
281238
let containsUncompressedPublicKeys = false;
282239

283240
// The transaction.
@@ -646,8 +603,7 @@ exports.createTransaction = function (params) {
646603
throw new Error('transaction too large: estimated size ' + minerFeeInfo.size + ' bytes');
647604
}
648605

649-
// Reset outputs array (use outer scope variable)
650-
outputs = [];
606+
const outputs: Output[] = [];
651607

652608
recipients.forEach(function (recipient) {
653609
let script;
@@ -783,75 +739,8 @@ exports.createTransaction = function (params) {
783739
});
784740
};
785741

786-
// Build PSBT with all signing metadata embedded
787-
const buildPsbt = function (): utxolib.bitgo.UtxoPsbt {
788-
const psbt = utxolib.bitgo.createPsbtForNetwork({ network });
789-
790-
// Need wallet keychains for PSBT metadata
791-
const walletKeychains = params.wallet.keychains;
792-
if (!walletKeychains || walletKeychains.length !== 3) {
793-
throw new Error('Wallet keychains required for PSBT format');
794-
}
795-
796-
const rootWalletKeys = createRootWalletKeysFromV1Keychains(walletKeychains);
797-
utxolib.bitgo.addXpubsToPsbt(psbt, rootWalletKeys);
798-
799-
// Add multisig inputs with PSBT metadata
800-
for (const unspent of unspents) {
801-
const { chain, index } = parseChainPath(unspent.chainPath) as { chain: utxolib.bitgo.ChainCode; index: number };
802-
803-
const walletUnspent: utxolib.bitgo.WalletUnspent<bigint> = {
804-
id: `${unspent.tx_hash}:${unspent.tx_output_n}`,
805-
address: unspent.address,
806-
chain,
807-
index,
808-
value: BigInt(unspent.value),
809-
};
810-
811-
utxolib.bitgo.addWalletUnspentToPsbt(psbt, walletUnspent, rootWalletKeys, 'user', 'backup', {
812-
skipNonWitnessUtxo: true,
813-
});
814-
}
815-
816-
// Fee single key inputs are not supported with PSBT yet - throw to trigger fallback to legacy
817-
if (feeSingleKeyUnspentsUsed.length > 0) {
818-
throw new Error('PSBT does not support feeSingleKey inputs - use legacy transaction format');
819-
}
820-
821-
// Add outputs (recipients, change, fees, OP_RETURNs) - already calculated in outputs array
822-
for (const output of outputs) {
823-
psbt.addOutput({
824-
script: (output as ScriptOutput).script,
825-
value: BigInt(output.amount),
826-
});
827-
}
828-
829-
return psbt;
830-
};
831-
832742
// Serialize the transaction, returning what is needed to sign it
833743
const serialize = function () {
834-
// Build and return PSBT format when usePsbt is explicitly true
835-
// PSBT hex is returned in transactionHex field for backward compatibility
836-
// Use utxolib.bitgo.isPsbt() to detect if transactionHex contains PSBT or legacy tx
837-
if (params.usePsbt === true) {
838-
const psbt = buildPsbt();
839-
return {
840-
transactionHex: psbt.toHex(),
841-
fee: fee,
842-
changeAddresses: changeOutputs.map(function (co) {
843-
return _.pick(co, ['address', 'path', 'amount']);
844-
}),
845-
walletId: params.wallet.id(),
846-
feeRate: feeRate,
847-
instant: params.instant,
848-
bitgoFee: bitgoFeeInfo,
849-
estimatedSize: minerFeeInfo.size,
850-
travelInfos: travelInfos,
851-
};
852-
}
853-
854-
// Legacy format: return transactionHex with separate unspents array
855744
// only need to return the unspents that were used and just the chainPath, redeemScript, and instant flag
856745
const pickedUnspents: any = _.map(unspents, function (unspent) {
857746
return _.pick(unspent, ['chainPath', 'redeemScript', 'instant', 'witnessScript', 'script', 'value']);

modules/sdk-api/src/v1/wallet.ts

Lines changed: 2 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,6 @@ import { tryPromise } from '../util';
3131
const TransactionBuilder = require('./transactionBuilder');
3232
const PendingApproval = require('./pendingapproval');
3333

34-
// PSBT rollout: 0% on mainnet, 100% on testnet
35-
const V1_PSBT_ROLLOUT_PERCENT = 0;
36-
37-
function shouldUsePsbt(bitgo: any, explicitUsePsbt?: boolean): boolean {
38-
// Explicit setting always wins
39-
if (explicitUsePsbt !== undefined) {
40-
return explicitUsePsbt;
41-
}
42-
43-
// Testnet: always PSBT
44-
const network = common.Environments[bitgo.getEnv()]?.network;
45-
if (network !== 'bitcoin') {
46-
return true;
47-
}
48-
49-
// Mainnet: 10% rollout
50-
return Math.random() * 100 < V1_PSBT_ROLLOUT_PERCENT;
51-
}
52-
5334
const { getExternalChainCode, getInternalChainCode, isChainCode, scriptTypeForChain } = utxolib.bitgo;
5435
const request = require('superagent');
5536

@@ -913,33 +894,6 @@ Wallet.prototype.createTransaction = function (params, callback) {
913894
params.validate = params.validate !== undefined ? params.validate : this.bitgo.getValidate();
914895
params.wallet = this;
915896

916-
// Apply PSBT rollout logic (respects explicit usePsbt if set)
917-
const wantsPsbt = shouldUsePsbt(this.bitgo, params.usePsbt);
918-
919-
if (wantsPsbt) {
920-
// Try PSBT first, fall back to legacy on failure
921-
return TransactionBuilder.createTransaction({ ...params, usePsbt: true })
922-
.then((result: any) => {
923-
result.psbtAttempt = { success: true };
924-
return result;
925-
})
926-
.catch((psbtError: Error) => {
927-
// PSBT failed - fall back to legacy and capture error for backend reporting
928-
console.warn('PSBT transaction failed, falling back to legacy');
929-
return TransactionBuilder.createTransaction({ ...params, usePsbt: false }).then((result: any) => {
930-
result.psbtAttempt = {
931-
success: false,
932-
stack: psbtError.stack?.split('\n').slice(0, 5).join('\n'), // First 5 lines only
933-
};
934-
return result;
935-
});
936-
})
937-
.then(callback)
938-
.catch(callback);
939-
}
940-
941-
// Legacy path
942-
params.usePsbt = false;
943897
return TransactionBuilder.createTransaction(params).then(callback).catch(callback);
944898
};
945899

@@ -959,15 +913,8 @@ Wallet.prototype.createTransaction = function (params, callback) {
959913
Wallet.prototype.signTransaction = function (params, callback) {
960914
params = _.extend({}, params);
961915

962-
// Route to PSBT signing if params.psbt exists OR if transactionHex contains a PSBT
963-
// Use utxolib.bitgo.isPsbt() to auto-detect PSBT format in transactionHex
964-
if (params.psbt || (params.transactionHex && utxolib.bitgo.isPsbt(params.transactionHex))) {
965-
const psbtHex = params.psbt || params.transactionHex;
966-
return tryPromise(() => signPsbtRequest({ psbt: psbtHex, keychain: params.keychain }))
967-
.then(function (result) {
968-
// Return result with transactionHex containing the signed PSBT for consistency
969-
return { tx: result.psbt, transactionHex: result.psbt };
970-
})
916+
if (params.psbt) {
917+
return tryPromise(() => signPsbtRequest(params))
971918
.then(callback)
972919
.catch(callback);
973920
}
@@ -1655,7 +1602,6 @@ Wallet.prototype.accelerateTransaction = function accelerateTransaction(params,
16551602
const changeAddress = await this.createAddress({ chain: changeChain });
16561603

16571604
// create the child tx and broadcast
1658-
// Use legacy format - PSBT rollout applies to user-facing createTransaction only
16591605
// @ts-expect-error - no implicit this
16601606
const tx = await this.createAndSignTransaction({
16611607
unspents: unspentsToUse,
@@ -1672,7 +1618,6 @@ Wallet.prototype.accelerateTransaction = function accelerateTransaction(params,
16721618
},
16731619
xprv: params.xprv,
16741620
walletPassphrase: params.walletPassphrase,
1675-
usePsbt: false,
16761621
});
16771622

16781623
// child fee rate must be in sat/kB, so we need to convert
@@ -1727,7 +1672,6 @@ Wallet.prototype.createAndSignTransaction = function (params, callback) {
17271672
}
17281673

17291674
// @ts-expect-error - no implicit this
1730-
// Build transaction (legacy format by default, PSBT when usePsbt: true)
17311675
const transaction = (await this.createTransaction(params)) as any;
17321676
const fee = transaction.fee;
17331677
const feeRate = transaction.feeRate;
@@ -1760,7 +1704,6 @@ Wallet.prototype.createAndSignTransaction = function (params, callback) {
17601704

17611705
transaction.feeSingleKeyWIF = params.feeSingleKeyWIF;
17621706
// @ts-expect-error - no implicit this
1763-
// signTransaction auto-detects PSBT vs legacy from transactionHex
17641707
const result = await this.signTransaction(transaction);
17651708
return _.extend(result, {
17661709
fee,
@@ -1770,7 +1713,6 @@ Wallet.prototype.createAndSignTransaction = function (params, callback) {
17701713
travelInfos,
17711714
estimatedSize,
17721715
unspents,
1773-
psbtAttempt: transaction.psbtAttempt, // Propagate PSBT attempt info for error reporting
17741716
});
17751717
}
17761718
.call(this)

0 commit comments

Comments
 (0)