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

Commit b0ffd72

Browse files
committed
ledger backup and recovery
1 parent 1e91bb4 commit b0ffd72

22 files changed

+518
-16
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ flashInstalled=Flash is already installed and can be enabled in Preferences > Se
126126
goToPrefs=Open Preferences
127127
goToAdobe=Reinstall Flash
128128
allowFlashPlayer=Allow {{origin}} to run Flash Player?
129-
129+
ledgerBackupText=Your ledger keys are {{paymentId}} and {{passphrase}}
130130
error=Error
131131
caseSensitivity=Match case
132132
nameField=Title:

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

+23-1
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,12 @@ 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
65-
notifications=notifications
66+
ok=Ok
67+
notifications=Show payment notifications
6668
moneyAdd=Use your debit/credit card
6769
moneyAddSubTitle=No Bitcoin needed!
6870
outsideUSAPayment=Need to buy Bitcoin outside of the USA?
@@ -71,6 +73,11 @@ add=Fund with debit/credit
7173
transferTime=Transfer may take up to 40 minutes
7274
addFundsTitle=Add funds…
7375
addFunds=Three ways to add funds to your Brave Wallet
76+
copy=Copy
77+
firstKey=Key 1
78+
secondKey=Key 2
79+
firstRecoveryKey=Recovery Key 1
80+
secondRecoveryKey=Recovery Key 2
7481
copyToClipboard=Copy to clipboard
7582
smartphoneTitle=Use your smartphone app to transfer Bitcoin
7683
displayQRCode=Display QR code
@@ -110,6 +117,21 @@ offerSearchSuggestions=Autocomplete search term as you type
110117
doNotTrackTitle=Do Not Track
111118
doNotTrack=Send a 'Do Not Track' header with browsing requests (requires browser restart)
112119
blockCanvasFingerprinting=Fingerprinting Protection (may break some websites)
120+
advancedSettings=Advanced Settings...
121+
advancedSettingsTitle=Advanced Settings for Brave Payments
122+
ledgerRecoveryTitle=Recover your Brave wallet
123+
ledgerRecoverySubtitle=Enter your recovery keys below
124+
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.
125+
ledgerBackupTitle=Backup your Brave wallet
126+
ledgerBackupContent=Below, you will find the anonymized recovery keys that are required if you ever lose access to this computer.
127+
minimumPageTimeSetting=Minimum page time before logging a visit
128+
minimumVisitsSetting=Minimum visits for publisher relevancy
129+
backupLedger=Backup your wallet
130+
balanceRecovered={{balance}} BTC was recovered and transferred to your Brave wallet.
131+
recoverLedger=Recover your wallet
132+
recover=Recover
133+
printKeys=Print keys
134+
saveRecoveryFile=Save recovery file...
113135
advancedPrivacySettings=Advanced Privacy Settings:
114136
braveryDefaults=Bravery Defaults
115137
blockAttackSites=Block reported attack sites (not available yet)

app/ledger.js

+84-2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ const appStore = require('../js/stores/appStore')
5353
const eventStore = require('../js/stores/eventStore')
5454
const rulesolver = require('./extensions/brave/content/scripts/pageInformation')
5555
const ledgerUtil = require('./common/lib/ledgerUtil')
56+
const Tabs = require('./browser/tabs')
57+
const {fileUrl} = require('../js/lib/appUrlUtil')
5658

5759
// TBD: remove these post beta [MTR]
5860
const logPath = 'ledger-log.json'
@@ -133,9 +135,25 @@ const doAction = (action) => {
133135
case settings.PAYMENTS_ENABLED:
134136
initialize(action.value)
135137
break
138+
136139
case settings.PAYMENTS_CONTRIBUTION_AMOUNT:
137140
setPaymentInfo(action.value)
138141
break
142+
143+
case settings.MINIMUM_VISIT_TIME:
144+
if (action.value <= 0) break
145+
146+
synopsis.options.minDuration = action.value
147+
updatePublisherInfo()
148+
break
149+
150+
case settings.MINIMUM_VISTS:
151+
if (action.value <= 0) break
152+
153+
synopsis.options.minPublisherVisits = action.value
154+
updatePublisherInfo()
155+
break
156+
139157
default:
140158
break
141159
}
@@ -215,6 +233,51 @@ var boot = () => {
215233
})
216234
}
217235

