Skip to content

Commit

Permalink
Adding new possible version of generated output of compilation (KLAB …
Browse files Browse the repository at this point in the history
…friendly) (TrueFiEng#102)

* added three version of generated output:
    * singleton json file for each sol file
    * one combined json for all (KLAB friendly)
    * both of above
* new config for tests added
* simple test for combined output
  • Loading branch information
tkazana-eth authored and marekkirejczyk committed Apr 23, 2019
1 parent 7bb1148 commit 9c2b420
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 3 deletions.
27 changes: 27 additions & 0 deletions docs/release-notes/2.0.10.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Summary 2.0.10

Add support for generating KLAB (a formal verification tool for Solidity, see: https://github.com/dapphub/klab)
friendly compilation output.

An example of full KLAB friendly config file is the following:

```
module.exports = {
compiler: process.env.WAFFLE_COMPILER,
legacyOutput: true,
outputType: 'all',
compilerOptions: {
outputSelection: {
"*": {
"*": [ "evm.bytecode.object", "evm.deployedBytecode.object",
"abi" ,
"evm.bytecode.sourceMap", "evm.deployedBytecode.sourceMap" ],
"": [ "ast" ]
},
}
}
};
```

For details, see updated appropriate documentation file.
58 changes: 58 additions & 0 deletions docs/source/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,64 @@ For detailed list of options go to
`'Target options' <https://solidity.readthedocs.io/en/v0.5.1/using-the-compiler.html#target-options>`_ and `'Compiler Input and Output JSON Description' <https://solidity.readthedocs.io/en/v0.5.1/using-the-compiler.html#compiler-input-and-output-json-description>`_).


KLAB compatibility
------------------

The default compilation process is not compatible with KLAB
(a formal verification tool, see: https://github.com/dapphub/klab). To compile contracts to work with KLAB one must:

1. Set appropriate compiler options, i.e.:

::

compilerOptions: {
outputSelection: {
"*": {
"*": [ "evm.bytecode.object", "evm.deployedBytecode.object",
"abi" ,
"evm.bytecode.sourceMap", "evm.deployedBytecode.sourceMap" ],
"": [ "ast" ]
},
}
}


2. Set appropriate output type. We support two types: one (default) generates single file for each contract
and second (KLAB friendly) generates one file (Combined-Json.json) combining all contracts. The second type does not meet
(in contrary to the first one) all official solidity standards since KLAB requirements are slightly modified.
To choice of the output is set in config file, i.e.:

::

outputType: 'combined'

Possible options are:
- `'multiple'`: single file for each contract;
- `'combined'`: one KLAB friendly file;
- `'all'`: generates both above outputs.

An example of full KLAB friendly config file:

::

module.exports = {
compiler: process.env.WAFFLE_COMPILER,
legacyOutput: true,
outputType: 'all',
compilerOptions: {
outputSelection: {
"*": {
"*": [ "evm.bytecode.object", "evm.deployedBytecode.object",
"abi" ,
"evm.bytecode.sourceMap", "evm.deployedBytecode.sourceMap" ],

"": [ "ast" ]
},
}
}
};

Monorepo
--------
Waffle works well with mono-repositories. It is enough to set up common npmPath in the configuration file to make it work.
Expand Down
49 changes: 47 additions & 2 deletions lib/compiler/saveOutput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,33 @@ export interface BytecodeJson {

export interface EvmJson {
bytecode: BytecodeJson;
deployedBytecode?: BytecodeJson;
}

export interface ContractJson {
interface: object[];
'srcmap-runtime'?: string;
srcmap?: string;
bin?: string;
'bin-runtime'?: string;
interface?: object[];
abi: object[];
bytecode: string;
bytecode?: string;
evm: EvmJson;
}

export async function saveOutput(output: any, config: Config, filesystem = fs) {
config.outputType = config.outputType || 'multiple';

if (['multiple', 'all'].includes(config.outputType)) {
saveOutputSingletons(output, config, filesystem);
}

if (['combined', 'all'].includes(config.outputType)) {
saveOutputCombined(output, config, filesystem);
}
}

export async function saveOutputSingletons(output: any, config: Config, filesystem = fs) {
for (const [, file] of Object.entries(output.contracts)) {
for (const [contractName, contractJson] of Object.entries(file)) {
const filePath = join(config.targetPath, `${contractName}.json`);
Expand All @@ -33,6 +50,34 @@ export async function saveOutput(output: any, config: Config, filesystem = fs) {
}
}

export async function saveOutputCombined(output: any, config: Config, filesystem = fs) {
for (const [key, file] of Object.entries(output.contracts)) {
for (const [contractName, contractJson] of Object.entries(file)) {
contractJson.bin = contractJson.evm.bytecode.object;
contractJson['bin-runtime'] = contractJson.evm.deployedBytecode.object;
contractJson.srcmap = contractJson.evm.bytecode.sourceMap;
contractJson['srcmap-runtime'] = contractJson.evm.deployedBytecode.sourceMap;

output.contracts[String(key) + ':' + String(contractName)] = contractJson;
}
delete output.contracts[key];
}

const allSources: string[] = [];

for (const [key, value] of Object.entries(output.sources) as any) {
value.AST = value.ast;
delete value.ast;
allSources.push(key);
}

output.sourceList = allSources;

filesystem.writeFileSync(join(config.targetPath, `Combined-Json.json`),
JSON.stringify(output, null, 2));

}

function getContent(contractJson: ContractJson, config: Config) {
if (config.legacyOutput || !contractJson.interface) {
contractJson.interface = contractJson.abi;
Expand Down
1 change: 1 addition & 0 deletions lib/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface Config {
legacyOutput?: string;
allowedPaths?: string[];
compilerOptions?: Record<string, any>;
outputType?: 'multiple' | 'combined' | 'all';
}

const defaultConfig: Config = {
Expand Down
13 changes: 12 additions & 1 deletion test/compiler/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ const configurations = [
'./test/projects/custom/config_docker.json',
'./test/projects/custom/config_promise.js',
'./test/projects/custom_solidity_4/config_solcjs.json',
'./test/projects/custom_solidity_4/config_docker.json'
'./test/projects/custom_solidity_4/config_docker.json',
'./test/projects/custom/config_combined.js'
];

const artefacts = [
Expand Down Expand Up @@ -86,6 +87,16 @@ describe('E2E: Compiler integration', async () => {
});
}

if (['all', 'combined'].includes(configuration.outputType)) {
it('produce Combined-Json.json', async () => {
const filePath = join(targetPath, 'Combined-Json.json');
const content = JSON.parse(readFileContent(filePath));
expect(content).to.have.property('contracts');
expect(content).to.have.property('sources');
expect(content).to.have.property('sourceList');
});
}

it('produce abi', async () => {
for (const artefact of artefacts) {
const filePath = join(targetPath, artefact);
Expand Down
21 changes: 21 additions & 0 deletions test/projects/custom/config_combined.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module.exports = {
name: "KLAB friendly configuration",
sourcesPath: "./test/projects/custom/custom_contracts",
targetPath: "./test/projects/custom/custom_build",
npmPath: "./test/projects/custom/custom_node_modules",
compiler: process.env.WAFFLE_COMPILER,
legacyOutput: true,
outputType: 'all',
compilerOptions: {
outputSelection: {
"*": {
"*": [ "evm.bytecode.object", "evm.deployedBytecode.object",
"abi" ,
"evm.bytecode.sourceMap", "evm.deployedBytecode.sourceMap" ],

"": [ "ast" ]
},
}
}
};

0 comments on commit 9c2b420

Please sign in to comment.