UNPKG

7.03 kBJavaScriptView Raw
1/*
2 This file is part of web3.js.
3
4 web3.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 web3.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 web3.js. If not, see <http://www.gnu.org/licenses/>.
16 */
17/**
18 * @file soliditySha3.js
19 * @author Fabian Vogelsteller <fabian@ethereum.org>
20 * @date 2017
21 */
22
23var _ = require('underscore');
24var BN = require('bn.js');
25var utils = require('./utils.js');
26
27
28var _elementaryName = function (name) {
29 /*jshint maxcomplexity:false */
30
31 if (name.startsWith('int[')) {
32 return 'int256' + name.slice(3);
33 } else if (name === 'int') {
34 return 'int256';
35 } else if (name.startsWith('uint[')) {
36 return 'uint256' + name.slice(4);
37 } else if (name === 'uint') {
38 return 'uint256';
39 } else if (name.startsWith('fixed[')) {
40 return 'fixed128x128' + name.slice(5);
41 } else if (name === 'fixed') {
42 return 'fixed128x128';
43 } else if (name.startsWith('ufixed[')) {
44 return 'ufixed128x128' + name.slice(6);
45 } else if (name === 'ufixed') {
46 return 'ufixed128x128';
47 }
48 return name;
49};
50
51// Parse N from type<N>
52var _parseTypeN = function (type) {
53 var typesize = /^\D+(\d+).*$/.exec(type);
54 return typesize ? parseInt(typesize[1], 10) : null;
55};
56
57// Parse N from type[<N>]
58var _parseTypeNArray = function (type) {
59 var arraySize = /^\D+\d*\[(\d+)\]$/.exec(type);
60 return arraySize ? parseInt(arraySize[1], 10) : null;
61};
62
63var _parseNumber = function (arg) {
64 var type = typeof arg;
65 if (type === 'string') {
66 if (utils.isHexStrict(arg)) {
67 return new BN(arg.replace(/0x/i,''), 16);
68 } else {
69 return new BN(arg, 10);
70 }
71 } else if (type === 'number') {
72 return new BN(arg);
73 } else if (utils.isBigNumber(arg)) {
74 return new BN(arg.toString(10));
75 } else if (utils.isBN(arg)) {
76 return arg;
77 } else {
78 throw new Error(arg +' is not a number');
79 }
80};
81
82var _solidityPack = function (type, value, arraySize) {
83 /*jshint maxcomplexity:false */
84
85 var size, num;
86 type = _elementaryName(type);
87
88
89 if (type === 'bytes') {
90
91 if (value.replace(/^0x/i,'').length % 2 !== 0) {
92 throw new Error('Invalid bytes characters '+ value.length);
93 }
94
95 return value;
96 } else if (type === 'string') {
97 return utils.utf8ToHex(value);
98 } else if (type === 'bool') {
99 return value ? '01' : '00';
100 } else if (type.startsWith('address')) {
101 if(arraySize) {
102 size = 64;
103 } else {
104 size = 40;
105 }
106
107 if(!utils.isAddress(value)) {
108 throw new Error(value +' is not a valid address, or the checksum is invalid.');
109 }
110
111 return utils.leftPad(value.toLowerCase(), size);
112 }
113
114 size = _parseTypeN(type);
115
116 if (type.startsWith('bytes')) {
117
118 if(!size) {
119 throw new Error('bytes[] not yet supported in solidity');
120 }
121
122 // must be 32 byte slices when in an array
123 if(arraySize) {
124 size = 32;
125 }
126
127 if (size < 1 || size > 32 || size < value.replace(/^0x/i,'').length / 2 ) {
128 throw new Error('Invalid bytes' + size +' for '+ value);
129 }
130
131 return utils.rightPad(value, size * 2);
132 } else if (type.startsWith('uint')) {
133
134 if ((size % 8) || (size < 8) || (size > 256)) {
135 throw new Error('Invalid uint'+size+' size');
136 }
137
138 num = _parseNumber(value);
139 if (num.bitLength() > size) {
140 throw new Error('Supplied uint exceeds width: ' + size + ' vs ' + num.bitLength());
141 }
142
143 if(num.lt(new BN(0))) {
144 throw new Error('Supplied uint '+ num.toString() +' is negative');
145 }
146
147 return size ? utils.leftPad(num.toString('hex'), size/8 * 2) : num;
148 } else if (type.startsWith('int')) {
149
150 if ((size % 8) || (size < 8) || (size > 256)) {
151 throw new Error('Invalid int'+size+' size');
152 }
153
154 num = _parseNumber(value);
155 if (num.bitLength() > size) {
156 throw new Error('Supplied int exceeds width: ' + size + ' vs ' + num.bitLength());
157 }
158
159 if(num.lt(new BN(0))) {
160 return num.toTwos(size).toString('hex');
161 } else {
162 return size ? utils.leftPad(num.toString('hex'), size/8 * 2) : num;
163 }
164
165 } else {
166 // FIXME: support all other types
167 throw new Error('Unsupported or invalid type: ' + type);
168 }
169};
170
171
172var _processSoliditySha3Args = function (arg) {
173 /*jshint maxcomplexity:false */
174
175 if(_.isArray(arg)) {
176 throw new Error('Autodetection of array types is not supported.');
177 }
178
179 var type, value = '';
180 var hexArg, arraySize;
181
182 // if type is given
183 if (_.isObject(arg) && (arg.hasOwnProperty('v') || arg.hasOwnProperty('t') || arg.hasOwnProperty('value') || arg.hasOwnProperty('type'))) {
184 type = arg.hasOwnProperty('t') ? arg.t : arg.type;
185 value = arg.hasOwnProperty('v') ? arg.v : arg.value;
186
187 // otherwise try to guess the type
188 } else {
189
190 type = utils.toHex(arg, true);
191 value = utils.toHex(arg);
192
193 if (!type.startsWith('int') && !type.startsWith('uint')) {
194 type = 'bytes';
195 }
196 }
197
198 if ((type.startsWith('int') || type.startsWith('uint')) && typeof value === 'string' && !/^(-)?0x/i.test(value)) {
199 value = new BN(value);
200 }
201
202 // get the array size
203 if(_.isArray(value)) {
204 arraySize = _parseTypeNArray(type);
205 if(arraySize && value.length !== arraySize) {
206 throw new Error(type +' is not matching the given array '+ JSON.stringify(value));
207 } else {
208 arraySize = value.length;
209 }
210 }
211
212
213 if (_.isArray(value)) {
214 hexArg = value.map(function (val) {
215 return _solidityPack(type, val, arraySize).toString('hex').replace('0x','');
216 });
217 return hexArg.join('');
218 } else {
219 hexArg = _solidityPack(type, value, arraySize);
220 return hexArg.toString('hex').replace('0x','');
221 }
222
223};
224
225/**
226 * Hashes solidity values to a sha3 hash using keccak 256
227 *
228 * @method soliditySha3
229 * @return {Object} the sha3
230 */
231var soliditySha3 = function () {
232 /*jshint maxcomplexity:false */
233
234 var args = Array.prototype.slice.call(arguments);
235
236 var hexArgs = _.map(args, _processSoliditySha3Args);
237
238 // console.log(args, hexArgs);
239 // console.log('0x'+ hexArgs.join(''));
240
241 return utils.sha3('0x'+ hexArgs.join(''));
242};
243
244
245module.exports = soliditySha3;