go-ethereum/lib/abi.js

266 lines
7.2 KiB
JavaScript
Raw Normal View History

2014-11-14 06:11:47 -06:00
/*
This file is part of ethereum.js.
ethereum.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ethereum.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with ethereum.js. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file abi.js
* @authors:
* Marek Kotewicz <marek@ethdev.com>
2014-12-21 18:13:49 -06:00
* Gav Wood <g@ethdev.com>
2014-11-14 06:11:47 -06:00
* @date 2014
*/
2014-11-12 11:59:29 -06:00
2015-01-06 11:29:38 -06:00
// TODO: is these line is supposed to be here?
if (process.env.NODE_ENV !== 'build') {
var web3 = require('./web3'); // jshint ignore:line
}
2014-12-21 18:13:49 -06:00
// TODO: make these be actually accurate instead of falling back onto JS's doubles.
var hexToDec = function (hex) {
return parseInt(hex, 16).toString();
};
var decToHex = function (dec) {
return parseInt(dec).toString(16);
};
2014-11-12 11:59:29 -06:00
var findIndex = function (array, callback) {
var end = false;
var i = 0;
for (; i < array.length && !end; i++) {
end = callback(array[i]);
}
return end ? i - 1 : -1;
};
2014-11-12 21:21:51 -06:00
var findMethodIndex = function (json, methodName) {
return findIndex(json, function (method) {
return method.name === methodName;
});
};
2014-12-21 18:13:49 -06:00
var padLeft = function (string, chars) {
2015-01-06 06:26:51 -06:00
return new Array(chars - string.length + 1).join("0") + string;
2014-11-12 11:59:29 -06:00
};
var calcBitPadding = function (type, expected) {
var value = type.slice(expected.length);
if (value === "") {
return 32;
}
return parseInt(value) / 8;
};
var calcBytePadding = function (type, expected) {
var value = type.slice(expected.length);
if (value === "") {
return 32;
}
return parseInt(value);
};
var calcRealPadding = function (type, expected) {
var value = type.slice(expected.length);
if (value === "") {
return 32;
}
var sizes = value.split('x');
for (var padding = 0, i = 0; i < sizes; i++) {
padding += (sizes[i] / 8);
}
return padding;
};
2014-11-12 21:21:51 -06:00
var setupInputTypes = function () {
var prefixedType = function (prefix, calcPadding) {
2014-11-12 11:59:29 -06:00
return function (type, value) {
var expected = prefix;
if (type.indexOf(expected) !== 0) {
return false;
}
var padding = calcPadding(type, expected);
2014-12-21 18:13:49 -06:00
if (typeof value === "number")
value = value.toString(16);
2015-01-06 11:29:38 -06:00
else if (typeof value === "string")
value = web3.toHex(value);
2014-12-21 18:13:49 -06:00
else if (value.indexOf('0x') === 0)
value = value.substr(2);
else
value = (+value).toString(16);
return padLeft(value, padding * 2);
2014-11-12 11:59:29 -06:00
};
};
2014-11-12 21:21:51 -06:00
var namedType = function (name, padding, formatter) {
2014-11-12 11:59:29 -06:00
return function (type, value) {
if (type !== name) {
2014-12-21 18:13:49 -06:00
return false;
2014-11-12 11:59:29 -06:00
}
2014-12-21 18:13:49 -06:00
return padLeft(formatter ? formatter(value) : value, padding * 2);
2014-11-12 11:59:29 -06:00
};
};
2014-11-12 21:21:51 -06:00
var formatBool = function (value) {
2014-12-21 18:13:49 -06:00
return value ? '0x1' : '0x0';
2014-11-12 21:21:51 -06:00
};
2014-11-12 11:59:29 -06:00
return [
prefixedType('uint', calcBitPadding),
prefixedType('int', calcBitPadding),
prefixedType('hash', calcBitPadding),
prefixedType('string', calcBytePadding),
prefixedType('real', calcRealPadding),
prefixedType('ureal', calcRealPadding),
2014-11-12 11:59:29 -06:00
namedType('address', 20),
2014-11-12 21:21:51 -06:00
namedType('bool', 1, formatBool),
2014-11-12 11:59:29 -06:00
];
};
2014-11-12 21:21:51 -06:00
var inputTypes = setupInputTypes();
2014-11-12 11:59:29 -06:00
2014-11-12 21:21:51 -06:00
var toAbiInput = function (json, methodName, params) {
2014-11-12 11:59:29 -06:00
var bytes = "";
2014-11-12 21:21:51 -06:00
var index = findMethodIndex(json, methodName);
2014-12-21 18:13:49 -06:00
2014-11-12 11:59:29 -06:00
if (index === -1) {
return;
}
2014-12-21 18:13:49 -06:00
bytes = "0x" + padLeft(index.toString(16), 2);
2014-11-12 11:59:29 -06:00
var method = json[index];
2014-12-21 18:13:49 -06:00
2014-11-12 11:59:29 -06:00
for (var i = 0; i < method.inputs.length; i++) {
var found = false;
2014-11-12 21:21:51 -06:00
for (var j = 0; j < inputTypes.length && !found; j++) {
2014-12-21 18:13:49 -06:00
found = inputTypes[j](method.inputs[i].type, params[i]);
2014-11-12 11:59:29 -06:00
}
if (!found) {
console.error('unsupported json type: ' + method.inputs[i].type);
}
bytes += found;
}
return bytes;
};
2014-11-12 21:21:51 -06:00
var setupOutputTypes = function () {
2015-01-06 11:29:38 -06:00
var prefixedType = function (prefix, calcPadding) {
2014-11-12 21:21:51 -06:00
return function (type) {
var expected = prefix;
if (type.indexOf(expected) !== 0) {
return -1;
}
2014-12-21 18:13:49 -06:00
var padding = calcPadding(type, expected);
2014-11-12 21:21:51 -06:00
return padding * 2;
};
};
var namedType = function (name, padding) {
return function (type) {
2014-12-21 18:13:49 -06:00
return name === type ? padding * 2 : -1;
2014-11-12 21:21:51 -06:00
};
};
var formatInt = function (value) {
2014-12-21 18:13:49 -06:00
return value.length <= 8 ? +parseInt(value, 16) : hexToDec(value);
};
var formatHash = function (value) {
return "0x" + value;
2014-11-12 21:21:51 -06:00
};
var formatBool = function (value) {
return value === '1' ? true : false;
};
2015-01-06 11:29:38 -06:00
var formatString = function (value) {
return web3.toAscii(value);
};
2014-11-12 21:21:51 -06:00
return [
{ padding: prefixedType('uint', calcBitPadding), format: formatInt },
{ padding: prefixedType('int', calcBitPadding), format: formatInt },
{ padding: prefixedType('hash', calcBitPadding), format: formatHash },
{ padding: prefixedType('string', calcBytePadding), format: formatString },
{ padding: prefixedType('real', calcRealPadding), format: formatInt },
{ padding: prefixedType('ureal', calcRealPadding), format: formatInt },
2014-11-12 21:21:51 -06:00
{ padding: namedType('address', 20) },
{ padding: namedType('bool', 1), format: formatBool }
];
};
var outputTypes = setupOutputTypes();
var fromAbiOutput = function (json, methodName, output) {
var index = findMethodIndex(json, methodName);
if (index === -1) {
return;
}
2014-12-21 18:13:49 -06:00
2014-11-12 21:21:51 -06:00
output = output.slice(2);
var result = [];
var method = json[index];
for (var i = 0; i < method.outputs.length; i++) {
var padding = -1;
for (var j = 0; j < outputTypes.length && padding === -1; j++) {
padding = outputTypes[j].padding(method.outputs[i].type);
}
if (padding === -1) {
// not found output parsing
continue;
}
var res = output.slice(0, padding);
var formatter = outputTypes[j - 1].format;
2014-12-21 18:13:49 -06:00
result.push(formatter ? formatter(res) : ("0x" + res));
2014-11-12 21:21:51 -06:00
output = output.slice(padding);
}
return result;
};
2014-11-14 06:11:47 -06:00
var inputParser = function (json) {
var parser = {};
2014-11-13 05:24:34 -06:00
json.forEach(function (method) {
2014-11-14 06:11:47 -06:00
parser[method.name] = function () {
2014-11-13 05:24:34 -06:00
var params = Array.prototype.slice.call(arguments);
return toAbiInput(json, method.name, params);
};
});
2014-11-14 06:11:47 -06:00
return parser;
2014-11-12 11:59:29 -06:00
};
2014-11-14 06:11:47 -06:00
var outputParser = function (json) {
var parser = {};
json.forEach(function (method) {
parser[method.name] = function (output) {
return fromAbiOutput(json, method.name, output);
};
});
return parser;
};
module.exports = {
inputParser: inputParser,
outputParser: outputParser
};