github.com/jonasnick/go-ethereum@v0.7.12-0.20150216215225-22176f05d387/cmd/mist/assets/ext/ethereum.js/lib/abi.js (about) 1 /* 2 This file is part of ethereum.js. 3 4 ethereum.js is free software: you can redistribute it and/or modify 5 it under the terms of the GNU Lesser General Public License as published by 6 the Free Software Foundation, either version 3 of the License, or 7 (at your option) any later version. 8 9 ethereum.js is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU Lesser General Public License for more details. 13 14 You should have received a copy of the GNU Lesser General Public License 15 along with ethereum.js. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 /** @file abi.js 18 * @authors: 19 * Marek Kotewicz <marek@ethdev.com> 20 * Gav Wood <g@ethdev.com> 21 * @date 2014 22 */ 23 24 var web3 = require('./web3'); 25 var utils = require('./utils'); 26 var types = require('./types'); 27 var c = require('./const'); 28 var f = require('./formatters'); 29 30 var displayTypeError = function (type) { 31 console.error('parser does not support type: ' + type); 32 }; 33 34 /// This method should be called if we want to check if givent type is an array type 35 /// @returns true if it is, otherwise false 36 var arrayType = function (type) { 37 return type.slice(-2) === '[]'; 38 }; 39 40 var dynamicTypeBytes = function (type, value) { 41 // TODO: decide what to do with array of strings 42 if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length. 43 return f.formatInputInt(value.length); 44 return ""; 45 }; 46 47 var inputTypes = types.inputTypes(); 48 49 /// Formats input params to bytes 50 /// @param abi contract method inputs 51 /// @param array of params that will be formatted to bytes 52 /// @returns bytes representation of input params 53 var formatInput = function (inputs, params) { 54 var bytes = ""; 55 var padding = c.ETH_PADDING * 2; 56 57 /// first we iterate in search for dynamic 58 inputs.forEach(function (input, index) { 59 bytes += dynamicTypeBytes(input.type, params[index]); 60 }); 61 62 inputs.forEach(function (input, i) { 63 var typeMatch = false; 64 for (var j = 0; j < inputTypes.length && !typeMatch; j++) { 65 typeMatch = inputTypes[j].type(inputs[i].type, params[i]); 66 } 67 if (!typeMatch) { 68 displayTypeError(inputs[i].type); 69 } 70 71 var formatter = inputTypes[j - 1].format; 72 var toAppend = ""; 73 74 if (arrayType(inputs[i].type)) 75 toAppend = params[i].reduce(function (acc, curr) { 76 return acc + formatter(curr); 77 }, ""); 78 else 79 toAppend = formatter(params[i]); 80 81 bytes += toAppend; 82 }); 83 return bytes; 84 }; 85 86 var dynamicBytesLength = function (type) { 87 if (arrayType(type) || type === 'string') // only string itself that is dynamic; stringX is static length. 88 return c.ETH_PADDING * 2; 89 return 0; 90 }; 91 92 var outputTypes = types.outputTypes(); 93 94 /// Formats output bytes back to param list 95 /// @param contract abi method outputs 96 /// @param bytes representtion of output 97 /// @returns array of output params 98 var formatOutput = function (outs, output) { 99 100 output = output.slice(2); 101 var result = []; 102 var padding = c.ETH_PADDING * 2; 103 104 var dynamicPartLength = outs.reduce(function (acc, curr) { 105 return acc + dynamicBytesLength(curr.type); 106 }, 0); 107 108 var dynamicPart = output.slice(0, dynamicPartLength); 109 output = output.slice(dynamicPartLength); 110 111 outs.forEach(function (out, i) { 112 var typeMatch = false; 113 for (var j = 0; j < outputTypes.length && !typeMatch; j++) { 114 typeMatch = outputTypes[j].type(outs[i].type); 115 } 116 117 if (!typeMatch) { 118 displayTypeError(outs[i].type); 119 } 120 121 var formatter = outputTypes[j - 1].format; 122 if (arrayType(outs[i].type)) { 123 var size = f.formatOutputUInt(dynamicPart.slice(0, padding)); 124 dynamicPart = dynamicPart.slice(padding); 125 var array = []; 126 for (var k = 0; k < size; k++) { 127 array.push(formatter(output.slice(0, padding))); 128 output = output.slice(padding); 129 } 130 result.push(array); 131 } 132 else if (types.prefixedType('string')(outs[i].type)) { 133 dynamicPart = dynamicPart.slice(padding); 134 result.push(formatter(output.slice(0, padding))); 135 output = output.slice(padding); 136 } else { 137 result.push(formatter(output.slice(0, padding))); 138 output = output.slice(padding); 139 } 140 }); 141 142 return result; 143 }; 144 145 /// @param json abi for contract 146 /// @returns input parser object for given json abi 147 /// TODO: refactor creating the parser, do not double logic from contract 148 var inputParser = function (json) { 149 var parser = {}; 150 json.forEach(function (method) { 151 var displayName = utils.extractDisplayName(method.name); 152 var typeName = utils.extractTypeName(method.name); 153 154 var impl = function () { 155 var params = Array.prototype.slice.call(arguments); 156 return formatInput(method.inputs, params); 157 }; 158 159 if (parser[displayName] === undefined) { 160 parser[displayName] = impl; 161 } 162 163 parser[displayName][typeName] = impl; 164 }); 165 166 return parser; 167 }; 168 169 /// @param json abi for contract 170 /// @returns output parser for given json abi 171 var outputParser = function (json) { 172 var parser = {}; 173 json.forEach(function (method) { 174 175 var displayName = utils.extractDisplayName(method.name); 176 var typeName = utils.extractTypeName(method.name); 177 178 var impl = function (output) { 179 return formatOutput(method.outputs, output); 180 }; 181 182 if (parser[displayName] === undefined) { 183 parser[displayName] = impl; 184 } 185 186 parser[displayName][typeName] = impl; 187 }); 188 189 return parser; 190 }; 191 192 /// @param function/event name for which we want to get signature 193 /// @returns signature of function/event with given name 194 var signatureFromAscii = function (name) { 195 return web3.sha3(web3.fromAscii(name)).slice(0, 2 + c.ETH_SIGNATURE_LENGTH * 2); 196 }; 197 198 var eventSignatureFromAscii = function (name) { 199 return web3.sha3(web3.fromAscii(name)); 200 }; 201 202 module.exports = { 203 inputParser: inputParser, 204 outputParser: outputParser, 205 formatInput: formatInput, 206 formatOutput: formatOutput, 207 signatureFromAscii: signatureFromAscii, 208 eventSignatureFromAscii: eventSignatureFromAscii 209 }; 210