|
|
@ -5,7 +5,9 @@ import bitcoin from 'bitcoinjs-lib' |
|
|
|
import bs58check from 'bs58check' |
|
|
|
import Btc from '@ledgerhq/hw-app-btc' |
|
|
|
|
|
|
|
const networks = [ |
|
|
|
import { computeTransaction } from 'helpers/btc' |
|
|
|
|
|
|
|
export const networks = [ |
|
|
|
{ |
|
|
|
...bitcoin.networks.bitcoin, |
|
|
|
family: 1, |
|
|
@ -36,7 +38,7 @@ function parseHexString(str) { |
|
|
|
return result |
|
|
|
} |
|
|
|
|
|
|
|
function createXPUB({ depth, fingerprint, childnum, chainCode, publicKey, network }) { |
|
|
|
function createXpub({ depth, fingerprint, childnum, chainCode, publicKey, network }) { |
|
|
|
return [ |
|
|
|
network.toString(16).padStart(8, 0), |
|
|
|
depth.toString(16).padStart(2, 0), |
|
|
@ -49,7 +51,8 @@ function createXPUB({ depth, fingerprint, childnum, chainCode, publicKey, networ |
|
|
|
|
|
|
|
function encodeBase58Check(vchIn) { |
|
|
|
vchIn = parseHexString(vchIn) |
|
|
|
return bs58check.encode(new Uint8Array(vchIn)) |
|
|
|
|
|
|
|
return bs58check.encode(Buffer.from(vchIn)) |
|
|
|
} |
|
|
|
|
|
|
|
function getPath({ coin, account, segwit }) { |
|
|
@ -78,10 +81,52 @@ function getTransactions(addresses) { |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
export default async transport => { |
|
|
|
const coin = 1 |
|
|
|
const account = 0 |
|
|
|
const segwit = true |
|
|
|
export async function getAccount({ hdnode, segwit, network }) { |
|
|
|
const script = segwit ? parseInt(network.scriptHash, 10) : parseInt(network.pubKeyHash, 10) |
|
|
|
|
|
|
|
let transactions = [] |
|
|
|
|
|
|
|
const nextPath = start => { |
|
|
|
const count = 20 |
|
|
|
const getAddress = path => getPublicAddress(hdnode, path, script, segwit) |
|
|
|
|
|
|
|
return Promise.all( |
|
|
|
Array.from(Array(count).keys()).map(v => |
|
|
|
Promise.all([ |
|
|
|
getAddress(`0/${v + start}`), // external chain
|
|
|
|
getAddress(`1/${v + start}`), // internal chain
|
|
|
|
]), |
|
|
|
), |
|
|
|
).then(async results => { |
|
|
|
const currentAddresses = results.reduce((result, v) => [...result, ...v], []) |
|
|
|
|
|
|
|
const { data: { txs } } = await getTransactions(currentAddresses) |
|
|
|
|
|
|
|
transactions = [...transactions, ...txs.map(computeTransaction(currentAddresses))] |
|
|
|
|
|
|
|
if (txs.length > 0) { |
|
|
|
return nextPath(start + (count - 1)) |
|
|
|
} |
|
|
|
|
|
|
|
return { |
|
|
|
balance: transactions.reduce((result, v) => { |
|
|
|
result += v.balance |
|
|
|
return result |
|
|
|
}, 0), |
|
|
|
transactions, |
|
|
|
} |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
return nextPath(0) |
|
|
|
} |
|
|
|
|
|
|
|
export function getHDNode({ xpub58, network }) { |
|
|
|
return bitcoin.HDNode.fromBase58(xpub58, network) |
|
|
|
} |
|
|
|
|
|
|
|
export default async ({ transport, currentAccounts, onProgress, coin = 1, segwit = true }) => { |
|
|
|
const btc = new Btc(transport) |
|
|
|
|
|
|
|
const network = networks[coin] |
|
|
|
|
|
|
@ -91,8 +136,6 @@ export default async transport => { |
|
|
|
|
|
|
|
await transport.exchange(`e014000005${p2pkh}${p2sh}${fam.substr(-2)}`, [0x9000]) |
|
|
|
|
|
|
|
const btc = new Btc(transport) |
|
|
|
|
|
|
|
const getPublicKey = path => btc.getWalletPublicKey(path) |
|
|
|
|
|
|
|
let result = bitcoin.crypto.sha256( |
|
|
@ -102,47 +145,54 @@ export default async transport => { |
|
|
|
) |
|
|
|
result = bitcoin.crypto.ripemd160(result) |
|
|
|
|
|
|
|
const fingerprint = ((result[0] << 24) | (result[1] << 16) | (result[2] << 8) | result[3]) >>> 0 |
|
|
|
onProgress(null) |
|
|
|
|
|
|
|
const { publicKey, chainCode } = await getPublicKey(getPath({ segwit, coin, account })) |
|
|
|
const compressPublicKey = getCompressPublicKey(publicKey) |
|
|
|
const fingerprint = ((result[0] << 24) | (result[1] << 16) | (result[2] << 8) | result[3]) >>> 0 |
|
|
|
|
|
|
|
const childnum = (0x80000000 | account) >>> 0 |
|
|
|
const xpub = createXPUB({ |
|
|
|
depth: 3, |
|
|
|
fingerprint, |
|
|
|
childnum, |
|
|
|
chainCode, |
|
|
|
publicKey: compressPublicKey, |
|
|
|
network: network.bip32.public, |
|
|
|
}) |
|
|
|
const getXpub58ByAccount = async ({ account, network }) => { |
|
|
|
const { publicKey, chainCode } = await getPublicKey(getPath({ segwit, coin, account })) |
|
|
|
const compressPublicKey = getCompressPublicKey(publicKey) |
|
|
|
|
|
|
|
const xpub58 = encodeBase58Check(xpub) |
|
|
|
const childnum = (0x80000000 | account) >>> 0 |
|
|
|
|
|
|
|
const hdnode = bitcoin.HDNode.fromBase58(xpub58, network) |
|
|
|
const xpub = createXpub({ |
|
|
|
depth: 3, |
|
|
|
fingerprint, |
|
|
|
childnum, |
|
|
|
chainCode, |
|
|
|
publicKey: compressPublicKey, |
|
|
|
network: network.bip32.public, |
|
|
|
}) |
|
|
|
|
|
|
|
const script = segwit ? parseInt(network.scriptHash, 10) : parseInt(network.pubKeyHash, 10) |
|
|
|
return encodeBase58Check(xpub) |
|
|
|
} |
|
|
|
|
|
|
|
const nextPath = async i => { |
|
|
|
if (i <= 0x7fffffff) { |
|
|
|
for (let j = 0; j < 2; j++) { |
|
|
|
const path = `${j}/${i}` |
|
|
|
const getAllAccounts = async (currentAccount = 0, accounts = {}) => { |
|
|
|
const xpub58 = await getXpub58ByAccount({ account: currentAccount, network }) |
|
|
|
|
|
|
|
const address = getPublicAddress(hdnode, path, script, segwit) |
|
|
|
console.log('address', address) |
|
|
|
if (currentAccounts.includes(xpub58)) { |
|
|
|
return getAllAccounts(currentAccount + 1, accounts) // skip existing account
|
|
|
|
} |
|
|
|
|
|
|
|
const { data: { txs } } = await getTransactions(address) // eslint-disable-line no-await-in-loop
|
|
|
|
const hdnode = getHDNode({ xpub58, network }) |
|
|
|
const { transactions, balance } = await getAccount({ hdnode, network, segwit }) |
|
|
|
|
|
|
|
console.log('txs', txs.length) |
|
|
|
onProgress({ |
|
|
|
account: currentAccount, |
|
|
|
transactions: transactions.length, |
|
|
|
}) |
|
|
|
|
|
|
|
if (j === 1 && i < 10) { |
|
|
|
nextPath(++i) |
|
|
|
} |
|
|
|
if (transactions.length > 0) { |
|
|
|
accounts[currentAccount] = { |
|
|
|
id: xpub58, |
|
|
|
balance, |
|
|
|
transactions, |
|
|
|
} |
|
|
|
} else { |
|
|
|
console.log('meeeh') |
|
|
|
return getAllAccounts(currentAccount + 1, accounts) |
|
|
|
} |
|
|
|
|
|
|
|
return accounts |
|
|
|
} |
|
|
|
|
|
|
|
nextPath(0) |
|
|
|
return getAllAccounts() |
|
|
|
} |