ethers.js/packages/bytes/lib/index.js

397 lines
13 KiB
JavaScript
Raw Normal View History

2019-05-15 01:48:48 +03:00
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
2019-08-02 09:10:58 +03:00
var logger_1 = require("@ethersproject/logger");
var _version_1 = require("./_version");
var logger = new logger_1.Logger(_version_1.version);
2019-05-15 01:48:48 +03:00
///////////////////////////////
function isHexable(value) {
return !!(value.toHexString);
}
function addSlice(array) {
if (array.slice) {
return array;
}
array.slice = function () {
var args = Array.prototype.slice.call(arguments);
return addSlice(new Uint8Array(Array.prototype.slice.apply(array, args)));
};
return array;
}
function isBytesLike(value) {
return ((isHexString(value) && !(value.length % 2)) || isBytes(value));
}
exports.isBytesLike = isBytesLike;
function isBytes(value) {
if (value == null) {
return false;
}
if (value.constructor === Uint8Array) {
return true;
}
if (typeof (value) === "string") {
return false;
}
if (value.length == null) {
return false;
}
for (var i = 0; i < value.length; i++) {
var v = value[i];
if (v < 0 || v >= 256 || (v % 1)) {
return false;
}
}
return true;
}
exports.isBytes = isBytes;
function arrayify(value, options) {
if (!options) {
options = {};
}
if (typeof (value) === "number") {
2019-08-02 09:10:58 +03:00
logger.checkSafeUint53(value, "invalid arrayify value");
2019-05-15 01:48:48 +03:00
var result = [];
while (value) {
result.unshift(value & 0xff);
value /= 256;
}
if (result.length === 0) {
result.push(0);
}
return addSlice(new Uint8Array(result));
}
if (options.allowMissingPrefix && typeof (value) === "string" && value.substring(0, 2) !== "0x") {
value = "0x" + value;
}
if (isHexable(value)) {
value = value.toHexString();
}
if (isHexString(value)) {
var hex = value.substring(2);
2019-09-28 09:36:19 +03:00
if (hex.length % 2) {
if (options.hexPad === "left") {
hex = "0x0" + hex.substring(2);
}
else if (options.hexPad === "right") {
hex += "0";
}
else {
logger.throwArgumentError("hex data is odd-length", "value", value);
}
2019-05-15 01:48:48 +03:00
}
var result = [];
for (var i = 0; i < hex.length; i += 2) {
result.push(parseInt(hex.substring(i, i + 2), 16));
}
return addSlice(new Uint8Array(result));
}
if (isBytes(value)) {
return addSlice(new Uint8Array(value));
}
2019-08-02 09:10:58 +03:00
return logger.throwArgumentError("invalid arrayify value", "value", value);
2019-05-15 01:48:48 +03:00
}
exports.arrayify = arrayify;
function concat(items) {
var objects = items.map(function (item) { return arrayify(item); });
var length = objects.reduce(function (accum, item) { return (accum + item.length); }, 0);
var result = new Uint8Array(length);
objects.reduce(function (offset, object) {
result.set(object, offset);
return offset + object.length;
}, 0);
return addSlice(result);
}
exports.concat = concat;
function stripZeros(value) {
var result = arrayify(value);
if (result.length === 0) {
return result;
}
// Find the first non-zero entry
var start = 0;
while (start < result.length && result[start] === 0) {
start++;
}
// If we started with zeros, strip them
if (start) {
result = result.slice(start);
}
return result;
}
exports.stripZeros = stripZeros;
function zeroPad(value, length) {
value = arrayify(value);
if (value.length > length) {
2019-08-02 09:10:58 +03:00
logger.throwArgumentError("value out of range", "value", arguments[0]);
2019-05-15 01:48:48 +03:00
}
var result = new Uint8Array(length);
result.set(value, length - value.length);
return addSlice(result);
}
exports.zeroPad = zeroPad;
function isHexString(value, length) {
if (typeof (value) !== "string" || !value.match(/^0x[0-9A-Fa-f]*$/)) {
return false;
}
if (length && value.length !== 2 + 2 * length) {
return false;
}
return true;
}
exports.isHexString = isHexString;
var HexCharacters = "0123456789abcdef";
function hexlify(value, options) {
if (!options) {
options = {};
}
if (typeof (value) === "number") {
2019-08-02 09:10:58 +03:00
logger.checkSafeUint53(value, "invalid hexlify value");
2019-05-15 01:48:48 +03:00
var hex = "";
while (value) {
hex = HexCharacters[value & 0x0f] + hex;
value = Math.floor(value / 16);
}
if (hex.length) {
if (hex.length % 2) {
hex = "0" + hex;
}
return "0x" + hex;
}
return "0x00";
}
if (options.allowMissingPrefix && typeof (value) === "string" && value.substring(0, 2) !== "0x") {
value = "0x" + value;
}
if (isHexable(value)) {
return value.toHexString();
}
if (isHexString(value)) {
2019-09-28 09:36:19 +03:00
if (value.length % 2) {
if (options.hexPad === "left") {
value = "0x0" + value.substring(2);
}
else if (options.hexPad === "right") {
value += "0";
}
else {
logger.throwArgumentError("hex data is odd-length", "value", value);
}
2019-05-15 01:48:48 +03:00
}
return value.toLowerCase();
}
if (isBytes(value)) {
var result = "0x";
for (var i = 0; i < value.length; i++) {
var v = value[i];
result += HexCharacters[(v & 0xf0) >> 4] + HexCharacters[v & 0x0f];
}
return result;
}
2019-08-02 09:10:58 +03:00
return logger.throwArgumentError("invalid hexlify value", "value", value);
2019-05-15 01:48:48 +03:00
}
exports.hexlify = hexlify;
/*
function unoddify(value: BytesLike | Hexable | number): BytesLike | Hexable | number {
if (typeof(value) === "string" && value.length % 2 && value.substring(0, 2) === "0x") {
return "0x0" + value.substring(2);
}
return value;
}
*/
function hexDataLength(data) {
if (typeof (data) !== "string") {
data = hexlify(data);
}
else if (!isHexString(data) || (data.length % 2)) {
return null;
}
return (data.length - 2) / 2;
}
exports.hexDataLength = hexDataLength;
function hexDataSlice(data, offset, endOffset) {
if (typeof (data) !== "string") {
data = hexlify(data);
}
else if (!isHexString(data) || (data.length % 2)) {
2019-08-02 09:10:58 +03:00
logger.throwArgumentError("invalid hexData", "value", data);
2019-05-15 01:48:48 +03:00
}
offset = 2 + 2 * offset;
if (endOffset != null) {
return "0x" + data.substring(offset, 2 + 2 * endOffset);
}
return "0x" + data.substring(offset);
}
exports.hexDataSlice = hexDataSlice;
function hexConcat(items) {
var result = "0x";
items.forEach(function (item) {
result += hexlify(item).substring(2);
});
return result;
}
exports.hexConcat = hexConcat;
function hexValue(value) {
2019-09-28 09:36:19 +03:00
var trimmed = hexStripZeros(hexlify(value, { hexPad: "left" }));
2019-05-15 01:48:48 +03:00
if (trimmed === "0x") {
return "0x0";
}
return trimmed;
}
exports.hexValue = hexValue;
function hexStripZeros(value) {
if (typeof (value) !== "string") {
value = hexlify(value);
}
if (!isHexString(value)) {
2019-08-02 09:10:58 +03:00
logger.throwArgumentError("invalid hex string", "value", value);
2019-05-15 01:48:48 +03:00
}
value = value.substring(2);
var offset = 0;
while (offset < value.length && value[offset] === "0") {
offset++;
}
return "0x" + value.substring(offset);
}
exports.hexStripZeros = hexStripZeros;
function hexZeroPad(value, length) {
if (typeof (value) !== "string") {
value = hexlify(value);
}
else if (!isHexString(value)) {
2019-08-02 09:10:58 +03:00
logger.throwArgumentError("invalid hex string", "value", value);
2019-05-15 01:48:48 +03:00
}
if (value.length > 2 * length + 2) {
2019-08-02 09:10:58 +03:00
logger.throwArgumentError("value out of range", "value", arguments[1]);
2019-05-15 01:48:48 +03:00
}
while (value.length < 2 * length + 2) {
value = "0x0" + value.substring(2);
}
return value;
}
exports.hexZeroPad = hexZeroPad;
function splitSignature(signature) {
var result = {
r: "0x",
s: "0x",
_vs: "0x",
recoveryParam: 0,
v: 0
};
if (isBytesLike(signature)) {
var bytes = arrayify(signature);
if (bytes.length !== 65) {
2019-08-02 09:10:58 +03:00
logger.throwArgumentError("invalid signature string; must be 65 bytes", "signature", signature);
2019-05-15 01:48:48 +03:00
}
// Get the r and s
result.r = hexlify(bytes.slice(0, 32));
result.s = hexlify(bytes.slice(32, 64));
// Reduce v to the canonical 27 or 28
result.v = bytes[64];
if (result.v !== 27 && result.v !== 28) {
result.v = 27 + (result.v % 2);
}
// Compute recoveryParam from v
result.recoveryParam = (result.v - 27);
// Compute _vs from recoveryParam and s
if (result.recoveryParam) {
bytes[32] |= 0x80;
}
result._vs = hexlify(bytes.slice(32, 64));
}
else {
result.r = signature.r;
result.s = signature.s;
result.v = signature.v;
result.recoveryParam = signature.recoveryParam;
result._vs = signature._vs;
// Normalize v into a canonical 27 or 28
if (result.v != null && !(result.v == 27 || result.v == 28)) {
result.v = 27 + (result.v % 2);
}
// Populate a missing v or recoveryParam if possible
if (result.recoveryParam == null && result.v != null) {
result.recoveryParam = 1 - (result.v % 2);
}
else if (result.recoveryParam != null && result.v == null) {
result.v = 27 + result.recoveryParam;
}
else if (result.recoveryParam != null && result.v != null) {
if (result.v !== 27 + result.recoveryParam) {
2019-08-02 09:10:58 +03:00
logger.throwArgumentError("signature v mismatch recoveryParam", "signature", signature);
2019-05-15 01:48:48 +03:00
}
}
// Make sure r and s are padded properly
if (result.r != null) {
result.r = hexZeroPad(result.r, 32);
}
if (result.s != null) {
result.s = hexZeroPad(result.s, 32);
}
// If the _vs is available, use it to populate missing s, v and recoveryParam
// and verify non-missing s, v and recoveryParam
if (result._vs != null) {
result._vs = hexZeroPad(result._vs, 32);
if (result._vs.length > 66) {
2019-08-02 09:10:58 +03:00
logger.throwArgumentError("signature _vs overflow", "signature", signature);
2019-05-15 01:48:48 +03:00
}
var vs = arrayify(result._vs);
var recoveryParam = ((vs[0] >= 128) ? 1 : 0);
var v = 27 + result.recoveryParam;
// Use _vs to compute s
vs[0] &= 0x7f;
var s = hexlify(vs);
// Check _vs aggress with other parameters
if (result.s == null) {
result.s = s;
}
else if (result.s !== s) {
2019-08-02 09:10:58 +03:00
logger.throwArgumentError("signature v mismatch _vs", "signature", signature);
2019-05-15 01:48:48 +03:00
}
if (result.v == null) {
result.v = v;
}
else if (result.v !== v) {
2019-08-02 09:10:58 +03:00
logger.throwArgumentError("signature v mismatch _vs", "signature", signature);
2019-05-15 01:48:48 +03:00
}
if (recoveryParam == null) {
result.recoveryParam = recoveryParam;
}
else if (result.recoveryParam !== recoveryParam) {
2019-08-02 09:10:58 +03:00
logger.throwArgumentError("signature recoveryParam mismatch _vs", "signature", signature);
2019-05-15 01:48:48 +03:00
}
}
// After all populating, both v and recoveryParam are still missing...
if (result.v == null && result.recoveryParam == null) {
2019-08-02 09:10:58 +03:00
logger.throwArgumentError("signature requires at least one of recoveryParam, v or _vs", "signature", signature);
2019-05-15 01:48:48 +03:00
}
// Check for canonical v
if (result.v !== 27 && result.v !== 28) {
2019-08-02 09:10:58 +03:00
logger.throwArgumentError("signature v not canonical", "signature", signature);
2019-05-15 01:48:48 +03:00
}
// Check that r and s are in range
if (result.r.length > 66 || result.s.length > 66) {
2019-08-02 09:10:58 +03:00
logger.throwArgumentError("signature overflow r or s", "signature", signature);
2019-05-15 01:48:48 +03:00
}
if (result._vs == null) {
var vs = arrayify(result.s);
if (vs[0] >= 128) {
2019-08-02 09:10:58 +03:00
logger.throwArgumentError("signature s out of range", "signature", signature);
2019-05-15 01:48:48 +03:00
}
if (result.recoveryParam) {
vs[0] |= 0x80;
}
result._vs = hexlify(vs);
}
}
return result;
}
exports.splitSignature = splitSignature;
function joinSignature(signature) {
signature = splitSignature(signature);
return hexlify(concat([
signature.r,
signature.s,
(signature.recoveryParam ? "0x1c" : "0x1b")
]));
}
exports.joinSignature = joinSignature;