on master help sign in
Add Ethereum private key auth.
a54b5acJared Tobin
src/Bridge.js+19−1
@@ -17,11 +17,29 @@ class Bridge extends React.Component {
17 constructor(props) {
17 constructor(props) {
18 super(props)
18 super(props)
19
19
20+ const networkType =
21+ process.env.NODE_ENV === 'development'
22+ ? NETWORK_NAMES.LOCAL
23+ : NETWORK_NAMES.MAINNET
24+
25+ // NB (jtobin):
26+ //
27+ // Note that the 'wallet' prop has type depending on the 'walletType' prop.
28+ // These could be bound together in a single structure (so that
29+ // 'walletType' acted more explicitly as a data constructor of sorts) but
30+ // it doesn't necessarily help us much, since we branch on 'walletType'
31+ // before setting 'wallet'.
32+ //
33+ // Wallets are always wrapped in Maybe. For most authentication
34+ // mechanisms, Maybe wraps a BIP32 HD wallet; for Ethereum private keys,
35+ // JSON keystore files, and Metamask authentication, it wraps an
36+ // 'EthereumWallet'.
37+
20 this.state = {
38 this.state = {
21 // routes
39 // routes
22 routeCrumbs: Stack([ ROUTE_NAMES.LANDING ]),
40 routeCrumbs: Stack([ ROUTE_NAMES.LANDING ]),
23 // network
41 // network
24− networkType: NETWORK_NAMES.MAINNET,
42+ networkType: networkType,
25 web3: Maybe.Nothing(),
43 web3: Maybe.Nothing(),
26 contracts: Maybe.Nothing(),
44 contracts: Maybe.Nothing(),
27 // wallet
45 // wallet
src/lib/router.js+10−2
@@ -10,6 +10,7 @@ import Network from '../views/Network'
10import Mnemonic from '../views/Mnemonic'
10import Mnemonic from '../views/Mnemonic'
11import Ledger from '../views/Ledger'
11import Ledger from '../views/Ledger'
12import Trezor from '../views/Trezor'
12import Trezor from '../views/Trezor'
13+import PrivateKey from '../views/PrivateKey'
13import ViewPoint from '../views/ViewPoint'
14import ViewPoint from '../views/ViewPoint'
14import SentTransaction from '../views/SentTransaction'
15import SentTransaction from '../views/SentTransaction'
15import SetKeys from '../views/SetKeys'
16import SetKeys from '../views/SetKeys'
@@ -25,7 +26,7 @@ import Ticket from '../views/Ticket'
25import Transfer from '../views/Transfer'
26import Transfer from '../views/Transfer'
26import Wallet from '../views/Wallet'
27import Wallet from '../views/Wallet'
27
28
28−import { addressFromSecp256k1Public } from './wallet'
29+import { addressFromSecp256k1Public, EthereumWallet } from './wallet'
29import { renderNetworkType } from './network'
30import { renderNetworkType } from './network'
30import { BRIDGE_ERROR } from './error'
31import { BRIDGE_ERROR } from './error'
31
32
@@ -40,6 +41,7 @@ const ROUTE_NAMES = {
40 SHARDS: Symbol('SHARDS'),
41 SHARDS: Symbol('SHARDS'),
41 LEDGER: Symbol('LEDGER'),
42 LEDGER: Symbol('LEDGER'),
42 TREZOR: Symbol('TREZOR'),
43 TREZOR: Symbol('TREZOR'),
44+ PRIVATE_KEY: Symbol('PRIVATE_KEY'),
43 VIEW_SHIP: Symbol('VIEW_SHIP'),
45 VIEW_SHIP: Symbol('VIEW_SHIP'),
44 SHIPS: Symbol('SHIPS'),
46 SHIPS: Symbol('SHIPS'),
45 SHIP: Symbol('SHIP'),
47 SHIP: Symbol('SHIP'),
@@ -68,6 +70,7 @@ const createRoutes = () => {
68 routes[ROUTE_NAMES.SHARDS] = Shards
70 routes[ROUTE_NAMES.SHARDS] = Shards
69 routes[ROUTE_NAMES.LEDGER] = Ledger
71 routes[ROUTE_NAMES.LEDGER] = Ledger
70 routes[ROUTE_NAMES.TREZOR] = Trezor
72 routes[ROUTE_NAMES.TREZOR] = Trezor
73+ routes[ROUTE_NAMES.PRIVATE_KEY] = PrivateKey
71 routes[ROUTE_NAMES.SHIPS] = Points
74 routes[ROUTE_NAMES.SHIPS] = Points
72 routes[ROUTE_NAMES.SHIP] = Point
75 routes[ROUTE_NAMES.SHIP] = Point
73 routes[ROUTE_NAMES.SET_MANAGEMENT_PROXY] = SetManagementProxy
76 routes[ROUTE_NAMES.SET_MANAGEMENT_PROXY] = SetManagementProxy
@@ -98,7 +101,9 @@ const renderRoute = (props, route) => {
98 ? wallet.matchWith({
101 ? wallet.matchWith({
99 Nothing: () => 'Wallet',
102 Nothing: () => 'Wallet',
100 Just: (wal) =>
103 Just: (wal) =>
104+ wal.value instanceof EthereumWallet
105+ ? wal.value.address
101− addressFromSecp256k1Public(wal.value.publicKey)
106+ : addressFromSecp256k1Public(wal.value.publicKey)
102 })
107 })
103
108
104 : route === ROUTE_NAMES.MNEMONIC
109 : route === ROUTE_NAMES.MNEMONIC
@@ -116,6 +121,9 @@ const renderRoute = (props, route) => {
116 : route === ROUTE_NAMES.TREZOR
121 : route === ROUTE_NAMES.TREZOR
117 ? 'Trezor'
122 ? 'Trezor'
118
123
124+ : route === ROUTE_NAMES.PRIVATE_KEY
125+ ? 'Private Key'
126+
119 : route === ROUTE_NAMES.SHIPS
127 : route === ROUTE_NAMES.SHIPS
120 ? 'Points'
128 ? 'Points'
121
129
src/lib/wallet.js+14−2
@@ -15,7 +15,16 @@ const WALLET_NAMES = {
15 TICKET: Symbol('TICKET'),
15 TICKET: Symbol('TICKET'),
16 SHARDS: Symbol('SHARDS'),
16 SHARDS: Symbol('SHARDS'),
17 LEDGER: Symbol('LEDGER'),
17 LEDGER: Symbol('LEDGER'),
18− TREZOR: Symbol('TREZOR')
18+ TREZOR: Symbol('TREZOR'),
19+ PRIVATE_KEY: Symbol('PRIVATE_KEY')
20+}
21+
22+function EthereumWallet(privateKey) {
23+ const sec = Buffer.from(privateKey, 'hex')
24+ this.privateKey = sec
25+ this.publicKey = secp256k1.publicKeyCreate(sec)
26+ const pub = this.publicKey.toString('hex')
27+ this.address = addressFromSecp256k1Public(pub)
19}
28}
20
29
21const renderWalletType = (wallet) =>
30const renderWalletType = (wallet) =>
@@ -29,6 +38,8 @@ const renderWalletType = (wallet) =>
29 ? 'Ledger'
38 ? 'Ledger'
30 : wallet === WALLET_NAMES.TREZOR
39 : wallet === WALLET_NAMES.TREZOR
31 ? 'Trezor'
40 ? 'Trezor'
41+ : wallet === WALLET_NAMES.PRIVATE_KEY
42+ ? 'Private Key'
32 : 'Wallet'
43 : 'Wallet'
33
44
34const addressFromSecp256k1Public = pub => {
45const addressFromSecp256k1Public = pub => {
@@ -105,5 +116,6 @@ export {
105 addressFromSecp256k1Public,
116 addressFromSecp256k1Public,
106 eqAddr,
117 eqAddr,
107 walletFromMnemonic,
118 walletFromMnemonic,
108− addHexPrefix
119+ addHexPrefix,
120+ EthereumWallet
109}
121}
src/views/PrivateKey.jsnew file+86
@@ -0,0 +1,86 @@
1+import Maybe from 'folktale/maybe'
2+import React from 'react'
3+import { Button } from '../components/Base'
4+import { RequiredInput, InnerLabel, InputCaption } from '../components/Base'
5+import { Row, Col, H1, Form } from '../components/Base'
6+
7+import { ROUTE_NAMES } from '../lib/router'
8+import { EthereumWallet } from '../lib/wallet'
9+
10+const secre = /[0-9A-Fa-f]{64}/g
11+
12+class PrivateKey extends React.Component {
13+
14+ constructor(props) {
15+ super(props)
16+
17+ this.state = {
18+ privateKey: '',
19+ }
20+
21+ this.handlePrivateKeyInput = this.handlePrivateKeyInput.bind(this)
22+ }
23+
24+ handlePrivateKeyInput(privateKey) {
25+ this.setState({ privateKey })
26+ this.constructWallet(privateKey)
27+ }
28+
29+ constructWallet(privateKey) {
30+ const { setWallet } = this.props
31+ if (secre.test(privateKey) === true) {
32+ const wallet = new EthereumWallet(privateKey)
33+ setWallet(Maybe.Just(wallet))
34+ } else {
35+ setWallet(Maybe.Nothing())
36+ }
37+ }
38+
39+ render() {
40+ const { pushRoute, popRoute, wallet } = this.props
41+ const { privateKey } = this.state
42+
43+ return (
44+ <Row>
45+ <Col className={'measure-md'}>
46+ <H1 className={'mb-4'}>{ 'Enter Your Private Key' }</H1>
47+ <Form>
48+ <InputCaption>
49+ { `Please enter your raw Ethereum private key here.` }
50+ </InputCaption>
51+
52+ <RequiredInput
53+ className='pt-8 mt-8'
54+ prop-size='md'
55+ prop-format='innerLabel'
56+ type='text'
57+ name='privateKey'
58+ onChange={ this.handlePrivateKeyInput }
59+ value={ privateKey }
60+ autocomplete='off'
61+ autoFocus>
62+ <InnerLabel>{'Private Key'}</InnerLabel>
63+ </RequiredInput>
64+
65+ <Button
66+ className={'mt-10'}
67+ prop-size={ 'wide lg' }
68+ disabled={ Maybe.Nothing.hasInstance(wallet) }
69+ onClick={ () => {
70+ popRoute()
71+ pushRoute(ROUTE_NAMES.SHIPS)
72+ }
73+ }
74+ >
75+ { 'Continue →' }
76+ </Button>
77+ </Form>
78+ </Col>
79+ </Row>
80+
81+
82+ )
83+ }
84+}
85+
86+export default PrivateKey
src/views/Wallet.js+9
@@ -109,6 +109,13 @@ class Wallet extends React.Component {
109 { 'Trezor' }
109 { 'Trezor' }
110 </DropdownItem>
110 </DropdownItem>
111
111
112+ <DropdownDivider />
113+
114+ <DropdownItem
115+ onClick={ () => this.selectClose(() => props.setWalletType(WALLET_NAMES.PRIVATE_KEY)) }
116+ >
117+ { 'Ethereum Private Key' }
118+ </DropdownItem>
112
119
113
120
114
121
@@ -128,6 +135,8 @@ class Wallet extends React.Component {
128 ? ROUTE_NAMES.LEDGER
135 ? ROUTE_NAMES.LEDGER
129 : props.walletType === WALLET_NAMES.TREZOR
136 : props.walletType === WALLET_NAMES.TREZOR
130 ? ROUTE_NAMES.TREZOR
137 ? ROUTE_NAMES.TREZOR
138+ : props.walletType === WALLET_NAMES.PRIVATE_KEY
139+ ? ROUTE_NAMES.PRIVATE_KEY
131 : ROUTE_NAMES.DEFAULT
140 : ROUTE_NAMES.DEFAULT
132 )
141 )
133 }
142 }