UNPKG

@test-org122/hypernet-core

Version:

Hypernet Core. Represents the SDK for running the Hypernet Protocol.

142 lines (123 loc) 5.56 kB
import { Result, HexString } from "@interfaces/objects"; import { InvalidParametersError, InvalidPaymentIdError } from "@interfaces/objects/errors"; import { EPaymentType } from "@interfaces/types"; import { IPaymentIdUtils } from "@interfaces/utilities"; import { ethers } from "ethers"; import { err, ok } from "neverthrow"; /** * An abstract class for creating & converting payment IDs, as well as verifying * correctness, and extracting information from the ID (such as type, domain, UUID) * * A paymentID is a 64-length hexadecimal string: * characters 0-19: domain (encoded as ascii text --> hex) * characters 20-32: type (encoded as ascii text --> hex) * characters 32-63: UUID (encoded as hex) */ export class PaymentIdUtils implements IPaymentIdUtils { /** * Returns an ascii representation of the domain portion of the paymentID string. * (characters 0-19 of the paymentIdString) * @param paymentIdString */ public getDomain(paymentIdString: HexString): Result<string, InvalidPaymentIdError> { const paymentIdValidRes = this.isValidPaymentId(paymentIdString); if (paymentIdValidRes.isErr() || !paymentIdValidRes.value) { return err(new InvalidPaymentIdError(`Not a valid paymentId: '${paymentIdString}'`)); } const domainHex = paymentIdString.substr(2, 20); const domain = Buffer.from(domainHex, "hex").toString("ascii"); return ok(domain.trim()); } /** * Returns an ascii representation of the type portion of the paymentID string. * (characters 20-31 of the paymentIdString) * @param paymentIdString */ public getType(paymentIdString: HexString): Result<EPaymentType, InvalidPaymentIdError> { const paymentIdValidRes = this.isValidPaymentId(paymentIdString); if (paymentIdValidRes.isErr() || !paymentIdValidRes.value) { return err(new InvalidPaymentIdError(`Not a valid paymentId: '${paymentIdString}'`)); } const typeHex = paymentIdString.substr(22, 12); const type = Buffer.from(typeHex, "hex").toString("ascii"); const trimmedType = type.trim(); if (trimmedType === EPaymentType.Pull) { return ok(EPaymentType.Pull); } if (trimmedType === EPaymentType.Push) { return ok(EPaymentType.Push); } return err(new InvalidPaymentIdError(`Type did not correspond to a known EPaymentType, got '${type}'`)); } /** * Returns the UUID portion of the paymentID string. * (characters 32-63 of the paymentIdString) * @param paymentIdString */ public getUUID(paymentIdString: HexString): Result<string, InvalidPaymentIdError> { const paymentIdValidRes = this.isValidPaymentId(paymentIdString); if (paymentIdValidRes.isErr() || !paymentIdValidRes.value) { return err(new InvalidPaymentIdError(`Not a valid paymentId: '${paymentIdString}'`)); } const UUID = paymentIdString.substr(34, 32); return ok(UUID); } /** * A valid payment ID is exactly 64 characters, hexadecimal, refixed with 0x. * @param paymentIdString */ public isValidPaymentId(paymentIdString: HexString): Result<boolean, InvalidParametersError> { const overallRegex = /^0x[0-9A-Fa-f]{64}$/; return ok(overallRegex.test(paymentIdString)); // TODO: Uses ethers library, may be better than regex return ok(ethers.utils.isHexString(paymentIdString) && ethers.utils.hexDataLength(paymentIdString) == 64); } /** * Given domain, type, and uuid, returns the computed paymentId * @param domain Alphanumeric string of 10 characters or less * @param type Alphanumeric string of 6 characters or less * @param uuid Hex string of 32 characterx exactly */ public makePaymentId(domain: string, type: string, uuid: string): Result<HexString, InvalidParametersError> { const domainRegex = /^[0-9A-Za-z]{1,10}$/; const typeRegex = /^[0-9A-Za-z]{1,6}$/; const uuidRegex = /^[0-9A-Fa-f]{32}$/; // strip out dashes from the uuid first uuid = uuid.split("-").join(""); if (!domainRegex.test(domain)) { return err(new InvalidParametersError(`Domain must be 10 alphanumeric characters or less, got ${domain}`)); } if (!typeRegex.test(type)) { return err(new InvalidParametersError(`Type must be 6 alphanumeric characters or less, got ${type}`)); } if (!uuidRegex.test(uuid)) { return err(new InvalidParametersError(`UUID must be exactly 16 hex characters, got ${uuid}`)); } // Pad with spaces to reach static lengths domain = domain.padEnd(10); type = type.padEnd(6); // Convert domain and type to hex (/w ascii encoding) const domainHex = Buffer.from(domain, "ascii").toString("hex"); // console.log(`Domain: ${domain}`) // console.log(`DomainHex: ${domainHex}`) const typeHex = Buffer.from(type, "ascii").toString("hex"); // Sanity check if (domainHex.length !== 20) { return err(new InvalidParametersError(`Domain hex wasn't 20 chars long, got '${domainHex}'`)); } if (typeHex.length !== 12) { return err(new InvalidParametersError(`Type hex wasn't 12 chars long, got '${typeHex}'`)); } const paymentId = "0x" + domainHex + typeHex + uuid; const isValidRes = this.isValidPaymentId(paymentId); if (isValidRes.isOk() && isValidRes.value) { return ok(paymentId); } // Either an error or invalid, either way, it's an invalid parameter issue for us return err( new InvalidParametersError( `Unable to create a valid payment ID from domain: ${domain}, type: ${type}, and uuid: ${uuid}`, ), ); } }