ethers.js/packages/testcases/generation-scripts/abi.js

530 lines
19 KiB
JavaScript
Raw Normal View History

2019-05-15 01:48:48 +03:00
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//let web3 = new Web3(new Web3.providers.HttpProvider('http://127.0.0.1:8549'));
2019-07-23 07:04:32 +03:00
//import { compile as _compile } from "solc";
var solc_1 = require("@ethersproject/cli/solc");
2019-05-15 01:48:48 +03:00
var __1 = require("..");
var ethereumjs_util_1 = require("ethereumjs-util");
function hasPrefix(str, prefix) {
return (str.substring(0, prefix.length) === prefix);
}
function repeat(str, count) {
var result = "";
for (var i = 0; i < count; i++) {
result += str;
}
return result;
}
function indent(tabs) {
var result = '';
while (result.length < 4 * tabs) {
result += " ";
}
return result;
}
function getStructName(base) {
return "Struct" + ethereumjs_util_1.keccak256(base).slice(0, 4).toString("hex");
}
var Code = /** @class */ (function () {
function Code() {
this.depth = 0;
this.lines = [];
}
Object.defineProperty(Code.prototype, "code", {
get: function () {
return this.lines.join("\n"); //.replace(/ +\n/g, "\n").replace(/(\}\n\n+)/g, "}\n");
},
enumerable: true,
configurable: true
});
Code.prototype.comment = function (line) {
this.add("");
this.add("/" + "/ " + line);
};
Code.prototype.add = function (line) {
var open = (line.trim().substring(line.trim().length - 1) === "{");
var close = line.trim()[0] === "}";
if (close) {
this.depth--;
}
this.lines.push(indent(this.depth) + line);
//if (close) { this.lines.push(""); }
if (open) {
this.depth++;
}
};
return Code;
}());
var chars = [];
function addChars(start, length) {
for (var i = start; i < start + length; i++) {
chars.push(String.fromCharCode(i));
}
}
addChars(48, 10);
addChars(65, 26);
// Returns the functions required to generate code for a specific parameter type and value
function getGenCode(testData) {
var type = testData.type;
var value = (testData.value != null) ? testData.value : "__crash__";
var isArray = type.match(/^(.*)(\[[0-9]*\])$/);
if (isArray) {
var base_1 = isArray[1];
var suffix_1 = isArray[2];
var isDynamic_1 = (isArray[2] === "[]");
return {
assign: function (name, code) {
if (isDynamic_1) {
//let child = getGenCode({ type: base });
//let decl = child.decl(name).split(" ");
var struct = base_1;
if (type.substring(0, 5) === "tuple") {
struct = getStructName(base_1);
}
code.add(name + " = new " + struct + "[](" + String(value.length) + ");");
}
value.forEach(function (value, index) {
console.log("SSS", base_1, value);
var child = getGenCode({ type: base_1, value: value });
child.assign(name + "[" + String(index) + "]", code);
});
},
decl: function (name) {
var child = getGenCode({ type: isArray[1] });
// Inject the array suffix to the type and add memory location
// - uint256 p0 => uint256[] memory p0
// - bytes memory p0 => bytes[] memory p0
var result = child.decl(name).split(" ");
result[0] = result[0] + suffix_1;
if (result[1] !== "memory") {
result.splice(1, 0, "memory");
}
return result.join(" ");
},
structs: function (code) {
var child = getGenCode({ type: isArray[1] });
child.structs(code);
}
};
}
var isTuple = type.match(/^tuple\((.*)\)$/);
if (isTuple) {
var children_1 = [];
// Split up the child types
var accum = "";
var balance = 0;
var types = isTuple[1];
for (var i = 0; i < types.length; i++) {
var c = types[i];
if (c === "(") {
balance++;
accum += c;
}
else if (c === ")") {
balance--;
accum += c;
}
else if (c === ",") {
if (balance === 0) {
children_1.push(accum);
accum = "";
}
else {
accum += c;
}
}
else {
accum += c;
}
}
if (accum) {
children_1.push(accum);
}
return {
assign: function (name, code) {
children_1.forEach(function (child, index) {
console.log("TT", child, value[index]);
getGenCode({
type: child,
value: value[index]
}).assign(name + ".m" + String(index), code);
});
},
decl: function (name) {
return (getStructName(type) + " memory " + name);
},
structs: function (code) {
// Include any dependency Structs first
children_1.forEach(function (child) {
getGenCode({ type: child }).structs(code);
});
// Add this struct
code.add("struct " + getStructName(type) + " {");
children_1.forEach(function (child, index) {
var decl = getGenCode({
type: child
}).decl("m" + String(index)).replace(" memory ", " ");
code.add(decl + ";");
});
code.add("}");
}
};
}
var isFixedBytes = type.match(/^bytes([0-9]+)$/);
var isNumber = type.match(/^(u?)int([0-9]*)$/);
var isFixedNumber = type.match(/^(u?)fixed(([0-9]+)x([0-9]+))?$/);
if (type === "address" || type === "bool" || isNumber || isFixedNumber || isFixedBytes) {
return {
assign: function (name, code) {
if (type === "boolean") {
code.add(name + " = " + (value ? "true" : "false") + ";");
}
else if (isFixedBytes) {
code.add(name + " = hex\"" + value.substring(2) + "\";");
}
else {
code.add(name + " = " + value + ";");
}
},
decl: function (name) {
return (type + " " + name);
},
structs: function (code) { }
};
}
if (type === "string") {
return {
assign: function (name, code) {
code.add(name + " = " + JSON.stringify(value) + ";");
},
decl: function (name) {
return ("string memory " + name);
},
structs: function (code) { }
};
}
if (type === "bytes") {
var valueBytes_1 = Buffer.from(value.substring(2), "hex");
return {
assign: function (name, code) {
code.add("{");
code.add("bytes memory temp = new bytes(" + valueBytes_1.length + ");");
code.add(name + " = temp;");
code.add("assembly {");
// Store the length
code.add("mstore(temp, " + valueBytes_1.length + ")");
// Store each byte
for (var i = 0; i < valueBytes_1.length; i++) {
code.add("mstore8(add(temp, " + (32 + i) + "), " + valueBytes_1[i] + ")");
}
code.add("}");
code.add("}");
},
decl: function (name) {
return ("bytes memory " + name);
},
structs: function (code) { }
};
}
throw new Error("Could not produce GenCode: " + type);
return null;
}
// Generates a random type and value for the type
function generateTest(seed) {
var basetype = __1.randomNumber(seed + "-type", 0, 10);
switch (basetype) {
// Address
case 0:
return function (seed) {
var value = ethereumjs_util_1.toChecksumAddress(__1.randomHexString(seed + "-value", 20));
return {
type: "address",
value: value
};
};
// Boolean
case 1:
return function (seed) {
var value = (__1.randomNumber(seed + "-value", 0, 2) ? true : false);
return {
type: "bool",
value: value
};
};
// Number
case 2: {
var signed_1 = __1.randomNumber(seed + "-signed", 0, 2);
var width_1 = __1.randomNumber(seed + "-width", 0, 33) * 8;
var type_1 = (signed_1 ? "" : "u") + "int";
// Allow base int and uint
if (width_1) {
type_1 += String(width_1);
}
else {
width_1 = 256;
}
return function (seed) {
var hex = __1.randomHexString(seed + "-value", width_1 / 8).substring(2);
if (signed_1) {
// Sign bit set (we don't bother with 2's compliment
var msb = parseInt(hex[0], 16);
if (msb >= 8) {
hex = "-" + String(msb & 0x7) + hex.substring(1);
}
}
var value = (new ethereumjs_util_1.BN(hex, 16)).toString();
return {
type: type_1,
value: value
};
};
}
// Fixed
case 3: {
// Fixed Point values are not supported yet
return generateTest(seed + "-next");
var signed_2 = __1.randomNumber(seed + "-signed", 0, 2);
var width_2 = __1.randomNumber(seed + "-width", 0, 33) * 8;
var decimals_1 = 0;
var maxDecimals = (new ethereumjs_util_1.BN(repeat("7f", ((width_2 === 0) ? 32 : (width_2 / 8))), 16)).toString().length - 1;
var attempt = 0;
while (true) {
decimals_1 = __1.randomNumber(seed + "-decimals" + String(attempt), 0, 80);
if (decimals_1 < maxDecimals) {
break;
}
attempt++;
}
var type_2 = (signed_2 ? "" : "u") + "fixed";
// Allow base int and uint
if (width_2) {
type_2 += String(width_2) + "x" + String(decimals_1);
}
else {
width_2 = 128;
decimals_1 = 18;
}
return function (seed) {
var hex = __1.randomHexString(seed + "-value", width_2 / 8).substring(2);
// Use the top bit to indicate negative values
var negative = false;
if (signed_2) {
// Sign bit set (we don't bother with 2's compliment
var msb = parseInt(hex[0], 16);
if (msb >= 8) {
hex = String(msb & 0x7) + hex.substring(1);
negative = true;
}
}
// Zero-pad the value so we get at least 1 whole digit
var dec = (new ethereumjs_util_1.BN(hex, 16)).toString();
while (dec.length < decimals_1 + 1) {
dec = "0" + dec;
}
// Split the decimals with the decimal point
var split = dec.length - decimals_1;
var value = dec.substring(0, split) + "." + dec.substring(split);
if (negative) {
value = "-" + value;
}
// Prevent ending in a decimal (e.g. "45."
if (value.substring(value.length - 1) === ".") {
value = value.substring(0, value.length - 1);
}
return {
type: type_2,
value: value
};
};
}
// BytesXX
case 4: {
var length_1 = __1.randomNumber(seed + "-length", 1, 33);
var type_3 = "bytes" + String(length_1);
return function (seed) {
var value = __1.randomHexString(seed + "-value", length_1);
return {
type: type_3,
value: value
};
};
}
// String
case 5:
return function (seed) {
var length = __1.randomNumber(seed + "-length", 0, 36);
var value = "";
while (value.length < length) {
value += chars[__1.randomNumber(seed + "-value" + String(value.length), 0, chars.length)];
}
return {
type: "string",
value: value
};
};
// Bytes
case 6:
return function (seed) {
var length = __1.randomNumber(seed + "-length", 0, 12); // @TODO: increase this
var value = __1.randomHexString(seed + "-value", length);
//let valueBytes = Buffer.from(value.substring(2), "hex");
return {
type: "bytes",
value: value
};
};
// Fixed-Length Array (e.g. address[4])
case 7:
// Falls-through
// Dynamic-Length Array (e.g. address[])
case 8: {
var dynamic_1 = (basetype === 8);
var subType_1 = generateTest(seed + "-subtype");
var length_2 = __1.randomNumber(seed + "-length", 1, 3);
var suffix = "[" + ((!dynamic_1) ? length_2 : "") + "]";
var type_4 = subType_1("-index0").type + suffix;
return function (seed) {
if (dynamic_1) {
length_2 = __1.randomNumber(seed + "-length", 0, 3);
}
var children = [];
for (var i = 0; i < length_2; i++) {
children.push(subType_1(seed + "-index" + String(i)));
}
return {
type: type_4,
value: children.map(function (data) { return data.value; })
};
};
}
// Tuple
case 9: {
var count = __1.randomNumber(seed + "-count", 1, 8);
var subTypes_1 = [];
for (var i = 0; i < count; i++) {
var cSeed = seed + "-subtype" + String(i);
subTypes_1.push(generateTest(cSeed));
}
var type_5 = "tuple(" + subTypes_1.map(function (s) { return s("-index0").type; }).join(",") + ")";
var struct_1 = "Struct" + __1.randomHexString(seed + "-name", 4).substring(2);
return function (seed) {
var children = [];
subTypes_1.forEach(function (subType) {
children.push(subType(seed + "-value"));
});
return {
type: type_5,
struct: struct_1,
value: children.map(function (c) { return c.value; }),
};
};
}
}
throw new Error("bad things");
return null;
}
// Returns true iff the types are able to be non-standard pack encoded
function checkPack(types) {
for (var i = 0; i < types.length; i++) {
var type = types[i];
if (hasPrefix(type, "tuple")) {
return false;
}
if (hasPrefix(type, "bytes[")) {
return false;
}
if (hasPrefix(type, "string[")) {
return false;
}
var firstDynamic = type.indexOf("[]");
if (firstDynamic >= 0 && firstDynamic != type.length - 2) {
return false;
}
}
return true;
}
// Generates a Solidity source files with the parameter types and values
function generateSolidity(params) {
var plist = [];
for (var i = 0; i < params.length; i++) {
plist.push("p" + String(i));
}
var genCodes = params.map(function (p) { return getGenCode(p); });
var code = new Code();
///////////////////
// Pragma
code.add("pragma experimental ABIEncoderV2;");
code.add("pragma solidity ^0.5.5;");
code.add("");
///////////////////
// Header
code.add("contract Test {");
///////////////////
// Structs
genCodes.forEach(function (genCode) {
genCode.structs(code);
});
///////////////////
// test function
code.add("function test() public pure returns (" + genCodes.map(function (g, i) { return (g.decl("p" + String(i))); }).join(", ") + ") {");
genCodes.forEach(function (genCode, index) {
genCode.assign("p" + index, code);
});
code.add("}");
///////////////////
// encode
code.add("function encode() public pure returns (bytes memory data){");
code.comment("Declare all parameters");
genCodes.forEach(function (genCode, index) {
code.add(genCode.decl("p" + index) + ";");
});
code.comment("Assign all parameters");
genCodes.forEach(function (genCode, index) {
genCode.assign("p" + index, code);
});
code.add("");
code.add("return abi.encode(" + params.map(function (p, i) { return ("p" + i); }).join(", ") + ");");
code.add("}");
///////////////////
// encodePacked
if (checkPack(params.map(function (p) { return p.type; }))) {
code.add("function encodePacked() public pure returns (bytes memory data){");
code.comment("Declare all parameters");
genCodes.forEach(function (genCode, index) {
code.add(genCode.decl("p" + index) + ";");
});
code.comment("Assign all parameters");
genCodes.forEach(function (genCode, index) {
genCode.assign("p" + index, code);
});
code.add("");
code.add("return abi.encodePacked(" + params.map(function (p, i) { return ("p" + i); }).join(", ") + ");");
code.add("}");
}
///////////////////
// Footer
code.add("}");
return code.code;
}
for (var i = 0; i < 100; i++) {
var params = [];
console.log(i, __1.randomNumber(String(i) + "-length", 1, 6));
var length_3 = __1.randomNumber(String(i) + "-length", 1, 6);
for (var j = 0; j < length_3; j++) {
params.push(generateTest(String(i) + String(j) + "-type")(String(i) + String(j) + "-test"));
}
var solidity = generateSolidity(params);
console.log(solidity);
console.log(i);
2019-07-23 07:04:32 +03:00
var bytecode = solc_1.compile(solidity)[0].bytecode;
2019-05-15 01:48:48 +03:00
//console.log(params.map(p => p.type).join(", "));
//console.log(bytecode);
var testcase = {
//solidity: solidity,
bytecode: bytecode,
types: params.map(function (p) { return p.type; }),
value: params.map(function (p) { return p.value; }),
};
console.log(testcase);
}