Merge pull request #6064 from BlueWallet/fix-5681

ADD: support more wallet export files from Coldcard (closes #5681)
This commit is contained in:
GLaDOS 2024-01-30 12:33:36 +00:00 committed by GitHub
commit ad51a03779
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 83 additions and 1 deletions

View File

@ -219,6 +219,7 @@ export class AbstractWallet {
}
setSecret(newSecret: string): this {
const origSecret = newSecret;
this.secret = newSecret.trim().replace('bitcoin:', '').replace('BITCOIN:', '');
if (this.secret.startsWith('BC1')) this.secret = this.secret.toLowerCase();
@ -238,7 +239,7 @@ export class AbstractWallet {
if (derivationPath.startsWith("m/84'/0'/") && this.secret.toLowerCase().startsWith('xpub')) {
// need to convert xpub to zpub
this.secret = this._xpubToZpub(this.secret);
this.secret = this._xpubToZpub(this.secret.split('/')[0]);
}
if (derivationPath.startsWith("m/49'/0'/") && this.secret.toLowerCase().startsWith('xpub')) {
@ -288,6 +289,7 @@ export class AbstractWallet {
? parsedSecret.AccountKeyPath
: `m/${parsedSecret.AccountKeyPath}`;
if (parsedSecret.CoboVaultFirmwareVersion) this.use_with_hardware_wallet = true;
return this;
}
} catch (_) {}
@ -322,6 +324,31 @@ export class AbstractWallet {
}
}
// is it new-wasabi.json exported from coldcard?
try {
const json = JSON.parse(origSecret);
if (json.MasterFingerprint && json.ExtPubKey) {
// technically we should allow choosing which format user wants, BIP44 / BIP49 / BIP84, but meh...
this.secret = this._xpubToZpub(json.ExtPubKey);
const mfp = Buffer.from(json.MasterFingerprint, 'hex').reverse().toString('hex');
this.masterFingerprint = parseInt(mfp, 16);
return this;
}
} catch (_) {}
// is it sparrow-export ?
try {
const json = JSON.parse(origSecret);
if (json.chain && json.chain === 'BTC' && json.xfp && json.bip84) {
// technically we should allow choosing which format user wants, BIP44 / BIP49 / BIP84, but meh...
this.secret = json.bip84._pub;
const mfp = Buffer.from(json.xfp, 'hex').reverse().toString('hex');
this.masterFingerprint = parseInt(mfp, 16);
this._derivationPath = json.bip84.deriv;
return this;
}
} catch (_) {}
return this;
}

View File

@ -16,6 +16,7 @@ import {
} from '../../class';
import startImport from '../../class/wallet-import';
import * as BlueElectrum from '../../blue_modules/BlueElectrum';
const fs = require('fs');
jest.setTimeout(90 * 1000);
@ -504,4 +505,55 @@ describe('import procedure', () => {
assert.strictEqual(store.state.wallets[1].type, HDSegwitBech32Wallet.type);
assert.strictEqual(store.state.wallets.length, 2);
});
it('can import coldcard mk4 descriptor.txt', async () => {
const store = createStore();
const { promise } = startImport(
fs.readFileSync('tests/unit/fixtures/coldcardmk4/descriptor.txt').toString('utf8'),
false,
false,
...store.callbacks,
);
await promise;
assert.strictEqual(store.state.wallets.length, 1);
assert.strictEqual(store.state.wallets[0].type, WatchOnlyWallet.type);
assert.strictEqual(store.state.wallets[0].getMasterFingerprintHex(), '086ee178');
assert.strictEqual(store.state.wallets[0].getDerivationPath(), "m/84'/0'/0'");
assert.strictEqual(store.state.wallets[0]._getExternalAddressByIndex(0), 'bc1q5y4r767v5fzx74ez4nw36hjqrhr4ayeyut5px6');
});
it('can import coldcard mk4 new-wasabi.json', async () => {
const store = createStore();
const { promise } = startImport(
fs.readFileSync('tests/unit/fixtures/coldcardmk4/new-wasabi.json').toString('utf8'),
false,
false,
...store.callbacks,
);
await promise;
assert.strictEqual(store.state.wallets.length, 1);
assert.strictEqual(store.state.wallets[0].type, WatchOnlyWallet.type);
assert.strictEqual(store.state.wallets[0].getMasterFingerprintHex(), '086ee178');
assert.strictEqual(store.state.wallets[0].getDerivationPath(), "m/84'/0'/0'");
assert.strictEqual(store.state.wallets[0]._getExternalAddressByIndex(0), 'bc1q5y4r767v5fzx74ez4nw36hjqrhr4ayeyut5px6');
});
it('can import coldcard mk4 sparrow-export.json', async () => {
const store = createStore();
const { promise } = startImport(
fs.readFileSync('tests/unit/fixtures/coldcardmk4/sparrow-export.json').toString('utf8'),
false,
false,
...store.callbacks,
);
await promise;
assert.strictEqual(store.state.wallets.length, 1);
assert.strictEqual(store.state.wallets[0].type, WatchOnlyWallet.type);
assert.strictEqual(store.state.wallets[0].getMasterFingerprintHex(), '086ee178');
assert.strictEqual(store.state.wallets[0].getDerivationPath(), "m/84'/0'/0'");
assert.strictEqual(store.state.wallets[0]._getExternalAddressByIndex(0), 'bc1q5y4r767v5fzx74ez4nw36hjqrhr4ayeyut5px6');
});
});

