2018-05-21 13:38:13 +00:00
|
|
|
const utils = require('web3-utils');
|
|
|
|
const _ = require('underscore');
|
|
|
|
|
|
|
|
|
|
|
|
// generates random inputs based on the inputs of an ABI
|
|
|
|
class ContractFuzzer {
|
2018-06-04 18:25:42 +00:00
|
|
|
constructor(embark) {
|
|
|
|
//this.abi = abi;
|
|
|
|
this.embark = embark;
|
|
|
|
this.logger = embark.logger;
|
|
|
|
this.events = embark.events;
|
|
|
|
|
|
|
|
this.registerConsoleCommand();
|
|
|
|
}
|
|
|
|
|
2018-06-05 18:54:43 +00:00
|
|
|
// main function to call, takes in iteration number and a contract and returns a map object
|
|
|
|
// composed of method names -> fuzzed inputs.
|
2018-06-04 18:25:42 +00:00
|
|
|
generateFuzz(iterations, contract) {
|
2018-06-05 18:39:10 +00:00
|
|
|
let fuzzMap = {};
|
2018-06-05 18:54:43 +00:00
|
|
|
for (let i = 0; i < iterations; i++) contract.abiDefinition.filter((x) => x.inputs && x.inputs.length != 0).forEach((abiMethod) => {
|
2018-06-05 18:39:10 +00:00
|
|
|
let name = abiMethod.type === "constructor" ? "constructor" : abiMethod.name;
|
|
|
|
console.log("name");
|
2018-06-04 18:25:42 +00:00
|
|
|
let inputTypes = abiMethod.inputs.map(input => input.type);
|
|
|
|
console.log("INPUT TYPES:", inputTypes);
|
|
|
|
let fuzzedInputs = _.map(inputTypes, this.getTypeFuzz.bind(this));
|
|
|
|
console.log("FUZZED INPUTS:", fuzzedInputs);
|
2018-06-05 18:39:10 +00:00
|
|
|
fuzzMap[name] = fuzzedInputs;
|
|
|
|
console.log("FUZZ KEYS SO FAR:", Object.keys(fuzzMap));
|
|
|
|
console.log("FUZZ VALS SO FAR:", Object.values(fuzzMap));
|
|
|
|
});
|
|
|
|
console.log("FUZZ:", Object.keys(fuzzMap), Object.values(fuzzMap));
|
|
|
|
return fuzzMap;
|
2018-06-04 18:25:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
getTypeFuzz(typeString) {
|
|
|
|
const self = this;
|
2018-06-05 18:39:10 +00:00
|
|
|
console.log("TYPESTRING:", typeString);
|
2018-06-04 18:25:42 +00:00
|
|
|
// Group 0: uint256[3]
|
|
|
|
// Group 1: uint256
|
|
|
|
// Group 2: uint
|
|
|
|
// Group 3: 256
|
|
|
|
// Group 4: [3]
|
|
|
|
// Group 5: 3
|
|
|
|
let regexObj = typeString.match(/((bool|int|uint|bytes|string|address)([0-9]*)?)(\[([0-9]*)\])*$/);
|
2018-06-05 18:39:10 +00:00
|
|
|
console.log("REGEX OBJ:", regexObj);
|
2018-06-04 18:25:42 +00:00
|
|
|
let type = regexObj[1];
|
2018-06-05 18:39:10 +00:00
|
|
|
console.log("type:", type);
|
|
|
|
let kind = regexObj[2];
|
|
|
|
console.log("kind:", kind);
|
2018-06-04 18:25:42 +00:00
|
|
|
let size = regexObj[3];
|
2018-06-05 18:39:10 +00:00
|
|
|
console.log("size:", size);
|
|
|
|
let array = regexObj[4];
|
|
|
|
console.log("array:", array);
|
|
|
|
let arraySize = regexObj[5];
|
|
|
|
console.log("array size:", arraySize);
|
|
|
|
switch(true) {
|
2018-06-05 18:54:43 +00:00
|
|
|
case array !== undefined: {
|
2018-06-05 18:39:10 +00:00
|
|
|
// if it's a dynamic array pick a number between 1 and 256 for length of array
|
|
|
|
let length = arraySize === undefined || arraySize === null || arraySize === '' ? Math.floor((Math.random() * 256) + 1) : arraySize;
|
|
|
|
console.log("LENGTH: ", length);
|
2018-06-05 18:54:43 +00:00
|
|
|
return self.generateArrayOfType(length, type);
|
|
|
|
}
|
2018-06-05 18:39:10 +00:00
|
|
|
case kind == "bool":
|
2018-06-04 18:25:42 +00:00
|
|
|
return self.generateRandomBool();
|
2018-06-05 18:39:10 +00:00
|
|
|
case kind == "uint" || kind == "int":
|
2018-06-04 18:25:42 +00:00
|
|
|
return self.generateRandomInt(size);
|
2018-06-05 18:39:10 +00:00
|
|
|
case kind === "bytes" && size !== undefined:
|
2018-06-04 18:25:42 +00:00
|
|
|
return self.generateRandomStaticBytes(size);
|
2018-06-05 18:39:10 +00:00
|
|
|
case kind === "string" || kind === "bytes":
|
2018-06-05 18:54:43 +00:00
|
|
|
return self.generateRandomDynamicType();
|
2018-06-05 18:39:10 +00:00
|
|
|
case kind === "address":
|
2018-06-04 18:25:42 +00:00
|
|
|
return self.generateRandomAddress();
|
|
|
|
default:
|
|
|
|
throw new Error("Couldn't find proper ethereum abi type");
|
2018-06-05 18:39:10 +00:00
|
|
|
}
|
2018-06-04 18:25:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
generateRandomBool() {
|
|
|
|
return _.sample([true, false]);
|
|
|
|
}
|
|
|
|
|
|
|
|
generateArrayOfType(length, type) {
|
|
|
|
var arr = [];
|
|
|
|
for (var i = 0; i < length; i++) {
|
2018-06-05 18:39:10 +00:00
|
|
|
arr.push(this.getTypeFuzz(type));
|
2018-06-04 18:25:42 +00:00
|
|
|
}
|
2018-06-05 18:39:10 +00:00
|
|
|
console.log("Final Array:", arr);
|
2018-06-04 18:25:42 +00:00
|
|
|
return arr;
|
|
|
|
}
|
|
|
|
|
|
|
|
generateRandomDynamicType() {
|
|
|
|
return Math.random().toString(36).slice(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
generateRandomStaticBytes(size) {
|
|
|
|
return utils.randomHex(size);
|
|
|
|
}
|
|
|
|
|
|
|
|
generateRandomInt(size) {
|
2018-06-04 20:35:20 +00:00
|
|
|
return utils.toBN(utils.randomHex(size / 8));
|
2018-06-04 18:25:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
generateRandomAddress() {
|
|
|
|
return utils.randomHex(20);
|
|
|
|
}
|
|
|
|
|
|
|
|
registerConsoleCommand() {
|
|
|
|
const self = this;
|
|
|
|
self.embark.registerConsoleCommand((cmd, _options) => {
|
|
|
|
let splitCmd = cmd.split(' ');
|
|
|
|
let cmdName = splitCmd[0];
|
|
|
|
let contractName = splitCmd[1];
|
|
|
|
let iterations = splitCmd[2] === undefined ? 1 : splitCmd[2];
|
|
|
|
if (cmdName === 'fuzz') {
|
|
|
|
self.events.request('contracts:contract', contractName, (contract) => {
|
|
|
|
self.logger.info("-- fuzzed vals for " + contractName);
|
2018-06-05 18:54:43 +00:00
|
|
|
this.generateFuzz(iterations, contract);
|
2018-06-04 18:25:42 +00:00
|
|
|
});
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = ContractFuzzer;
|