From 7e7f6ec3649704fa3f921b0666fc9e0f93e3ee76 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Tue, 4 Aug 2020 16:37:14 +0200 Subject: [PATCH] encoding: Add limit parameters to Bech32.encode and .decode --- packages/encoding/src/bech32.spec.ts | 67 +++++++++++++++++++++++++--- packages/encoding/src/bech32.ts | 11 +++-- packages/encoding/types/bech32.d.ts | 3 +- 3 files changed, 70 insertions(+), 11 deletions(-) diff --git a/packages/encoding/src/bech32.spec.ts b/packages/encoding/src/bech32.spec.ts index 59e6d2c531..da9e6f8290 100644 --- a/packages/encoding/src/bech32.spec.ts +++ b/packages/encoding/src/bech32.spec.ts @@ -6,14 +6,69 @@ describe("Bech32", () => { // bech32 -e -h eth 9d4e856e572e442f0a4b2763e72d08a0e99d8ded const ethAddressRaw = fromHex("9d4e856e572e442f0a4b2763e72d08a0e99d8ded"); - it("encodes", () => { - expect(Bech32.encode("eth", ethAddressRaw)).toEqual("eth1n48g2mjh9ezz7zjtya37wtgg5r5emr0drkwlgw"); + describe("encode", () => { + it("works", () => { + expect(Bech32.encode("eth", ethAddressRaw)).toEqual("eth1n48g2mjh9ezz7zjtya37wtgg5r5emr0drkwlgw"); + }); + + it("works for very short data", () => { + expect(() => Bech32.encode("eth", new Uint8Array(1))).not.toThrow(); + }); + + it("works for very long prefixes", () => { + expect(() => Bech32.encode("p".repeat(70), new Uint8Array(20))).toThrowError(/exceeds length limit/i); + }); + + // See https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#Bech32 + it("works if result is 90 characters", () => { + const result = Bech32.encode("eth", new Uint8Array(50)); + expect(result.length).toEqual(90); + }); + + it("throws if result exceeds 90 characters", () => { + expect(() => Bech32.encode("eth", new Uint8Array(51))).toThrowError(/exceeds length limit/i); + }); + + it("works if a limit parameter is provided", () => { + const limit = 1024; + const result = Bech32.encode("eth", new Uint8Array(51), limit); + expect(result).toEqual( + "eth1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqug55er", + ); + expect(result.length).toBeGreaterThan(90); + }); + + it("throws if result exceeds the provided limit parameter", () => { + const limit = 10; + expect(() => Bech32.encode("eth", ethAddressRaw, limit)).toThrowError(/exceeds length limit/i); + }); }); - it("decodes", () => { - expect(Bech32.decode("eth1n48g2mjh9ezz7zjtya37wtgg5r5emr0drkwlgw")).toEqual({ - prefix: "eth", - data: ethAddressRaw, + describe("decode", () => { + it("works", () => { + expect(Bech32.decode("eth1n48g2mjh9ezz7zjtya37wtgg5r5emr0drkwlgw")).toEqual({ + prefix: "eth", + data: ethAddressRaw, + }); + }); + + it("works for addresses which exceed the specification limit of 90 characters", () => { + // Example from https://github.com/cosmos/cosmos-sdk/pull/6237#issuecomment-658116534 + expect(() => + Bech32.decode( + "cosmospub1ytql0csgqvfzd666axrjzqmn5q2ucztcyxw8hvlzen94ay05tegaerkug5pn3xn8wqdymt598ufzd666axrjzqsxllmwacap3f6xyc4x30jl8ecrcs2tze3zzgxkmthcsqxnqxhwwgfzd666axrjzqs2rlu3wz5gnslgpprszjr8r65n0d6y39q657th77eyvengtk3z0y6h2pnk", + ), + ).not.toThrow(); + }); + + it("throws for addresses which exceed the specification limit of 90 characters if a limit is specified", () => { + // Example from https://github.com/cosmos/cosmos-sdk/pull/6237#issuecomment-658116534 + expect(() => + Bech32.decode( + "cosmospub1ytql0csgqvfzd666axrjzqmn5q2ucztcyxw8hvlzen94ay05tegaerkug5pn3xn8wqdymt598ufzd666axrjzqsxllmwacap3f6xyc4x30jl8ecrcs2tze3zzgxkmthcsqxnqxhwwgfzd666axrjzqs2rlu3wz5gnslgpprszjr8r65n0d6y39q657th77eyvengtk3z0y6h2pnk", + 90, + ), + ).toThrowError(/exceeds length limit/i); }); }); }); diff --git a/packages/encoding/src/bech32.ts b/packages/encoding/src/bech32.ts index 38fedde429..94d606813e 100644 --- a/packages/encoding/src/bech32.ts +++ b/packages/encoding/src/bech32.ts @@ -1,13 +1,16 @@ import * as bech32 from "bech32"; export class Bech32 { - public static encode(prefix: string, data: Uint8Array): string { - const address = bech32.encode(prefix, bech32.toWords(data)); + public static encode(prefix: string, data: Uint8Array, limit?: number): string { + const address = bech32.encode(prefix, bech32.toWords(data), limit); return address; } - public static decode(address: string): { readonly prefix: string; readonly data: Uint8Array } { - const decodedAddress = bech32.decode(address); + public static decode( + address: string, + limit = Infinity, + ): { readonly prefix: string; readonly data: Uint8Array } { + const decodedAddress = bech32.decode(address, limit); return { prefix: decodedAddress.prefix, data: new Uint8Array(bech32.fromWords(decodedAddress.words)), diff --git a/packages/encoding/types/bech32.d.ts b/packages/encoding/types/bech32.d.ts index 8ec29b94ae..9ddd6388df 100644 --- a/packages/encoding/types/bech32.d.ts +++ b/packages/encoding/types/bech32.d.ts @@ -1,7 +1,8 @@ export declare class Bech32 { - static encode(prefix: string, data: Uint8Array): string; + static encode(prefix: string, data: Uint8Array, limit?: number): string; static decode( address: string, + limit?: number, ): { readonly prefix: string; readonly data: Uint8Array;