Skip to content

Commit 0022a7c

Browse files
authored
Merge pull request #303 from ethereum/smt_wrapper
Run SMT solver on requested queries
2 parents 1cee2ae + 1aaffe1 commit 0022a7c

File tree

3 files changed

+94
-2
lines changed

3 files changed

+94
-2
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,13 @@
3939
},
4040
"homepage": "https://github.com/ethereum/solc-js#readme",
4141
"dependencies": {
42+
"command-exists": "^1.2.8",
4243
"fs-extra": "^0.30.0",
4344
"keccak": "^1.0.2",
4445
"memorystream": "^0.3.1",
4546
"require-from-string": "^2.0.0",
4647
"semver": "^5.5.0",
48+
"tmp": "0.0.33",
4749
"yargs": "^11.0.0"
4850
},
4951
"devDependencies": {

smtchecker.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
var commandExistsSync = require('command-exists').sync;
2+
var execSync = require('child_process').execSync;
3+
var fs = require('fs-extra');
4+
var tmp = require('tmp');
5+
6+
var potentialSolvers = [
7+
{
8+
name: 'z3',
9+
params: ''
10+
},
11+
{
12+
name: 'cvc4',
13+
params: '--lang=smt2'
14+
}
15+
];
16+
var solvers = potentialSolvers.filter(solver => commandExistsSync(solver.name));
17+
18+
function solve (query) {
19+
var tmpFile = tmp.fileSync();
20+
fs.writeFileSync(tmpFile.name, query);
21+
// TODO For now only the first SMT solver found is used.
22+
// At some point a computation similar to the one done in
23+
// SMTPortfolio::check should be performed, where the results
24+
// given by different solvers are compared and an error is
25+
// reported if solvers disagree (i.e. SAT vs UNSAT).
26+
var solverOutput = execSync(solvers[0].name + ' ' + solvers[0].params + ' ' + tmpFile.name);
27+
// Trigger early manual cleanup
28+
tmpFile.removeCallback();
29+
return solverOutput.toString();
30+
}
31+
32+
// This function checks the standard JSON output for auxiliaryInputRequested,
33+
// where smtlib2queries represent the queries created by the SMTChecker.
34+
// The function runs an SMT solver on each query and adjusts the input for
35+
// another run.
36+
// Returns null if no solving is requested.
37+
function handleSMTQueries (inputJSON, outputJSON) {
38+
var auxInputReq = outputJSON.auxiliaryInputRequested;
39+
if (!auxInputReq) {
40+
return null;
41+
}
42+
43+
var queries = auxInputReq.smtlib2queries;
44+
if (!queries || Object.keys(queries) === 0) {
45+
return null;
46+
}
47+
48+
if (solvers.length === 0) {
49+
throw new Error('No SMT solver available. Assertion checking will not be performed.');
50+
}
51+
52+
var responses = {};
53+
for (var query in queries) {
54+
responses[query] = solve(queries[query]);
55+
}
56+
57+
// Note: all existing solved queries are replaced.
58+
// This assumes that all neccessary queries are quested above.
59+
inputJSON.auxiliaryInput = { smtlib2responses: responses };
60+
return inputJSON;
61+
}
62+
63+
module.exports = {
64+
handleSMTQueries: handleSMTQueries
65+
};

solcjs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ var originalUncaughtExceptionListeners = process.listeners("uncaughtException");
66
var fs = require('fs-extra');
77
var path = require('path');
88
var solc = require('./index.js');
9+
var smtchecker = require('./smtchecker.js');
910
// FIXME: remove annoying exception catcher of Emscripten
1011
// see https://github.com/chriseth/browser-solidity/issues/167
1112
process.removeAllListeners('uncaughtException');
@@ -51,8 +52,32 @@ function abort (msg) {
5152
}
5253

5354
if (argv['standard-json']) {
54-
var input = fs.readFileSync(process.stdin.fd);
55-
console.log(solc.compile(input.toString('utf8')))
55+
var input = fs.readFileSync(process.stdin.fd).toString('utf8');
56+
var output = solc.compileStandardWrapper(input);
57+
58+
try {
59+
var inputJSON = smtchecker.handleSMTQueries(JSON.parse(input), JSON.parse(output));
60+
if (inputJSON) {
61+
output = solc.compileStandardWrapper(JSON.stringify(inputJSON));
62+
}
63+
}
64+
catch (e) {
65+
var addError = {
66+
component: "general",
67+
formattedMessage: e.toString(),
68+
message: e.toString(),
69+
type: "Warning"
70+
};
71+
72+
var outputJSON = JSON.parse(output);
73+
if (!outputJSON.errors) {
74+
outputJSON.errors = []
75+
}
76+
outputJSON.errors.push(addError);
77+
output = JSON.stringify(outputJSON);
78+
}
79+
80+
console.log(output);
5681
process.exit(0);
5782
} else if (files.length === 0) {
5883
console.error('Must provide a file');

0 commit comments

Comments
 (0)