View File

@ -0,0 +1 @@
wpkh([086ee178/84h/0h/0h]xpub6CqWTnie1ut9ZDD9xDeCn1VXk83VdAPSm8ZPfPNbb8w5z1e7jyy8zuX721uKj8u4GNxXqAevgEZjciUansnyz6ZhnSKyQWZwx2dpAxuCuDe/<0;1>/*)#mthwej8w

View File

@ -0,0 +1 @@
{"ColdCardFirmwareVersion": "5.2.2", "MasterFingerprint": "086EE178", "ExtPubKey": "xpub6CqWTnie1ut9ZDD9xDeCn1VXk83VdAPSm8ZPfPNbb8w5z1e7jyy8zuX721uKj8u4GNxXqAevgEZjciUansnyz6ZhnSKyQWZwx2dpAxuCuDe"}

View File

@ -0,0 +1 @@
{"chain": "BTC", "xfp": "086EE178", "account": 0, "xpub": "xpub661MyMwAqRbcFA7Da9RWMKATMVYCGLC1JMGGPXs9cWHQnUJFFJ4Eo8PVwAcZP1EPa3rJNC192YA7ZLgN8BqETEK2mky8Co8V77aXB86sW9S", "bip44": {"name": "p2pkh", "xfp": "0949B877", "deriv": "m/44'/0'/0'", "xpub": "xpub6DNdnDgd2ULEUeRfpQhqJ8S9aqxZCWi9e4YijwPX1DsVfzPRPn2fk9jy72pn7rS2SAMWgfQK1JU3WsusseeConHH2LVjPEZkhxRBhFTdX69", "desc": "pkh([086ee178/44h/0h/0h]xpub6DNdnDgd2ULEUeRfpQhqJ8S9aqxZCWi9e4YijwPX1DsVfzPRPn2fk9jy72pn7rS2SAMWgfQK1JU3WsusseeConHH2LVjPEZkhxRBhFTdX69/<0;1>/*)#estefkna", "first": "1CuhYHhWe4awuUtgX5qREo5xYaoReXPLnZ"}, "bip49": {"name": "p2sh-p2wpkh", "xfp": "17BEC390", "deriv": "m/49'/0'/0'", "xpub": "xpub6CryNpp881kryGrBGvt2PB7gxCueY5bejof1qFcW1jMXPmcUByWeuhR2PAAhdTZe5viheDXkbvLY1jewwr7e1BMdBH1GiPnJU5A6JGHyV95", "desc": "sh(wpkh([086ee178/49h/0h/0h]xpub6CryNpp881kryGrBGvt2PB7gxCueY5bejof1qFcW1jMXPmcUByWeuhR2PAAhdTZe5viheDXkbvLY1jewwr7e1BMdBH1GiPnJU5A6JGHyV95/<0;1>/*))#76em9dr6", "_pub": "ypub6XhEgVV3GhJLpa3J7HfebGDC8B46Uhb9evBEceWPPjjQSsRhSdgDXm5AQN8HdNDZVZqWPh8K4ah5u2GWfYXeoR3E3chhJJbnjoDjgnzrviL", "first": "3DCeGZNxxhQDh5kAMi1EQQcpbBpo6pocHU"}, "bip84": {"name": "p2wpkh", "xfp": "7461A59E", "deriv": "m/84'/0'/0'", "xpub": "xpub6CqWTnie1ut9ZDD9xDeCn1VXk83VdAPSm8ZPfPNbb8w5z1e7jyy8zuX721uKj8u4GNxXqAevgEZjciUansnyz6ZhnSKyQWZwx2dpAxuCuDe", "desc": "wpkh([086ee178/84h/0h/0h]xpub6CqWTnie1ut9ZDD9xDeCn1VXk83VdAPSm8ZPfPNbb8w5z1e7jyy8zuX721uKj8u4GNxXqAevgEZjciUansnyz6ZhnSKyQWZwx2dpAxuCuDe/<0;1>/*)#mthwej8w", "_pub": "zpub6rW3584UKGy7FobPcwDTCBgY64LPWQNSbMbqEBANM9gr6DGaFJJGF2qP4RpVixCu5fC9L7r3bZGqPHhiEGd1aZvuX7ipaLCvVUm6xBzBhzQ", "first": "bc1q5y4r767v5fzx74ez4nw36hjqrhr4ayeyut5px6"}, "bip48_1": {"name": "p2sh-p2wsh", "xfp": "87843A8F", "deriv": "m/48'/0'/0'/1'", "xpub": "xpub6FAiR9GaPmCUdaQsWZuFEAMHiQnucQWs3681hXX3HnFDRXaksPSpFXD7Fw1216kng1uRCU8J5ULsfyoepFcu6foFEfFHQC8cFJUMLvy8gLd", "desc": "sh(wsh(sortedmulti(M,[086ee178/48'/0'/0'/1']xpub6FAiR9GaPmCUdaQsWZuFEAMHiQnucQWs3681hXX3HnFDRXaksPSpFXD7Fw1216kng1uRCU8J5ULsfyoepFcu6foFEfFHQC8cFJUMLvy8gLd/0/*,...)))", "_pub": "Ypub6ku4r3fw7QJKuSmNHb9rGKnbcAycmPBxGUHuQBgU3ZTW6oxttSzexhjB5qv5ZSdcK86CpXiyRM5vgS2yqBBs3PbWwU47PWR6QkosKWT9sZt"}, "bip48_2": {"name": "p2wsh", "xfp": "A57AB9B4", "deriv": "m/48'/0'/0'/2'", "xpub": "xpub6FAiR9GaPmCUfYtaQCR8Q8GH3bz5h9AQX8wu7up4MUdRdpY63R1KM9jJM6AqFiRpHn5dN15ewLxomv2UZ34aXsSB4b2SpgasgwPYBTwSJW9", "desc": "wsh(sortedmulti(M,[086ee178/48'/0'/0'/2']xpub6FAiR9GaPmCUfYtaQCR8Q8GH3bz5h9AQX8wu7up4MUdRdpY63R1KM9jJM6AqFiRpHn5dN15ewLxomv2UZ34aXsSB4b2SpgasgwPYBTwSJW9/0/*,...))", "_pub": "Zpub75jL9iLrG5qoniSC1aTMeNo67LKEnjpzfde1bxsNVGDbNCjTK8iigPuWCD3UoxxZLXPDjYGtjt4QfesNHf3ZGpv3djXhPugr87nhYb91mrY"}, "bip45": {"name": "p2sh", "xfp": "5D5A20AA", "deriv": "m/45'", "xpub": "xpub67x6MAer9yqzxozdhCAd1LNV2m8c34Vahi8AfNjRu8tndg3BvTb2FpotopkC1w98q29nUnGZ1eq6vWCjGMjx5m62Jk1L2koGDjnR69C1HcK", "desc": "sh(sortedmulti(M,[086ee178/45']xpub67x6MAer9yqzxozdhCAd1LNV2m8c34Vahi8AfNjRu8tndg3BvTb2FpotopkC1w98q29nUnGZ1eq6vWCjGMjx5m62Jk1L2koGDjnR69C1HcK/0/*,...))"}}