49fdd23d58
* Move secureApi to shell * Extract isTestnet test * Use mobx + subscriptions for status * Re-add status indicator * Add lerna * Move intial packages to js/packages * Move 3rdparty/{email,sms}-verification to correct location * Move package.json & README to library src * Move tests for library packages * Move views & dapps to packages * Move i18n to root * Move shell to actual src (main app) * Remove ~ references * Change ~ to root (explicit imports) * Finalise convert of ~ * Move views into dapps as well * Move dapps to packages/ * Fix references * Update css * Update test spec locations * Update tests * Case fix * Skip flakey tests * Update enzyme * Skip previously ignored tests * Allow empty api for hw * Re-add theme for embed
157 lines
5.0 KiB
JavaScript
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`);
|
|
}
|
|
}
|
|
}
|