Skip to content

Commit 00d6460

Browse files
OttoAllmendingerllm-git
andcommitted
feat(utxo-bin): add network support to fromFixedScript command
Add ability to specify different networks when converting wallet keys to descriptors. Maps keys to the specified network when necessary. Issue: BTC-2170 Co-authored-by: llm-git <[email protected]>
1 parent 138578a commit 00d6460

File tree

4 files changed

+68
-37
lines changed

4 files changed

+68
-37
lines changed

modules/utxo-bin/src/commands/cmdDescriptor/fromFixedScript.ts

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,51 @@
11
import { CommandModule } from 'yargs';
2+
import * as utxolib from '@bitgo/utxo-lib';
23
import { getNamedDescriptorsForRootWalletKeys } from '@bitgo/utxo-core/descriptor';
34

4-
import { FormatTreeOrJson, formatTreeOrJson, getRootWalletKeys, keyOptions, KeyOptions } from '../../args';
5+
import {
6+
FormatTreeOrJson,
7+
formatTreeOrJson,
8+
getNetworkOptionsDemand,
9+
getRootWalletKeys,
10+
keyOptions,
11+
KeyOptions,
12+
} from '../../args';
513
import { formatObjAsTree } from '../../format';
614

15+
type Triple<T> = [T, T, T];
16+
717
type ArgsFixedScriptToDescriptor = KeyOptions & {
18+
network: utxolib.Network;
819
format: FormatTreeOrJson;
920
};
1021

