From ea175a390ae7a8e0441a3b3a45d40313ff5684cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dandelion=20Man=C3=A9?= Date: Fri, 19 Jun 2020 11:20:02 -0700 Subject: [PATCH] Add Address.fromRaw (#1871) This adds a fromRaw method to the Address module so that we may properly parse serialized addresses. It has a type signature of `string => Address`, and throws an error if the string is not a valid address. I basically copied the implementation out of the `assertValid` method. I considered deduplicating them (i.e. having `assertValid` simply call `fromRaw`), but `assertValid` had some extra logic around accepting a prefix for the error message, and it felt simpler to simply copy+paste rather than trying to wrap it. Test plan: I added unit tests; `yarn test` passes. --- src/core/address.js | 38 +++++++++++++++++++++++++++++++++++++- src/core/address.test.js | 27 +++++++++++++++++++++++++++ src/core/trie.js | 2 +- 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/core/address.js b/src/core/address.js index c80f705..1039dc3 100644 --- a/src/core/address.js +++ b/src/core/address.js @@ -5,7 +5,7 @@ import deepFreeze from "deep-freeze"; import * as MapUtil from "../util/map"; -export interface AddressModule
{ +export interface AddressModule { /** * Assert at runtime that the provided address is actually a valid * address of this kind, throwing an error if it is not. If `what` is @@ -73,6 +73,19 @@ export interface AddressModule
{ * e.g., `toParts(["ban"])` is not a prefix of `toParts(["banana"])`. */ hasPrefix(address: Address, prefix: Address): boolean; + + /** + * Interpret the provided string as an Address. + * + * Addresses are natively stored as strings. This method verifies + * that the provided "raw" address is actually an Address, so that + * you can have a type-level assurance that a string is an Address. + * + * This is useful if e.g. you are loading serialized Addresses. + * + * Throws an error if the string is not a valid Address. + */ + fromRaw(raw: string): Address; } export type Options = {| @@ -224,6 +237,28 @@ export function makeAddressModule(options: Options): AddressModule { return address.startsWith(prefix); } + function fromRaw(address: string): Address { + if (!address.endsWith(separator)) { + throw new Error( + `address does not end with separator: ${stringify(address)}` + ); + } + if (!address.startsWith(nonceWithSeparator)) { + for (const [ + otherNonceWithSeparator, + otherName, + ] of otherNoncesWithSeparators) { + if (address.startsWith(otherNonceWithSeparator)) { + throw new Error( + `expected ${name}, got ${otherName}: ${stringify(address)}` + ); + } + } + throw new Error(`expected ${name}, got: ${stringify(address)}`); + } + return address; + } + const result = { assertValid, assertValidParts, @@ -233,6 +268,7 @@ export function makeAddressModule(options: Options): AddressModule { toString, append, hasPrefix, + fromRaw, }; return deepFreeze(result); } diff --git a/src/core/address.test.js b/src/core/address.test.js index 6bb7ac9..b624f22 100644 --- a/src/core/address.test.js +++ b/src/core/address.test.js @@ -164,6 +164,33 @@ describe("core/address", () => { }); }); + describe("fromRaw", () => { + const {FooAddress, BarAddress} = makeModules(); + function thunk(x: string) { + return () => FooAddress.fromRaw(x); + } + it("throws on an empty string", () => { + expect(thunk("")).toThrow("address does not end with separator"); + }); + it("throws on a string that doesn't start with the right separator", () => { + expect(thunk("\0")).toThrow("expected FooAddress, got"); + }); + it("throws on a string from a different address module", () => { + expect(thunk(BarAddress.empty)).toThrow( + "expected FooAddress, got BarAddress" + ); + }); + function roundTrip(x) { + expect(FooAddress.fromRaw(x)).toEqual(x); + } + it("works on an empty address", () => { + roundTrip(FooAddress.empty); + }); + it("works on a non-empty address", () => { + roundTrip(FooAddress.fromParts(["foo", "bar"])); + }); + }); + describe("fromParts", () => { const {FooAddress, BarAddress} = makeModules(); diff --git a/src/core/trie.js b/src/core/trie.js index d8f9d7e..ffe10e1 100644 --- a/src/core/trie.js +++ b/src/core/trie.js @@ -13,7 +13,7 @@ const EMPTY_ENTRY_SYMBOL = Symbol("EMPTY"); type Entry = {|+map: RecursiveMap, value: V | typeof EMPTY_ENTRY_SYMBOL|}; type RecursiveMap = Map>; -class BaseTrie { +class BaseTrie { addressModule: AddressModule; entry: Entry;