Skip to content
This repository was archived by the owner on Dec 11, 2019. It is now read-only.

Commit 5dac2e3

Browse files
committed
ledger backup and recovery
1 parent c9e23a3 commit 5dac2e3

File tree

12 files changed

+272
-7
lines changed

12 files changed

+272
-7
lines changed

app/extensions/brave/locales/en-US/preferences.properties

+18
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ bitcoinVisitAccount=Transfer BTC
5959
bitcoinBalance=Please transfer: 
6060
bitcoinWalletNotAvailable=Wallet information not available. :(
6161
usd=$
62+
cancel=Cancel
6263
done=Done
6364
off=off
6465
on=on
@@ -71,6 +72,9 @@ add=Fund with debit/credit
7172
transferTime=Transfer may take up to 40 minutes
7273
addFundsTitle=Add funds…
7374
addFunds=Three ways to add funds to your Brave Wallet
75+
copy=Copy
76+
firstKey=Key 1
77+
secondKey=Key 2
7478
copyToClipboard=Copy to clipboard
7579
smartphoneTitle=Use your smartphone app to transfer Bitcoin
7680
displayQRCode=Display QR code
@@ -110,6 +114,20 @@ offerSearchSuggestions=Autocomplete search term as you type
110114
doNotTrackTitle=Do Not Track
111115
doNotTrack=Send a 'Do Not Track' header with browsing requests (requires browser restart)
112116
blockCanvasFingerprinting=Fingerprinting Protection (may break some websites)
117+
advancedSettings=Advanced Settings...
118+
advancedSettingsTitle=Advanced Settings for Brave Payments
119+
ledgerRecoveryTitle=Recover your Brave wallet
120+
ledgerRecoverySubtitle=Enter your recovery keys below
121+
ledgerRecoveryContent=The balance of the recovered wallet will be transferred to your new Brave wallet. The old wallet will still exist as an empty wallet.
122+
ledgerBackupTitle=Backup your Brave wallet
123+
ledgerBackupContent=Below, you will find the anonymized recovery keys that are required if you ever lose access to this computer. We recommend that you print or save these keys and store them in a safe place, like your local safe deposit box, or under your mattress. It's really up to you!
124+
minimumPageTimeSetting=Minimum page time before logging a visit
125+
minimumVisitsSetting=Minimum visits for publisher relevancy
126+
backupLedger=Backup your wallet
127+
recoverLedger=Recover your wallet
128+
recover=Recover
129+
printKeys=Print keys
130+
saveRecoveryFile=Save recovery file...
113131
advancedPrivacySettings=Advanced Privacy Settings:
114132
braveryDefaults=Bravery Defaults
115133
blockAttackSites=Block reported attack sites (not available yet)

app/ledger.js

+35-2
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,25 @@ const doAction = (action) => {
133133
case settings.PAYMENTS_ENABLED:
134134
initialize(action.value, 'changeSettingPaymentsEnabled')
135135
break
136+
136137
case settings.PAYMENTS_CONTRIBUTION_AMOUNT:
137138
setPaymentInfo(action.value)
138139
break
140+
141+
case settings.MINIMUM_VISIT_TIME:
142+
if (action.value <= 0) break
143+
144+
synopsis.options.minDuration = action.value
145+
updatePublisherInfo()
146+
break
147+
148+
case settings.MINIMUM_VISTS:
149+
if (action.value <= 0) break
150+
151+
synopsis.options.minPublisherVisits = action.value
152+
updatePublisherInfo()
153+
break
154+
139155
default:
140156
break
141157
}
@@ -569,6 +585,8 @@ var enable = (paymentsEnabled) => {
569585
*/
570586

571587
var publisherInfo = {
588+
options: undefined,
589+
572590
synopsis: undefined,
573591

574592
_internal: {
@@ -599,13 +617,15 @@ var updatePublisherInfo = () => {
599617
syncWriter(pathName(synopsisPath), synopsis, () => {})
600618
publisherInfo.synopsis = synopsisNormalizer()
601619

620+
publisherInfo.options = synopsis.options
621+
602622
if (publisherInfo._internal.debugP) {
603623
data = []
604624
publisherInfo.synopsis.forEach((entry) => {
605625
data.push(underscore.extend(underscore.omit(entry, [ 'faviconURL' ]), { faviconURL: entry.faviconURL && '...' }))
606626
})
607627

608-
console.log('\nupdatePublisherInfo: ' + JSON.stringify(data, null, 2))
628+
console.log('\nupdatePublisherInfo: ' + JSON.stringify({ options: publisherInfo.options, synopsis: data }, null, 2))
609629
}
610630

611631
appActions.updatePublisherInfo(underscore.omit(publisherInfo, [ '_internal' ]))
@@ -869,6 +889,14 @@ var ledgerInfo = {
869889
buyURL: undefined,
870890
bravery: undefined,
871891

892+
// wallet credentials
893+
paymentId: undefined,
894+
passphrase: undefined,
895+
896+
// advanced ledger settings
897+
minDuration: undefined,
898+
minPublisherVisits: undefined,
899+
872900
hasBitcoinHandler: false,
873901

874902
// geoIP/exchange information
@@ -1107,6 +1135,12 @@ var getStateInfo = (state) => {
11071135
var info = state.paymentInfo
11081136
var then = underscore.now() - msecs.year
11091137

1138+
ledgerInfo.paymentId = state.properties.wallet.paymentId
1139+
ledgerInfo.passphrase = state.properties.wallet.keychains.passphrase
1140+
1141+
ledgerInfo.minDuration = synopsis.options.minDuration / msecs.second
1142+
ledgerInfo.minPublisherVisits = synopsis.options.minPublisherVisits
1143+
11101144
ledgerInfo.created = !!state.properties.wallet
11111145
ledgerInfo.creating = !ledgerInfo.created
11121146

@@ -1228,7 +1262,6 @@ var getPaymentInfo = () => {
12281262

12291263
info = underscore.extend(info, underscore.pick(body, [ 'buyURL', 'buyURLExpires', 'balance', 'unconfirmed', 'satoshis' ]))
12301264
info.address = client.getWalletAddress()
1231-
info.passphrase = client.getWalletPassphrase()
12321265
if ((amount) && (currency)) {
12331266
info = underscore.extend(info, { amount: amount, currency: currency })
12341267
if ((body.rates) && (body.rates[currency])) {

docs/state.md

+7-3
Original file line numberDiff line numberDiff line change
@@ -478,8 +478,8 @@ WindowStore
478478
error: object // error object returned
479479
}
480480
},
481-
publisherInfo: [ // one entry for each publisher having a non-zero `score`
482-
{
481+
publisherInfo: {
482+
synopsis: [ { // one entry for each publisher having a non-zero `score`
483483
rank: number, // i.e., 1, 2, 3, ...
484484
verified: boolean, // there is a verified wallet for this publisher
485485
site: string, // publisher name, e.g., "wikipedia.org"
@@ -493,8 +493,12 @@ WindowStore
493493
percentage: number, // i.e., 0, 1, ... 100
494494
publisherURL: string, // publisher site, e.g., "https://wikipedia.org/"
495495
faviconURL: string // i.e., "data:image/...;base64,..."
496+
} ],
497+
options: {
498+
minDuration: number, // e.g., 8000 for 8 seconds
499+
minPublisherVisits: number // e.g., 0
496500
}
497-
],
501+
}
498502
autofillAddressDetail: {
499503
name: string,
500504
organization: string,

js/about/aboutActions.js

+21
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,27 @@ const AboutActions = {
7575
})
7676
},
7777

78+
/**
79+
* Generates a file with the users backup keys
80+
*/
81+
generateKeyFile: function (backupAction) {
82+
AboutActions.dispatchAction({
83+
actionType: AppConstants.APP_BACKUP_KEYS,
84+
backupAction
85+
})
86+
},
87+
88+
/**
89+
* Loads a URL in a new frame in a safe way.
90+
* It is important that it is not a simple anchor because it should not
91+
* preserve the about preload script. See #672
92+
*/
93+
viewKeyFile: function () {
94+
AboutActions.dispatchAction({
95+
actionType: WindowConstants.WINDOW_VIEW_KEY
96+
})
97+
},
98+
7899
/**
79100
* Click through a certificate error.
80101
*

js/about/preferences.js

+154-1
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,8 @@ class TabsTab extends ImmutableComponent {
721721
class PaymentsTab extends ImmutableComponent {
722722
constructor () {
723723
super()
724+
this.printKeys = this.printKeys.bind(this)
725+
this.saveKeys = this.saveKeys.bind(this)
724726
this.createWallet = this.createWallet.bind(this)
725727
}
726728

@@ -730,6 +732,22 @@ class PaymentsTab extends ImmutableComponent {
730732
}
731733
}
732734

735+
copyToClipboard (text) {
736+
aboutActions.setClipboard(text)
737+
}
738+
739+
generateKeyFile (backupAction) {
740+
aboutActions.generateKeyFile(backupAction)
741+
}
742+
743+
printKeys () {
744+
this.generateKeyFile('print')
745+
}
746+
747+
saveKeys () {
748+
this.generateKeyFile('save')
749+
}
750+
733751
get enabled () {
734752
return getSetting(settings.PAYMENTS_ENABLED, this.props.settings)
735753
}
@@ -839,6 +857,120 @@ class PaymentsTab extends ImmutableComponent {
839857
</div>
840858
}
841859

860+
get advancedSettingsContent () {
861+
const minDuration = this.props.ledgerData.get('minDuration')
862+
const minPublisherVisits = this.props.ledgerData.get('minPublisherVisits')
863+
864+
return <div className='board'>
865+
<div className='panel'>
866+
<div className='settingsPanelDivider'>
867+
<div data-l10n-id='minimumPageTimeSetting' />
868+
<SettingsList>
869+
<SettingItem>
870+
<select
871+
id='fundsSelectBox'
872+
defaultValue={minDuration || 8}
873+
onChange={changeSetting.bind(null, this.props.onChangeSetting, settings.MINIMUM_VISIT_TIME)}>>
874+
<option value='5'>5 seconds</option>
875+
<option value='8'>8 seconds</option>
876+
<option value='60'>1 minute</option>
877+
</select>
878+
</SettingItem>
879+
</SettingsList>
880+
<div data-l10n-id='minimumVisitsSetting' />
881+
<SettingsList>
882+
<SettingItem>
883+
<select
884+
id='fundsSelectBox'
885+
defaultValue={minPublisherVisits || 5}
886+
onChange={changeSetting.bind(null, this.props.onChangeSetting, settings.MINIMUM_VISTS)}>>>
887+
<option value='2'>2 visits</option>
888+
<option value='5'>5 visits</option>
889+
<option value='10'>10 visits</option>
890+
</select>
891+
</SettingItem>
892+
</SettingsList>
893+
</div>
894+
{this.enabled
895+
? <SettingCheckbox
896+
dataL10nId='notifications'
897+
prefKey={settings.PAYMENTS_NOTIFICATIONS}
898+
settings={this.props.settings}
899+
onChangeSetting={this.props.onChangeSetting} />
900+
: null}
901+
</div>
902+
</div>
903+
}
904+
905+
get advancedSettingsFooter () {
906+
return <div className='panel advancedSettingsFooter'>
907+
<Button l10nId='backupLedger' className='primaryButton' onClick={this.props.showOverlay.bind(this, 'ledgerBackup')} />
908+
<Button l10nId='recoverLedger' className='primaryButton' onClick={this.props.showOverlay.bind(this, 'ledgerRecovery')} />
909+
<Button l10nId='done' className='whiteButton inlineButton' onClick={this.props.hideOverlay.bind(this, 'advancedSettings')} />
910+
</div>
911+
}
912+
913+
get ledgerBackupContent () {
914+
const paymentId = this.props.ledgerData.get('paymentId')
915+
const passphrase = this.props.ledgerData.get('passphrase')
916+
917+
return <div className='board'>
918+
<div className='panel'>
919+
<span data-l10n-id='ledgerBackupContent' />
920+
<div>
921+
<div className='copyContainer'>
922+
<Button l10nId='copy' className='whiteButton inlineButton' onClick={this.copyToClipboard.bind(this, paymentId)} />
923+
</div>
924+
<div className='keyContainer'>
925+
<h2 data-l10n-id='firstKey' />
926+
<span>{paymentId}</span>
927+
</div>
928+
</div>
929+
<div>
930+
<div className='copyContainer'>
931+
<Button l10nId='copy' className='whiteButton inlineButton' onClick={this.copyToClipboard.bind(this, passphrase)} />
932+
</div>
933+
<div className='keyContainer'>
934+
<h2 data-l10n-id='secondKey' />
935+
<span>{passphrase}</span>
936+
</div>
937+
</div>
938+
</div>
939+
</div>
940+
}
941+
942+
get ledgerBackupFooter () {
943+
return <div className='panel advancedSettingsFooter'>
944+
<Button l10nId='printKeys' className='primaryButton' onClick={this.printKeys} />
945+
<Button l10nId='saveRecoveryFile' className='primaryButton' onClick={this.saveKeys} />
946+
<Button l10nId='done' className='whiteButton inlineButton' onClick={this.props.hideOverlay.bind(this, 'ledgerBackup')} />
947+
</div>
948+
}
949+
950+
get ledgerRecoveryContent () {
951+
return <div className='board'>
952+
<div className='panel'>
953+
<h3 data-l10n-id='ledgerRecoverySubtitle' />
954+
<span data-l10n-id='ledgerRecoveryContent' />
955+
<SettingsList>
956+
<SettingItem>
957+
<h2 data-l10n-id='firstKey' />
958+
<input type='text' />
959+
<h2 data-l10n-id='secondKey' />
960+
<input type='text' />
961+
</SettingItem>
962+
</SettingsList>
963+
</div>
964+
</div>
965+
}
966+
967+
get ledgerRecoveryFooter () {
968+
return <div className='panel advancedSettingsFooter'>
969+
<Button l10nId='cancel' className='whiteButton inlineButton' onClick={this.props.hideOverlay.bind(this, 'ledgerBackup')} />
970+
<Button l10nId='recover' className='primaryButton' />
971+
</div>
972+
}
973+
842974
get nextReconcileDate () {
843975
const ledgerData = this.props.ledgerData
844976
if (!ledgerData.get('reconcileStamp')) {
@@ -947,6 +1079,21 @@ class PaymentsTab extends ImmutableComponent {
9471079
? <ModalOverlay title={'paymentHistoryTitle'} customTitleClasses={'paymentHistory'} content={this.paymentHistoryContent} footer={this.paymentHistoryFooter} onHide={this.props.hideOverlay.bind(this, 'paymentHistory')} />
9481080
: null
9491081
}
1082+
{
1083+
this.enabled && this.props.advancedSettingsOverlayVisible
1084+
? <ModalOverlay title={'advancedSettingsTitle'} content={this.advancedSettingsContent} footer={this.advancedSettingsFooter} onHide={this.props.hideOverlay.bind(this, 'advancedSettings')} />
1085+
: null
1086+
}
1087+
{
1088+
this.enabled && this.props.ledgerBackupOverlayVisible
1089+
? <ModalOverlay title={'ledgerBackupTitle'} content={this.ledgerBackupContent} footer={this.ledgerBackupFooter} onHide={this.props.hideOverlay.bind(this, 'ledgerBackup')} />
1090+
: null
1091+
}
1092+
{
1093+
this.enabled && this.props.ledgerRecoveryOverlayVisible
1094+
? <ModalOverlay title={'ledgerRecoveryTitle'} content={this.ledgerRecoveryContent} footer={this.ledgerRecoveryFooter} onHide={this.props.hideOverlay.bind(this, 'ledgerRecovery')} />
1095+
: null
1096+
}
9501097
<div className='titleBar'>
9511098
<div className='sectionTitleWrapper pull-left'>
9521099
<span className='sectionTitle'>Brave Payments</span>
@@ -957,7 +1104,7 @@ class PaymentsTab extends ImmutableComponent {
9571104
<span data-l10n-id='off' />
9581105
<SettingCheckbox dataL10nId='on' prefKey={settings.PAYMENTS_ENABLED} settings={this.props.settings} onChangeSetting={this.props.onChangeSetting} />
9591106
</div>
960-
{this.enabled ? <SettingCheckbox dataL10nId='notifications' prefKey={settings.PAYMENTS_NOTIFICATIONS} settings={this.props.settings} onChangeSetting={this.props.onChangeSetting} /> : null}
1107+
<Button l10nId='advancedSettings' className='whiteButton inlineButton' onClick={this.props.showOverlay.bind(this, 'advancedSettings')} />
9611108
</div>
9621109
</div>
9631110
{
@@ -1367,6 +1514,9 @@ class AboutPreferences extends React.Component {
13671514
bitcoinOverlayVisible: false,
13681515
qrcodeOverlayVisible: false,
13691516
paymentHistoryOverlayVisible: false,
1517+
advancedSettingsOverlayVisible: false,
1518+
ledgerBackupOverlayVisible: false,
1519+
ledgerRecoveryOverlayVisible: false,
13701520
addFundsOverlayVisible: false,
13711521
preferenceTab: hash.toUpperCase() in preferenceTabs ? hash : preferenceTabs.GENERAL,
13721522
hintNumber: this.getNextHintNumber(),
@@ -1497,6 +1647,9 @@ class AboutPreferences extends React.Component {
14971647
bitcoinOverlayVisible={this.state.bitcoinOverlayVisible}
14981648
qrcodeOverlayVisible={this.state.qrcodeOverlayVisible}
14991649
paymentHistoryOverlayVisible={this.state.paymentHistoryOverlayVisible}
1650+
advancedSettingsOverlayVisible={this.state.advancedSettingsOverlayVisible}
1651+
ledgerBackupOverlayVisible={this.state.ledgerBackupOverlayVisible}
1652+
ledgerRecoveryOverlayVisible={this.state.ledgerRecoveryOverlayVisible}
15001653
addFundsOverlayVisible={this.state.addFundsOverlayVisible}
15011654
showOverlay={this.setOverlayVisible.bind(this, true)}
15021655
hideOverlay={this.setOverlayVisible.bind(this, false)} />

js/constants/appConstants.js

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const AppConstants = {
3737
APP_CLEAR_MESSAGE_BOXES: _, /** @param {string} origin */
3838
APP_ADD_WORD: _, /** @param {string} word, @param {boolean} learn */
3939
APP_SET_DICTIONARY: _, /** @param {string} locale */
40+
APP_BACKUP_KEYS: _,
4041
APP_ADD_AUTOFILL_ADDRESS: _,
4142
APP_REMOVE_AUTOFILL_ADDRESS: _,
4243
APP_ADD_AUTOFILL_CREDIT_CARD: _,

0 commit comments

Comments
 (0)