on master help sign in
Add Ethereum private key auth.

* Adds an 'EthereumWallet' constructor for non-HD wallet auth * Adds a PrivateKey view to allow auth by raw Ethereum private key

commita54b5aceb1bb343d413946da3b64c2bdf274137b authorJared Tobin <jared@jtobin.io> dateMon 21 Jan 2019, 05:35 (8 years ago) parent4d7dc34
5 files changed+138−5
src/Bridge.js+19−1 src/lib/router.js+10−2 src/lib/wallet.js+14−2 src/views/PrivateKey.js+86 src/views/Wallet.js+9
src/Bridge.js+19−1
@@ -17,11 +17,29 @@ class Bridge extends React.Component {
1717 constructor(props) {
1818 super(props)
1919
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+
2038 this.state = {
2139 // routes
2240 routeCrumbs: Stack([ ROUTE_NAMES.LANDING ]),
2341 // network
24− networkType: NETWORK_NAMES.MAINNET,
42+ networkType: networkType,
2543 web3: Maybe.Nothing(),
2644 contracts: Maybe.Nothing(),
2745 // wallet
src/lib/router.js+10−2
@@ -10,6 +10,7 @@ import Network from '../views/Network'
1010import Mnemonic from '../views/Mnemonic'
1111import Ledger from '../views/Ledger'
1212import Trezor from '../views/Trezor'
13+import PrivateKey from '../views/PrivateKey'
1314import ViewPoint from '../views/ViewPoint'
1415import SentTransaction from '../views/SentTransaction'
1516import SetKeys from '../views/SetKeys'
@@ -25,7 +26,7 @@ import Ticket from '../views/Ticket'
2526import Transfer from '../views/Transfer'
2627import Wallet from '../views/Wallet'
2728
28−import { addressFromSecp256k1Public } from './wallet'
29+import { addressFromSecp256k1Public, EthereumWallet } from './wallet'
2930import { renderNetworkType } from './network'
3031import { BRIDGE_ERROR } from './error'
3132
@@ -40,6 +41,7 @@ const ROUTE_NAMES = {
4041 SHARDS: Symbol('SHARDS'),
4142 LEDGER: Symbol('LEDGER'),
4243 TREZOR: Symbol('TREZOR'),
44+ PRIVATE_KEY: Symbol('PRIVATE_KEY'),
4345 VIEW_SHIP: Symbol('VIEW_SHIP'),
4446 SHIPS: Symbol('SHIPS'),
4547 SHIP: Symbol('SHIP'),
@@ -68,6 +70,7 @@ const createRoutes = () => {
6870 routes[ROUTE_NAMES.SHARDS] = Shards
6971 routes[ROUTE_NAMES.LEDGER] = Ledger
7072 routes[ROUTE_NAMES.TREZOR] = Trezor
73+ routes[ROUTE_NAMES.PRIVATE_KEY] = PrivateKey
7174 routes[ROUTE_NAMES.SHIPS] = Points
7275 routes[ROUTE_NAMES.SHIP] = Point
7376 routes[ROUTE_NAMES.SET_MANAGEMENT_PROXY] = SetManagementProxy
@@ -98,7 +101,9 @@ const renderRoute = (props, route) => {
98101 ? wallet.matchWith({
99102 Nothing: () => 'Wallet',
100103 Just: (wal) =>
101− addressFromSecp256k1Public(wal.value.publicKey)
104+ wal.value instanceof EthereumWallet
105+ ? wal.value.address
106+ : addressFromSecp256k1Public(wal.value.publicKey)
102107 })
103108
104109 : route === ROUTE_NAMES.MNEMONIC
@@ -116,6 +121,9 @@ const renderRoute = (props, route) => {
116121 : route === ROUTE_NAMES.TREZOR
117122 ? 'Trezor'
118123
124+ : route === ROUTE_NAMES.PRIVATE_KEY
125+ ? 'Private Key'
126+
119127 : route === ROUTE_NAMES.SHIPS
120128 ? 'Points'
121129
src/lib/wallet.js+14−2
@@ -15,7 +15,16 @@ const WALLET_NAMES = {
1515 TICKET: Symbol('TICKET'),
1616 SHARDS: Symbol('SHARDS'),
1717 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)
1928}
2029
2130const renderWalletType = (wallet) =>
@@ -29,6 +38,8 @@ const renderWalletType = (wallet) =>
2938 ? 'Ledger'
3039 : wallet === WALLET_NAMES.TREZOR
3140 ? 'Trezor'
41+ : wallet === WALLET_NAMES.PRIVATE_KEY
42+ ? 'Private Key'
3243 : 'Wallet'
3344
3445const addressFromSecp256k1Public = pub => {
@@ -105,5 +116,6 @@ export {
105116 addressFromSecp256k1Public,
106117 eqAddr,
107118 walletFromMnemonic,
108− addHexPrefix
119+ addHexPrefix,
120+ EthereumWallet
109121}
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 {
109109 { 'Trezor' }
110110 </DropdownItem>
111111
112+ <DropdownDivider />
113+
114+ <DropdownItem
115+ onClick={ () => this.selectClose(() => props.setWalletType(WALLET_NAMES.PRIVATE_KEY)) }
116+ >
117+ { 'Ethereum Private Key' }
118+ </DropdownItem>
112119
113120
114121
@@ -128,6 +135,8 @@ class Wallet extends React.Component {
128135 ? ROUTE_NAMES.LEDGER
129136 : props.walletType === WALLET_NAMES.TREZOR
130137 ? ROUTE_NAMES.TREZOR
138+ : props.walletType === WALLET_NAMES.PRIVATE_KEY
139+ ? ROUTE_NAMES.PRIVATE_KEY
131140 : ROUTE_NAMES.DEFAULT
132141 )
133142 }