236+
/*
237+
* Print or Save Recovery Keys
238+
*/
239+
240+
var backupKeys = (appState, action) => {
241+
const paymentId = appState.getIn(['ledgerInfo', 'paymentId'])
242+
const passphrase = appState.getIn(['ledgerInfo', 'passphrase'])
243+
const message = locale.translation('ledgerBackupText', {paymentId, passphrase})
244+
const filePath = path.join(app.getPath('userData'), '/brave_wallet_recovery.txt')
245+
246+
fs.writeFile(filePath, message, (err) => {
247+
if (err) {
248+
console.log(err)
249+
} else {
250+
Tabs.create({url: fileUrl(filePath)}).then((webContents) => {
251+
if (action.backupAction === 'print') {
252+
webContents.print({silent: false, printBackground: false})
253+
} else {
254+
webContents.downloadURL(fileUrl(filePath))
255+
}
256+
}).catch((err) => {
257+
console.error(err)
258+
})
259+
}
260+
})
261+
262+
return appState
263+
}
264+
265+
/*
266+
* Recover Ledger Keys
267+
*/
268+
269+
var recoverKeys = (appState, action) => {
270+
client.recoverWallet(action.firstRecoveryKey, action.secondRecoveryKey, (err, body) => {
271+
if (err) {
272+
setImmediate(() => appActions.ledgerRecoveryFailed())
273+
} else {
274+
setImmediate(() => appActions.ledgerRecoverySucceeded())
275+
}
276+
})
277+
278+
return appState
279+
}
280+
218281
/*
219282
* IPC entry point
220283
*/
@@ -571,6 +634,8 @@ var enable = (paymentsEnabled) => {
571634
*/
572635

573636
var publisherInfo = {
637+
options: undefined,
638+
574639
synopsis: undefined,
575640

576641
_internal: {
@@ -601,13 +666,15 @@ var updatePublisherInfo = () => {
601666
syncWriter(pathName(synopsisPath), synopsis, () => {})
602667
publisherInfo.synopsis = synopsisNormalizer()
603668

669+
publisherInfo.synopsisOptions = synopsis.options
670+
604671
if (publisherInfo._internal.debugP) {
605672
data = []
606673
publisherInfo.synopsis.forEach((entry) => {
607674
data.push(underscore.extend(underscore.omit(entry, [ 'faviconURL' ]), { faviconURL: entry.faviconURL && '...' }))
608675
})
609676

610-
console.log('\nupdatePublisherInfo: ' + JSON.stringify(data, null, 2))
677+
console.log('\nupdatePublisherInfo: ' + JSON.stringify({ options: publisherInfo.synopsisOptions, synopsis: data }, null, 2))
611678
}
612679

613680
appActions.updatePublisherInfo(underscore.omit(publisherInfo, [ '_internal' ]))
@@ -871,6 +938,14 @@ var ledgerInfo = {
871938
buyURL: undefined,
872939
bravery: undefined,
873940

941+
// wallet credentials
942+
paymentId: undefined,
943+
passphrase: undefined,
944+
945+
// advanced ledger settings
946+
minDuration: undefined,
947+
minPublisherVisits: undefined,
948+
874949
hasBitcoinHandler: false,
875950

876951
// geoIP/exchange information
@@ -1109,6 +1184,12 @@ var getStateInfo = (state) => {
11091184
var info = state.paymentInfo
11101185
var then = underscore.now() - msecs.year
11111186

1187+
ledgerInfo.paymentId = state.properties.wallet.paymentId
1188+
ledgerInfo.passphrase = state.properties.wallet.keychains.passphrase
1189+
1190+
ledgerInfo.minDuration = synopsis.options.minDuration
1191+
ledgerInfo.minPublisherVisits = synopsis.options.minPublisherVisits
1192+
11121193
ledgerInfo.created = !!state.properties.wallet
11131194
ledgerInfo.creating = !ledgerInfo.created
11141195

@@ -1230,7 +1311,6 @@ var getPaymentInfo = () => {
12301311

12311312
info = underscore.extend(info, underscore.pick(body, [ 'buyURL', 'buyURLExpires', 'balance', 'unconfirmed', 'satoshis' ]))
12321313
info.address = client.getWalletAddress()
1233-
info.passphrase = client.getWalletPassphrase()
12341314
if ((amount) && (currency)) {
12351315
info = underscore.extend(info, { amount: amount, currency: currency })
12361316
if ((body.rates) && (body.rates[currency])) {
@@ -1452,6 +1532,8 @@ const showNotificationPaymentDone = (transactionContributionFiat) => {
14521532

14531533
module.exports = {
14541534
init: init,
1535+
recoverKeys: recoverKeys,
1536+
backupKeys: backupKeys,
14551537
quit: quit,
14561538
boot: boot
14571539
}

app/locale.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ var rendererIdentifiers = function () {
5252
'deleteBookmark',
5353
'deleteHistoryEntry',
5454
'deleteLedgerEntry',
55+
'ledgerBackupText',
5556
'editFolder',
5657
'editBookmark',
5758
'unmuteTabs',
@@ -229,13 +230,19 @@ var ctx = null
229230
var translations = {}
230231
var lang = 'en-US'
231232

233+
// todo: FSI/PDI stripping can probably be replaced once
234+
// https://github.com/l20n/l20n.js/commit/2fea50bf43c43a8e930a519a37f0f64f3626e885
235+
// is released
236+
const FSI = '\u2068'
237+
const PDI = '\u2069'
238+
232239
// Return a translate token from cache or a placeholder
233240
// indicating that no translation is available
234241
exports.translation = function (token, replacements = {}) {
235242
if (translations[token]) {
236243
let returnVal = translations[token]
237244
for (var key in replacements) {
238-
returnVal = returnVal.replace(new RegExp('{{\\s*' + key + '\\s*}}'), replacements[key])
245+
returnVal = returnVal.replace(new RegExp(FSI + '{{\\s*' + key + '\\s*}}' + PDI), replacements[key])
239246
}
240247
return returnVal
241248
} else {

app/sessionStore.js

+4
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,10 @@ module.exports.cleanAppData = (data, isShutdown) => {
233233
data.temporarySiteSettings = {}
234234
// Delete Flash state since this is checked on startup
235235
delete data.flashInitialized
236+
// Delete Recovery status on shut down
237+
try {
238+
delete data.ui.about.preferences.recoverySucceeded
239+
} catch (e) {}
236240
// We used to store a huge list of IDs but we didn't use them.
237241
// Get rid of them here.
238242
delete data.windows

docs/appActions.md

+12
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,18 @@ Dispatches a message to clear all completed downloads
104104

105105

106106

107+
### ledgerRecoverySucceeded()
108+
109+
Dispatches a message to clear all completed downloads
110+
111+
112+
113+
### ledgerRecoveryFailed()
114+
115+
Dispatches a message to clear all completed downloads
116+
117+
118+
107119
### setDefaultWindowSize(size)
108120

109121
Sets the default window size

docs/state.md

+8-3
Original file line numberDiff line numberDiff line change
@@ -461,14 +461,15 @@ WindowStore
461461
exchangeName: string, // the name of the BTC exchange
462462
exchangeURL: string // the URL of the BTC exchange
463463
},
464+
recoverySucceeded: boolean // the status of an attempted recovery
464465
paymentIMG: string, // the QR code equivalent of `paymentURL` expressed as "data:image/...;base64,..."
465466
error: { // non-null if the last updateLedgerInfo happened concurrently with an error
466467
caller: string // function in which error was handled
467468
error: object // error object returned
468469
}
469470
},
470-
publisherInfo: [ // one entry for each publisher having a non-zero `score`
471-
{
471+
publisherInfo: {
472+
synopsis: [ { // one entry for each publisher having a non-zero `score`
472473
rank: number, // i.e., 1, 2, 3, ...
473474
verified: boolean, // there is a verified wallet for this publisher
474475
site: string, // publisher name, e.g., "wikipedia.org"
@@ -482,8 +483,12 @@ WindowStore
482483
percentage: number, // i.e., 0, 1, ... 100
483484
publisherURL: string, // publisher site, e.g., "https://wikipedia.org/"
484485
faviconURL: string // i.e., "data:image/...;base64,..."
486+
} ],
487+
synopsisOptions: {
488+
minDuration: number, // e.g., 8000 for 8 seconds
489+
minPublisherVisits: number // e.g., 0
485490
}
486-
],
491+
}
487492
autofillAddressDetail: {
488493
name: string,
489494
organization: string,

js/about/aboutActions.js

+30
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,36 @@ const aboutActions = {
8787
})
8888
},
8989

90+
/**
91+
* Generates a file with the users backup keys
92+
*/
93+
ledgerGenerateKeyFile: function (backupAction) {
94+
aboutActions.dispatchAction({
95+
actionType: appConstants.APP_BACKUP_KEYS,
96+
backupAction
97+
})
98+
},
99+
100+
/**
101+
* Recover wallet by merging old wallet into new one
102+
*/
103+
ledgerRecoverWallet: function (firstRecoveryKey, secondRecoveryKey) {
104+
aboutActions.dispatchAction({
105+
actionType: appConstants.APP_RECOVER_WALLET,
106+
firstRecoveryKey,
107+
secondRecoveryKey
108+
})
109+
},
110+
111+
/**
112+
* Clear wallet recovery status
113+
*/
114+
clearRecoveryStatus: function () {
115+
aboutActions.dispatchAction({
116+
actionType: appConstants.APP_CLEAR_RECOVERY
117+
})
118+
},
119+
90120
/**
91121
* Click through a certificate error.
92122
*

0 commit comments

Comments
 (0)