Merge pull request #938 from 0xProject/sol-cov-fixes

Sol cov fixes
This commit is contained in:
Fabio Berger 2018-08-13 16:40:46 -04:00 committed by GitHub
commit 7340338626
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 162 additions and 22 deletions

View File

@ -2,6 +2,10 @@
{ {
"version": "1.0.1-rc.4", "version": "1.0.1-rc.4",
"changes": [ "changes": [
{
"note": "Allow for additional properties in txData schema",
"pr": 938
},
{ {
"note": "Change hexSchema to match `0x`", "note": "Change hexSchema to match `0x`",
"pr": 937 "pr": 937

View File

@ -29,5 +29,4 @@ export const txDataSchema = {
}, },
required: ['from'], required: ['from'],
type: 'object', type: 'object',
additionalProperties: false,
}; };

View File

@ -849,10 +849,6 @@ describe('Schema', () => {
{ {
gas: new BigNumber(42), gas: new BigNumber(42),
}, },
{
from: NULL_ADDRESS,
unknownProp: 'here',
},
{}, {},
[], [],
new BigNumber(1), new BigNumber(1),

View File

@ -1,4 +1,28 @@
[ [
{
"version": "2.0.0",
"changes": [
{
"note":
"Fix a bug when eth_call coverage was not computed because of silent schema validation failures",
"pr": 938
},
{
"note": "Make `TruffleArtifactAdapter` read the `truffle.js` config for `solc` settings",
"pr": 938
},
{
"note":
"Change the first param of `TruffleArtifactAdapter` to be the `projectRoot` instead of `sourcesDir`",
"pr": 938
},
{
"note":
"Throw a helpful error message if truffle artifacts were generated with a different solc version than the one passed in",
"pr": 938
}
]
},
{ {
"version": "1.0.3", "version": "1.0.3",
"changes": [ "changes": [

View File

@ -1,25 +1,40 @@
import { Compiler, CompilerOptions } from '@0xproject/sol-compiler'; import { Compiler, CompilerOptions } from '@0xproject/sol-compiler';
import * as rimraf from 'rimraf'; import * as fs from 'fs';
import * as glob from 'glob';
import * as path from 'path';
import { ContractData } from '../types'; import { ContractData } from '../types';
import { AbstractArtifactAdapter } from './abstract_artifact_adapter'; import { AbstractArtifactAdapter } from './abstract_artifact_adapter';
import { SolCompilerArtifactAdapter } from './sol_compiler_artifact_adapter'; import { SolCompilerArtifactAdapter } from './sol_compiler_artifact_adapter';
const DEFAULT_TRUFFLE_ARTIFACTS_DIR = './build/contracts';
interface TruffleConfig {
solc?: any;
contracts_build_directory?: string;
}
export class TruffleArtifactAdapter extends AbstractArtifactAdapter { export class TruffleArtifactAdapter extends AbstractArtifactAdapter {
private readonly _solcVersion: string; private readonly _solcVersion: string;
private readonly _sourcesPath: string; private readonly _projectRoot: string;
constructor(sourcesPath: string, solcVersion: string) { constructor(projectRoot: string, solcVersion: string) {
super(); super();
this._solcVersion = solcVersion; this._solcVersion = solcVersion;
this._sourcesPath = sourcesPath; this._projectRoot = projectRoot;
} }
public async collectContractsDataAsync(): Promise<ContractData[]> { public async collectContractsDataAsync(): Promise<ContractData[]> {
const artifactsDir = '.0x-artifacts'; const artifactsDir = '.0x-artifacts';
const contractsDir = path.join(this._projectRoot, 'contracts');
const truffleConfig = this._getTruffleConfig();
const solcConfig = truffleConfig.solc || {};
const truffleArtifactsDirectory = truffleConfig.contracts_build_directory || DEFAULT_TRUFFLE_ARTIFACTS_DIR;
this._assertSolidityVersionIsCorrect(truffleArtifactsDirectory);
const compilerOptions: CompilerOptions = { const compilerOptions: CompilerOptions = {
contractsDir: this._sourcesPath, contractsDir,
artifactsDir, artifactsDir,
compilerSettings: { compilerSettings: {
...solcConfig,
outputSelection: { outputSelection: {
['*']: { ['*']: {
['*']: ['abi', 'evm.bytecode.object', 'evm.deployedBytecode.object'], ['*']: ['abi', 'evm.bytecode.object', 'evm.deployedBytecode.object'],
@ -31,9 +46,38 @@ export class TruffleArtifactAdapter extends AbstractArtifactAdapter {
}; };
const compiler = new Compiler(compilerOptions); const compiler = new Compiler(compilerOptions);
await compiler.compileAsync(); await compiler.compileAsync();
const solCompilerArtifactAdapter = new SolCompilerArtifactAdapter(artifactsDir, this._sourcesPath); const solCompilerArtifactAdapter = new SolCompilerArtifactAdapter(artifactsDir, contractsDir);
const contractsDataFrom0xArtifacts = await solCompilerArtifactAdapter.collectContractsDataAsync(); const contractsDataFrom0xArtifacts = await solCompilerArtifactAdapter.collectContractsDataAsync();
rimraf.sync(artifactsDir);
return contractsDataFrom0xArtifacts; return contractsDataFrom0xArtifacts;
} }
private _getTruffleConfig(): TruffleConfig {
const truffleConfigFileShort = path.resolve(path.join(this._projectRoot, 'truffle.js'));
const truffleConfigFileLong = path.resolve(path.join(this._projectRoot, 'truffle-config.js'));
if (fs.existsSync(truffleConfigFileShort)) {
const truffleConfig = require(truffleConfigFileShort);
return truffleConfig;
} else if (fs.existsSync(truffleConfigFileLong)) {
const truffleConfig = require(truffleConfigFileLong);
return truffleConfig;
} else {
throw new Error(
`Neither ${truffleConfigFileShort} nor ${truffleConfigFileLong} exists. Make sure the project root is correct`,
);
}
}
private _assertSolidityVersionIsCorrect(truffleArtifactsDirectory: string): void {
const artifactsGlob = `${truffleArtifactsDirectory}/**/*.json`;
const artifactFileNames = glob.sync(artifactsGlob, { absolute: true });
for (const artifactFileName of artifactFileNames) {
const artifact = JSON.parse(fs.readFileSync(artifactFileName).toString());
const compilerVersion = artifact.compiler.version;
if (!compilerVersion.startsWith(this._solcVersion)) {
throw new Error(
`${artifact.contractName} was compiled with solidity ${compilerVersion} but specified version is ${
this._solcVersion
} making it impossible for sol-cov to process traces`,
);
}
}
}
} }

View File

@ -1,7 +1,7 @@
import { BlockchainLifecycle } from '@0xproject/dev-utils'; import { BlockchainLifecycle } from '@0xproject/dev-utils';
import { Callback, ErrorCallback, NextCallback, Subprovider } from '@0xproject/subproviders'; import { Callback, ErrorCallback, NextCallback, Subprovider } from '@0xproject/subproviders';
import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { CallDataRPC, marshaller, Web3Wrapper } from '@0xproject/web3-wrapper';
import { CallData, JSONRPCRequestPayload, Provider, TxData } from 'ethereum-types'; import { JSONRPCRequestPayload, Provider, TxData } from 'ethereum-types';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { Lock } from 'semaphore-async-await'; import { Lock } from 'semaphore-async-await';
@ -152,7 +152,7 @@ export abstract class TraceCollectionSubprovider extends Subprovider {
cb(); cb();
} }
private async _onCallOrGasEstimateExecutedAsync( private async _onCallOrGasEstimateExecutedAsync(
callData: Partial<CallData>, callData: Partial<CallDataRPC>,
_err: Error | null, _err: Error | null,
_callResult: string, _callResult: string,
cb: Callback, cb: Callback,
@ -160,22 +160,24 @@ export abstract class TraceCollectionSubprovider extends Subprovider {
await this._recordCallOrGasEstimateTraceAsync(callData); await this._recordCallOrGasEstimateTraceAsync(callData);
cb(); cb();
} }
private async _recordCallOrGasEstimateTraceAsync(callData: Partial<CallData>): Promise<void> { private async _recordCallOrGasEstimateTraceAsync(callData: Partial<CallDataRPC>): Promise<void> {
// We don't want other transactions to be exeucted during snashotting period, that's why we lock the // We don't want other transactions to be exeucted during snashotting period, that's why we lock the
// transaction execution for all transactions except our fake ones. // transaction execution for all transactions except our fake ones.
await this._lock.acquire(); await this._lock.acquire();
const blockchainLifecycle = new BlockchainLifecycle(this._web3Wrapper); const blockchainLifecycle = new BlockchainLifecycle(this._web3Wrapper);
await blockchainLifecycle.startAsync(); await blockchainLifecycle.startAsync();
const fakeTxData: MaybeFakeTxData = { const fakeTxData = {
gas: BLOCK_GAS_LIMIT, gas: BLOCK_GAS_LIMIT.toString(16), // tslint:disable-line:custom-no-magic-numbers
isFakeTransaction: true, // This transaction (and only it) is allowed to come through when the lock is locked isFakeTransaction: true, // This transaction (and only it) is allowed to come through when the lock is locked
...callData, ...callData,
from: callData.from || this._defaultFromAddress, from: callData.from || this._defaultFromAddress,
}; };
try { try {
const txHash = await this._web3Wrapper.sendTransactionAsync(fakeTxData); const txData = marshaller.unmarshalTxData(fakeTxData);
const txHash = await this._web3Wrapper.sendTransactionAsync(txData);
await this._web3Wrapper.awaitTransactionMinedAsync(txHash, 0); await this._web3Wrapper.awaitTransactionMinedAsync(txHash, 0);
} catch (err) { } catch (err) {
// TODO(logvinov) Check that transaction failed and not some other exception
// Even if this transaction failed - we've already recorded it's trace. // Even if this transaction failed - we've already recorded it's trace.
_.noop(); _.noop();
} }

View File

@ -1,5 +1,4 @@
import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { marshaller, Web3Wrapper } from '@0xproject/web3-wrapper';
import { marshaller } from '@0xproject/web3-wrapper/lib/src/marshaller';
import { JSONRPCRequestPayload, Provider } from 'ethereum-types'; import { JSONRPCRequestPayload, Provider } from 'ethereum-types';
import { Callback, ErrorCallback } from '../types'; import { Callback, ErrorCallback } from '../types';

View File

@ -1,4 +1,17 @@
[ [
{
"version": "1.2.0",
"changes": [
{
"note": "Export marshaller to convert between RPC and user-space data formats",
"pr": 938
},
{
"note": "Export RPC types",
"pr": 938
}
]
},
{ {
"timestamp": 1532619515, "timestamp": 1532619515,
"version": "1.1.2", "version": "1.1.2",

View File

@ -1,2 +1,13 @@
export { Web3Wrapper } from './web3_wrapper'; export { Web3Wrapper } from './web3_wrapper';
export { Web3WrapperErrors, NodeType } from './types'; export { marshaller } from './marshaller';
export {
Web3WrapperErrors,
NodeType,
CallDataRPC,
CallTxDataBaseRPC,
AbstractBlockRPC,
BlockWithoutTransactionDataRPC,
BlockWithTransactionDataRPC,
TransactionRPC,
TxDataRPC,
} from './types';

View File

@ -25,7 +25,15 @@ import {
TxDataRPC, TxDataRPC,
} from './types'; } from './types';
/**
* Utils to convert ethereum structures from user-space format to RPC format. (marshall/unmarshall)
*/
export const marshaller = { export const marshaller = {
/**
* Unmarshall block without transaction data
* @param blockWithHexValues block to unmarshall
* @return unmarshalled block without transaction data
*/
unmarshalIntoBlockWithoutTransactionData( unmarshalIntoBlockWithoutTransactionData(
blockWithHexValues: BlockWithoutTransactionDataRPC, blockWithHexValues: BlockWithoutTransactionDataRPC,
): BlockWithoutTransactionData { ): BlockWithoutTransactionData {
@ -41,6 +49,11 @@ export const marshaller = {
}; };
return block; return block;
}, },
/**
* Unmarshall block with transaction data
* @param blockWithHexValues block to unmarshall
* @return unmarshalled block with transaction data
*/
unmarshalIntoBlockWithTransactionData(blockWithHexValues: BlockWithTransactionDataRPC): BlockWithTransactionData { unmarshalIntoBlockWithTransactionData(blockWithHexValues: BlockWithTransactionDataRPC): BlockWithTransactionData {
const block = { const block = {
...blockWithHexValues, ...blockWithHexValues,
@ -59,6 +72,11 @@ export const marshaller = {
}); });
return block; return block;
}, },
/**
* Unmarshall transaction
* @param txRpc transaction to unmarshall
* @return unmarshalled transaction
*/
unmarshalTransaction(txRpc: TransactionRPC): Transaction { unmarshalTransaction(txRpc: TransactionRPC): Transaction {
const tx = { const tx = {
...txRpc, ...txRpc,
@ -73,6 +91,11 @@ export const marshaller = {
}; };
return tx; return tx;
}, },
/**
* Unmarshall transaction data
* @param txDataRpc transaction data to unmarshall
* @return unmarshalled transaction data
*/
unmarshalTxData(txDataRpc: TxDataRPC): TxData { unmarshalTxData(txDataRpc: TxDataRPC): TxData {
if (_.isUndefined(txDataRpc.from)) { if (_.isUndefined(txDataRpc.from)) {
throw new Error(`txData must include valid 'from' value.`); throw new Error(`txData must include valid 'from' value.`);
@ -86,6 +109,11 @@ export const marshaller = {
}; };
return txData; return txData;
}, },
/**
* Marshall transaction data
* @param txData transaction data to marshall
* @return marshalled transaction data
*/
marshalTxData(txData: Partial<TxData>): Partial<TxDataRPC> { marshalTxData(txData: Partial<TxData>): Partial<TxDataRPC> {
if (_.isUndefined(txData.from)) { if (_.isUndefined(txData.from)) {
throw new Error(`txData must include valid 'from' value.`); throw new Error(`txData must include valid 'from' value.`);
@ -107,6 +135,11 @@ export const marshaller = {
}); });
return txDataRPC; return txDataRPC;
}, },
/**
* Marshall call data
* @param callData call data to marshall
* @return marshalled call data
*/
marshalCallData(callData: Partial<CallData>): Partial<CallDataRPC> { marshalCallData(callData: Partial<CallData>): Partial<CallDataRPC> {
const callTxDataBase = { const callTxDataBase = {
...callData, ...callData,
@ -119,12 +152,22 @@ export const marshaller = {
}; };
return callDataRPC; return callDataRPC;
}, },
/**
* Marshall address
* @param address address to marshall
* @return marshalled address
*/
marshalAddress(address: string): string { marshalAddress(address: string): string {
if (addressUtils.isAddress(address)) { if (addressUtils.isAddress(address)) {
return ethUtil.addHexPrefix(address); return ethUtil.addHexPrefix(address);
} }
throw new Error(`Invalid address encountered: ${address}`); throw new Error(`Invalid address encountered: ${address}`);
}, },
/**
* Marshall block param
* @param blockParam block param to marshall
* @return marshalled block param
*/
marshalBlockParam(blockParam: BlockParam | string | number | undefined): string | undefined { marshalBlockParam(blockParam: BlockParam | string | number | undefined): string | undefined {
if (_.isUndefined(blockParam)) { if (_.isUndefined(blockParam)) {
return BlockParamLiteral.Latest; return BlockParamLiteral.Latest;
@ -132,6 +175,11 @@ export const marshaller = {
const encodedBlockParam = _.isNumber(blockParam) ? utils.numberToHex(blockParam) : blockParam; const encodedBlockParam = _.isNumber(blockParam) ? utils.numberToHex(blockParam) : blockParam;
return encodedBlockParam; return encodedBlockParam;
}, },
/**
* Unmarshall log
* @param rawLog log to unmarshall
* @return unmarshalled log
*/
unmarshalLog(rawLog: RawLogEntry): LogEntry { unmarshalLog(rawLog: RawLogEntry): LogEntry {
const formattedLog = { const formattedLog = {
...rawLog, ...rawLog,