Skip to content

Commit 4703312

Browse files
authored
Merge pull request #135 from liquality/wallet-network-check
Wallet providers assert matching network
2 parents 02147d0 + c82f810 commit 4703312

File tree

6 files changed

+110
-16
lines changed

6 files changed

+110
-16
lines changed

src/providers/LedgerProvider.js

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import Transport from '@ledgerhq/hw-transport-node-hid'
2-
import Provider from '../Provider'
2+
import WalletProvider from './WalletProvider'
33
import { WalletError } from '../errors'
44

5-
export default class LedgerProvider extends Provider {
5+
export default class LedgerProvider extends WalletProvider {
66
static isSupported () {
77
return Transport.isSupported()
88
}
99

10-
constructor (App, baseDerivationPath) {
11-
super()
10+
constructor (App, baseDerivationPath, network) {
11+
super(network)
1212

1313
this._App = App
1414
this._baseDerivationPath = baseDerivationPath
15+
this._network = network
1516
this._addressCache = {}
1617
}
1718

@@ -26,8 +27,8 @@ export default class LedgerProvider extends Provider {
2627
}
2728

2829
errorProxy (target, func) {
29-
if (Object.getOwnPropertyNames(target).includes(func)) {
30-
const method = target[func]
30+
const method = target[func]
31+
if (Object.getOwnPropertyNames(target).includes(func) && typeof method === 'function') {
3132
return async (...args) => {
3233
try {
3334
const result = await method.bind(target)(...args)
@@ -38,7 +39,7 @@ export default class LedgerProvider extends Provider {
3839
}
3940
}
4041
} else {
41-
return target[func]
42+
return method
4243
}
4344
}
4445

@@ -57,6 +58,11 @@ export default class LedgerProvider extends Provider {
5758
return this._appInstance
5859
}
5960

61+
async getConnectedNetwork () {
62+
// Ledger apps do not provide connected network. It is separated in firmware.
63+
return this._network
64+
}
65+
6066
getDerivationPathFromIndex (index, change = false) {
6167
const changePath = change ? '1/' : '0/'
6268
return this._baseDerivationPath + changePath + index

src/providers/WalletProvider.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import _ from 'lodash'
2+
import Provider from '../Provider'
3+
import { WalletError } from '../errors'
4+
5+
export default class WalletProvider extends Provider {
6+
constructor (network) {
7+
super()
8+
this._network = network
9+
this._methods = Object.getOwnPropertyNames(WalletProvider.prototype)
10+
.filter(method => ![
11+
'constructor',
12+
'_networkMatchProxy',
13+
'getConnectedNetwork',
14+
'assertNetworkMatch'
15+
].includes(method))
16+
return network ? new Proxy(this, { get: this._networkMatchProxy.bind(this) }) : this
17+
}
18+
19+
_networkMatchProxy (target, func) {
20+
const method = target[func]
21+
if (this._methods.includes(func)) {
22+
return async (...args) => {
23+
await this.assertNetworkMatch()
24+
return method.bind(target)(...args)
25+
}
26+
} else {
27+
return method
28+
}
29+
}
30+
31+
async assertNetworkMatch () {
32+
const connectedNetwork = await this.getConnectedNetwork()
33+
if (!_.isEqual(connectedNetwork, this._network)) {
34+
throw new WalletError(`Network mismatch. Configured network '${this._network.name}' does not match connected network '${connectedNetwork.name}'`)
35+
}
36+
}
37+
38+
getAddresses () {
39+
throw new Error('getAddresses not implemented.')
40+
}
41+
42+
getUsedAddresses () {
43+
throw new Error('getUsedAddresses not implemented.')
44+
}
45+
46+
getUnusedAddresses () {
47+
throw new Error('getUnusedAddresses not implemented.')
48+
}
49+
50+
signMessage () {
51+
throw new Error('signMessage not implemented.')
52+
}
53+
54+
async getConnectedNetwork () {
55+
throw new Error('getConnectedNetwork not implemented.')
56+
}
57+
}

src/providers/bitcoin/BitcoinLedgerProvider.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import Bitcoin from '@ledgerhq/hw-app-btc'
33

44
import { BigNumber } from 'bignumber.js'
55
import { base58, padHexStart } from '../../crypto'
6-
import { pubKeyToAddress, addressToPubKeyHash, compressPubKey } from './BitcoinUtil'
6+
import { pubKeyToAddress, addressToPubKeyHash, compressPubKey, getAddressNetwork } from './BitcoinUtil'
77
import Address from '../../Address'
88
import networks from './networks'
99
import bip32 from 'bip32'
1010

1111
export default class BitcoinLedgerProvider extends LedgerProvider {
1212
constructor (chain = { network: networks.bitcoin, segwit: false }, numberOfBlockConfirmation = 1) {
13-
super(Bitcoin, `${chain.segwit ? '49' : '44'}'/${chain.network.coinType}'/0'/`)
13+
super(Bitcoin, `${chain.segwit ? '49' : '44'}'/${chain.network.coinType}'/0'/`, chain.network)
1414
this._derivationPath = `${chain.segwit ? '49' : '44'}'/${chain.network.coinType}'/0'/`
1515
this._network = chain.network
1616
this._bjsnetwork = chain.network.name.replace('bitcoin_', '') // for bitcoin js
@@ -502,4 +502,10 @@ export default class BitcoinLedgerProvider extends LedgerProvider {
502502
async getAddresses (startingIndex = 0, numAddresses = 1, change = false) {
503503
return this.getLedgerAddresses(startingIndex, numAddresses, change)
504504
}
505+
506+
async getConnectedNetwork () {
507+
const walletPubKey = await this.getWalletPublicKey(this._baseDerivationPath)
508+
const network = getAddressNetwork(walletPubKey.bitcoinAddress)
509+
return network
510+
}
505511
}

src/providers/bitcoin/BitcoinUtil.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import _ from 'lodash'
2+
13
import {
24
ensureBuffer,
35
hash160,
@@ -112,6 +114,17 @@ function toHexInt (number) {
112114
toHexDigit(number & 0xff)
113115
)
114116
}
117+
/**
118+
* Get a network object from an address
119+
* @param {string} address The bitcoin address
120+
* @return {Network}
121+
*/
122+
function getAddressNetwork (address) {
123+
const prefix = base58.decode(address).toString('hex').substring(0, 2).toUpperCase()
124+
const networkKey = _.findKey(networks,
125+
network => [network.pubKeyHash, network.scriptHash].includes(prefix))
126+
return networks[networkKey]
127+
}
115128

116129
export {
117130
toHexInt,
@@ -120,5 +133,6 @@ export {
120133
pubKeyHashToAddress,
121134
addressToPubKeyHash,
122135
reverseBuffer,
123-
scriptNumEncode
136+
scriptNumEncode,
137+
getAddressNetwork
124138
}

src/providers/ethereum/EthereumMetaMaskProvider.js

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
import { isFunction } from 'lodash'
1+
import _ from 'lodash'
22
import { BigNumber } from 'bignumber.js'
3-
import Provider from '../../Provider'
3+
import WalletProvider from '../WalletProvider'
44
import { formatEthResponse, ensureHexEthFormat, ensureHexStandardFormat } from './EthereumUtil'
55
import { WalletError } from '../../errors'
6+
import networks from './networks'
67

7-
export default class EthereumMetaMaskProvider extends Provider {
8+
export default class EthereumMetaMaskProvider extends WalletProvider {
89
constructor (metamaskProvider, network) {
9-
super()
10-
if (!isFunction(metamaskProvider.sendAsync)) {
10+
super(network)
11+
if (!_.isFunction(metamaskProvider.sendAsync)) {
1112
throw new Error('Invalid MetaMask Provider')
1213
}
1314

@@ -111,4 +112,13 @@ export default class EthereumMetaMaskProvider extends Provider {
111112

112113
return parseInt(networkId)
113114
}
115+
116+
async getConnectedNetwork () {
117+
const networkId = await this.getWalletNetworkId()
118+
const network = _.findKey(networks, network => network.networkId === networkId)
119+
if (networkId && !network) {
120+
return { name: 'unknown', networkId }
121+
}
122+
return networks[network]
123+
}
114124
}

test/integration/swap/common.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ const bitcoinWithNode = new Client()
1515
bitcoinWithNode.addProvider(new providers.bitcoin.BitcoreRPCProvider(config.bitcoin.rpc.host, config.bitcoin.rpc.username, config.bitcoin.rpc.password))
1616
bitcoinWithNode.addProvider(new providers.bitcoin.BitcoinJsLibSwapProvider({ network: bitcoinNetworks[config.bitcoin.network] }))
1717

18+
const ethereumNetworks = providers.ethereum.networks
1819
const ethereumWithMetaMask = new Client()
1920
ethereumWithMetaMask.addProvider(new providers.ethereum.EthereumRPCProvider(config.ethereum.rpc.host))
20-
ethereumWithMetaMask.addProvider(new providers.ethereum.EthereumMetaMaskProvider(metaMaskConnector.getProvider()))
21+
ethereumWithMetaMask.addProvider(new providers.ethereum.EthereumMetaMaskProvider(metaMaskConnector.getProvider(), ethereumNetworks[config.ethereum.network]))
2122
ethereumWithMetaMask.addProvider(new providers.ethereum.EthereumSwapProvider())
2223

2324
const ethereumWithNode = new Client()

0 commit comments

Comments
 (0)