openethereum/js-old/src/abi/decoder/decoder.js

157 lines
5.0 KiB
JavaScript

// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity 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 General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import utf8 from 'utf8';
import Token from '../token/token';
import BytesTaken from './bytesTaken';
import DecodeResult from './decodeResult';
import ParamType from '../spec/paramType/paramType';
import { sliceData } from '../util/slice';
import { asAddress, asBool, asI32, asU32 } from '../util/sliceAs';
import { isArray, isInstanceOf } from '../util/types';
const NULL = '0000000000000000000000000000000000000000000000000000000000000000';
export default class Decoder {
static decode (params, data) {
if (!isArray(params)) {
throw new Error('Parameters should be array of ParamType');
}
const slices = sliceData(data);
let offset = 0;
return params.map((param) => {
const result = Decoder.decodeParam(param, slices, offset);
offset = result.newOffset;
return result.token;
});
}
static peek (slices, position) {
if (!slices || !slices[position]) {
return NULL;
}
return slices[position];
}
static takeBytes (slices, position, length) {
const slicesLength = Math.floor((length + 31) / 32);
let bytesStr = '';
for (let idx = 0; idx < slicesLength; idx++) {
bytesStr = `${bytesStr}${Decoder.peek(slices, position + idx)}`;
}
const bytes = (bytesStr.substr(0, length * 2).match(/.{1,2}/g) || []).map((code) => parseInt(code, 16));
return new BytesTaken(bytes, position + slicesLength);
}
static decodeParam (param, slices, offset) {
if (!isInstanceOf(param, ParamType)) {
throw new Error('param should be instanceof ParamType');
}
const tokens = [];
let taken;
let lengthOffset;
let length;
let newOffset;
switch (param.type) {
case 'address':
return new DecodeResult(new Token(param.type, asAddress(Decoder.peek(slices, offset))), offset + 1);
case 'bool':
return new DecodeResult(new Token(param.type, asBool(Decoder.peek(slices, offset))), offset + 1);
case 'int':
return new DecodeResult(new Token(param.type, asI32(Decoder.peek(slices, offset))), offset + 1);
case 'uint':
return new DecodeResult(new Token(param.type, asU32(Decoder.peek(slices, offset))), offset + 1);
case 'fixedBytes':
taken = Decoder.takeBytes(slices, offset, param.length);
return new DecodeResult(new Token(param.type, taken.bytes), taken.newOffset);
case 'bytes':
lengthOffset = asU32(Decoder.peek(slices, offset)).div(32).toNumber();
length = asU32(Decoder.peek(slices, lengthOffset)).toNumber();
taken = Decoder.takeBytes(slices, lengthOffset + 1, length);
return new DecodeResult(new Token(param.type, taken.bytes), offset + 1);
case 'string':
if (param.indexed) {
taken = Decoder.takeBytes(slices, offset, 32);
return new DecodeResult(new Token('fixedBytes', taken.bytes), offset + 1);
}
lengthOffset = asU32(Decoder.peek(slices, offset)).div(32).toNumber();
length = asU32(Decoder.peek(slices, lengthOffset)).toNumber();
taken = Decoder.takeBytes(slices, lengthOffset + 1, length);
const str = taken.bytes.map((code) => String.fromCharCode(code)).join('');
let decoded;
try {
decoded = utf8.decode(str);
} catch (error) {
decoded = str;
}
return new DecodeResult(new Token(param.type, decoded), offset + 1);
case 'array':
lengthOffset = asU32(Decoder.peek(slices, offset)).div(32).toNumber();
length = asU32(Decoder.peek(slices, lengthOffset)).toNumber();
newOffset = lengthOffset + 1;
for (let idx = 0; idx < length; idx++) {
const result = Decoder.decodeParam(param.subtype, slices, newOffset);
newOffset = result.newOffset;
tokens.push(result.token);
}
return new DecodeResult(new Token(param.type, tokens), offset + 1);
case 'fixedArray':
newOffset = offset;
for (let idx = 0; idx < param.length; idx++) {
const result = Decoder.decodeParam(param.subtype, slices, newOffset);
newOffset = result.newOffset;
tokens.push(result.token);
}
return new DecodeResult(new Token(param.type, tokens), newOffset);
default:
throw new Error(`Invalid param type ${param.type} in decodeParam`);
}
}
}