mirror of
https://github.com/transatoshi-mw/grin-web-wallet.git
synced 2025-10-06 15:52:47 +00:00
703 lines
18 KiB
JavaScript
Executable File
703 lines
18 KiB
JavaScript
Executable File
// Use strict
|
|
"use strict";
|
|
|
|
|
|
// Classes
|
|
|
|
// Protocol Buffers class
|
|
class ProtocolBuffers {
|
|
|
|
// Public
|
|
|
|
// Decode
|
|
static decode(messageType, data, schema) {
|
|
|
|
// Check if schema for message type isn't known
|
|
if(messageType.toFixed() in schema === false) {
|
|
|
|
// Throw error
|
|
throw "Schema for message type isn't known.";
|
|
}
|
|
|
|
// Get message schema
|
|
var messageSchema = schema[messageType.toFixed()];
|
|
|
|
// Initialize result
|
|
var result = {};
|
|
|
|
// Go through all fields in the data
|
|
for(var i = 0; i < data["length"];) {
|
|
|
|
// Get field tag
|
|
var fieldTag = ProtocolBuffers.decodeVarint(data, i);
|
|
|
|
// Go to start of the field payload
|
|
i += ProtocolBuffers.getVarintLength(data, i);
|
|
|
|
// Check if field tag is too big
|
|
if(fieldTag > Number.MAX_SAFE_INTEGER) {
|
|
|
|
// Throw error
|
|
throw "Field tag is too big.";
|
|
}
|
|
|
|
// Get field number
|
|
var fieldNumber = fieldTag.toNumber() >>> 3;
|
|
|
|
// Get field wire type
|
|
var fieldWireType = fieldTag.toNumber() & 0b111;
|
|
|
|
// Check if field is known in the message schema
|
|
if(fieldNumber.toFixed() in messageSchema === true) {
|
|
|
|
// Check field's expected type
|
|
switch(messageSchema[fieldNumber.toFixed()]["Type"]) {
|
|
|
|
// Uint, bool, enum, or sint
|
|
case ProtocolBuffers.UINT_SCHEMA_DATA_TYPE:
|
|
case ProtocolBuffers.BOOL_SCHEMA_DATA_TYPE:
|
|
case ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE:
|
|
case ProtocolBuffers.SINT_SCHEMA_DATA_TYPE:
|
|
|
|
// Check if field wire type isn't correct
|
|
if(fieldWireType !== ProtocolBuffers.VARINT_WIRE_TYPE) {
|
|
|
|
// Throw error
|
|
throw "Field wire type isn't correct.";
|
|
}
|
|
|
|
// Break
|
|
break;
|
|
|
|
// String or bytes
|
|
case ProtocolBuffers.STRING_SCHEMA_DATA_TYPE:
|
|
case ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE:
|
|
|
|
// Check if field wire type isn't correct
|
|
if(fieldWireType !== ProtocolBuffers.LEN_WIRE_TYPE) {
|
|
|
|
// Throw error
|
|
throw "Field wire type isn't correct.";
|
|
}
|
|
|
|
// Break
|
|
break;
|
|
}
|
|
|
|
// Check if field doesn't exist in the result
|
|
if(messageSchema[fieldNumber.toFixed()]["Name"] in result === false) {
|
|
|
|
// Create field in result
|
|
result[messageSchema[fieldNumber.toFixed()]["Name"]] = [];
|
|
}
|
|
}
|
|
|
|
// Check field wire type
|
|
switch(fieldWireType) {
|
|
|
|
// Varint
|
|
case ProtocolBuffers.VARINT_WIRE_TYPE:
|
|
|
|
// Check if field is known in the message schema
|
|
if(fieldNumber.toFixed() in messageSchema === true) {
|
|
|
|
// Get value from the field payload
|
|
var value = ProtocolBuffers.decodeVarint(data, i);
|
|
|
|
// Check field's expected type
|
|
switch(messageSchema[fieldNumber.toFixed()]["Type"]) {
|
|
|
|
// Uint
|
|
case ProtocolBuffers.UINT_SCHEMA_DATA_TYPE:
|
|
|
|
// Append value to result
|
|
result[messageSchema[fieldNumber.toFixed()]["Name"]].push(value);
|
|
|
|
// Break
|
|
break;
|
|
|
|
// Bool
|
|
case ProtocolBuffers.BOOL_SCHEMA_DATA_TYPE:
|
|
|
|
// Append value as a boolean to result
|
|
result[messageSchema[fieldNumber.toFixed()]["Name"]].push(value.isZero() === false);
|
|
|
|
// Break
|
|
break;
|
|
|
|
// Enum
|
|
case ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE:
|
|
|
|
// Check if value is too big
|
|
if(value > Number.MAX_SAFE_INTEGER) {
|
|
|
|
// Throw error
|
|
throw "Value is too big.";
|
|
}
|
|
|
|
// Append value as an enum to result
|
|
result[messageSchema[fieldNumber.toFixed()]["Name"]].push(value.toNumber());
|
|
|
|
// Break
|
|
break;
|
|
|
|
// Sint
|
|
case ProtocolBuffers.SINT_SCHEMA_DATA_TYPE:
|
|
|
|
// Get if value is even
|
|
if(value.modulo(2).isZero() === true) {
|
|
|
|
// Append value as a positive number to result
|
|
result[messageSchema[fieldNumber.toFixed()]["Name"]].push(value.dividedToIntegerBy(2));
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Append value as a negative number to result
|
|
result[messageSchema[fieldNumber.toFixed()]["Name"]].push(value.plus(1).dividedToIntegerBy(-2));
|
|
}
|
|
|
|
// Break
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Go to next field
|
|
i += ProtocolBuffers.getVarintLength(data, i);
|
|
|
|
// Break
|
|
break;
|
|
|
|
// I64
|
|
case ProtocolBuffers.I64_WIRE_TYPE:
|
|
|
|
// Check if field payload doesn't contain data
|
|
if(i + Common.BYTES_IN_A_UINT64 > data["length"]) {
|
|
|
|
// Throw error
|
|
throw "Field payload doesn't contain data.";
|
|
}
|
|
|
|
// Go to next field
|
|
i += Common.BYTES_IN_A_UINT64;
|
|
|
|
// Break
|
|
break;
|
|
|
|
// Len
|
|
case ProtocolBuffers.LEN_WIRE_TYPE:
|
|
|
|
// Get length from the field payload
|
|
var length = ProtocolBuffers.decodeVarint(data, i);
|
|
|
|
// Go to the field payload's data
|
|
i += ProtocolBuffers.getVarintLength(data, i);
|
|
|
|
// Check if length is too big
|
|
if(length > Number.MAX_SAFE_INTEGER) {
|
|
|
|
// Throw error
|
|
throw "Length is too big.";
|
|
}
|
|
|
|
// Check if field payload doesn't contain data
|
|
if(i + length.toNumber() > data["length"]) {
|
|
|
|
// Throw error
|
|
throw "Field payload doesn't contain data.";
|
|
}
|
|
|
|
// Check if field is known in the message schema
|
|
if(fieldNumber.toFixed() in messageSchema === true) {
|
|
|
|
// Get value from the field payload's data
|
|
var value = data.subarray(i, i + length.toNumber());
|
|
|
|
// Check field's expected type
|
|
switch(messageSchema[fieldNumber.toFixed()]["Type"]) {
|
|
|
|
// String
|
|
case ProtocolBuffers.STRING_SCHEMA_DATA_TYPE:
|
|
|
|
// Append value as a string to result
|
|
result[messageSchema[fieldNumber.toFixed()]["Name"]].push((new TextDecoder("utf-8", {"fatal": true})).decode(value));
|
|
|
|
// Break
|
|
break;
|
|
|
|
// Bytes
|
|
case ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE:
|
|
|
|
// Append value to result
|
|
result[messageSchema[fieldNumber.toFixed()]["Name"]].push(value);
|
|
|
|
// Break
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Go to next field
|
|
i += length.toNumber();
|
|
|
|
// Break
|
|
break;
|
|
|
|
// I32
|
|
case ProtocolBuffers.I32_WIRE_TYPE:
|
|
|
|
// Check if field payload doesn't contain data
|
|
if(i + Common.BYTES_IN_A_UINT32 > data["length"]) {
|
|
|
|
// Throw error
|
|
throw "Field payload doesn't contain data.";
|
|
}
|
|
|
|
// Go to next field
|
|
i += Common.BYTES_IN_A_UINT32;
|
|
|
|
// Break
|
|
break;
|
|
|
|
// Default
|
|
default:
|
|
|
|
// Throw error
|
|
throw "Field wire type isn't known.";
|
|
}
|
|
}
|
|
|
|
// Return result
|
|
return result;
|
|
}
|
|
|
|
// Encode
|
|
static encode(messageType, data, schema) {
|
|
|
|
// Check if schema for message type isn't known
|
|
if(messageType.toFixed() in schema === false) {
|
|
|
|
// Throw error
|
|
throw "Schema for message type isn't known.";
|
|
}
|
|
|
|
// Get message schema
|
|
var messageSchema = schema[messageType.toFixed()];
|
|
|
|
// Initialize result
|
|
var result = new Uint8Array([]);
|
|
|
|
// Go through all values in the data
|
|
for(var name in data) {
|
|
|
|
if(data.hasOwnProperty(name) === true) {
|
|
|
|
// Initialize value found
|
|
var valueFound = false;
|
|
|
|
// Go through all fields in the message schema
|
|
for(var fieldNumber in messageSchema) {
|
|
|
|
if(messageSchema.hasOwnProperty(fieldNumber) === true) {
|
|
|
|
// Check if field is for the value
|
|
if(messageSchema[fieldNumber]["Name"] === name) {
|
|
|
|
// Set value found
|
|
valueFound = true;
|
|
|
|
// Check field's type
|
|
switch(messageSchema[fieldNumber]["Type"]) {
|
|
|
|
// Uint
|
|
case ProtocolBuffers.UINT_SCHEMA_DATA_TYPE:
|
|
|
|
// Check if value type isn't correct
|
|
if(data[name] instanceof BigNumber === false) {
|
|
|
|
// Throw error
|
|
throw "Value's type isn't correct.";
|
|
}
|
|
|
|
// Set field wire type
|
|
var fieldWireType = ProtocolBuffers.VARINT_WIRE_TYPE;
|
|
|
|
// Set field payload
|
|
var fieldPayload = ProtocolBuffers.encodeVarint(data[name]);
|
|
|
|
// Break
|
|
break;
|
|
|
|
// Bool
|
|
case ProtocolBuffers.BOOL_SCHEMA_DATA_TYPE:
|
|
|
|
// Check if value type isn't correct
|
|
if(typeof data[name] !== "boolean") {
|
|
|
|
// Throw error
|
|
throw "Value's type isn't correct.";
|
|
}
|
|
|
|
// Set field wire type
|
|
var fieldWireType = ProtocolBuffers.VARINT_WIRE_TYPE;
|
|
|
|
// Set field payload
|
|
var fieldPayload = ProtocolBuffers.encodeVarint(new BigNumber((data[name] === true) ? 1 : 0));
|
|
|
|
// Break
|
|
break;
|
|
|
|
// Enum
|
|
case ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE:
|
|
|
|
// Check if value type isn't correct
|
|
if(typeof data[name] !== "number") {
|
|
|
|
// Throw error
|
|
throw "Value's type isn't correct.";
|
|
}
|
|
|
|
// Set field wire type
|
|
var fieldWireType = ProtocolBuffers.VARINT_WIRE_TYPE;
|
|
|
|
// Set field payload
|
|
var fieldPayload = ProtocolBuffers.encodeVarint(new BigNumber(data[name]));
|
|
|
|
// Break
|
|
break;
|
|
|
|
// String
|
|
case ProtocolBuffers.STRING_SCHEMA_DATA_TYPE:
|
|
|
|
// Check if value's type isn't correct
|
|
if(typeof data[name] !== "string") {
|
|
|
|
// Throw error
|
|
throw "Value's type isn't correct.";
|
|
}
|
|
|
|
// Set field wire type
|
|
var fieldWireType = ProtocolBuffers.LEN_WIRE_TYPE;
|
|
|
|
// Set field payload
|
|
var fieldPayload = Common.mergeArrays([
|
|
|
|
// Length
|
|
ProtocolBuffers.encodeVarint(new BigNumber(data[name]["length"])),
|
|
|
|
// Data
|
|
(new TextEncoder()).encode(data[name])
|
|
]);
|
|
|
|
// Break
|
|
break;
|
|
|
|
// Bytes
|
|
case ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE:
|
|
|
|
// Check if value's type isn't correct
|
|
if(data[name] instanceof Uint8Array === false) {
|
|
|
|
// Throw error
|
|
throw "Value's type isn't correct.";
|
|
}
|
|
|
|
// Check if no data exists and data is optional
|
|
if(data[name]["length"] === 0 && "Optional" in messageSchema[fieldNumber] === true && messageSchema[fieldNumber]["Optional"] === true) {
|
|
|
|
// Clear value found
|
|
valueFound = false;
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Set field wire type
|
|
var fieldWireType = ProtocolBuffers.LEN_WIRE_TYPE;
|
|
|
|
// Set field payload
|
|
var fieldPayload = Common.mergeArrays([
|
|
|
|
// Length
|
|
ProtocolBuffers.encodeVarint(new BigNumber(data[name]["length"])),
|
|
|
|
// Data
|
|
data[name]
|
|
]);
|
|
}
|
|
|
|
// Break
|
|
break;
|
|
|
|
// Sint
|
|
case ProtocolBuffers.SINT_SCHEMA_DATA_TYPE:
|
|
|
|
// Check if value type isn't correct
|
|
if(data[name] instanceof BigNumber === false) {
|
|
|
|
// Throw error
|
|
throw "Value's type isn't correct.";
|
|
}
|
|
|
|
// Set field wire type
|
|
var fieldWireType = ProtocolBuffers.VARINT_WIRE_TYPE;
|
|
|
|
// Check if value is positive
|
|
if(data[name].isPositive() === true) {
|
|
|
|
// Set field payload
|
|
var fieldPayload = ProtocolBuffers.encodeVarint(data[name].multipliedBy(2));
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Set field payload
|
|
var fieldPayload = ProtocolBuffers.encodeVarint(data[name].multipliedBy(-2).minus(1));
|
|
}
|
|
|
|
// Break
|
|
break;
|
|
}
|
|
|
|
// Break
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if value was found in message schema
|
|
if(valueFound === true) {
|
|
|
|
// Set field tag
|
|
var fieldTag = ProtocolBuffers.encodeVarint(new BigNumber((fieldNumber << 3) | fieldWireType));
|
|
|
|
// Append field tag and field payload to the result
|
|
result = Common.mergeArrays([
|
|
|
|
// Result
|
|
result,
|
|
|
|
// Field tag
|
|
fieldTag,
|
|
|
|
// Field payload
|
|
fieldPayload
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return result
|
|
return result;
|
|
}
|
|
|
|
// Uint schema data type
|
|
static get UINT_SCHEMA_DATA_TYPE() {
|
|
|
|
// Return uint data type
|
|
return 0;
|
|
}
|
|
|
|
// Bool schema data type
|
|
static get BOOL_SCHEMA_DATA_TYPE() {
|
|
|
|
// Return bool data type
|
|
return ProtocolBuffers.UINT_SCHEMA_DATA_TYPE + 1;
|
|
}
|
|
|
|
// Enum schema data type
|
|
static get ENUM_SCHEMA_DATA_TYPE() {
|
|
|
|
// Return enum data type
|
|
return ProtocolBuffers.BOOL_SCHEMA_DATA_TYPE + 1;
|
|
}
|
|
|
|
// String schema data type
|
|
static get STRING_SCHEMA_DATA_TYPE() {
|
|
|
|
// Return string data type
|
|
return ProtocolBuffers.ENUM_SCHEMA_DATA_TYPE + 1;
|
|
}
|
|
|
|
// Bytes schema data type
|
|
static get BYTES_SCHEMA_DATA_TYPE() {
|
|
|
|
// Return bytes data type
|
|
return ProtocolBuffers.STRING_SCHEMA_DATA_TYPE + 1;
|
|
}
|
|
|
|
// Sint schema data type
|
|
static get SINT_SCHEMA_DATA_TYPE() {
|
|
|
|
// Return sint data type
|
|
return ProtocolBuffers.BYTES_SCHEMA_DATA_TYPE + 1;
|
|
}
|
|
|
|
// Private
|
|
|
|
// Decode varint
|
|
static decodeVarint(data, offset) {
|
|
|
|
// Initialize payloads
|
|
var payloads = [];
|
|
|
|
// Loop through all bytes in the varint
|
|
do {
|
|
|
|
// Check if data doesn't contain a varint
|
|
if(offset >= data["length"]) {
|
|
|
|
// Throw error
|
|
throw "Data doesn't contain a varint.";
|
|
}
|
|
|
|
// Get if continuation bit is set
|
|
var continuationBitSet = (data[offset] & 0b10000000) !== 0;
|
|
|
|
// Append payload to list
|
|
payloads.unshift(data[offset] & 0b01111111);
|
|
|
|
// Increment offset
|
|
++offset;
|
|
|
|
} while(continuationBitSet === true);
|
|
|
|
// Create bit writer
|
|
var bitWriter = new BitWriter();
|
|
|
|
// Add padding bits to bit writer
|
|
bitWriter.setBits(0, payloads["length"] % Common.BITS_IN_A_BYTE);
|
|
|
|
// Go through all payloads
|
|
for(var i = 0; i < payloads["length"]; ++i) {
|
|
|
|
// Add payload to the bit writer
|
|
bitWriter.setBits(payloads[i], 7);
|
|
}
|
|
|
|
// Return number value of the bit writer's bytes
|
|
return new BigNumber(Common.HEX_PREFIX + Common.toHexString(bitWriter.getBytes()));
|
|
}
|
|
|
|
// Get varint length
|
|
static getVarintLength(data, offset) {
|
|
|
|
// Initialize length
|
|
var length = 0;
|
|
|
|
// Loop through all bytes in the varint
|
|
do {
|
|
|
|
// Check if data doesn't contain a varint
|
|
if(offset + length >= data["length"]) {
|
|
|
|
// Throw error
|
|
throw "Data doesn't contain a varint.";
|
|
}
|
|
|
|
// Get if continuation bit is set
|
|
var continuationBitSet = (data[offset + length] & 0b10000000) !== 0;
|
|
|
|
// Increment length
|
|
++length;
|
|
|
|
} while(continuationBitSet === true);
|
|
|
|
// Return length
|
|
return length;
|
|
}
|
|
|
|
// Encode varint
|
|
static encodeVarint(value) {
|
|
|
|
// Get value as bytes
|
|
var bytes = value.toBytes(BigNumber.BIG_ENDIAN);
|
|
|
|
// Initialize result
|
|
var result = (new Uint8Array((bytes["length"] > 1 || bytes[0] >= 0b10000000) ? Math.ceil(bytes["length"] * Common.BITS_IN_A_BYTE / 7) : bytes["length"])).fill(0);
|
|
|
|
// Initialize bit reader with the bytes
|
|
var bitReader = new BitReader(bytes);
|
|
|
|
// Go through all bytes in the result
|
|
for(var i = result["length"] - 1; i >= 0; --i) {
|
|
|
|
// Set byte's continuation bit
|
|
result[i] |= (i === result["length"] - 1) ? 0b00000000 : 0b10000000;
|
|
|
|
// Check if bytes can be encoded in one payload
|
|
if(bytes["length"] === 1 && bytes[0] < 0b10000000) {
|
|
|
|
// Set byte's payload
|
|
result[i] |= bitReader.getBits(8);
|
|
}
|
|
|
|
// Otherwise check at the ending byte and a fewer than seven bits remain
|
|
else if(i === result["length"] - 1 && bytes["length"] * Common.BITS_IN_A_BYTE % 7 !== 0) {
|
|
|
|
// Set byte's payload
|
|
result[i] |= bitReader.getBits(bytes["length"] * Common.BITS_IN_A_BYTE % 7);
|
|
}
|
|
|
|
// Otherwise
|
|
else {
|
|
|
|
// Set byte's payload
|
|
result[i] |= bitReader.getBits(7);
|
|
}
|
|
}
|
|
|
|
// Return result
|
|
return result;
|
|
}
|
|
|
|
// Varint wire type
|
|
static get VARINT_WIRE_TYPE() {
|
|
|
|
// Return varint wire type
|
|
return 0;
|
|
}
|
|
|
|
// I64 wire type
|
|
static get I64_WIRE_TYPE() {
|
|
|
|
// Return i64 wire type
|
|
return ProtocolBuffers.VARINT_WIRE_TYPE + 1;
|
|
}
|
|
|
|
// Len wire type
|
|
static get LEN_WIRE_TYPE() {
|
|
|
|
// Return len wire type
|
|
return ProtocolBuffers.I64_WIRE_TYPE + 1;
|
|
}
|
|
|
|
// Sgroup wire type
|
|
static get SGROUP_WIRE_TYPE() {
|
|
|
|
// Return sgroup wire type
|
|
return ProtocolBuffers.LEN_WIRE_TYPE + 1;
|
|
}
|
|
|
|
// Egroup wire type
|
|
static get EGROUP_WIRE_TYPE() {
|
|
|
|
// Return egroup wire type
|
|
return ProtocolBuffers.SGROUP_WIRE_TYPE + 1;
|
|
}
|
|
|
|
// I32 wire type
|
|
static get I32_WIRE_TYPE() {
|
|
|
|
// Return i32 wire type
|
|
return ProtocolBuffers.EGROUP_WIRE_TYPE + 1;
|
|
}
|
|
}
|
|
|
|
|
|
// Main function
|
|
|
|
// Set global object's Protocol Buffers
|
|
globalThis["ProtocolBuffers"] = ProtocolBuffers;
|