22+
function mapKeyToNetwork(key: utxolib.BIP32Interface, network: utxolib.Network): utxolib.BIP32Interface {
23+
key = utxolib.bip32.fromBase58(key.toBase58());
24+
key.network = network;
25+
return key;
26+
}
27+
28+
function mapRootWalletKeysToNetwork(
29+
rootWalletKeys: utxolib.bitgo.RootWalletKeys,
30+
network: utxolib.Network
31+
): utxolib.bitgo.RootWalletKeys {
32+
return new utxolib.bitgo.RootWalletKeys(
33+
rootWalletKeys.triple.map((key) => mapKeyToNetwork(key, network)) as Triple<utxolib.BIP32Interface>,
34+
rootWalletKeys.derivationPrefixes
35+
);
36+
}
37+
1138
export const cmdFromFixedScript: CommandModule<unknown, ArgsFixedScriptToDescriptor> = {
1239
command: 'fromFixedScript',
1340
describe: 'Convert BitGo FixedScript RootWalletKeys to output descriptors',
1441
builder(b) {
15-
return b.options(keyOptions).options({ format: formatTreeOrJson });
42+
return b.option(getNetworkOptionsDemand('bitcoin')).options(keyOptions).options({ format: formatTreeOrJson });
1643
},
1744
handler(argv): void {
18-
const rootWalletKeys = getRootWalletKeys(argv);
45+
let rootWalletKeys = getRootWalletKeys(argv);
46+
if (argv.network !== utxolib.networks.bitcoin) {
47+
rootWalletKeys = mapRootWalletKeysToNetwork(rootWalletKeys, argv.network);
48+
}
1949
const descriptorMap = getNamedDescriptorsForRootWalletKeys(rootWalletKeys);
2050
const obj = Object.fromEntries(
2151
[...descriptorMap].map(([name, descriptor]) => [name, descriptor?.toString() ?? null])

modules/utxo-bin/test/cmdDescriptor/fromFixedScript.ts

Lines changed: 23 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,44 +6,33 @@ import { getFixtureString } from '../fixtures';
66
import { getKeyTriple } from '../bip32.util';
77
import { captureConsole } from '../captureConsole';
88

9-
describe('cmdDescriptor fromFixedScript', function () {
10-
it('should be a yargs command', function () {
11-
assert.strictEqual(typeof cmdFromFixedScript.command, 'string');
12-
assert.strictEqual(typeof cmdFromFixedScript.describe, 'string');
13-
assert.strictEqual(typeof cmdFromFixedScript.builder, 'function');
14-
assert.strictEqual(typeof cmdFromFixedScript.handler, 'function');
15-
});
9+
function keyArgs(): string[] {
10+
const [userKey, backupKey, bitgoKey] = getKeyTriple('generateAddress').map((k) => k.neutered().toBase58());
11+
return ['--userKey', userKey, '--backupKey', backupKey, '--bitgoKey', bitgoKey, '--scriptType', 'p2sh'];
12+
}
1613

17-
it('should output expected descriptor for valid keys', async function () {
18-
const [userKey, backupKey, bitgoKey] = getKeyTriple('generateAddress').map((k) => k.neutered().toBase58());
19-
const argv = [
20-
'fromFixedScript',
21-
'--userKey',
22-
userKey,
23-
'--backupKey',
24-
backupKey,
25-
'--bitgoKey',
26-
bitgoKey,
27-
'--scriptType',
28-
'p2sh',
29-
'--network',
30-
'testnet',
31-
];
14+
describe('cmdDescriptor fromFixedScript', function () {
15+
function runTest(argv: string[], fixtureName: string) {
16+
it(`should output expected descriptor (${fixtureName})`, async function () {
17+
const y = yargs(argv)
18+
.command(cmdFromFixedScript)
19+
.exitProcess(false)
20+
.fail((msg, err) => {
21+
throw err || new Error(msg);
22+
});
3223

33-
const y = yargs(argv)
34-
.command(cmdFromFixedScript)
35-
.exitProcess(false)
36-
.fail((msg, err) => {
37-
throw err || new Error(msg);
24+
const { stdout, stderr } = await captureConsole(async () => {
25+
await y.parse();
3826
});
3927

40-
const { stdout, stderr } = await captureConsole(async () => {
41-
await y.parse();
28+
// Compare output to fixture, or check for expected descriptor substring
29+
const expected = await getFixtureString(`test/fixtures/fromFixedScript/${fixtureName}.txt`, stdout);
30+
assert.strictEqual(stdout.trim(), expected.trim());
31+
assert.strictEqual(stderr, '');
4232
});
33+
}
34+
35+
runTest(['fromFixedScript', ...keyArgs()], 'default');
4336

44-
// Compare output to fixture, or check for expected descriptor substring
45-
const expected = await getFixtureString('test/fixtures/fromFixedScript/descriptors.txt', stdout);
46-
assert.strictEqual(stdout.trim(), expected.trim());
47-
assert.strictEqual(stderr, '');
48-
});
37+
runTest(['fromFixedScript', ...keyArgs(), '--network', 'testnet'], 'network-testnet');
4938
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
descriptors
2+
├── p2sh/external: "sh(multi(2,tpubD6NzVbkrYhZ4X1jAiYEZqPSavoJ15M837vb71xr4kDnoympejW4518ioDZt8wpxSyNxuFRFehfTZqCLChmwRtvL1N58WvdtRF4Y4k7suApz/0/0/0/*,tpubD6NzVbkrYhZ4XP8gD3zSDFJUfnPnVYdGHhUvAeeQaP8CwYHuGEiCMQaNhmtwvz6tA22ymCQ565HJ5Mt37a2zweFAcEYEK52JNuVT1rWz9x8/0/0/0/*,tpubD6NzVbkrYhZ4X69NJMJcJ8NqwEVeoxxVqY7oLm1ot4b9KUH8Jtp7CNcwfvXuih78QMrigstU9Qqs6sDtB3MwvdJS15cH2TzfazoP2REqXVN/0/0/0/*))#k9dvpjv5"
3+
├── p2sh/internal: "sh(multi(2,tpubD6NzVbkrYhZ4X1jAiYEZqPSavoJ15M837vb71xr4kDnoympejW4518ioDZt8wpxSyNxuFRFehfTZqCLChmwRtvL1N58WvdtRF4Y4k7suApz/0/0/1/*,tpubD6NzVbkrYhZ4XP8gD3zSDFJUfnPnVYdGHhUvAeeQaP8CwYHuGEiCMQaNhmtwvz6tA22ymCQ565HJ5Mt37a2zweFAcEYEK52JNuVT1rWz9x8/0/0/1/*,tpubD6NzVbkrYhZ4X69NJMJcJ8NqwEVeoxxVqY7oLm1ot4b9KUH8Jtp7CNcwfvXuih78QMrigstU9Qqs6sDtB3MwvdJS15cH2TzfazoP2REqXVN/0/0/1/*))#79yt5eua"
4+
├── p2shP2wsh/external: "sh(wsh(multi(2,tpubD6NzVbkrYhZ4X1jAiYEZqPSavoJ15M837vb71xr4kDnoympejW4518ioDZt8wpxSyNxuFRFehfTZqCLChmwRtvL1N58WvdtRF4Y4k7suApz/0/0/10/*,tpubD6NzVbkrYhZ4XP8gD3zSDFJUfnPnVYdGHhUvAeeQaP8CwYHuGEiCMQaNhmtwvz6tA22ymCQ565HJ5Mt37a2zweFAcEYEK52JNuVT1rWz9x8/0/0/10/*,tpubD6NzVbkrYhZ4X69NJMJcJ8NqwEVeoxxVqY7oLm1ot4b9KUH8Jtp7CNcwfvXuih78QMrigstU9Qqs6sDtB3MwvdJS15cH2TzfazoP2REqXVN/0/0/10/*)))#5l20an83"
5+
├── p2shP2wsh/internal: "sh(wsh(multi(2,tpubD6NzVbkrYhZ4X1jAiYEZqPSavoJ15M837vb71xr4kDnoympejW4518ioDZt8wpxSyNxuFRFehfTZqCLChmwRtvL1N58WvdtRF4Y4k7suApz/0/0/11/*,tpubD6NzVbkrYhZ4XP8gD3zSDFJUfnPnVYdGHhUvAeeQaP8CwYHuGEiCMQaNhmtwvz6tA22ymCQ565HJ5Mt37a2zweFAcEYEK52JNuVT1rWz9x8/0/0/11/*,tpubD6NzVbkrYhZ4X69NJMJcJ8NqwEVeoxxVqY7oLm1ot4b9KUH8Jtp7CNcwfvXuih78QMrigstU9Qqs6sDtB3MwvdJS15cH2TzfazoP2REqXVN/0/0/11/*)))#kvxa9rda"
6+
├── p2wsh/external: "wsh(multi(2,tpubD6NzVbkrYhZ4X1jAiYEZqPSavoJ15M837vb71xr4kDnoympejW4518ioDZt8wpxSyNxuFRFehfTZqCLChmwRtvL1N58WvdtRF4Y4k7suApz/0/0/20/*,tpubD6NzVbkrYhZ4XP8gD3zSDFJUfnPnVYdGHhUvAeeQaP8CwYHuGEiCMQaNhmtwvz6tA22ymCQ565HJ5Mt37a2zweFAcEYEK52JNuVT1rWz9x8/0/0/20/*,tpubD6NzVbkrYhZ4X69NJMJcJ8NqwEVeoxxVqY7oLm1ot4b9KUH8Jtp7CNcwfvXuih78QMrigstU9Qqs6sDtB3MwvdJS15cH2TzfazoP2REqXVN/0/0/20/*))#7c7n6f3u"
7+
├── p2wsh/internal: "wsh(multi(2,tpubD6NzVbkrYhZ4X1jAiYEZqPSavoJ15M837vb71xr4kDnoympejW4518ioDZt8wpxSyNxuFRFehfTZqCLChmwRtvL1N58WvdtRF4Y4k7suApz/0/0/21/*,tpubD6NzVbkrYhZ4XP8gD3zSDFJUfnPnVYdGHhUvAeeQaP8CwYHuGEiCMQaNhmtwvz6tA22ymCQ565HJ5Mt37a2zweFAcEYEK52JNuVT1rWz9x8/0/0/21/*,tpubD6NzVbkrYhZ4X69NJMJcJ8NqwEVeoxxVqY7oLm1ot4b9KUH8Jtp7CNcwfvXuih78QMrigstU9Qqs6sDtB3MwvdJS15cH2TzfazoP2REqXVN/0/0/21/*))#350cqv2q"
8+
├── p2tr/external: null
9+
├── p2tr/internal: null
10+
├── p2trMusig2/external: null
11+
└── p2trMusig2/internal: null
12+

0 commit comments

Comments
 (0)