Consensys / armlet

a MythX API client wrapper
MIT License
17 stars 7 forks source link

Asynchronous or non-polling analyses function #11

Open rocky opened 6 years ago

rocky commented 6 years ago

Since running Mythril can be slow especially for IDE's (even in its "quick" mode), we want an asynchronous version that submits the job and just returns the UUID.

The client, e.g. an IDE, is then responsible for periodically querying for results. This is an adjust to issue #5.

fgimenez commented 6 years ago

I'm of course ok with adding the ability to return the analysis UUID, but I'm afraid that won't help to make the analysis request asynchronous because it already is :)

Currently analyze returns a promise, as all the aycnchronous methods in the web3.js library, for instance. The client code is not blocked after the call to analyze, and can attach code to be executed when the promise resolves or is rejected using the then/catch method chain.

For example the real-time desktop notifier https://github.com/ConsenSys/ramuh works using this asynchronous behaviour: when a watched file changes, the notifier uses armlet to request an analysis but, because the request doesn't block, it is able to start processing other files changed while waiting for the first analysis results.

To illustrate with an example how promises work, please give this script a try:

const armlet = require('armlet')
const data = {
  "contractName": "PublicStorageArray",
  "bytecode": "0x6080604052602060405190810160405280600060010260001916600019168152506000906001610030929190610043565b5034801561003d57600080fd5b506100bb565b828054828255906000526020600020908101928215610085579160200282015b82811115610084578251829060001916905591602001919060010190610063565b5b5090506100929190610096565b5090565b6100b891905b808211156100b457600081600090555060010161009c565b5090565b90565b60d8806100c96000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063017a9105146044575b600080fd5b348015604f57600080fd5b50606c60048036038101908080359060200190929190505050608a565b60405180826000191660001916815260200191505060405180910390f35b600081815481101515609857fe5b9060005260206000200160009150905054815600a165627a7a723058202d2c9eea16b4c2ab27df1b9936fb5262631e02bd928ce857ee5321bcb92c0a210029",
  "deployedBytecode": "0x608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063017a9105146044575b600080fd5b348015604f57600080fd5b50606c60048036038101908080359060200190929190505050608a565b60405180826000191660001916815260200191505060405180910390f35b600081815481101515609857fe5b9060005260206000200160009150905054815600a165627a7a723058202d2c9eea16b4c2ab27df1b9936fb5262631e02bd928ce857ee5321bcb92c0a210029",
  "sourceMap": "26:73:0:-;;;58:38;;;;;;;;;93:1;85:10;;58:38;;;;;;;;;;;;;;;;;:::i;:::-;;26:73;8:9:-1;5:2;;;30:1;27;20:12;5:2;26:73:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;",
  "deployedSourceMap": "26:73:0:-;;;;;;;;;;;;;;;;;;;;;;;;58:38;;8:9:-1;5:2;;;30:1;27;20:12;5:2;58:38:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o",
  "sourceList": ["Main.sol"],
  "analysisMode": "full",
  "sources": {
    "Main.sol": "pragma solidity ^0.4.22;\n\ncontract PublicStorageArray {\n  bytes32[] public states = [bytes32(0)];\n}\n"
  }
}
const client = new armlet.Client({apiKey: 'ace9b42d-4c97-407c-b197-a9b2e5ab6759', userEmail: 'myemail'}, 'https://staging.api.mythril.ai');
client.analyze({data, timeout: 20000}).then(i => console.log('and here it is! :)')).catch(e => console.log(e))
console.log('hope this analysis won\'t take too long...')

Saving it in a directory as test.js, after running npm i armlet, from the node console execute require('./test'). You will see that hope this analysis won't take too long... is printed first and, after the analysis is obtained from the API, and here it is! :) is printed, showing that the program didn't block after the analyze call, and that it actually worked asyncronously.

We need somehow to poll the API to get the results and IMO delegating the responsibility of polling to the client won't help that much. But as said above, is an esasy change, let me know WDYT.

rocky commented 6 years ago

Ok - good. Thanks for clearing up my misunderstanding then.

There is a subtle difference, but I am not sure it is worth the distinction right now. In a REPL (read-eval-print loop), the natural place to check is before a read. So those better places to do the polling.

As it is now, I guess you'd queue the result taking time from other potential things going on and before the read check a queue. Again though, I'm not sure this is worth the distinction.

I'll probably close this soon after I play with what you report.

Thanks for the information.