Merge pull request #993 from ethereum/implementsign

Integrate personal mode
pull/3094/head
yann300 7 years ago committed by GitHub
commit 2db91cc180
  1. 6
      src/app.js
  2. 50
      src/app/execution/txRunner.js
  3. 22
      src/app/tabs/run-tab.js
  4. 16
      src/app/tabs/settings-tab.js
  5. 2
      src/app/ui/copy-to-clipboard.js
  6. 45
      src/app/ui/modal-dialog-custom.js
  7. 2
      src/app/ui/tooltip.js
  8. 30
      src/universal-dapp.js

@ -255,6 +255,9 @@ function run () {
config: self._api.config, config: self._api.config,
detectNetwork: (cb) => { detectNetwork: (cb) => {
executionContext.detectNetwork(cb) executionContext.detectNetwork(cb)
},
personalMode: () => {
return self._api.config.get('settings/personal-mode')
} }
}, },
opt: { removable: false, removable_instances: true } opt: { removable: false, removable_instances: true }
@ -623,6 +626,9 @@ function run () {
}, },
getCompilationResult: () => { getCompilationResult: () => {
return compiler.lastCompilationResult return compiler.lastCompilationResult
},
newAccount: (pass, cb) => {
udapp.newAccount(pass, cb)
} }
} }
var rhpEvents = { var rhpEvents = {

@ -11,6 +11,7 @@ var csjs = require('csjs-inject')
var remixLib = require('remix-lib') var remixLib = require('remix-lib')
var styleGuide = remixLib.ui.styleGuide var styleGuide = remixLib.ui.styleGuide
var styles = styleGuide() var styles = styleGuide()
var modal = require('../ui/modal-dialog-custom')
var css = csjs` var css = csjs`
.txInfoBox { .txInfoBox {
@ -25,12 +26,10 @@ var css = csjs`
} }
` `
function TxRunner (vmaccounts, opts) { function TxRunner (vmaccounts, api) {
this.personalMode = opts.personalMode this._api = api
this.blockNumber = 0 this.blockNumber = 0
this.runAsync = true this.runAsync = true
this.config = opts.config
this.detectNetwork = opts.detectNetwork
if (executionContext.isVM()) { if (executionContext.isVM()) {
this.blockNumber = 1150000 // The VM is running in Homestead mode, which started at this block. this.blockNumber = 1150000 // The VM is running in Homestead mode, which started at this block.
this.runAsync = false // We have to run like this cause the VM Event Manager does not support running multiple txs at the same time. this.runAsync = false // We have to run like this cause the VM Event Manager does not support running multiple txs at the same time.
@ -45,23 +44,21 @@ TxRunner.prototype.rawRun = function (args, cb) {
} }
TxRunner.prototype.execute = function (args, callback) { TxRunner.prototype.execute = function (args, callback) {
var self = this
function execute (gasPrice) { function execute (gasPrice) {
if (gasPrice) tx.gasPrice = executionContext.web3().toHex(gasPrice) if (gasPrice) tx.gasPrice = executionContext.web3().toHex(gasPrice)
var sendTransaction = self.personalMode ? executionContext.web3().personal.sendTransaction : executionContext.web3().eth.sendTransaction if (self._api.personalMode()) {
try { modal.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account ' + tx.from, '', (value) => {
sendTransaction(tx, function (err, resp) { sendTransaction(executionContext.web3().personal.sendTransaction, tx, value, callback)
if (err) { }, () => {
return callback(err, resp) return callback('Canceled by user.')
}
tryTillResponse(resp, callback)
}) })
} catch (e) { } else {
return callback(`Send transaction failed: ${e.message} . if you use an injected provider, please check it is properly unlocked. `) sendTransaction(executionContext.web3().eth.sendTransaction, tx, null, callback)
} }
} }
var self = this
var from = args.from var from = args.from
var to = args.to var to = args.to
var data = args.data var data = args.data
@ -107,8 +104,8 @@ TxRunner.prototype.execute = function (args, callback) {
tx.gas = gasEstimation tx.gas = gasEstimation
if (!self.config.getUnpersistedProperty('doNotShowTransactionConfirmationAgain')) { if (!self._api.config.getUnpersistedProperty('doNotShowTransactionConfirmationAgain')) {
self.detectNetwork((err, network) => { self._api.detectNetwork((err, network) => {
if (err) { if (err) {
console.log(err) console.log(err)
} else { } else {
@ -117,7 +114,7 @@ TxRunner.prototype.execute = function (args, callback) {
modalDialog('Confirm transaction', content, modalDialog('Confirm transaction', content,
{ label: 'Confirm', { label: 'Confirm',
fn: () => { fn: () => {
self.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) self._api.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked)
if (!content.gasPriceStatus) { if (!content.gasPriceStatus) {
callback('Given gas grice is not correct') callback('Given gas grice is not correct')
} else { } else {
@ -206,6 +203,21 @@ function tryTillResponse (txhash, done) {
}) })
} }
function sendTransaction (sendTx, tx, pass, callback) {
var cb = function (err, resp) {
if (err) {
return callback(err, resp)
}
tryTillResponse(resp, callback)
}
var args = pass !== null ? [tx, pass, cb] : [tx, cb]
try {
sendTx.apply({}, args)
} catch (e) {
return callback(`Send transaction failed: ${e.message} . if you use an injected provider, please check it is properly unlocked. `)
}
}
function run (self, tx, stamp, callback) { function run (self, tx, stamp, callback) {
if (!self.runAsync && Object.keys(self.pendingTxs).length) { if (!self.runAsync && Object.keys(self.pendingTxs).length) {
self.queusTxs.push({ tx, stamp, callback }) self.queusTxs.push({ tx, stamp, callback })
@ -234,7 +246,7 @@ function confirmDialog (tx, gasEstimation, self) {
<div>Amount: ${amount} Ether</div> <div>Amount: ${amount} Ether</div>
<div>Gas estimation: ${gasEstimation}</div> <div>Gas estimation: ${gasEstimation}</div>
<div>Gas limit: ${tx.gas}</div> <div>Gas limit: ${tx.gas}</div>
<div>Gas price: <input id='gasprice' oninput=${gasPriceChanged} /> Gwei</div> <div>Gas price: <input id='gasprice' oninput=${gasPriceChanged} /> Gwei <span> (visit <a target='_blank' href='https://ethgasstation.info'>ethgasstation.info</a> to get more info about gas price)</span></div>
<div>Max transaction fee:<span id='txfee'></span></div> <div>Max transaction fee:<span id='txfee'></span></div>
<div>Data:</div> <div>Data:</div>
<pre class=${css.wrapword}>${tx.data}</pre> <pre class=${css.wrapword}>${tx.data}</pre>

@ -10,6 +10,7 @@ var executionContext = require('../../execution-context')
var copyToClipboard = require('../ui/copy-to-clipboard') var copyToClipboard = require('../ui/copy-to-clipboard')
var Recorder = require('../../recorder') var Recorder = require('../../recorder')
var EventManager = require('remix-lib').EventManager var EventManager = require('remix-lib').EventManager
var addTooltip = require('../ui/tooltip')
// -------------- styling ---------------------- // -------------- styling ----------------------
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
@ -191,6 +192,14 @@ var css = csjs`
} }
.transactionActions { .transactionActions {
float: right; float: right;
}
.createAccount {
margin-left: 5px;
cursor: pointer;
}
.createAccount:hover {
color: ${styles.colors.orange};
}
` `
module.exports = runTab module.exports = runTab
@ -282,7 +291,7 @@ function fillAccountsList (appAPI, container) {
var $txOrigin = $(container.querySelector('#txorigin')) var $txOrigin = $(container.querySelector('#txorigin'))
$txOrigin.empty() $txOrigin.empty()
appAPI.udapp().getAccounts((err, accounts) => { appAPI.udapp().getAccounts((err, accounts) => {
if (err) { console.log(err) } if (err) { addTooltip(`Cannot get account list: ${err}`) }
if (accounts && accounts[0]) { if (accounts && accounts[0]) {
for (var a in accounts) { $txOrigin.append($('<option />').val(accounts[a]).text(accounts[a])) } for (var a in accounts) { $txOrigin.append($('<option />').val(accounts[a]).text(accounts[a])) }
$txOrigin.val(accounts[0]) $txOrigin.val(accounts[0])
@ -543,6 +552,16 @@ function settings (container, appAPI, appEvents) {
}) })
} }
setInterval(updateNetwork, 5000) setInterval(updateNetwork, 5000)
function newAccount () {
appAPI.newAccount('', (error, address) => {
if (!error) {
container.querySelector('#txorigin').appendChild(yo`<option value=${address}>${address}</option>`)
addTooltip(`account ${address} created`)
} else {
addTooltip('Cannot create an account: ' + error)
}
})
}
var el = yo` var el = yo`
<div class="${css.settings}"> <div class="${css.settings}">
<div class="${css.crow}"> <div class="${css.crow}">
@ -579,6 +598,7 @@ function settings (container, appAPI, appEvents) {
<div class="${css.col1_1}">Account</div> <div class="${css.col1_1}">Account</div>
<select name="txorigin" class="${css.select}" id="txorigin"></select> <select name="txorigin" class="${css.select}" id="txorigin"></select>
${copyToClipboard(() => document.querySelector('#runTabView #txorigin').value)} ${copyToClipboard(() => document.querySelector('#runTabView #txorigin').value)}
<i class="fa fa-plus-square-o ${css.createAccount}" aria-hidden="true" onclick=${newAccount} title="Create a new account"></i>
</div> </div>
<div class="${css.crow}"> <div class="${css.crow}">
<div class="${css.col1_1}">Gas limit</div> <div class="${css.col1_1}">Gas limit</div>

@ -90,6 +90,7 @@ function SettingsTab (container, appAPI, appEvents, opts) {
var queryParams = new QueryParams() var queryParams = new QueryParams()
var optionVM = yo`<input id="alwaysUseVM" type="checkbox">` var optionVM = yo`<input id="alwaysUseVM" type="checkbox">`
var personal = yo`<input id="personal" type="checkbox">`
var el = yo` var el = yo`
<div class="${css.settingsTabView} "id="settingsView"> <div class="${css.settingsTabView} "id="settingsView">
<div class="${css.info}"> <div class="${css.info}">
@ -113,6 +114,16 @@ function SettingsTab (container, appAPI, appEvents, opts) {
<div><input id="optimize" type="checkbox"></div> <div><input id="optimize" type="checkbox"></div>
<span class="${css.checkboxText}">Enable Optimization</span> <span class="${css.checkboxText}">Enable Optimization</span>
</div> </div>
<div class="${css.crow}">
<div>${personal}></div>
<span class="${css.checkboxText}">Enable Personal Mode (transaction sent over Web3 will use the web3.personal API - be sure the endpoint is opened before enabling it -).</span>
<div>
This mode allows to provide the passphrase in the Remix interface without having to unlock the account. <br>
Although this is very convenient, you should <b>completely trust</b> the backend you are connected to (Geth, Parity, ...). <br>
It is not recommended (and also most likely not relevant) to use this mode with an injected provider (Mist, Metamask, ...) or with JavaScript VM <br>
Remix never persist any passphrase.
</div>
</div>
</div> </div>
<div class="${css.info}"> <div class="${css.info}">
<div class=${css.title}>Themes</div> <div class=${css.title}>Themes</div>
@ -166,6 +177,11 @@ function SettingsTab (container, appAPI, appEvents, opts) {
appAPI.config.set('settings/always-use-vm', !appAPI.config.get('settings/always-use-vm')) appAPI.config.set('settings/always-use-vm', !appAPI.config.get('settings/always-use-vm'))
}) })
personal.checked = appAPI.config.get('settings/personal-mode') || false
personal.addEventListener('change', event => {
appAPI.config.set('settings/personal-mode', !appAPI.config.get('settings/personal-mode'))
})
var optimize = el.querySelector('#optimize') var optimize = el.querySelector('#optimize')
if ((queryParams.get().optimize === 'true')) { if ((queryParams.get().optimize === 'true')) {
optimize.setAttribute('checked', true) optimize.setAttribute('checked', true)

@ -29,7 +29,7 @@ module.exports = function copyToClipboard (getContent) {
} }
} catch (e) {} } catch (e) {}
copy(copiableContent) copy(copiableContent)
addTooltip(event, 'Successfully copied!') addTooltip('Successfully copied!')
} }
} }
return copyIcon return copyIcon

