Skip to content

Commit 5833179

Browse files
Merge pull request #178 from DIG-Network/release/v0.0.1-alpha.191
Release/v0.0.1 alpha.191
2 parents 7e17f15 + d301863 commit 5833179

File tree

4 files changed

+111
-79
lines changed

4 files changed

+111
-79
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
44

5+
### [0.0.1-alpha.191](https://github.com/DIG-Network/dig-chia-sdk/compare/v0.0.1-alpha.190...v0.0.1-alpha.191) (2024-11-01)
6+
7+
8+
### Features
9+
10+
* add base64 getters to udi class ([ca2f498](https://github.com/DIG-Network/dig-chia-sdk/commit/ca2f49810278d01bd460ae6929a4e6d58ea98aa7))
11+
512
### [0.0.1-alpha.190](https://github.com/DIG-Network/dig-chia-sdk/compare/v0.0.1-alpha.189...v0.0.1-alpha.190) (2024-11-01)
613

714

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@dignetwork/dig-sdk",
3-
"version": "0.0.1-alpha.190",
3+
"version": "0.0.1-alpha.191",
44
"description": "",
55
"type": "commonjs",
66
"main": "./dist/index.js",

src/utils/Udi.ts

Lines changed: 101 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,108 @@
1-
import * as urns from 'urns';
2-
import { createHash } from 'crypto';
3-
import { encode as base32Encode, decode as base32Decode } from 'hi-base32';
4-
5-
//
6-
// This class encapsulates the concept of a Universal Data Identifier (UDI), which is a
7-
// standardized way to identify resources across the distributed DIG mesh network.
8-
// The UDI format: urn:dig:chainName:storeId:rootHash/resourceKey
9-
// This allows unique resource identification across the DIG network.
10-
//
1+
import * as urns from "urns";
2+
import { createHash } from "crypto";
3+
import { encode as base32Encode, decode as base32Decode } from "hi-base32";
4+
115
class Udi {
126
readonly chainName: string;
13-
private readonly _storeId: Buffer;
14-
private readonly _rootHash: Buffer | null;
7+
private readonly _storeIdHex: string;
8+
private readonly _rootHashHex: string | null;
159
readonly resourceKey: string | null;
1610
static readonly nid: string = "dig";
1711
static readonly namespace: string = `urn:${Udi.nid}`;
1812

1913
constructor(
2014
chainName: string,
21-
storeId: string | Buffer,
22-
rootHash: string | Buffer | null = null,
15+
storeId: string,
16+
rootHash: string | null = null,
2317
resourceKey: string | null = null
2418
) {
2519
if (!storeId) {
2620
throw new Error("storeId cannot be empty");
2721
}
2822
this.chainName = chainName || "chia";
29-
this._storeId = Udi.convertToBuffer(storeId);
30-
this._rootHash = rootHash ? Udi.convertToBuffer(rootHash) : null;
23+
this._storeIdHex = Udi.verifyAndFormatHex(storeId);
24+
this._rootHashHex = rootHash ? Udi.verifyAndFormatHex(rootHash) : null;
3125
this.resourceKey = resourceKey;
3226
}
3327

34-
static convertToBuffer(input: string | Buffer): Buffer {
35-
if (Buffer.isBuffer(input)) {
36-
return input;
37-
}
38-
39-
if (Udi.isHex(input)) {
40-
return Buffer.from(input, 'hex');
41-
}
42-
43-
if (Udi.isBase32(input)) {
44-
return Buffer.from(base32Decode(input.toUpperCase(), false)); // Decode with uppercase
28+
static verifyAndFormatHex(input: string): string {
29+
if (!/^[a-fA-F0-9]{64}$/.test(input)) {
30+
throw new Error("Input must be a 64-character hex string.");
4531
}
46-
47-
throw new Error("Invalid input encoding. Must be 32-byte hex or Base32 string.");
48-
}
49-
50-
static isHex(input: string): boolean {
51-
return /^[a-fA-F0-9]{64}$/.test(input);
52-
}
53-
54-
static isBase32(input: string): boolean {
55-
return /^[A-Z2-7]{52}$/.test(input.toUpperCase());
56-
}
57-
58-
withRootHash(rootHash: string | Buffer | null): Udi {
59-
return new Udi(this.chainName, this._storeId, rootHash, this.resourceKey);
60-
}
61-
62-
withResourceKey(resourceKey: string | null): Udi {
63-
return new Udi(this.chainName, this._storeId, this._rootHash, resourceKey);
32+
return input;
6433
}
6534

6635
static fromUrn(urn: string): Udi {
6736
const parsedUrn = urns.parseURN(urn);
68-
if (parsedUrn.nid.toLowerCase() !== Udi.nid) {
37+
if (parsedUrn.nid !== Udi.nid) {
6938
throw new Error(`Invalid nid: ${parsedUrn.nid}`);
7039
}
7140

72-
const parts = parsedUrn.nss.split(':');
41+
const parts = parsedUrn.nss.split(":");
7342
if (parts.length < 2) {
7443
throw new Error(`Invalid UDI format: ${parsedUrn.nss}`);
7544
}
7645

7746
const chainName = parts[0];
78-
const storeId = parts[1].split('/')[0];
47+
const storeIdHex = Udi.convertToHex(parts[1].split("/")[0]);
7948

80-
let rootHash: string | null = null;
49+
let rootHashHex: string | null = null;
8150
if (parts.length > 2) {
82-
rootHash = parts[2].split('/')[0];
51+
rootHashHex = Udi.convertToHex(parts[2].split("/")[0]);
8352
}
8453

85-
const pathParts = parsedUrn.nss.split('/');
54+
const pathParts = parsedUrn.nss.split("/");
8655
let resourceKey: string | null = null;
8756
if (pathParts.length > 1) {
88-
resourceKey = pathParts.slice(1).join('/');
57+
resourceKey = pathParts.slice(1).join("/");
8958
}
9059

91-
return new Udi(chainName, storeId, rootHash, resourceKey);
60+
return new Udi(chainName, storeIdHex, rootHashHex, resourceKey);
9261
}
9362

94-
toUrn(encoding: 'hex' | 'base32' | 'base64' = 'hex'): string {
95-
const storeIdStr = this.bufferToString(this._storeId, encoding);
63+
static convertToHex(input: string): string {
64+
// Attempt hex conversion first
65+
if (/^[a-fA-F0-9]{64}$/.test(input)) return input;
66+
67+
// Convert from Base32
68+
try {
69+
const paddedInput = Udi.addBase32Padding(input.toUpperCase());
70+
const buffer = Buffer.from(base32Decode(paddedInput, false));
71+
return buffer.toString("hex");
72+
} catch (e) {
73+
console.warn("Base32 decoding failed, trying Base64 encoding...");
74+
}
75+
76+
// Convert from Base64
77+
try {
78+
const standardBase64 = Udi.addBase64Padding(Udi.toStandardBase64(input));
79+
const buffer = Buffer.from(standardBase64, "base64");
80+
return buffer.toString("hex");
81+
} catch (e) {
82+
throw new Error("Invalid input encoding. Must be 32-byte hex, Base32, or Base64 string.");
83+
}
84+
}
85+
86+
static addBase32Padding(input: string): string {
87+
const paddingNeeded = (8 - (input.length % 8)) % 8;
88+
return input + "=".repeat(paddingNeeded);
89+
}
90+
91+
static toStandardBase64(base64UrlSafe: string): string {
92+
return base64UrlSafe.replace(/-/g, "+").replace(/_/g, "/");
93+
}
94+
95+
static addBase64Padding(base64: string): string {
96+
const paddingNeeded = (4 - (base64.length % 4)) % 4;
97+
return base64 + "=".repeat(paddingNeeded);
98+
}
99+
100+
toUrn(encoding: "hex" | "base32" | "base64" = "hex"): string {
101+
const storeIdStr = this.formatBufferAsEncoding(this._storeIdHex, encoding);
96102
let urn = `${Udi.namespace}:${this.chainName}:${storeIdStr}`;
97103

98-
if (this._rootHash) {
99-
const rootHashStr = this.bufferToString(this._rootHash, encoding);
104+
if (this._rootHashHex) {
105+
const rootHashStr = this.formatBufferAsEncoding(this._rootHashHex, encoding);
100106
urn += `:${rootHashStr}`;
101107
}
102108

@@ -107,24 +113,29 @@ class Udi {
107113
return urn;
108114
}
109115

110-
bufferToString(buffer: Buffer, encoding: 'hex' | 'base32' | 'base64'): string {
111-
switch (encoding) {
112-
case 'hex':
113-
return buffer.toString('hex');
114-
case 'base32':
115-
return base32Encode(buffer).toUpperCase().replace(/=+$/, ''); // Convert to uppercase and remove padding
116-
case 'base64':
117-
return buffer.toString('base64').toLowerCase(); // Convert to lowercase
118-
default:
119-
throw new Error("Unsupported encoding");
116+
private formatBufferAsEncoding(hexString: string, encoding: "hex" | "base32" | "base64"): string {
117+
const buffer = Buffer.from(hexString, "hex");
118+
if (encoding === "hex") {
119+
return hexString;
120+
} else if (encoding === "base32") {
121+
return base32Encode(buffer).replace(/=+$/, ""); // Strip padding for Base32
122+
} else if (encoding === "base64") {
123+
return Udi.toBase64UrlSafe(buffer.toString("base64")); // Convert to URL-safe Base64
120124
}
125+
throw new Error("Unsupported encoding type");
126+
}
127+
128+
static toBase64UrlSafe(base64Standard: string): string {
129+
return base64Standard.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
121130
}
122131

123132
equals(other: Udi): boolean {
124133
return (
125-
this._storeId.equals(other._storeId) &&
134+
this._storeIdHex === other._storeIdHex &&
126135
this.chainName === other.chainName &&
127-
(this._rootHash && other._rootHash ? this._rootHash.equals(other._rootHash) : this._rootHash === other._rootHash) &&
136+
(this._rootHashHex && other._rootHashHex
137+
? this._rootHashHex === other._rootHashHex
138+
: this._rootHashHex === other._rootHashHex) &&
128139
this.resourceKey === other.resourceKey
129140
);
130141
}
@@ -134,23 +145,37 @@ class Udi {
134145
}
135146

136147
clone(): Udi {
137-
return new Udi(this.chainName, this._storeId, this._rootHash, this.resourceKey);
148+
return new Udi(this.chainName, this._storeIdHex, this._rootHashHex, this.resourceKey);
138149
}
139150

140151
hashCode(): string {
141-
const hash = createHash('sha256');
152+
const hash = createHash("sha256");
142153
hash.update(this.toUrn());
143-
return hash.digest('hex');
154+
return hash.digest("hex");
144155
}
145156

146-
// Getter for storeId as a hex string
147157
get storeId(): string {
148-
return this._storeId.toString('hex');
158+
return this._storeIdHex;
149159
}
150160

151-
// Getter for rootHash as a hex string
152161
get rootHash(): string | null {
153-
return this._rootHash ? this._rootHash.toString('hex') : null;
162+
return this._rootHashHex;
163+
}
164+
165+
get storeIdBase32(): string {
166+
return this.formatBufferAsEncoding(this._storeIdHex, "base32");
167+
}
168+
169+
get rootHashBase32(): string | null {
170+
return this._rootHashHex ? this.formatBufferAsEncoding(this._rootHashHex, "base32") : null;
171+
}
172+
173+
get storeIdBase64(): string {
174+
return this.formatBufferAsEncoding(this._storeIdHex, "base64");
175+
}
176+
177+
get rootHashBase64(): string | null {
178+
return this._rootHashHex ? this.formatBufferAsEncoding(this._rootHashHex, "base64") : null;
154179
}
155180
}
156181

0 commit comments

Comments
 (0)