/*
* Copyright ConsenSys Software Inc.
*
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
*
* http://mozilla.org/MPL/2.0/
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: MPL-2.0
*/
/* eslint-disable */
const assert = require("assert");
const ethUtils = require("ethereumjs-util");
const { BN, rlp } = ethUtils;
const _typeof =
typeof Symbol === "function" && typeof Symbol.iterator === "symbol"
? function(obj) {
return typeof obj;
}
: function(obj) {
return obj &&
typeof Symbol === "function" &&
obj.constructor === Symbol &&
obj !== Symbol.prototype
? "symbol"
: typeof obj;
};
/**
* Attempts to turn a value into a `Buffer`. As input it supports `Buffer`, `String`, `Number`, null/undefined, `BN` and other objects with a `toArray()` method.
* @param {*} v the value
*/
function toBuffer(v) {
if (!Buffer.isBuffer(v)) {
if (Array.isArray(v)) {
v = Buffer.from(v);
} else if (typeof v === "string") {
if (ethUtils.isHexString(v)) {
v = Buffer.from(ethUtils.padToEven(ethUtils.stripHexPrefix(v)), "hex");
} else if (v === "restricted" || v === "unrestricted") {
// handle restriction field
v = Buffer.from(v);
} else if (
v.match(
/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/
)
) {
// handle base64 strings, i.e. privacyGroupId
// ^ # Start of input
// ([0-9a-zA-Z+/]{4})* # Groups of 4 valid characters decode
// # to 24 bits of data for each group
// ( # Either ending with:
// ([0-9a-zA-Z+/]{2}==) # two valid characters followed by ==
// | # , or
// ([0-9a-zA-Z+/]{3}=) # three valid characters followed by =
// )? # , or nothing
// $ # End of input
v = Buffer.from(v, "base64");
} else {
throw new Error(
`Cannot convert string to buffer. toBuffer only supports 0x-prefixed hex strings and this string was given: ${v}`
);
}
} else if (typeof v === "number") {
v = ethUtils.intToBuffer(v);
} else if (v === null || v === undefined) {
v = Buffer.allocUnsafe(0);
} else if (BN.isBN(v)) {
v = v.toArrayLike(Buffer);
} else if (v.toArray) {
// converts a BN to a Buffer
v = Buffer.from(v.toArray());
} else {
throw new Error("invalid type");
}
}
return v;
}
/**
* Defines properties on a `Object`. It make the assumption that underlying data is binary.
* @param {Object} self the `Object` to define properties on
* @param {Array} fields an array fields to define. Fields can contain:
* * `name` - the name of the properties
* * `length` - the number of bytes the field can have
* * `allowLess` - if the field can be less than the length
* * `allowEmpty`
* @param {*} data data to be validated against the definitions
*/
function defineProperties(self, fields, data) {
self.raw = [];
self._fields = [];
// attach the `toJSON`
self.toJSON = function(label) {
if (label) {
const obj = {};
self._fields.forEach(function(field) {
obj[field] = `0x${self[field].toString("hex")}`;
});
return obj;
}
return ethUtils.baToJSON(this.raw);
};
self.serialize = function serialize() {
// handle privacyGroupId and privateFor
const arr = self.raw.slice();
if (self.raw[10][0].length !== 0 && self.raw[11].length === 32) {
throw Error(
"privacyGroupId and privateFor fields are mutually exclusive"
);
}
if (self.raw[11].length === 32) {
arr.splice(10, 1);
} else {
arr.splice(11, 1);
}
return rlp.encode(arr);
};
fields.forEach(function(field, i) {
self._fields.push(field.name);
function getter() {
return self.raw[i];
}
function setter(v) {
// handle array of Buffer
if (field.bufferArray) {
v = v.map(toBuffer);
} else {
v = toBuffer(v);
}
if (v.toString("hex") === "00" && !field.allowZero) {
v = Buffer.allocUnsafe(0);
}
if (field.allowLess && field.length) {
v = ethUtils.stripZeros(v);
assert(
field.length >= v.length,
`The field ${field.name} must not have more ${field.length} bytes`
);
} else if (!(field.allowZero && v.length === 0) && field.length) {
assert(
field.length === v.length,
`The field ${field.name} must have byte length of ${field.length}`
);
}
self.raw[i] = v;
}
Object.defineProperty(self, field.name, {
enumerable: true,
configurable: true,
get: getter,
set: setter
});
if (field.default) {
self[field.name] = field.default;
}
// attach alias
if (field.alias) {
Object.defineProperty(self, field.alias, {
enumerable: false,
configurable: true,
set: setter,
get: getter
});
}
});
// if the constuctor is passed data
if (data) {
if (typeof data === "string") {
data = Buffer.from(ethUtils.stripHexPrefix(data), "hex");
}
if (Buffer.isBuffer(data)) {
data = rlp.decode(data);
}
if (Array.isArray(data)) {
// handle array elements
if (data.length === 12) {
if (data[10].constructor === Array) {
data.splice(11, 0, "");
} else {
data.splice(10, 0, []);
}
}
if (data.length !== self._fields.length) {
throw new Error("wrong number of fields in data");
}
// make sure all the items are buffers
data.forEach(function(d, i) {
// handle array of Buffer
let v;
if (fields[i].bufferArray) {
v = d.map(toBuffer);
} else {
v = toBuffer(d);
}
self[self._fields[i]] = v;
});
} else if (
(typeof data === "undefined" ? "undefined" : _typeof(data)) === "object"
) {
const keys = Object.keys(data);
fields.forEach(function(field) {
if (keys.indexOf(field.name) !== -1)
self[field.name] = data[field.name];
if (keys.indexOf(field.alias) !== -1)
self[field.alias] = data[field.alias];
});
} else {
throw new Error("invalid data");
}
}
}
module.exports = {
...ethUtils,
toBuffer,
defineProperties
};