Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Commit

Permalink
Better handling of Solidity compliation (#4860)
Browse files Browse the repository at this point in the history
* Better use of SW

* Safe-guard against pending SW register bug (in Chrome)

* Added a simple Worker for Solidity compilation
  • Loading branch information
ngotchac authored and gavofyork committed Mar 11, 2017
1 parent 1c37ea5 commit e73d867
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 160 deletions.
2 changes: 2 additions & 0 deletions js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@
"scryptsy": "2.0.0",
"solc": "ngotchac/solc-js",
"store": "1.3.20",
"sw-toolbox": "^3.6.0",
"u2f-api": "0.0.9",
"u2f-api-polyfill": "0.4.3",
"uglify-js": "2.8.2",
Expand All @@ -210,6 +211,7 @@
"validator": "6.2.0",
"web3": "0.17.0-beta",
"whatwg-fetch": "2.0.1",
"worker-loader": "^0.8.0",
"zxcvbn": "4.4.1"
}
}
2 changes: 1 addition & 1 deletion js/src/contracts/snippets/wallet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ contract multisig {

// TODO: document
function execute(address _to, uint _value, bytes _data) external returns (bytes32 o_hash);
function confirm(bytes32 _h) external returns (bool o_success);
function confirm(bytes32 _h) returns (bool o_success);
}

// usage:
Expand Down
63 changes: 42 additions & 21 deletions js/src/redux/providers/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,57 @@

import PromiseWorker from 'promise-worker';
import runtime from 'serviceworker-webpack-plugin/lib/runtime';
import WebWorker from 'worker-loader!~/webWorker.js';

import { setWorker } from './workerActions';

