Skip to content

Commit

Permalink
Merge pull request ava-labs#115 from ava-labs/large_address_support
Browse files Browse the repository at this point in the history
Support more than 1024 addresses for atomic imports
  • Loading branch information
kanatliemre authored Jan 7, 2021
2 parents ac17888 + 4c81883 commit c82a36b
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 57 deletions.
6 changes: 5 additions & 1 deletion src/components/wallet/advanced/ChainImport.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ export default class ChainImport extends Vue {
if (!this.wallet) return
try {
let txId = await this.wallet.importToXChain('P')
let txId2 = await this.wallet.importToXChain('C')
// TODO: Change after ledger support
if (this.wallet.type !== 'ledger') {
let txId2 = await this.wallet.importToXChain('C')
}
this.onSuccess(txId)
} catch (e) {
this.onError(e)
Expand Down Expand Up @@ -119,6 +122,7 @@ export default class ChainImport extends Vue {
onError(err: Error) {
this.isLoading = false
console.error(err)
let msg = ''
if (err.message.includes('No atomic')) {
this.err = 'Nothing found to import.'
Expand Down
21 changes: 17 additions & 4 deletions src/explorer_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ async function getAddressHistory(
addrs: string[],
limit = 20,
chainID: string
): Promise<{ transactions: ITransactionData[] }> {
let query = addrs.map((val) => {
): Promise<ITransactionData[]> {
let selection = addrs.slice(0, 512)
let remaining = addrs.slice(512)

let query = selection.map((val) => {
let raw = val.split('-')[1]
return `address=${raw}`
})
Expand All @@ -27,7 +30,16 @@ async function getAddressHistory(
)}&limit=${limit}&sort=timestamp-desc&disableCount=1&chainID=${chainID}`

let res = await explorer_api.get(url)
return res.data
let txs = res.data.transactions

if (txs === null) txs = []

if (remaining.length > 0) {
let nextRes = await getAddressHistory(remaining, limit, chainID)
txs.push(...nextRes)
}

return txs
}

async function isAddressUsedX(addr: string) {
Expand Down Expand Up @@ -63,7 +75,8 @@ async function getAddressChains(addrs: string[]) {
})

let joined = cleanAddrs.join('&')
let url = `/x/addressChains?${joined}`
let url = `/v2/addressChains?${joined}&disableCount=1`

let res = await explorer_api.get(url)

return res.data.addressChains
Expand Down
38 changes: 38 additions & 0 deletions src/helpers/wallet_helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { UTXOSet as PlatformUTXOSet } from 'avalanche/dist/apis/platformvm/utxos'
import { avm, cChain, pChain } from '@/AVA'
import { UTXOSet as AVMUTXOSet } from 'avalanche/dist/apis/avm/utxos'

// Handles more than 1024 addresses
async function getAtomicUTXOsForAddresses(addrs: string[], chainAlias: string): Promise<any> {
let selection = addrs.slice(0, 1024)
let remaining = addrs.slice(1024)

let utxoSet = await getAtomicUTXOs(selection, chainAlias)

if (remaining.length > 0) {
// @ts-ignore
let nextSet = await getAtomicUTXOsForAddresses(remaining, chainAlias)
utxoSet = utxoSet.merge(nextSet)
}

return utxoSet
}

// todo: Use end index to get ALL utxos
async function getAtomicUTXOs(addrs: string[], chainAlias: string, endIndex: any = undefined) {
if (addrs.length > 1024) {
throw 'Number of addresses can not be greater than 1024.'
}

if (chainAlias === 'P') {
let result: PlatformUTXOSet = (await pChain.getUTXOs(addrs, avm.getBlockchainID())).utxos
return result
} else {
let resultP: AVMUTXOSet = (await avm.getUTXOs(addrs, pChain.getBlockchainID())).utxos
let resultC: AVMUTXOSet = (await avm.getUTXOs(addrs, cChain.getBlockchainID())).utxos
let result = resultP.merge(resultC)
return result
}
}

export { getAtomicUTXOsForAddresses }
26 changes: 15 additions & 11 deletions src/js/HdHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { getAddressChains } from '@/explorer_api'
import { NetworkItem } from '@/store/modules/network/types'
import { AvaNetwork } from '@/js/AvaNetwork'
import { ChainAlias } from './wallets/IAvaHdWallet'
import { getAtomicUTXOsForAddresses } from '@/helpers/wallet_helper'

const INDEX_RANGE: number = 20 // a gap of at least 20 indexes is needed to claim an index unused

Expand Down Expand Up @@ -258,17 +259,20 @@ class HdHelper {

async getAtomicUTXOs() {
let addrs: string[] = this.getAllDerivedAddresses()
// console.log(addrs);
if (this.chainId === 'P') {
let result: PlatformUTXOSet = (await pChain.getUTXOs(addrs, avm.getBlockchainID()))
.utxos
return result
} else {
let result: AVMUTXOSet = (await avm.getUTXOs(addrs, pChain.getBlockchainID())).utxos

let resultC: AVMUTXOSet = (await avm.getUTXOs(addrs, cChain.getBlockchainID())).utxos
return result.merge(resultC)
}
let result = await getAtomicUTXOsForAddresses(addrs, this.chainId)
return result
// // console.log(addrs);
// if (this.chainId === 'P') {
// let result: PlatformUTXOSet = (await pChain.getUTXOs(addrs, avm.getBlockchainID()))
// .utxos
// return result
// } else {
// let result: AVMUTXOSet = (await avm.getUTXOs(addrs, pChain.getBlockchainID())).utxos
//
// let resultC: AVMUTXOSet = (await avm.getUTXOs(addrs, cChain.getBlockchainID())).utxos
// return result.merge(resultC)
// }
}

// Not used?
Expand Down Expand Up @@ -337,7 +341,7 @@ class HdHelper {
// Scans the address space of this hd path and finds the last used index using the
// explorer API.
async findAvailableIndexExplorer(startIndex = 0): Promise<number> {
let upTo = 200
let upTo = 516

let addrs = this.getAllDerivedAddresses(startIndex + upTo, startIndex)
let addrChains = await getAddressChains(addrs)
Expand Down
38 changes: 33 additions & 5 deletions src/js/wallets/AvaHdWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,21 @@ export default class AvaHdWallet extends HdWalletCore implements IAvaHdWallet {
}

async getUTXOs(): Promise<void> {
this.getEthBalance()
// TODO: Move to shared file
this.isFetchUtxos = true
// If we are waiting for helpers to initialize delay the call
let isInit =
this.externalHelper.isInit && this.internalHelper.isInit && this.platformHelper.isInit
if (!isInit) {
setTimeout(() => {
this.getUTXOs()
}, 1000)
// console.info('HD Not ready try again in 1 sec..')
return
}

super.getUTXOs()
this.getEthBalance()
return
}

Expand Down Expand Up @@ -421,9 +434,16 @@ export default class AvaHdWallet extends HdWalletCore implements IAvaHdWallet {
// Owner addresses, the addresses we exported to
let pToAddr = this.platformHelper.getCurrentAddress()

let hrp = ava.getHRP()
let utxoAddrs = utxoSet
.getAddresses()
.map((addr) => bintools.addressToString(hrp, 'P', addr))
let fromAddrs = utxoAddrs
let ownerAddrs = utxoAddrs

const unsignedTx = await pChain.buildImportTx(
utxoSet,
pAddrs,
ownerAddrs,
avm.getBlockchainID(),
[pToAddr],
[pToAddr],
Expand All @@ -445,9 +465,17 @@ export default class AvaHdWallet extends HdWalletCore implements IAvaHdWallet {
}

let keyChain = this.getKeyChain() as AVMKeyChain
let xAddrs = keyChain.getAddressStrings()
// let xAddrs = keyChain.getAddressStrings()
let xToAddr = this.externalHelper.getCurrentAddress()

let hrp = ava.getHRP()
let utxoAddrs = utxoSet
.getAddresses()
.map((addr) => bintools.addressToString(hrp, 'X', addr))

let fromAddrs = utxoAddrs
let ownerAddrs = utxoAddrs

let sourceChainId
if (sourceChain === 'P') {
sourceChainId = pChain.getBlockchainID()
Expand All @@ -457,10 +485,10 @@ export default class AvaHdWallet extends HdWalletCore implements IAvaHdWallet {
// Owner addresses, the addresses we exported to
const unsignedTx = await avm.buildImportTx(
utxoSet,
xAddrs,
ownerAddrs,
sourceChainId,
[xToAddr],
xAddrs,
fromAddrs,
[xToAddr]
)
const tx = unsignedTx.sign(keyChain)
Expand Down
12 changes: 0 additions & 12 deletions src/js/wallets/HdWalletCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,18 +153,6 @@ class HdWalletCore {
}
// Fetches the utxos
async getUTXOs(): Promise<void> {
this.isFetchUtxos = true

let isInit =
this.externalHelper.isInit && this.internalHelper.isInit && this.platformHelper.isInit
if (!isInit) {
setTimeout(() => {
this.getUTXOs()
}, 1000)
// console.info('HD Not ready try again in 1 sec..')
return
}

this.internalHelper.updateUtxos().then((utxoSet) => {
this.updateFetchState()
this.updateUTXOSet()
Expand Down
61 changes: 49 additions & 12 deletions src/js/wallets/LedgerWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import AppAvax from '@obsidiansystems/hw-app-avalanche'

import { Buffer, BN } from 'avalanche'
import HDKey from 'hdkey'
import { ava, avm, bintools, pChain } from '@/AVA'
import { ava, avm, bintools, cChain, pChain } from '@/AVA'
var bippath = require('bip32-path')
import createHash from 'create-hash'
import store from '@/store'
Expand Down Expand Up @@ -43,6 +43,7 @@ import { HdWalletCore } from '@/js/wallets/HdWalletCore'
import { WalletNameType } from '@/store/types'
import { digestMessage } from '@/helpers/helper'
import { web3 } from '@/evm'
import { ChainIdType } from '@/constants'

class LedgerWallet extends HdWalletCore implements AvaWalletCore {
app: AppAvax
Expand Down Expand Up @@ -224,8 +225,21 @@ class LedgerWallet extends HdWalletCore implements AvaWalletCore {
}

async getUTXOs(): Promise<void> {
this.getEthBalance()
// TODO: Move to shared file
this.isFetchUtxos = true
// If we are waiting for helpers to initialize delay the call
let isInit =
this.externalHelper.isInit && this.internalHelper.isInit && this.platformHelper.isInit
if (!isInit) {
setTimeout(() => {
this.getUTXOs()
}, 1000)
// console.info('HD Not ready try again in 1 sec..')
return
}

super.getUTXOs()
this.getEthBalance()
return
}

Expand Down Expand Up @@ -381,13 +395,20 @@ class LedgerWallet extends HdWalletCore implements AvaWalletCore {
throw new Error('Nothing to import.')
}

let pAddrs = this.platformHelper.getAllDerivedAddresses()
// let pAddrs = this.platformHelper.getAllDerivedAddresses()
// Owner addresses, the addresses we exported to
let pToAddr = this.platformHelper.getCurrentAddress()

let hrp = ava.getHRP()
let utxoAddrs = utxoSet
.getAddresses()
.map((addr) => bintools.addressToString(hrp, 'P', addr))
// let fromAddrs = utxoAddrs
let ownerAddrs = utxoAddrs

const unsignedTx = await pChain.buildImportTx(
utxoSet,
pAddrs,
ownerAddrs,
avm.getBlockchainID(),
[pToAddr],
[pToAddr],
Expand All @@ -400,27 +421,43 @@ class LedgerWallet extends HdWalletCore implements AvaWalletCore {
return pChain.issueTx(tx)
}

async importToXChain(): Promise<string> {
// TODO: Move to Core HD file
async importToXChain(sourceChain: ChainIdType): Promise<string> {
const utxoSet = (await this.externalHelper.getAtomicUTXOs()) as AVMUTXOSet

if (utxoSet.getAllUTXOs().length === 0) {
throw new Error('Nothing to import.')
}

let externalIndex = this.externalHelper.hdIndex
// let xAddrs = this.externalHelper.getAllDerivedAddresses(externalIndex+20);
// let externalIndex = this.externalHelper.hdIndex
// let xAddrs = this.externalHelper.getAllDerivedAddresses()
let xToAddr = this.externalHelper.getCurrentAddress()
let externalAddresses = this.externalHelper.getExtendedAddresses()
let xAddrs = this.getDerivedAddresses()
// let externalAddresses = this.externalHelper.getExtendedAddresses()
// let xAddrs = this.getDerivedAddresses()
// let xToAddr = this.externalHelper.getAllDerivedAddresses(externalIndex+10);

let hrp = ava.getHRP()
let utxoAddrs = utxoSet
.getAddresses()
.map((addr) => bintools.addressToString(hrp, 'X', addr))

let fromAddrs = utxoAddrs
let ownerAddrs = utxoAddrs

let sourceChainId
if (sourceChain === 'P') {
sourceChainId = pChain.getBlockchainID()
} else {
sourceChainId = cChain.getBlockchainID()
}

// Owner addresses, the addresses we exported to
const unsignedTx = await avm.buildImportTx(
utxoSet,
externalAddresses,
pChain.getBlockchainID(),
ownerAddrs,
sourceChainId,
[xToAddr],
externalAddresses,
fromAddrs,
[xToAddr]
)

Expand Down
14 changes: 2 additions & 12 deletions src/store/modules/history/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,8 @@ const history_module: Module<HistoryState, RootState> = {

let limit = 20

let data = await getAddressHistory(avmAddrs, limit, avm.getBlockchainID())
let dataP = await getAddressHistory(pvmAddrs, limit, pChain.getBlockchainID())

let txs: ITransactionData[] = []
let txsP: ITransactionData[] = []

if (data.transactions !== null) {
txs = data.transactions
}
if (dataP.transactions !== null) {
txsP = dataP.transactions
}
let txs = await getAddressHistory(avmAddrs, limit, avm.getBlockchainID())
let txsP = await getAddressHistory(pvmAddrs, limit, pChain.getBlockchainID())

let transactions = txs
.concat(txsP)
Expand Down

0 comments on commit c82a36b

Please sign in to comment.