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

Commit 43c0100

Browse files
authored
Merge pull request #4396 from brave/ledger-redesign
ledger backup and recovery
2 parents c1fffde + b0ffd72 commit 43c0100

24 files changed

+626
-16
lines changed

app/browser/tabs.js

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
const {app, BrowserWindow, session, webContents} = require('electron')
2+
const extensions = process.atomBinding('extension')
3+
const { getIndexHTML } = require('../../js/lib/appUrlUtil')
4+
5+
let currentWebContents = {}
6+
let activeTab = null
7+
8+
const cleanupWebContents = (tabId) => {
9+
delete currentWebContents[tabId]
10+
}
11+
12+
const tabs = {
13+
init: () => {
14+
app.on('web-contents-created', function (event, tab) {
15+
// TODO(bridiver) - also exclude extension action windows??
16+
if (extensions.isBackgroundPage(tab) || tab.getURL() === getIndexHTML()) {
17+
return
18+
}
19+
let tabId = tab.getId()
20+
tab.on('destroyed', cleanupWebContents.bind(null, tabId))
21+
tab.on('crashed', cleanupWebContents.bind(null, tabId))
22+
tab.on('close', cleanupWebContents.bind(null, tabId))
23+
tab.on('set-active', function (evt, active) {
24+
if (active) {
25+
activeTab = tab
26+
}
27+
})
28+
currentWebContents[tabId] = tab
29+
})
30+
},
31+
32+
getWebContents: (tabId) => {
33+
return currentWebContents[tabId]
34+
},
35+
36+
create: (createProperties) => {
37+
return new Promise((resolve, reject) => {
38+
// TODO(bridiver) - make this available from electron
39+
var payload = {}
40+
process.emit('ELECTRON_GUEST_VIEW_MANAGER_NEXT_INSTANCE_ID', payload)
41+
var guestInstanceId = payload.returnValue
42+
43+
let win = BrowserWindow.getFocusedWindow()
44+
let windowId = createProperties.windowId
45+
if (windowId && windowId !== -2) {
46+
win = BrowserWindow.fromId(windowId) || win
47+
}
48+
if (!win) {
49+
reject('Could not find a window for new tab')
50+
return
51+
}
52+
let opener = null
53+
let newSession = session.defaultSession
54+
let openerTabId = createProperties.openerTabId
55+
if (openerTabId) {
56+
opener = tabs.getWebContents(openerTabId)
57+
if (!opener) {
58+
reject('Opener does not exist')
59+
return
60+
}
61+
// only use the opener if it is in the same window
62+
if (opener.webContents.hostWebContents !== win.webContents) {
63+
reject('Opener must be in the same window as new tab')
64+
return
65+
}
66+
}
67+
68+
opener = opener || activeTab
69+
if (opener) {
70+
newSession = opener.session
71+
} else {
72+
reject('Could not find an opener for new tab')
73+
return
74+
}
75+
76+
let webPreferences = {
77+
isGuest: true,
78+
embedder: win.webContents,
79+
session: newSession,
80+
guestInstanceId,
81+
delayedLoadUrl: createProperties.url || 'about:newtab'
82+
}
83+
webPreferences = Object.assign({}, opener.getWebPreferences(), webPreferences)
84+
let guest = webContents.create(webPreferences)
85+
process.emit('ELECTRON_GUEST_VIEW_MANAGER_REGISTER_GUEST', { sender: opener }, guest, guestInstanceId)
86+
87+
guest.once('did-finish-load', () => {
88+
resolve(guest)
89+
})
90+
let active = createProperties.active !== false
91+
if (!active) {
92+
active = createProperties.selected !== false
93+
}
94+
let disposition = active ? 'foreground-tab' : 'background-tab'
95+
96+
process.emit('ELECTRON_GUEST_VIEW_MANAGER_TAB_OPEN',
97+
{ sender: opener }, // event
98+
'about:blank',
99+
'',
100+
disposition,
101+
{ webPreferences: guest.getWebPreferences() })
102+
})
103+
}
104+
}
105+
106+
module.exports = tabs

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/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ const contentSettings = require('../js/state/contentSettings')
8080
const privacy = require('../js/state/privacy')
8181
const basicAuth = require('./browser/basicAuth')
8282
const async = require('async')
83+
const tabs = require('./browser/tabs')
8384

8485
// temporary fix for #4517, #4518 and #4472
8586
app.commandLine.appendSwitch('enable-use-zoom-for-dsf', 'false')
@@ -415,6 +416,7 @@ app.on('ready', () => {
415416
Menu.init(initialState, null)
416417
return loadedPerWindowState
417418
}).then((loadedPerWindowState) => {
419+
tabs.init()
418420
basicAuth.init()
419421
contentSettings.init()
420422
privacy.init()

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
@@ -56,6 +56,7 @@ var rendererIdentifiers = function () {
5656
'deleteHistoryEntry',
5757
'deleteHistoryEntries',
5858
'deleteLedgerEntry',
59+
'ledgerBackupText',
5960
'editFolder',
6061
'editBookmark',
6162
'unmuteTabs',
@@ -233,13 +234,19 @@ var ctx = null
233234
var translations = {}
234235
var lang = 'en-US'
235236

237+
// todo: FSI/PDI stripping can probably be replaced once
238+
// https://github.com/l20n/l20n.js/commit/2fea50bf43c43a8e930a519a37f0f64f3626e885
239+
// is released
240+
const FSI = '\u2068'
241+
const PDI = '\u2069'
242+
236243
// Return a translate token from cache or a placeholder
237244
// indicating that no translation is available
238245
exports.translation = function (token, replacements = {}) {
239246
if (translations[token]) {
240247
let returnVal = translations[token]
241248
for (var key in replacements) {
242-
returnVal = returnVal.replace(new RegExp('{{\\s*' + key + '\\s*}}'), replacements[key])
249+
returnVal = returnVal.replace(new RegExp(FSI + '{{\\s*' + key + '\\s*}}' + PDI), replacements[key])
243250
}
244251
return returnVal
245252
} 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

0 commit comments

Comments
 (0)