Browser Compatibility Updates (#107)

* BigInt -> big-integer to support older browsers
* Buffer read/write BigUInt64 removed for browser compatibility.
This commit is contained in:
Nathan Nichols
2020-10-22 15:36:45 -05:00
parent b197897227
commit b43e79a9af
10 changed files with 1288 additions and 1215 deletions

View File

@@ -12,6 +12,7 @@
"test": "test" "test": "test"
}, },
"dependencies": { "dependencies": {
"big-integer": "^1.6.48",
"create-hash": "^1.2.0", "create-hash": "^1.2.0",
"decimal.js": "^10.2.0", "decimal.js": "^10.2.0",
"ripple-address-codec": "^4.1.1" "ripple-address-codec": "^4.1.1"

View File

@@ -9,6 +9,7 @@ import { sha512Half, transactionID } from "./hashes";
import { FieldInstance } from "./enums"; import { FieldInstance } from "./enums";
import { STObject } from "./types/st-object"; import { STObject } from "./types/st-object";
import { JsonObject } from "./types/serialized-type"; import { JsonObject } from "./types/serialized-type";
import * as bigInt from "big-integer";
/** /**
* Construct a BinaryParser * Construct a BinaryParser
@@ -102,9 +103,10 @@ interface ClaimObject extends JsonObject {
* @returns the serialized object with appropriate prefix * @returns the serialized object with appropriate prefix
*/ */
function signingClaimData(claim: ClaimObject): Buffer { function signingClaimData(claim: ClaimObject): Buffer {
const num = bigInt(String(claim.amount));
const prefix = HashPrefix.paymentChannelClaim; const prefix = HashPrefix.paymentChannelClaim;
const channel = coreTypes.Hash256.from(claim.channel).toBytes(); const channel = coreTypes.Hash256.from(claim.channel).toBytes();
const amount = coreTypes.UInt64.from(BigInt(claim.amount)).toBytes(); const amount = coreTypes.UInt64.from(num).toBytes();
const bytesList = new BytesList(); const bytesList = new BytesList();

View File

@@ -10,6 +10,7 @@ import { UInt32 } from "./types/uint-32";
import { UInt8 } from "./types/uint-8"; import { UInt8 } from "./types/uint-8";
import { BinaryParser } from "./serdes/binary-parser"; import { BinaryParser } from "./serdes/binary-parser";
import { JsonObject } from "./types/serialized-type"; import { JsonObject } from "./types/serialized-type";
import * as bigInt from "big-integer";
/** /**
* Computes the hash of a list of objects * Computes the hash of a list of objects
@@ -119,7 +120,7 @@ function accountStateHash(param: Array<JsonObject>): Hash256 {
*/ */
interface ledgerObject { interface ledgerObject {
ledger_index: number; ledger_index: number;
total_coins: string | number | bigint; total_coins: string | number | bigInt.BigInteger;
parent_hash: string; parent_hash: string;
transaction_hash: string; transaction_hash: string;
account_hash: string; account_hash: string;
@@ -142,7 +143,9 @@ function ledgerHash(header: ledgerObject): Hash256 {
assert(header.close_flags !== undefined); assert(header.close_flags !== undefined);
UInt32.from<number>(header.ledger_index).toBytesSink(hash); UInt32.from<number>(header.ledger_index).toBytesSink(hash);
UInt64.from<bigint>(BigInt(header.total_coins)).toBytesSink(hash); UInt64.from<bigInt.BigInteger>(
bigInt(String(header.total_coins))
).toBytesSink(hash);
Hash256.from<string>(header.parent_hash).toBytesSink(hash); Hash256.from<string>(header.parent_hash).toBytesSink(hash);
Hash256.from<string>(header.transaction_hash).toBytesSink(hash); Hash256.from<string>(header.transaction_hash).toBytesSink(hash);
Hash256.from<string>(header.account_hash).toBytesSink(hash); Hash256.from<string>(header.account_hash).toBytesSink(hash);

View File

@@ -1,5 +1,6 @@
import { coreTypes } from "./types"; import { coreTypes } from "./types";
import { Decimal } from "decimal.js"; import { Decimal } from "decimal.js";
import * as bigInt from "big-integer";
/** /**
* class for encoding and decoding quality * class for encoding and decoding quality
@@ -15,7 +16,7 @@ class quality {
const decimal = new Decimal(quality); const decimal = new Decimal(quality);
const exponent = decimal.e - 15; const exponent = decimal.e - 15;
const qualityString = decimal.times(`1e${-exponent}`).abs().toString(); const qualityString = decimal.times(`1e${-exponent}`).abs().toString();
const bytes = coreTypes.UInt64.from(BigInt(qualityString)).toBytes(); const bytes = coreTypes.UInt64.from(bigInt(qualityString)).toBytes();
bytes[0] = exponent + 100; bytes[0] = exponent + 100;
return bytes; return bytes;
} }

View File

@@ -5,6 +5,7 @@ import { BinaryParser } from "../serdes/binary-parser";
import { AccountID } from "./account-id"; import { AccountID } from "./account-id";
import { Currency } from "./currency"; import { Currency } from "./currency";
import { JsonObject, SerializedType } from "./serialized-type"; import { JsonObject, SerializedType } from "./serialized-type";
import * as bigInt from "big-integer";
/** /**
* Constants for validating amounts * Constants for validating amounts
@@ -14,6 +15,7 @@ const MAX_IOU_EXPONENT = 80;
const MAX_IOU_PRECISION = 16; const MAX_IOU_PRECISION = 16;
const MAX_DROPS = new Decimal("1e17"); const MAX_DROPS = new Decimal("1e17");
const MIN_XRP = new Decimal("1e-6"); const MIN_XRP = new Decimal("1e-6");
const mask = bigInt(0x00000000ffffffff);
/** /**
* decimal.js configuration for Amount IOUs * decimal.js configuration for Amount IOUs
@@ -69,12 +71,17 @@ class Amount extends SerializedType {
return value; return value;
} }
const amount = Buffer.alloc(8); let amount = Buffer.alloc(8);
if (typeof value === "string") { if (typeof value === "string") {
Amount.assertXrpIsValid(value); Amount.assertXrpIsValid(value);
const number = BigInt(value); const number = bigInt(value);
amount.writeBigUInt64BE(number);
const intBuf = [Buffer.alloc(4), Buffer.alloc(4)];
intBuf[0].writeUInt32BE(Number(number.shiftRight(32)));
intBuf[1].writeUInt32BE(Number(number.and(mask)));
amount = Buffer.concat(intBuf);
amount[0] |= 0x40; amount[0] |= 0x40;
@@ -92,7 +99,13 @@ class Amount extends SerializedType {
.times(`1e${-(number.e - 15)}`) .times(`1e${-(number.e - 15)}`)
.abs() .abs()
.toString(); .toString();
amount.writeBigUInt64BE(BigInt(integerNumberString));
const num = bigInt(integerNumberString);
const intBuf = [Buffer.alloc(4), Buffer.alloc(4)];
intBuf[0].writeUInt32BE(Number(num.shiftRight(32)));
intBuf[1].writeUInt32BE(Number(num.and(mask)));
amount = Buffer.concat(intBuf);
amount[0] |= 0x80; amount[0] |= 0x80;
@@ -136,9 +149,13 @@ class Amount extends SerializedType {
const bytes = this.bytes; const bytes = this.bytes;
const isPositive = bytes[0] & 0x40; const isPositive = bytes[0] & 0x40;
const sign = isPositive ? "" : "-"; const sign = isPositive ? "" : "-";
bytes[0] &= 0x3f; bytes[0] &= 0x3f;
return `${sign}${bytes.readBigUInt64BE().toString()}`;
const msb = bigInt(bytes.slice(0, 4).readUInt32BE());
const lsb = bigInt(bytes.slice(4).readUInt32BE());
const num = msb.shiftLeft(32).or(lsb);
return `${sign}${num.toString()}`;
} else { } else {
const parser = new BinaryParser(this.toString()); const parser = new BinaryParser(this.toString());
const mantissa = parser.read(8); const mantissa = parser.read(8);

View File

@@ -1,5 +1,6 @@
import { BytesList } from "../serdes/binary-serializer"; import { BytesList } from "../serdes/binary-serializer";
import { BinaryParser } from "../serdes/binary-parser"; import { BinaryParser } from "../serdes/binary-parser";
import * as bigInt from "big-integer";
type JSON = string | number | boolean | null | undefined | JSON[] | JsonObject; type JSON = string | number | boolean | null | undefined | JSON[] | JsonObject;
@@ -20,7 +21,9 @@ class SerializedType {
return this.fromParser(parser, hint); return this.fromParser(parser, hint);
} }
static from(value: SerializedType | JSON | bigint): SerializedType { static from(
value: SerializedType | JSON | bigInt.BigInteger
): SerializedType {
throw new Error("from not implemented"); throw new Error("from not implemented");
return this.from(value); return this.from(value);
} }

View File

@@ -1,7 +1,10 @@
import { UInt } from "./uint"; import { UInt } from "./uint";
import { BinaryParser } from "../serdes/binary-parser"; import { BinaryParser } from "../serdes/binary-parser";
import * as bigInt from "big-integer";
import { isInstance } from "big-integer";
const HEX_REGEX = /^[A-F0-9]{16}$/; const HEX_REGEX = /^[A-F0-9]{16}$/;
const mask = bigInt(0x00000000ffffffff);
/** /**
* Derived UInt class for serializing/deserializing 64 bit UInt * Derived UInt class for serializing/deserializing 64 bit UInt
@@ -23,10 +26,12 @@ class UInt64 extends UInt {
/** /**
* Construct a UInt64 object * Construct a UInt64 object
* *
* @param val A UInt64, hex-string, bigint, or number * @param val A UInt64, hex-string, bigInt, or number
* @returns A UInt64 object * @returns A UInt64 object
*/ */
static from<T extends UInt64 | string | bigint | number>(val: T): UInt64 { static from<T extends UInt64 | string | bigInt.BigInteger | number>(
val: T
): UInt64 {
if (val instanceof UInt64) { if (val instanceof UInt64) {
return val; return val;
} }
@@ -37,8 +42,14 @@ class UInt64 extends UInt {
if (val < 0) { if (val < 0) {
throw new Error("value must be an unsigned integer"); throw new Error("value must be an unsigned integer");
} }
buf.writeBigUInt64BE(BigInt(val));
return new UInt64(buf); const number = bigInt(val);
const intBuf = [Buffer.alloc(4), Buffer.alloc(4)];
intBuf[0].writeUInt32BE(Number(number.shiftRight(32)));
intBuf[1].writeUInt32BE(Number(number.and(mask)));
return new UInt64(Buffer.concat(intBuf));
} }
if (typeof val === "string") { if (typeof val === "string") {
@@ -49,9 +60,12 @@ class UInt64 extends UInt {
return new UInt64(buf); return new UInt64(buf);
} }
if (typeof val === "bigint") { if (isInstance(val)) {
buf.writeBigUInt64BE(val); const intBuf = [Buffer.alloc(4), Buffer.alloc(4)];
return new UInt64(buf); intBuf[0].writeUInt32BE(Number(val.shiftRight(bigInt(32))));
intBuf[1].writeUInt32BE(Number(val.and(mask)));
return new UInt64(Buffer.concat(intBuf));
} }
throw new Error("Cannot construct UInt64 from given value"); throw new Error("Cannot construct UInt64 from given value");
@@ -71,8 +85,10 @@ class UInt64 extends UInt {
* *
* @returns the number represented buy this.bytes * @returns the number represented buy this.bytes
*/ */
valueOf(): bigint { valueOf(): bigInt.BigInteger {
return this.bytes.readBigUInt64BE(); const msb = bigInt(this.bytes.slice(0, 4).readUInt32BE());
const lsb = bigInt(this.bytes.slice(4).readUInt32BE());
return msb.shiftLeft(bigInt(32)).or(lsb);
} }
/** /**

View File

@@ -1,13 +1,17 @@
import * as bigInt from "big-integer";
import { Comparable } from "./serialized-type"; import { Comparable } from "./serialized-type";
/** /**
* Compare numbers and bigints n1 and n2 * Compare numbers and bigInts n1 and n2
* *
* @param n1 First object to compare * @param n1 First object to compare
* @param n2 Second object to compare * @param n2 Second object to compare
* @returns -1, 0, or 1, depending on how the two objects compare * @returns -1, 0, or 1, depending on how the two objects compare
*/ */
function compare(n1: number | bigint, n2: number | bigint): number { function compare(
n1: number | bigInt.BigInteger,
n2: number | bigInt.BigInteger
): number {
return n1 < n2 ? -1 : n1 == n2 ? 0 : 1; return n1 < n2 ? -1 : n1 == n2 ? 0 : 1;
} }
@@ -46,7 +50,7 @@ abstract class UInt extends Comparable {
* *
* @returns the value * @returns the value
*/ */
abstract valueOf(): number | bigint; abstract valueOf(): number | bigInt.BigInteger;
} }
export { UInt }; export { UInt };

View File

@@ -5,6 +5,7 @@ const { encode, decode } = require("../dist");
const { makeParser, BytesList, BinarySerializer } = binary; const { makeParser, BytesList, BinarySerializer } = binary;
const { coreTypes } = require("../dist/types"); const { coreTypes } = require("../dist/types");
const { UInt8, UInt16, UInt32, UInt64, STObject } = coreTypes; const { UInt8, UInt16, UInt32, UInt64, STObject } = coreTypes;
const bigInt = require("big-integer");
const { loadFixture } = require("./utils"); const { loadFixture } = require("./utils");
const fixtures = loadFixture("data-driven-tests.json"); const fixtures = loadFixture("data-driven-tests.json");
@@ -162,7 +163,7 @@ check(UInt64, 0xfeffffff, [0, 0, 0, 0, 254, 255, 255, 255]);
check(UInt64, -1, "throws"); check(UInt64, -1, "throws");
check(UInt64, 0, [0, 0, 0, 0, 0, 0, 0, 0]); check(UInt64, 0, [0, 0, 0, 0, 0, 0, 0, 0]);
check(UInt64, 1, [0, 0, 0, 0, 0, 0, 0, 1]); check(UInt64, 1, [0, 0, 0, 0, 0, 0, 0, 1]);
check(UInt64, BigInt(1), [0, 0, 0, 0, 0, 0, 0, 1]); check(UInt64, bigInt(1), [0, 0, 0, 0, 0, 0, 0, 1]);
function deliverMinTest() { function deliverMinTest() {
test("can serialize DeliverMin", () => { test("can serialize DeliverMin", () => {

File diff suppressed because it is too large Load Diff