@ -12,14 +12,35 @@ module.exports = {
modal('', yo`<div>${text}</div>`, null, { label: null }) modal('', yo`<div>${text}</div>`, null, { label: null })
}, },
prompt: function (title, text, inputValue, ok, cancel) { prompt: function (title, text, inputValue, ok, cancel) {
if (!inputValue) inputValue = '' prompt(title, text, false, inputValue, ok, cancel)
var input = yo`<input type='text' name='prompt_text' id='prompt_text' class="${css['prompt_text']}" value='${inputValue}' >` },
modal(title, yo`<div>${text}<div>${input}</div></div>`, promptPassphrase: function (title, text, inputValue, ok, cancel) {
prompt(title, text, true, inputValue, ok, cancel)
},
promptPassphraseCreation: function (ok, cancel) {
var text = 'Please provide a Passphrase for the account creation'
var input = yo`<div>
<input id="prompt1" type="password" name='prompt_text' class="${css['prompt_text']}" >
<br>
<br>
<input id="prompt2" type="password" name='prompt_text' class="${css['prompt_text']}" >
</div>`
modal(null, yo`<div>${text}<div>${input}</div></div>`,
{ {
fn: () => { if (typeof ok === 'function') ok(document.getElementById('prompt_text').value) } fn: () => {
if (typeof ok === 'function') {
if (input.querySelector('#prompt1').value === input.querySelector('#prompt2').value) {
ok(null, input.querySelector('#prompt1').value)
} else {
ok('Passphase does not match')
}
}
}
}, },
{ {
fn: () => { if (typeof cancel === 'function') cancel() } fn: () => {
if (typeof cancel === 'function') cancel()
}
} }
) )
}, },
@ -46,3 +67,17 @@ module.exports = {
) )
} }
} }
function prompt (title, text, hidden, inputValue, ok, cancel) {
if (!inputValue) inputValue = ''
var type = hidden ? 'password' : 'text'
var input = yo`<input type=${type} name='prompt_text' id='prompt_text' class="${css['prompt_text']}" value='${inputValue}' >`
modal(title, yo`<div>${text}<div>${input}</div></div>`,
{
fn: () => { if (typeof ok === 'function') ok(document.getElementById('prompt_text').value) }
},
{
fn: () => { if (typeof cancel === 'function') cancel() }
}
)
}