function getWorker () {
// Setup the Service Worker
if ('serviceWorker' in navigator) {
return runtime
.register()
.then(() => navigator.serviceWorker.ready)
// Setup the Service Worker
setupServiceWorker()
.then(() => console.log('SW is setup'))
.catch((error) => console.error('SW error', error));

function setupServiceWorker () {
if (!('serviceWorker' in navigator)) {
return Promise.reject('Service Worker is not available in your browser.');
}

const getServiceWorker = () => {
return navigator.serviceWorker.ready
.then((registration) => {
const worker = registration.active;

worker.controller = registration.active;

return new PromiseWorker(worker);
});
}
};

return Promise.reject('Service Worker is not available in your browser.');
return new Promise((resolve, reject) => {
// Safe guard for registration bugs (happens in Chrome sometimes)
const timeoutId = window.setTimeout(() => {
console.warn('could not register SW after 2.5s');
getServiceWorker().then(resolve).catch(reject);
}, 2500);

// Setup the Service Worker
runtime
.register()
.then(() => {
window.clearTimeout(timeoutId);
return getServiceWorker();
})
.then(resolve).catch(reject);
});
}

function getWorker () {
try {
const worker = new PromiseWorker(new WebWorker());

return Promise.resolve(worker);
} catch (error) {
return Promise.reject(error);
}
}

export const setupWorker = (store) => {
Expand All @@ -43,27 +75,16 @@ export const setupWorker = (store) => {
const state = getState();
const stateWorker = state.worker.worker;

if (stateWorker !== undefined && !(stateWorker && stateWorker._worker.state === 'redundant')) {
if (stateWorker !== undefined) {
return;
}

getWorker()
.then((worker) => {
if (worker) {
worker._worker.addEventListener('statechange', (event) => {
console.warn('worker state changed to', worker._worker.state);

// Re-install the new Worker
if (worker._worker.state === 'redundant') {
setupWorker(store);
}
});
}

dispatch(setWorker(worker));
})
.catch((error) => {
console.error('sw', error);
console.error('setupWorker', error);
dispatch(setWorker(null));
});
};
137 changes: 7 additions & 130 deletions js/src/serviceWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

import registerPromiseWorker from 'promise-worker/register';
import { Signer } from '~/util/signer';
import SolidityUtils from '~/util/solidity';
import toolbox from 'sw-toolbox';

const CACHE_NAME = 'parity-cache-v1';
toolbox.precache(self.serviceWorkerOption.assets);

registerPromiseWorker((msg) => {
return handleMessage(msg);
});
/**
* Cache the SOLC files : if not available, make a network request
*/
toolbox.router.any(/raw.githubusercontent.com\/ethereum\/solc-bin(.+)list\.json$/, toolbox.cacheFirst);
toolbox.router.any(/raw.githubusercontent.com\/ethereum\/solc-bin(.+)soljson(.+)\.js$/, toolbox.cacheFirst);

self.addEventListener('install', (event) => {
event.waitUntil(self.skipWaiting());
Expand All @@ -31,126 +31,3 @@ self.addEventListener('install', (event) => {
self.addEventListener('activate', (event) => {
event.waitUntil(self.clients.claim());
});

self.addEventListener('fetch', (event) => {
const { url } = event.request;

if (/raw.githubusercontent.com\/ethereum\/solc-bin(.+)list\.json$/.test(url)) {
// Return the cached version, but still update it in background
return event.respondWith(cachedFetcher(event.request, true));
}

if (/raw.githubusercontent.com\/ethereum\/solc-bin(.+)soljson(.+)\.js$/.test(url)) {
return event.respondWith(cachedFetcher(event.request));
}
});

self.solc = {};
self.files = {};

function cachedFetcher (request, update = false) {
return caches
.match(request)
.then((response) => {
// Return cached response if exists and no
// updates needed
if (response && !update) {
return response;
}

const fetcher = fetch(request.clone())
.then((response) => {
// Check if we received a valid response
if (!response || response.status !== 200) {
return response;
}

return caches
.open(CACHE_NAME)
.then((cache) => {
cache.put(request, response.clone());
return response;
});
});

// Cache hit - return response
// Still want to perform the fetch (update)
if (response) {
return response;
}

return fetcher;
});
}

function handleMessage (message) {
switch (message.action) {
case 'compile':
return compile(message.data);

case 'load':
return getCompiler(message.data).then(() => 'ok');

case 'setFiles':
return setFiles(message.data);

case 'getSignerSeed':
return getSignerSeed(message.data);

default:
console.warn(`unknown action "${message.action}"`);
return null;
}
}

function getSignerSeed (data) {
console.log('deriving seed from service-worker');
const { wallet, password } = data;

return Signer.getSeed(wallet, password);
}

function compile (data) {
const { build } = data;

return getCompiler(build)
.then((compiler) => {
return SolidityUtils.compile(data, compiler);
});
}

function setFiles (files) {
const prevFiles = self.files;
const nextFiles = files.reduce((obj, file) => {
obj[file.name] = file.sourcecode;
return obj;
}, {});

self.files = {
...prevFiles,
...nextFiles
};

return 'ok';
}

function getCompiler (build) {
const { longVersion } = build;

const fetcher = (url) => {
const request = new Request(url);

return cachedFetcher(request);
};

if (!self.solc[longVersion]) {
self.solc[longVersion] = SolidityUtils
.getCompiler(build, fetcher)
.then((compiler) => {
self.solc[longVersion] = compiler;
return compiler;
});
}

return Promise.resolve(self.solc[longVersion]);
}
20 changes: 13 additions & 7 deletions js/src/util/solidity.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,24 @@ export default class SolidityUtils {
return compiled;
}

static getCompiler (build, _fetcher) {
static getCompiler (build) {
const { longVersion, path } = build;

const URL = `https://raw.githubusercontent.com/ethereum/solc-bin/gh-pages/bin/${path}`;
const isWorker = typeof window !== 'object';

const fetcher = typeof _fetcher === 'function'
? _fetcher
: (url) => fetch(url);
if (isWorker) {
return new Promise((resolve, reject) => {
self.importScripts(URL);

const isWorker = typeof window !== 'object';
setTimeout(() => {
const compiler = solc(self.Module);

return resolve(compiler);
}, 50);
});
}

return fetcher(URL)
return fetch(URL)
.then((r) => r.text())
.then((code) => {
// `window` for main thread, `self` for workers
Expand Down
2 changes: 1 addition & 1 deletion js/src/views/WriteContract/writeContract.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class WriteContract extends Component {
className={ styles.editor }
style={ { flex: `${size}%` } }
>
<h2>{ this.renderTitle() }</h2>
<h2>asd{ this.renderTitle() }</h2>

<Editor
ref='editor'
Expand Down
Loading

0 comments on commit e73d867

Please sign in to comment.