From 85f4bae3c48f59ff9a2d3ab1f227bb79d69df0a9 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 11 Feb 2021 23:46:33 +0100 Subject: [PATCH 1/7] Create dedicated dates module --- .../src/adaptors/v0-33/hasher.spec.ts | 2 +- .../src/adaptors/v0-33/responses.ts | 2 +- packages/tendermint-rpc/src/dates.spec.ts | 41 ++++++++++++++++ packages/tendermint-rpc/src/dates.ts | 23 +++++++++ packages/tendermint-rpc/src/encodings.spec.ts | 47 +------------------ packages/tendermint-rpc/src/encodings.ts | 20 +------- packages/tendermint-rpc/src/index.ts | 10 +--- packages/tendermint-rpc/src/responses.ts | 3 +- packages/tendermint-rpc/src/types.ts | 6 +-- 9 files changed, 74 insertions(+), 80 deletions(-) create mode 100644 packages/tendermint-rpc/src/dates.spec.ts create mode 100644 packages/tendermint-rpc/src/dates.ts diff --git a/packages/tendermint-rpc/src/adaptors/v0-33/hasher.spec.ts b/packages/tendermint-rpc/src/adaptors/v0-33/hasher.spec.ts index cc3081b2..a9f0c4c5 100644 --- a/packages/tendermint-rpc/src/adaptors/v0-33/hasher.spec.ts +++ b/packages/tendermint-rpc/src/adaptors/v0-33/hasher.spec.ts @@ -1,7 +1,7 @@ import { fromBase64, fromHex } from "@cosmjs/encoding"; import { ReadonlyDate } from "readonly-date"; -import { ReadonlyDateWithNanoseconds } from "../../types"; +import { ReadonlyDateWithNanoseconds } from "../../dates"; import { hashBlock, hashTx } from "./hasher"; describe("Hasher", () => { diff --git a/packages/tendermint-rpc/src/adaptors/v0-33/responses.ts b/packages/tendermint-rpc/src/adaptors/v0-33/responses.ts index 93d17ec8..7c4cdb22 100644 --- a/packages/tendermint-rpc/src/adaptors/v0-33/responses.ts +++ b/packages/tendermint-rpc/src/adaptors/v0-33/responses.ts @@ -3,6 +3,7 @@ import { fromBase64, fromHex } from "@cosmjs/encoding"; import { JsonRpcSuccessResponse } from "@cosmjs/json-rpc"; import { assert } from "@cosmjs/utils"; +import { DateTime } from "../../dates"; import { assertArray, assertBoolean, @@ -11,7 +12,6 @@ import { assertObject, assertSet, assertString, - DateTime, dictionaryToStringMap, Integer, may, diff --git a/packages/tendermint-rpc/src/dates.spec.ts b/packages/tendermint-rpc/src/dates.spec.ts new file mode 100644 index 00000000..9b80b851 --- /dev/null +++ b/packages/tendermint-rpc/src/dates.spec.ts @@ -0,0 +1,41 @@ +import { ReadonlyDate } from "readonly-date"; + +import { DateTime } from "./dates"; + +describe("dates", () => { + describe("DateTime", () => { + it("decodes a string", () => { + expect(DateTime.decode("2020-12-15T10:57:26.778Z").nanoseconds).toEqual(0); + expect(DateTime.decode("2020-12-15T10:57:26.7789Z").nanoseconds).toEqual(900000); + expect(DateTime.decode("2020-12-15T10:57:26.77809Z").nanoseconds).toEqual(90000); + expect(DateTime.decode("2020-12-15T10:57:26.778009Z").nanoseconds).toEqual(9000); + expect(DateTime.decode("2020-12-15T10:57:26.7780009Z").nanoseconds).toEqual(900); + expect(DateTime.decode("2020-12-15T10:57:26.77800009Z").nanoseconds).toEqual(90); + expect(DateTime.decode("2020-12-15T10:57:26.778000009Z").nanoseconds).toEqual(9); + }); + + it("encodes a string", () => { + const date1 = new ReadonlyDate("2020-12-15T10:57:26.778Z"); + (date1 as any).nanoseconds = 0; + expect(DateTime.encode(date1)).toEqual("2020-12-15T10:57:26.778000000Z"); + const date2 = new ReadonlyDate("2020-12-15T10:57:26.778Z"); + (date2 as any).nanoseconds = 900000; + expect(DateTime.encode(date2)).toEqual("2020-12-15T10:57:26.778900000Z"); + const date3 = new ReadonlyDate("2020-12-15T10:57:26.778Z"); + (date3 as any).nanoseconds = 90000; + expect(DateTime.encode(date3)).toEqual("2020-12-15T10:57:26.778090000Z"); + const date4 = new ReadonlyDate("2020-12-15T10:57:26.778Z"); + (date4 as any).nanoseconds = 9000; + expect(DateTime.encode(date4)).toEqual("2020-12-15T10:57:26.778009000Z"); + const date5 = new ReadonlyDate("2020-12-15T10:57:26.778Z"); + (date5 as any).nanoseconds = 900; + expect(DateTime.encode(date5)).toEqual("2020-12-15T10:57:26.778000900Z"); + const date6 = new ReadonlyDate("2020-12-15T10:57:26.778Z"); + (date6 as any).nanoseconds = 90; + expect(DateTime.encode(date6)).toEqual("2020-12-15T10:57:26.778000090Z"); + const date7 = new ReadonlyDate("2020-12-15T10:57:26.778Z"); + (date7 as any).nanoseconds = 9; + expect(DateTime.encode(date7)).toEqual("2020-12-15T10:57:26.778000009Z"); + }); + }); +}); diff --git a/packages/tendermint-rpc/src/dates.ts b/packages/tendermint-rpc/src/dates.ts new file mode 100644 index 00000000..fe32dc51 --- /dev/null +++ b/packages/tendermint-rpc/src/dates.ts @@ -0,0 +1,23 @@ +import { fromRfc3339 } from "@cosmjs/encoding"; +import { ReadonlyDate } from "readonly-date"; + +export interface ReadonlyDateWithNanoseconds extends ReadonlyDate { + /* Nanoseconds after the time stored in a vanilla ReadonlyDate (millisecond granularity) */ + readonly nanoseconds?: number; +} + +export class DateTime { + public static decode(dateTimeString: string): ReadonlyDateWithNanoseconds { + const readonlyDate = fromRfc3339(dateTimeString); + const nanosecondsMatch = dateTimeString.match(/\.(\d+)Z$/); + const nanoseconds = nanosecondsMatch ? nanosecondsMatch[1].slice(3) : ""; + (readonlyDate as any).nanoseconds = parseInt(nanoseconds.padEnd(6, "0"), 10); + return readonlyDate as ReadonlyDateWithNanoseconds; + } + + public static encode(dateTime: ReadonlyDateWithNanoseconds): string { + const millisecondIso = dateTime.toISOString(); + const nanoseconds = dateTime.nanoseconds?.toString() ?? ""; + return `${millisecondIso.slice(0, -1)}${nanoseconds.padStart(6, "0")}Z`; + } +} diff --git a/packages/tendermint-rpc/src/encodings.spec.ts b/packages/tendermint-rpc/src/encodings.spec.ts index 88750e68..afea0600 100644 --- a/packages/tendermint-rpc/src/encodings.spec.ts +++ b/packages/tendermint-rpc/src/encodings.spec.ts @@ -1,53 +1,8 @@ import { ReadonlyDate } from "readonly-date"; -import { - DateTime, - encodeBlockId, - encodeBytes, - encodeInt, - encodeString, - encodeTime, - encodeVersion, -} from "./encodings"; -import { ReadonlyDateWithNanoseconds } from "./types"; +import { encodeBlockId, encodeBytes, encodeInt, encodeString, encodeTime, encodeVersion } from "./encodings"; describe("encodings", () => { - describe("DateTime", () => { - it("decodes a string", () => { - expect(DateTime.decode("2020-12-15T10:57:26.778Z").nanoseconds).toEqual(0); - expect(DateTime.decode("2020-12-15T10:57:26.7789Z").nanoseconds).toEqual(900000); - expect(DateTime.decode("2020-12-15T10:57:26.77809Z").nanoseconds).toEqual(90000); - expect(DateTime.decode("2020-12-15T10:57:26.778009Z").nanoseconds).toEqual(9000); - expect(DateTime.decode("2020-12-15T10:57:26.7780009Z").nanoseconds).toEqual(900); - expect(DateTime.decode("2020-12-15T10:57:26.77800009Z").nanoseconds).toEqual(90); - expect(DateTime.decode("2020-12-15T10:57:26.778000009Z").nanoseconds).toEqual(9); - }); - - it("encodes a string", () => { - const date1 = new ReadonlyDate("2020-12-15T10:57:26.778Z") as ReadonlyDateWithNanoseconds; - (date1 as any).nanoseconds = 0; - expect(DateTime.encode(date1)).toEqual("2020-12-15T10:57:26.778000000Z"); - const date2 = new ReadonlyDate("2020-12-15T10:57:26.778Z") as ReadonlyDateWithNanoseconds; - (date2 as any).nanoseconds = 900000; - expect(DateTime.encode(date2)).toEqual("2020-12-15T10:57:26.778900000Z"); - const date3 = new ReadonlyDate("2020-12-15T10:57:26.778Z") as ReadonlyDateWithNanoseconds; - (date3 as any).nanoseconds = 90000; - expect(DateTime.encode(date3)).toEqual("2020-12-15T10:57:26.778090000Z"); - const date4 = new ReadonlyDate("2020-12-15T10:57:26.778Z") as ReadonlyDateWithNanoseconds; - (date4 as any).nanoseconds = 9000; - expect(DateTime.encode(date4)).toEqual("2020-12-15T10:57:26.778009000Z"); - const date5 = new ReadonlyDate("2020-12-15T10:57:26.778Z") as ReadonlyDateWithNanoseconds; - (date5 as any).nanoseconds = 900; - expect(DateTime.encode(date5)).toEqual("2020-12-15T10:57:26.778000900Z"); - const date6 = new ReadonlyDate("2020-12-15T10:57:26.778Z") as ReadonlyDateWithNanoseconds; - (date6 as any).nanoseconds = 90; - expect(DateTime.encode(date6)).toEqual("2020-12-15T10:57:26.778000090Z"); - const date7 = new ReadonlyDate("2020-12-15T10:57:26.778Z") as ReadonlyDateWithNanoseconds; - (date7 as any).nanoseconds = 9; - expect(DateTime.encode(date7)).toEqual("2020-12-15T10:57:26.778000009Z"); - }); - }); - describe("encodeString", () => { it("works", () => { expect(encodeString("")).toEqual(Uint8Array.from([0])); diff --git a/packages/tendermint-rpc/src/encodings.ts b/packages/tendermint-rpc/src/encodings.ts index 59f6e042..ef7727ee 100644 --- a/packages/tendermint-rpc/src/encodings.ts +++ b/packages/tendermint-rpc/src/encodings.ts @@ -1,8 +1,8 @@ -import { fromRfc3339, toUtf8 } from "@cosmjs/encoding"; +import { toUtf8 } from "@cosmjs/encoding"; import { Int53 } from "@cosmjs/math"; +import { ReadonlyDateWithNanoseconds } from "./dates"; import { BlockId, Version } from "./responses"; -import { ReadonlyDateWithNanoseconds } from "./types"; /** * A runtime checker that ensures a given value is set (i.e. not undefined or null) @@ -156,22 +156,6 @@ export class Integer { } } -export class DateTime { - public static decode(dateTimeString: string): ReadonlyDateWithNanoseconds { - const readonlyDate = fromRfc3339(dateTimeString); - const nanosecondsMatch = dateTimeString.match(/\.(\d+)Z$/); - const nanoseconds = nanosecondsMatch ? nanosecondsMatch[1].slice(3) : ""; - (readonlyDate as any).nanoseconds = parseInt(nanoseconds.padEnd(6, "0"), 10); - return readonlyDate as ReadonlyDateWithNanoseconds; - } - - public static encode(dateTime: ReadonlyDateWithNanoseconds): string { - const millisecondIso = dateTime.toISOString(); - const nanoseconds = dateTime.nanoseconds?.toString() ?? ""; - return `${millisecondIso.slice(0, -1)}${nanoseconds.padStart(6, "0")}Z`; - } -} - // Encodings needed for hashing block headers // Several of these functions are inspired by https://github.com/nomic-io/js-tendermint/blob/tendermint-0.30/src/ diff --git a/packages/tendermint-rpc/src/index.ts b/packages/tendermint-rpc/src/index.ts index df736fb1..a099ee1f 100644 --- a/packages/tendermint-rpc/src/index.ts +++ b/packages/tendermint-rpc/src/index.ts @@ -1,7 +1,7 @@ export { Adaptor } from "./adaptor"; export { adaptor33, adaptor34 } from "./adaptors"; export { Client } from "./client"; -export { DateTime } from "./encodings"; +export { DateTime, ReadonlyDateWithNanoseconds } from "./dates"; export { AbciInfoRequest, AbciQueryParams, @@ -72,10 +72,4 @@ export { VoteType, } from "./responses"; export { HttpClient, WebsocketClient } from "./rpcclients"; // TODO: Why do we export those outside of this package? -export { - BlockIdFlag, - CommitSignature, - ReadonlyDateWithNanoseconds, - ValidatorEd25519Pubkey, - ValidatorPubkey, -} from "./types"; +export { BlockIdFlag, CommitSignature, ValidatorEd25519Pubkey, ValidatorPubkey } from "./types"; diff --git a/packages/tendermint-rpc/src/responses.ts b/packages/tendermint-rpc/src/responses.ts index 83e7ab65..5706726a 100644 --- a/packages/tendermint-rpc/src/responses.ts +++ b/packages/tendermint-rpc/src/responses.ts @@ -1,6 +1,7 @@ import { ReadonlyDate } from "readonly-date"; -import { CommitSignature, ReadonlyDateWithNanoseconds, ValidatorPubkey } from "./types"; +import { ReadonlyDateWithNanoseconds } from "./dates"; +import { CommitSignature, ValidatorPubkey } from "./types"; export type Response = | AbciInfoResponse diff --git a/packages/tendermint-rpc/src/types.ts b/packages/tendermint-rpc/src/types.ts index c71a10f7..1c62851c 100644 --- a/packages/tendermint-rpc/src/types.ts +++ b/packages/tendermint-rpc/src/types.ts @@ -1,11 +1,7 @@ // Types in this file are exported outside of the @cosmjs/tendermint-rpc package, // e.g. as part of a request or response -import { ReadonlyDate } from "readonly-date"; -export interface ReadonlyDateWithNanoseconds extends ReadonlyDate { - /* Nanoseconds after the time stored in a vanilla ReadonlyDate (millisecond granularity) */ - readonly nanoseconds?: number; -} +import { ReadonlyDateWithNanoseconds } from "./dates"; export interface ValidatorEd25519Pubkey { readonly algorithm: "ed25519"; From 96685b359782b7e7baa3433e5a7aec66143c9b9c Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 11 Feb 2021 23:54:20 +0100 Subject: [PATCH 2/7] Add fromRfc3339WithNanoseconds --- packages/tendermint-rpc/src/dates.spec.ts | 12 +++++++++++- packages/tendermint-rpc/src/dates.ts | 21 ++++++++++++++++----- packages/tendermint-rpc/src/index.ts | 2 +- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/packages/tendermint-rpc/src/dates.spec.ts b/packages/tendermint-rpc/src/dates.spec.ts index 9b80b851..fa857109 100644 --- a/packages/tendermint-rpc/src/dates.spec.ts +++ b/packages/tendermint-rpc/src/dates.spec.ts @@ -1,8 +1,18 @@ import { ReadonlyDate } from "readonly-date"; -import { DateTime } from "./dates"; +import { DateTime, fromRfc3339WithNanoseconds } from "./dates"; describe("dates", () => { + it("fromRfc3339WithNanoseconds", () => { + expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.778Z").nanoseconds).toEqual(0); + expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.7789Z").nanoseconds).toEqual(900000); + expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.77809Z").nanoseconds).toEqual(90000); + expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.778009Z").nanoseconds).toEqual(9000); + expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.7780009Z").nanoseconds).toEqual(900); + expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.77800009Z").nanoseconds).toEqual(90); + expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.778000009Z").nanoseconds).toEqual(9); + }); + describe("DateTime", () => { it("decodes a string", () => { expect(DateTime.decode("2020-12-15T10:57:26.778Z").nanoseconds).toEqual(0); diff --git a/packages/tendermint-rpc/src/dates.ts b/packages/tendermint-rpc/src/dates.ts index fe32dc51..e99e7968 100644 --- a/packages/tendermint-rpc/src/dates.ts +++ b/packages/tendermint-rpc/src/dates.ts @@ -6,13 +6,24 @@ export interface ReadonlyDateWithNanoseconds extends ReadonlyDate { readonly nanoseconds?: number; } +export interface DateWithNanoseconds extends Date { + /** Nanoseconds after the time stored in a vanilla Date (millisecond granularity) */ + nanoseconds?: number; +} + +export function fromRfc3339WithNanoseconds(dateTimeString: string): DateWithNanoseconds { + // fromRfc3339 should give the caller a regular Date directly + const out: DateWithNanoseconds = new Date(fromRfc3339(dateTimeString).getTime()); + const nanosecondsMatch = dateTimeString.match(/\.(\d+)Z$/); + const nanoseconds = nanosecondsMatch ? nanosecondsMatch[1].slice(3) : ""; + out.nanoseconds = parseInt(nanoseconds.padEnd(6, "0"), 10); + return out; +} + export class DateTime { + /** @deprecated Use fromRfc3339WithNanoseconds instead */ public static decode(dateTimeString: string): ReadonlyDateWithNanoseconds { - const readonlyDate = fromRfc3339(dateTimeString); - const nanosecondsMatch = dateTimeString.match(/\.(\d+)Z$/); - const nanoseconds = nanosecondsMatch ? nanosecondsMatch[1].slice(3) : ""; - (readonlyDate as any).nanoseconds = parseInt(nanoseconds.padEnd(6, "0"), 10); - return readonlyDate as ReadonlyDateWithNanoseconds; + return fromRfc3339WithNanoseconds(dateTimeString); } public static encode(dateTime: ReadonlyDateWithNanoseconds): string { diff --git a/packages/tendermint-rpc/src/index.ts b/packages/tendermint-rpc/src/index.ts index a099ee1f..17f2f367 100644 --- a/packages/tendermint-rpc/src/index.ts +++ b/packages/tendermint-rpc/src/index.ts @@ -1,7 +1,7 @@ export { Adaptor } from "./adaptor"; export { adaptor33, adaptor34 } from "./adaptors"; export { Client } from "./client"; -export { DateTime, ReadonlyDateWithNanoseconds } from "./dates"; +export { DateTime, ReadonlyDateWithNanoseconds, fromRfc3339WithNanoseconds } from "./dates"; export { AbciInfoRequest, AbciQueryParams, From 539b3292c9fcbbf26751d5940feaef2bdc77c9b0 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Thu, 11 Feb 2021 23:56:16 +0100 Subject: [PATCH 3/7] Use fromRfc3339WithNanoseconds --- .../tendermint-rpc/src/adaptors/v0-33/responses.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/tendermint-rpc/src/adaptors/v0-33/responses.ts b/packages/tendermint-rpc/src/adaptors/v0-33/responses.ts index 7c4cdb22..9182fa60 100644 --- a/packages/tendermint-rpc/src/adaptors/v0-33/responses.ts +++ b/packages/tendermint-rpc/src/adaptors/v0-33/responses.ts @@ -3,7 +3,7 @@ import { fromBase64, fromHex } from "@cosmjs/encoding"; import { JsonRpcSuccessResponse } from "@cosmjs/json-rpc"; import { assert } from "@cosmjs/utils"; -import { DateTime } from "../../dates"; +import { fromRfc3339WithNanoseconds } from "../../dates"; import { assertArray, assertBoolean, @@ -328,7 +328,7 @@ function decodeHeader(data: RpcHeader): responses.Header { version: decodeBlockVersion(data.version), chainId: assertNotEmpty(data.chain_id), height: Integer.parse(assertNotEmpty(data.height)), - time: DateTime.decode(assertNotEmpty(data.time)), + time: fromRfc3339WithNanoseconds(assertNotEmpty(data.time)), lastBlockId: decodeBlockId(data.last_block_id), @@ -417,7 +417,7 @@ function decodeCommitSignature(data: RpcSignature): CommitSignature { return { blockIdFlag: decodeBlockIdFlag(data.block_id_flag), validatorAddress: fromHex(data.validator_address), - timestamp: DateTime.decode(assertNotEmpty(data.timestamp)), + timestamp: fromRfc3339WithNanoseconds(assertNotEmpty(data.timestamp)), signature: fromBase64(assertNotEmpty(data.signature)), }; } @@ -488,7 +488,7 @@ interface GenesisResult { function decodeGenesis(data: RpcGenesisResponse): responses.GenesisResponse { return { - genesisTime: DateTime.decode(assertNotEmpty(data.genesis_time)), + genesisTime: fromRfc3339WithNanoseconds(assertNotEmpty(data.genesis_time)), chainId: assertNotEmpty(data.chain_id), consensusParams: decodeConsensusParams(data.consensus_params), validators: data.validators ? assertArray(data.validators).map(decodeValidatorGenesis) : [], @@ -568,7 +568,7 @@ function decodeSyncInfo(data: RpcSyncInfo): responses.SyncInfo { return { latestBlockHash: fromHex(assertNotEmpty(data.latest_block_hash)), latestAppHash: fromHex(assertNotEmpty(data.latest_app_hash)), - latestBlockTime: DateTime.decode(assertNotEmpty(data.latest_block_time)), + latestBlockTime: fromRfc3339WithNanoseconds(assertNotEmpty(data.latest_block_time)), latestBlockHeight: Integer.parse(assertNotEmpty(data.latest_block_height)), catchingUp: assertBoolean(data.catching_up), }; From 4223b5242d85c678d84f79253a76ab21e2d474da Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Fri, 12 Feb 2021 00:03:47 +0100 Subject: [PATCH 4/7] Add toRfc3339WithNanoseconds --- packages/tendermint-rpc/src/dates.spec.ts | 39 ++++++++++++++++++++++- packages/tendermint-rpc/src/dates.ts | 11 +++++-- packages/tendermint-rpc/src/index.ts | 7 +++- 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/packages/tendermint-rpc/src/dates.spec.ts b/packages/tendermint-rpc/src/dates.spec.ts index fa857109..4d028692 100644 --- a/packages/tendermint-rpc/src/dates.spec.ts +++ b/packages/tendermint-rpc/src/dates.spec.ts @@ -1,6 +1,6 @@ import { ReadonlyDate } from "readonly-date"; -import { DateTime, fromRfc3339WithNanoseconds } from "./dates"; +import { DateTime, DateWithNanoseconds, fromRfc3339WithNanoseconds, toRfc3339WithNanoseconds } from "./dates"; describe("dates", () => { it("fromRfc3339WithNanoseconds", () => { @@ -13,6 +13,43 @@ describe("dates", () => { expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.778000009Z").nanoseconds).toEqual(9); }); + describe("toRfc3339WithNanoseconds", () => { + it("works", () => { + const date1 = new ReadonlyDate("2020-12-15T10:57:26.778Z"); + (date1 as any).nanoseconds = 0; + expect(toRfc3339WithNanoseconds(date1)).toEqual("2020-12-15T10:57:26.778000000Z"); + const date2 = new ReadonlyDate("2020-12-15T10:57:26.778Z"); + (date2 as any).nanoseconds = 900000; + expect(toRfc3339WithNanoseconds(date2)).toEqual("2020-12-15T10:57:26.778900000Z"); + const date3 = new ReadonlyDate("2020-12-15T10:57:26.778Z"); + (date3 as any).nanoseconds = 90000; + expect(toRfc3339WithNanoseconds(date3)).toEqual("2020-12-15T10:57:26.778090000Z"); + const date4 = new ReadonlyDate("2020-12-15T10:57:26.778Z"); + (date4 as any).nanoseconds = 9000; + expect(toRfc3339WithNanoseconds(date4)).toEqual("2020-12-15T10:57:26.778009000Z"); + const date5 = new ReadonlyDate("2020-12-15T10:57:26.778Z"); + (date5 as any).nanoseconds = 900; + expect(toRfc3339WithNanoseconds(date5)).toEqual("2020-12-15T10:57:26.778000900Z"); + const date6 = new ReadonlyDate("2020-12-15T10:57:26.778Z"); + (date6 as any).nanoseconds = 90; + expect(toRfc3339WithNanoseconds(date6)).toEqual("2020-12-15T10:57:26.778000090Z"); + const date7 = new ReadonlyDate("2020-12-15T10:57:26.778Z"); + (date7 as any).nanoseconds = 9; + expect(toRfc3339WithNanoseconds(date7)).toEqual("2020-12-15T10:57:26.778000009Z"); + }); + + it("works for DateWithNanoseconds", () => { + const date1: DateWithNanoseconds = new Date("2020-12-15T10:57:26.778Z"); + date1.nanoseconds = 1; + expect(toRfc3339WithNanoseconds(date1)).toEqual("2020-12-15T10:57:26.778000001Z"); + }); + + it("works for Date", () => { + const date1 = new Date("2020-12-15T10:57:26.778Z"); + expect(toRfc3339WithNanoseconds(date1)).toEqual("2020-12-15T10:57:26.778000000Z"); + }); + }); + describe("DateTime", () => { it("decodes a string", () => { expect(DateTime.decode("2020-12-15T10:57:26.778Z").nanoseconds).toEqual(0); diff --git a/packages/tendermint-rpc/src/dates.ts b/packages/tendermint-rpc/src/dates.ts index e99e7968..e4928fc8 100644 --- a/packages/tendermint-rpc/src/dates.ts +++ b/packages/tendermint-rpc/src/dates.ts @@ -20,15 +20,20 @@ export function fromRfc3339WithNanoseconds(dateTimeString: string): DateWithNano return out; } +export function toRfc3339WithNanoseconds(dateTime: ReadonlyDateWithNanoseconds): string { + const millisecondIso = dateTime.toISOString(); + const nanoseconds = dateTime.nanoseconds?.toString() ?? ""; + return `${millisecondIso.slice(0, -1)}${nanoseconds.padStart(6, "0")}Z`; +} + export class DateTime { /** @deprecated Use fromRfc3339WithNanoseconds instead */ public static decode(dateTimeString: string): ReadonlyDateWithNanoseconds { return fromRfc3339WithNanoseconds(dateTimeString); } + /** @deprecated Use toRfc3339WithNanoseconds instead */ public static encode(dateTime: ReadonlyDateWithNanoseconds): string { - const millisecondIso = dateTime.toISOString(); - const nanoseconds = dateTime.nanoseconds?.toString() ?? ""; - return `${millisecondIso.slice(0, -1)}${nanoseconds.padStart(6, "0")}Z`; + return toRfc3339WithNanoseconds(dateTime); } } diff --git a/packages/tendermint-rpc/src/index.ts b/packages/tendermint-rpc/src/index.ts index 17f2f367..c6584a41 100644 --- a/packages/tendermint-rpc/src/index.ts +++ b/packages/tendermint-rpc/src/index.ts @@ -1,7 +1,12 @@ export { Adaptor } from "./adaptor"; export { adaptor33, adaptor34 } from "./adaptors"; export { Client } from "./client"; -export { DateTime, ReadonlyDateWithNanoseconds, fromRfc3339WithNanoseconds } from "./dates"; +export { + DateTime, + ReadonlyDateWithNanoseconds, + fromRfc3339WithNanoseconds, + toRfc3339WithNanoseconds, +} from "./dates"; export { AbciInfoRequest, AbciQueryParams, From a23461be11395dcf2731d87d13b54b5239f2fec4 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Fri, 12 Feb 2021 00:04:31 +0100 Subject: [PATCH 5/7] Fix testing structure --- packages/tendermint-rpc/src/dates.spec.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/tendermint-rpc/src/dates.spec.ts b/packages/tendermint-rpc/src/dates.spec.ts index 4d028692..dfdf0b94 100644 --- a/packages/tendermint-rpc/src/dates.spec.ts +++ b/packages/tendermint-rpc/src/dates.spec.ts @@ -3,14 +3,16 @@ import { ReadonlyDate } from "readonly-date"; import { DateTime, DateWithNanoseconds, fromRfc3339WithNanoseconds, toRfc3339WithNanoseconds } from "./dates"; describe("dates", () => { - it("fromRfc3339WithNanoseconds", () => { - expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.778Z").nanoseconds).toEqual(0); - expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.7789Z").nanoseconds).toEqual(900000); - expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.77809Z").nanoseconds).toEqual(90000); - expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.778009Z").nanoseconds).toEqual(9000); - expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.7780009Z").nanoseconds).toEqual(900); - expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.77800009Z").nanoseconds).toEqual(90); - expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.778000009Z").nanoseconds).toEqual(9); + describe("fromRfc3339WithNanoseconds", () => { + it("works", () => { + expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.778Z").nanoseconds).toEqual(0); + expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.7789Z").nanoseconds).toEqual(900000); + expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.77809Z").nanoseconds).toEqual(90000); + expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.778009Z").nanoseconds).toEqual(9000); + expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.7780009Z").nanoseconds).toEqual(900); + expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.77800009Z").nanoseconds).toEqual(90); + expect(fromRfc3339WithNanoseconds("2020-12-15T10:57:26.778000009Z").nanoseconds).toEqual(9); + }); }); describe("toRfc3339WithNanoseconds", () => { From 7e83910617143bd340ef5bfd3f2a3ae9dbd106b1 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Fri, 12 Feb 2021 14:28:50 +0100 Subject: [PATCH 6/7] Deprecate DateTime --- CHANGELOG.md | 5 +++++ packages/cosmwasm-stargate/src/cosmwasmclient.ts | 4 ++-- packages/stargate/src/stargateclient.ts | 4 ++-- packages/tendermint-rpc/src/dates.ts | 1 + 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dceef247..b33d6101 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,11 @@ - @cosmjs/launchpad-ledger: Renamed to @cosmjs/ledger-amino. - @cosmjs/ledger-amino: `LedgerSigner.sign` method renamed `signAmino`. +### Deprecated + +- @cosmjs/tendermint-rpc: Deprecate `DateTime` in favour of the free functions + `fromRfc3339WithNanoseconds` and `toRfc3339WithNanoseconds`. + ## 0.23.2 (2021-01-06) ### Security diff --git a/packages/cosmwasm-stargate/src/cosmwasmclient.ts b/packages/cosmwasm-stargate/src/cosmwasmclient.ts index 8b15dc71..d5d267c0 100644 --- a/packages/cosmwasm-stargate/src/cosmwasmclient.ts +++ b/packages/cosmwasm-stargate/src/cosmwasmclient.ts @@ -35,7 +35,7 @@ import { adaptor34, broadcastTxCommitSuccess, Client as TendermintClient, - DateTime, + toRfc3339WithNanoseconds, } from "@cosmjs/tendermint-rpc"; import { assert } from "@cosmjs/utils"; @@ -114,7 +114,7 @@ export class CosmWasmClient { }, height: response.block.header.height, chainId: response.block.header.chainId, - time: DateTime.encode(response.block.header.time), + time: toRfc3339WithNanoseconds(response.block.header.time), }, txs: response.block.txs, }; diff --git a/packages/stargate/src/stargateclient.ts b/packages/stargate/src/stargateclient.ts index bc26a5a7..8b6c5b4b 100644 --- a/packages/stargate/src/stargateclient.ts +++ b/packages/stargate/src/stargateclient.ts @@ -15,7 +15,7 @@ import { adaptor34, broadcastTxCommitSuccess, Client as TendermintClient, - DateTime, + toRfc3339WithNanoseconds, } from "@cosmjs/tendermint-rpc"; import { assert, assertDefinedAndNotNull } from "@cosmjs/utils"; import Long from "long"; @@ -185,7 +185,7 @@ export class StargateClient { }, height: response.block.header.height, chainId: response.block.header.chainId, - time: DateTime.encode(response.block.header.time), + time: toRfc3339WithNanoseconds(response.block.header.time), }, txs: response.block.txs, }; diff --git a/packages/tendermint-rpc/src/dates.ts b/packages/tendermint-rpc/src/dates.ts index e4928fc8..24199e4a 100644 --- a/packages/tendermint-rpc/src/dates.ts +++ b/packages/tendermint-rpc/src/dates.ts @@ -26,6 +26,7 @@ export function toRfc3339WithNanoseconds(dateTime: ReadonlyDateWithNanoseconds): return `${millisecondIso.slice(0, -1)}${nanoseconds.padStart(6, "0")}Z`; } +/** @deprecated Use fromRfc3339WithNanoseconds/toRfc3339WithNanoseconds instead */ export class DateTime { /** @deprecated Use fromRfc3339WithNanoseconds instead */ public static decode(dateTimeString: string): ReadonlyDateWithNanoseconds { From da9459553e9eec4eda2d701fe3febcbdf8430d23 Mon Sep 17 00:00:00 2001 From: Simon Warta Date: Fri, 12 Feb 2021 14:37:24 +0100 Subject: [PATCH 7/7] Let fromRfc3339 return mutable Date type --- CHANGELOG.md | 3 +++ packages/encoding/src/rfc3339.ts | 7 +++---- packages/tendermint-rpc/src/dates.ts | 3 +-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b33d6101..68cce41c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,9 @@ ### Changed +- @cosmjs/encoding: Change return type of `fromRfc3339` from `ReadonlyDate` to + `Date` as the caller becomes the owner of the object and can safely mutate it + in any way. - @cosmjs/launchpad-ledger: Renamed to @cosmjs/ledger-amino. - @cosmjs/ledger-amino: `LedgerSigner.sign` method renamed `signAmino`. diff --git a/packages/encoding/src/rfc3339.ts b/packages/encoding/src/rfc3339.ts index 1b58fe14..180ee995 100644 --- a/packages/encoding/src/rfc3339.ts +++ b/packages/encoding/src/rfc3339.ts @@ -7,7 +7,7 @@ function padded(integer: number, length = 2): string { return filled.substring(filled.length - length); } -export function fromRfc3339(str: string): ReadonlyDate { +export function fromRfc3339(str: string): Date { const matches = rfc3339Matcher.exec(str); if (!matches) { throw new Error("Date string is not in RFC3339 format"); @@ -40,9 +40,8 @@ export function fromRfc3339(str: string): ReadonlyDate { const tzOffset = tzOffsetSign * (tzOffsetHours * 60 + tzOffsetMinutes) * 60; // seconds - return new ReadonlyDate( - ReadonlyDate.UTC(year, month - 1, day, hour, minute, second, milliSeconds) - tzOffset * 1000, - ); + const timestamp = Date.UTC(year, month - 1, day, hour, minute, second, milliSeconds) - tzOffset * 1000; + return new Date(timestamp); } export function toRfc3339(date: Date | ReadonlyDate): string { diff --git a/packages/tendermint-rpc/src/dates.ts b/packages/tendermint-rpc/src/dates.ts index 24199e4a..4a15ccfc 100644 --- a/packages/tendermint-rpc/src/dates.ts +++ b/packages/tendermint-rpc/src/dates.ts @@ -12,8 +12,7 @@ export interface DateWithNanoseconds extends Date { } export function fromRfc3339WithNanoseconds(dateTimeString: string): DateWithNanoseconds { - // fromRfc3339 should give the caller a regular Date directly - const out: DateWithNanoseconds = new Date(fromRfc3339(dateTimeString).getTime()); + const out: DateWithNanoseconds = fromRfc3339(dateTimeString); const nanosecondsMatch = dateTimeString.match(/\.(\d+)Z$/); const nanoseconds = nanosecondsMatch ? nanosecondsMatch[1].slice(3) : ""; out.nanoseconds = parseInt(nanoseconds.padEnd(6, "0"), 10);