Merge pull request #993 from ethereum/implementsign

Integrate personal mode
pull/1/head
yann300 7 years ago committed by GitHub
commit ad7edf216c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  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,
detectNetwork: (cb) => {
executionContext.detectNetwork(cb)
},
personalMode: () => {
return self._api.config.get('settings/personal-mode')
}
},
opt: { removable: false, removable_instances: true }
@ -623,6 +626,9 @@ function run () {
},
getCompilationResult: () => {
return compiler.lastCompilationResult
},
newAccount: (pass, cb) => {
udapp.newAccount(pass, cb)
}
}
var rhpEvents = {

@ -11,6 +11,7 @@ var csjs = require('csjs-inject')
var remixLib = require('remix-lib')
var styleGuide = remixLib.ui.styleGuide
var styles = styleGuide()
var modal = require('../ui/modal-dialog-custom')
var css = csjs`
.txInfoBox {
@ -25,12 +26,10 @@ var css = csjs`
}
`
function TxRunner (vmaccounts, opts) {
this.personalMode = opts.personalMode
function TxRunner (vmaccounts, api) {
this._api = api
this.blockNumber = 0
this.runAsync = true
this.config = opts.config
this.detectNetwork = opts.detectNetwork
if (executionContext.isVM()) {
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.
@ -45,23 +44,21 @@ TxRunner.prototype.rawRun = function (args, cb) {
}
TxRunner.prototype.execute = function (args, callback) {
var self = this
function execute (gasPrice) {
if (gasPrice) tx.gasPrice = executionContext.web3().toHex(gasPrice)
var sendTransaction = self.personalMode ? executionContext.web3().personal.sendTransaction : executionContext.web3().eth.sendTransaction
try {
sendTransaction(tx, function (err, resp) {
if (err) {
return callback(err, resp)
}
tryTillResponse(resp, callback)
if (self._api.personalMode()) {
modal.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account ' + tx.from, '', (value) => {
sendTransaction(executionContext.web3().personal.sendTransaction, tx, value, callback)
}, () => {
return callback('Canceled by user.')
})
} catch (e) {
return callback(`Send transaction failed: ${e.message} . if you use an injected provider, please check it is properly unlocked. `)
} else {
sendTransaction(executionContext.web3().eth.sendTransaction, tx, null, callback)
}
}
var self = this
var from = args.from
var to = args.to
var data = args.data
@ -107,8 +104,8 @@ TxRunner.prototype.execute = function (args, callback) {
tx.gas = gasEstimation
if (!self.config.getUnpersistedProperty('doNotShowTransactionConfirmationAgain')) {
self.detectNetwork((err, network) => {
if (!self._api.config.getUnpersistedProperty('doNotShowTransactionConfirmationAgain')) {
self._api.detectNetwork((err, network) => {
if (err) {
console.log(err)
} else {
@ -117,7 +114,7 @@ TxRunner.prototype.execute = function (args, callback) {
modalDialog('Confirm transaction', content,
{ label: 'Confirm',
fn: () => {
self.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked)
self._api.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked)
if (!content.gasPriceStatus) {
callback('Given gas grice is not correct')
} 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) {
if (!self.runAsync && Object.keys(self.pendingTxs).length) {
self.queusTxs.push({ tx, stamp, callback })
@ -234,7 +246,7 @@ function confirmDialog (tx, gasEstimation, self) {
<div>Amount: ${amount} Ether</div>
<div>Gas estimation: ${gasEstimation}</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>Data:</div>
<pre class=${css.wrapword}>${tx.data}</pre>

@ -10,6 +10,7 @@ var executionContext = require('../../execution-context')
var copyToClipboard = require('../ui/copy-to-clipboard')
var Recorder = require('../../recorder')
var EventManager = require('remix-lib').EventManager
var addTooltip = require('../ui/tooltip')
// -------------- styling ----------------------
var csjs = require('csjs-inject')
@ -191,6 +192,14 @@ var css = csjs`
}
.transactionActions {
float: right;
}
.createAccount {
margin-left: 5px;
cursor: pointer;
}
.createAccount:hover {
color: ${styles.colors.orange};
}
`
module.exports = runTab
@ -282,7 +291,7 @@ function fillAccountsList (appAPI, container) {
var $txOrigin = $(container.querySelector('#txorigin'))
$txOrigin.empty()
appAPI.udapp().getAccounts((err, accounts) => {
if (err) { console.log(err) }
if (err) { addTooltip(`Cannot get account list: ${err}`) }
if (accounts && accounts[0]) {
for (var a in accounts) { $txOrigin.append($('<option />').val(accounts[a]).text(accounts[a])) }
$txOrigin.val(accounts[0])
@ -543,6 +552,16 @@ function settings (container, appAPI, appEvents) {
})
}
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`
<div class="${css.settings}">
<div class="${css.crow}">
@ -579,6 +598,7 @@ function settings (container, appAPI, appEvents) {
<div class="${css.col1_1}">Account</div>
<select name="txorigin" class="${css.select}" id="txorigin"></select>
${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 class="${css.crow}">
<div class="${css.col1_1}">Gas limit</div>

@ -90,6 +90,7 @@ function SettingsTab (container, appAPI, appEvents, opts) {
var queryParams = new QueryParams()
var optionVM = yo`<input id="alwaysUseVM" type="checkbox">`
var personal = yo`<input id="personal" type="checkbox">`
var el = yo`
<div class="${css.settingsTabView} "id="settingsView">
<div class="${css.info}">
@ -113,6 +114,16 @@ function SettingsTab (container, appAPI, appEvents, opts) {
<div><input id="optimize" type="checkbox"></div>
<span class="${css.checkboxText}">Enable Optimization</span>
</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 class="${css.info}">
<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'))
})
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')
if ((queryParams.get().optimize === 'true')) {
optimize.setAttribute('checked', true)

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

@ -12,14 +12,35 @@ module.exports = {
modal('', yo`<div>${text}</div>`, null, { label: null })
},
prompt: function (title, text, inputValue, ok, cancel) {
if (!inputValue) inputValue = ''
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>`,
prompt(title, text, false, inputValue, ok, cancel)
},
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>`
document.body.appendChild(tooltip)
setTimeout(function () {

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

Loading…
Cancel
Save