UNPKG

@keystonehq/hw-app-base

Version:

The base library of Keystone hardware wallet usb support

134 lines (114 loc) 7.36 kB
import { URDecoder } from '@ngraveio/bc-ur'; import { pathToKeypath, parseResponoseUR, buildCryptoAccount, buildCryptoHDKey, convertMulitAccountToCryptoAccount, CryptoMultiAccounts, generateURString } from '../src/index'; import { CryptoHDKey } from '@keystonehq/bc-ur-registry'; describe('pathToKeypath', () => { it('should convert path to keypath', () => { const path = 'm/44\'/60\'/0\'/0/0'; const keypath = pathToKeypath(path); expect(keypath.getPath()).toBe('44\'/60\'/0\'/0/0'); }); it('should convert path to keypath with index', () => { const path = 'm/44\'/60\'/0\'/0/x'; const keypath = pathToKeypath(path, 1); expect(keypath.getPath()).toBe('44\'/60\'/0\'/0/1'); }); }); describe('parseResponoseUR', () => { it('should parse UR', () => { const ur = 'UR:ETH-SIGN-REQUEST/ONADTPDAGDGEJKFXCSVANTFDPLMTCWEYVYWDKOWZZMAOHDIYYAIEGYLALFOEASMWROSTJYLFVEHECTFYUECHFEYKDWJYFWJZIACWUTGMLAROFYPTAHNSRKAEAEAEAEAEAEAEAEAEAEAEAEHDCXTTKSWKLADAFHOYCFSBZEVLGASORPNDYAWMRHAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAEAXLGKBOXSWLAAEADLALAAXADAAADAHTAADDYOEADLECSDWYKCSFNYKAEYKAEWKAEWKAOCYGMJYFLAXPAUEFEIS'; const result = parseResponoseUR(ur); expect(result.cbor.toString('hex')).toBe('a501d825504a734318e69d48ae961b32e1ea76f2ff025866f864518082a20994b8c77482e45f1f44de1745f52c74426c631bdd5280b844a9059cbb0000000000000000000000005820d178f480253fa119cbfee349c9b69bf8ebb900000000000000000000000000000000000000000000000000038d7ea4c680000180800301040105d90130a2018a182cf5183cf500f500f400f4021a52744703'); }); }); describe('buildCryptoAccount', () => { it('should build crypto account', () => { const keys = [ { publicKey: '02d178f480253fa119cbfee349c9b69bf8ebb900000000000000000000000000', chainCode: '02d178f480253fa119cbfee349c9b69bf8ebb900000000000000000000000000', mfp: '5820d178', }, { publicKey: '12d178f480253fa119cbfee349c9b69bf8ebb900000000000000000000000000', chainCode: '12d178f480253fa119cbfee349c9b69bf8ebb900000000000000000000000000', mfp: '5820d178', }, ]; const origin = 'm/44\'/60\'/0\'/0'; const note = 'note'; const account = buildCryptoAccount({ keys, origin, note }); expect(account.getMasterFingerprint().toString('hex')).toBe('5820d178'); expect(account.getOutputDescriptors().length).toBe(2); }); }); describe('buildCryptoHDKey', () => { it('should build crypto hd key', () => { const args = { publicKey: '02d178f480253fa119cbfee349c9b69bf8ebb900000000000000000000000000', chainCode: '02d178f480253fa119cbfee349c9b69bf8ebb900000000000000000000000000', mfp: '5820d178', origin: 'm/44\'/60\'/0\'/0', originIndex: 0, note: 'note', }; const key = buildCryptoHDKey(args); expect(key.isMaster()).toBe(false); expect(key.isPrivateKey()).toBe(false); expect(key.getUseInfo()).toBe(undefined); expect(key.getOrigin().getPath()).toBe('44\'/60\'/0\'/0'); }); describe('util', () => { it('should convert multi account to crypto account', () => { const UR1 = "UR:CRYPTO-MULTI-ACCOUNTS/OXADCYBGGDRPRFAOLYTAADDLOXAXHDCLAXVTUYJZOSSTCKJPOXAEMEBBCWNTSSHKIMQDPTCFDRSSCKWMVYWZPFFDSWGHFWFMSBAAHDCXOSHDAHRONSGDKKRDJYWLAEDWFMHDLBKEMSWDLACSPYCAGMFEIALEFEUEFNLRHYBDAMTAADDYOYADLNCSHFYKAEYKAEYKAYCYAALRAEGTAXJTGRIHKKJKJYJLJTIHCXEOCXGDJPJLAHIHEHDMEHDMDYHTYLLGAH"; const UR2 = "UR:CRYPTO-MULTI-ACCOUNTS/OXADCYBGGDRPRFAOLYTAADDLOXAXHDCLAOIDRDFWGHGESFFWCNSTFEBYQDKGPLGHUECPHFHEDAYAAXGWHTYAPKBENBGSENCLRHAAHDCXUEFLUTCHIOMTAHROGERSFXNDURGHKTSWSBMWZMHTGHCAFNPAJKGEZERSOTTEZCRHAMTAADDYOYADLNCSGHYKAEYKAEYKAYCYNEJEKBGRAXJTGRIHKKJKJYJLJTIHCXEOCXGDJPJLAHIHEHDMEHDMDYMNTNFYUE"; let URs = [UR1, UR2].map(eachUR => { const decoder = new URDecoder(); decoder.receivePart(eachUR); if (!decoder.isComplete()) { throw new Error('UR is incomplete'); } const resultUR = decoder.resultUR(); return CryptoMultiAccounts.fromCBOR(resultUR.cbor); }); let ca = convertMulitAccountToCryptoAccount(URs) expect(ca.getRegistryType().getType()).toBe('crypto-account') expect(ca.getOutputDescriptors().length).toBe(2) expect(ca.getMasterFingerprint().toString('hex')).toBe('1250b6bc') let outputDescriptors = ca.getOutputDescriptors() let key1 = outputDescriptors[0].getCryptoKey() as CryptoHDKey let key2 = outputDescriptors[1].getCryptoKey() as CryptoHDKey expect(key1.getBip32Key()).toBe('xpub6BghCcrqiqZKVhbMggpicCo9cziXGGyde4uW2adRcH6aLNYYPGL6vQRYS64pAp1cgCidiZ7zwGTHN2NmF4aJWijenLYmSeTVsyrauhWJDjA') expect(key2.getBip32Key()).toBe('xpub6CpjN9cV2eSSHvzA5113pRqD5qaWRhUXS7ABs5AqGiau3BbAnW2fx1JwEEwn9ugVAgx6vbpXAKEjQbKjYHHPCjaxHEwyfLcUvwxjbaBEPRe') let one = convertMulitAccountToCryptoAccount([URs[0]]) expect(one.getRegistryType().getType()).toBe('crypto-account') expect(one.getOutputDescriptors().length).toBe(1) expect(one.getMasterFingerprint().toString('hex')).toBe('1250b6bc') let keyOne = outputDescriptors[0].getCryptoKey() as CryptoHDKey expect(keyOne.getBip32Key()).toBe('xpub6BghCcrqiqZKVhbMggpicCo9cziXGGyde4uW2adRcH6aLNYYPGL6vQRYS64pAp1cgCidiZ7zwGTHN2NmF4aJWijenLYmSeTVsyrauhWJDjA') }); it('should throw error when mfp is not the same', () => { const UR1 = "ur:crypto-multi-accounts/onadcywlcscewfaolytaaddloeaxhdclaowdverokopdinhseeroisyalksaykctjshedprnuyjyfgrovawewftyghceglrpkgamtaaddyoyadlocsdwykcfadykykaeykaeykaxisjeihkkjkjyjljtihaaksdeeyeteeemeciaetieetdyiyeniadyenidhsiyidiheeenhsemieehemecdyiyeoiyiaiyeyeceneciyemahihehdmdydmeyksrlzmdi" const UR2 = "UR:CRYPTO-MULTI-ACCOUNTS/OXADCYBGGDRPRFAOLYTAADDLOXAXHDCLAOIDRDFWGHGESFFWCNSTFEBYQDKGPLGHUECPHFHEDAYAAXGWHTYAPKBENBGSENCLRHAAHDCXUEFLUTCHIOMTAHROGERSFXNDURGHKTSWSBMWZMHTGHCAFNPAJKGEZERSOTTEZCRHAMTAADDYOYADLNCSGHYKAEYKAEYKAYCYNEJEKBGRAXJTGRIHKKJKJYJLJTIHCXEOCXGDJPJLAHIHEHDMEHDMDYMNTNFYUE"; let URs = [UR1, UR2].map(eachUR => { const decoder = new URDecoder(); decoder.receivePart(eachUR); if (!decoder.isComplete()) { throw new Error('UR is incomplete'); } const resultUR = decoder.resultUR(); return CryptoMultiAccounts.fromCBOR(resultUR.cbor); }); expect(() => convertMulitAccountToCryptoAccount(URs)).toThrow('All accounts must have the same Master Fingerprint'); }) it('should error when ur list is empty', () => { expect(() => convertMulitAccountToCryptoAccount([])).toThrow('input list is empty'); }) it('generateURString - should generate ur string', () => { const cbor = 'a4011a1250b6bc0281d9012fa403582103e0db6ca7c71e72a40091141b9dc4596ab3a9192ac41eebe1f2b048c654423ecb045820a75805b89c5079ba74e9002c3e587f7c97ea8018ab1d5245638a45de3c845e0b06d90130a101861856f500f500f5081a0484004d036e4b657973746f6e6520332050726f0565312e312e30'; const type = 'crypto-multi-accounts'; const UR1 = "UR:CRYPTO-MULTI-ACCOUNTS/OXADCYBGGDRPRFAOLYTAADDLOXAXHDCLAXVTUYJZOSSTCKJPOXAEMEBBCWNTSSHKIMQDPTCFDRSSCKWMVYWZPFFDSWGHFWFMSBAAHDCXOSHDAHRONSGDKKRDJYWLAEDWFMHDLBKEMSWDLACSPYCAGMFEIALEFEUEFNLRHYBDAMTAADDYOYADLNCSHFYKAEYKAEYKAYCYAALRAEGTAXJTGRIHKKJKJYJLJTIHCXEOCXGDJPJLAHIHEHDMEHDMDYHTYLLGAH"; const ur = generateURString(cbor, type); expect(ur).toBe(UR1); }) }) })