Skip to content
This repository has been archived by the owner on Mar 17, 2024. It is now read-only.

Add async collection methods for BuidlerEVM #222

Merged
merged 9 commits into from
Oct 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ function Gas(runner, options) {
const watch = new TransactionWatcher(config);
const table = new GasTable(config);

// Expose internal methods to plugins
if (typeof options.attachments === "object") {
options.attachments.recordTransaction = watch.transaction.bind(watch);
}

// These call the cloud, start running them.
utils.setGasAndPriceRates(config);

Expand Down Expand Up @@ -71,12 +76,14 @@ function Gas(runner, options) {
});

runner.on("test", () => {
watch.beforeStartBlock = sync.blockNumber();
if (!config.provider) {
watch.beforeStartBlock = sync.blockNumber();
}
watch.data.resetAddressCache();
});

runner.on("hook end", hook => {
if (hook.title.includes("before each")) {
if (hook.title.includes("before each") && !config.provider) {
watch.itStartBlock = sync.blockNumber() + 1;
}
});
Expand All @@ -87,8 +94,11 @@ function Gas(runner, options) {
let gasUsedString;
let consumptionString;
let timeSpentString = color(test.speed, "%dms");
let gasUsed;

const gasUsed = watch.blocks();
if (!config.provider) {
gasUsed = watch.blocks();
}

if (gasUsed) {
gasUsedString = color("checkmark", "%d gas");
Expand Down
2 changes: 1 addition & 1 deletion lib/artifactor.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Artifactor {
constructor(config) {
this.config = config;
this.sync = new SyncRequest(config.url);
this.networkId = this.sync.getNetworkId();
this.networkId = !config.provider ? this.sync.getNetworkId() : null;
}

/**
Expand Down
3 changes: 2 additions & 1 deletion lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

class Config {
constructor(options = {}) {
this.blockLimit = 6718946;
this.blockLimit = options.blockLimit || 6718946;
this.defaultGasPrice = 5;

this.currency = options.currency || "eur";
Expand All @@ -22,6 +22,7 @@ class Config {
this.proxyResolver = options.proxyResolver || null;
this.metadata = options.metadata || null;
this.showMethodSig = options.showMethodSig || false;
this.provider = options.provider || null;

this.excludeContracts = Array.isArray(options.excludeContracts)
? options.excludeContracts
Expand Down
45 changes: 42 additions & 3 deletions lib/gasData.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,20 @@ class GasData {
*/
initialize(config) {
this.sync = new SyncRequest(config.url);
this.provider = config.provider;
const artifactor = new Artifactor(config);

const files = utils.listSolidityFiles(config.srcPath);

// Get the current blockLimit;
// TODO: This shouldn't be here - should be on the config object &
// fetched when the table is written or something.
const block = this.sync.getLatestBlock();
this.blockLimit = utils.gas(block.gasLimit);
this.blockLimit = config.blockLimit;

if (!this.blockLimit && !this.provider) {
const block = this.sync.getLatestBlock();
this.blockLimit = utils.gas(block.gasLimit);
}

files.forEach(file => {
utils
Expand All @@ -59,11 +64,16 @@ class GasData {
this.deployments.push(contractInfo);

// Report gas used during pre-test deployments (ex: truffle migrate)
if (contract.deployed && contract.deployed.transactionHash) {
if (
contract.deployed &&
contract.deployed.transactionHash &&
!this.provider
) {
const receipt = this.sync.getTransactionReceipt(
contract.deployed.transactionHash
);
if (receipt) {
// Sync: only runs for Truffle atm...
this.trackNameByAddress(name, contract.deployed.address);
contractInfo.gasData.push(utils.gas(receipt.gasUsed));
}
Expand Down Expand Up @@ -138,6 +148,35 @@ class GasData {
return this.codeHashMap[hash];
}

/**
* Map a contract name to the sha1 hash of the code stored at an address
* @param {String} name contract name
* @param {String} address contract address
*/
async asyncTrackNameByAddress(name, address) {
if (this.addressIsCached(address)) return;

const code = await this.provider.getCode(address);
const hash = code ? sha1(code) : null;
this.codeHashMap[hash] = name;
this.addressCache[address] = name;
}

/**
* Get the name of the contract stored at contract address
* @param {String} address contract address
* @return {String} contract name
*/
async asyncGetNameByAddress(address) {
if (this.addressIsCached(address)) {
return this.addressCache[address];
}

const code = await this.provider.getCode(address);
const hash = code ? sha1(code) : null;
return this.codeHashMap[hash];
}

/**
* Compares existing contract binaries to the input code for a
* new deployment transaction and returns the relevant contract.
Expand Down
3 changes: 3 additions & 0 deletions lib/gasTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,9 @@ class GasTable {
* @param {Object} info GasData instance
*/
saveCodeChecksData(info) {
delete this.config.provider;
delete info.provider;

const output = {
namespace: "ethGasReporter",
config: this.config,
Expand Down
18 changes: 18 additions & 0 deletions lib/proxyResolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class ProxyResolver {
this.unresolvedCalls = 0;
this.data = data;
this.sync = new SyncRequest(config.url);
this.provider = config.provider;

if (typeof config.proxyResolver === "function") {
this.resolve = config.proxyResolver.bind(this);
Expand Down Expand Up @@ -45,6 +46,23 @@ class ProxyResolver {
return match.name;
}
}

/**
* Tries to match bytecode deployed at address to deployedBytecode listed
* in artifacts. If found, adds this to the code-hash name mapping and
* returns name.
* @param {String} address contract address
* @return {String} contract name
*/
async asyncResolveByDeployedBytecode(address) {
const code = await this.provider.getCode(address);
const match = this.data.getContractByDeployedBytecode(code);

if (match) {
await this.data.asyncTrackNameByAddress(match.name, address);
return match.name;
}
}
}

module.exports = ProxyResolver;
58 changes: 58 additions & 0 deletions lib/transactionWatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class TransactionWatcher {
this.beforeStartBlock = 0; // Tracks from `before/beforeEach` transactions (methods & deploys)
this.data = new GasData();
this.sync = new SyncRequest(config.url);
this.provider = config.provider;
this.resolver = new ProxyResolver(this.data, config);
}

Expand Down Expand Up @@ -52,6 +53,12 @@ class TransactionWatcher {
return gasUsed;
}

async transaction(receipt, transaction) {
receipt.contractAddress
? await this._asyncCollectDeploymentsData(transaction, receipt)
: await this._asyncCollectMethodsData(transaction, receipt);
}

/**
* Extracts and stores deployments gas usage data for a tx
* @param {Object} transaction return value of `getTransactionByHash`
Expand All @@ -66,6 +73,23 @@ class TransactionWatcher {
}
}

/**
* Extracts and stores deployments gas usage data for a tx
* @param {Object} transaction return value of `getTransactionByHash`
* @param {Object} receipt
*/
async _asyncCollectDeploymentsData(transaction, receipt) {
const match = this.data.getContractByDeploymentInput(transaction.input);

if (match) {
await this.data.asyncTrackNameByAddress(
match.name,
receipt.contractAddress
);
match.gasData.push(utils.gas(receipt.gasUsed));
}
}

/**
* Extracts and stores methods gas usage data for a tx
* @param {Object} transaction return value of `getTransactionByHash`
Expand Down Expand Up @@ -98,6 +122,40 @@ class TransactionWatcher {
}
}

/**
* Extracts and stores methods gas usage data for a tx
* @param {Object} transaction return value of `getTransactionByHash`
* @param {Object} receipt
*/
async _asyncCollectMethodsData(transaction, receipt) {
let contractName = await this.data.asyncGetNameByAddress(transaction.to);

// Case: proxied call
if (this._isProxied(contractName, transaction.input)) {
contractName = this.resolver.resolve(transaction);

// Case: hidden contract factory deployment
} else if (!contractName) {
contractName = await this.resolver.asyncResolveByDeployedBytecode(
transaction.to
);
}

// Case: all else fails, use first match strategy
if (!contractName) {
contractName = this.resolver.resolveByMethodSignature(transaction);
}

const id = utils.getMethodID(contractName, transaction.input);

if (this.data.methods[id]) {
this.data.methods[id].gasData.push(utils.gas(receipt.gasUsed));
this.data.methods[id].numberOfCalls += 1;
} else {
this.resolver.unresolvedCalls++;
}
}

/**
* Returns true if there is a contract name associated with an address
* but method can't be matched to it
Expand Down
2 changes: 1 addition & 1 deletion mock/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eth-gas-reporter",
"version": "0.2.16",
"version": "0.2.17",
"description": "Mocha reporter which shows gas used per unit test.",
"main": "index.js",
"scripts": {
Expand Down