@ -38,7 +38,7 @@ var css = csjs`
} }
` `
module.exports = function addTooltip (event, tooltipText) { module.exports = function addTooltip (tooltipText) {
var tooltip = yo`<div class=${css.tooltip}>${tooltipText}</div>` var tooltip = yo`<div class=${css.tooltip}>${tooltipText}</div>`
document.body.appendChild(tooltip) document.body.appendChild(tooltip)
setTimeout(function () { setTimeout(function () {

@ -15,6 +15,7 @@ var txExecution = require('./app/execution/txExecution')
var helper = require('./lib/helper') var helper = require('./lib/helper')
var executionContext = require('./execution-context') var executionContext = require('./execution-context')
var copyToClipboard = require('./app/ui/copy-to-clipboard') var copyToClipboard = require('./app/ui/copy-to-clipboard')
var modalCustom = require('./app/ui/modal-dialog-custom')
// -------------- styling ---------------------- // -------------- styling ----------------------
var csjs = require('csjs-inject') var csjs = require('csjs-inject')
@ -159,17 +160,12 @@ function UniversalDApp (opts = {}) {
self.removable = opts.opt.removable self.removable = opts.opt.removable
self.removable_instances = opts.opt.removable_instances self.removable_instances = opts.opt.removable_instances
self.el = yo`<div class=${css.udapp}></div>` self.el = yo`<div class=${css.udapp}></div>`
self.personalMode = opts.opt.personalMode || false
self.contracts self.contracts
self.transactionContextAPI self.transactionContextAPI
executionContext.event.register('contextChanged', this, function (context) { executionContext.event.register('contextChanged', this, function (context) {
self.reset(self.contracts) self.reset(self.contracts)
}) })
self.txRunner = new TxRunner({}, { self.txRunner = new TxRunner({}, opts.api)
personalMode: this.personalMode,
config: self._api.config,
detectNetwork: self._api.detectNetwork
})
} }
UniversalDApp.prototype.reset = function (contracts, transactionContextAPI) { UniversalDApp.prototype.reset = function (contracts, transactionContextAPI) {
@ -187,26 +183,28 @@ UniversalDApp.prototype.reset = function (contracts, transactionContextAPI) {
this._addAccount('71975fbf7fe448e004ac7ae54cad0a383c3906055a65468714156a07385e96ce', '0x56BC75E2D63100000') this._addAccount('71975fbf7fe448e004ac7ae54cad0a383c3906055a65468714156a07385e96ce', '0x56BC75E2D63100000')
executionContext.vm().stateManager.cache.flush(function () {}) executionContext.vm().stateManager.cache.flush(function () {})
} }
this.txRunner = new TxRunner(this.accounts, { this.txRunner = new TxRunner(this.accounts, this._api)
personalMode: this.personalMode,
config: this._api.config,
detectNetwork: this._api.detectNetwork
})
} }
UniversalDApp.prototype.newAccount = function (password, cb) { UniversalDApp.prototype.newAccount = function (password, cb) {
if (!executionContext.isVM()) { if (!executionContext.isVM()) {
if (!this.personalMode) { if (!this._api.personalMode()) {
return cb('Not running in personal mode') return cb('Not running in personal mode')
} }
executionContext.web3().personal.newAccount(password, cb) modalCustom.promptPassphraseCreation((error, passphrase) => {
if (error) {
modalCustom.alert(error)
} else {
executionContext.web3().personal.newAccount(passphrase, cb)
}
}, () => {})
} else { } else {
var privateKey var privateKey
do { do {
privateKey = crypto.randomBytes(32) privateKey = crypto.randomBytes(32)
} while (!ethJSUtil.isValidPrivate(privateKey)) } while (!ethJSUtil.isValidPrivate(privateKey))
this._addAccount(privateKey) this._addAccount(privateKey, '0x56BC75E2D63100000')
cb(null, '0x' + ethJSUtil.privateToAddress(privateKey)) cb(null, '0x' + ethJSUtil.privateToAddress(privateKey).toString('hex'))
} }
} }
@ -233,7 +231,7 @@ UniversalDApp.prototype.getAccounts = function (cb) {
if (!executionContext.isVM()) { if (!executionContext.isVM()) {
// Weirdness of web3: listAccounts() is sync, `getListAccounts()` is async // Weirdness of web3: listAccounts() is sync, `getListAccounts()` is async
// See: https://github.com/ethereum/web3.js/issues/442 // See: https://github.com/ethereum/web3.js/issues/442
if (self.personalMode) { if (this._api.personalMode()) {
executionContext.web3().personal.getListAccounts(cb) executionContext.web3().personal.getListAccounts(cb)
} else { } else {
executionContext.web3().eth.getAccounts(cb) executionContext.web3().eth.getAccounts(cb)

Loading…
Cancel
Save