Update to latest engine. Add sync-v2. Various fixes.

This commit is contained in:
2026-09-07 03:33:44 +00:00
parent 051fc0c9ac
commit f01f9bc56b
31 changed files with 5977 additions and 1392 deletions
+122
View File
@@ -0,0 +1,122 @@
import { binToHex, flattenBinArray, secp256k1, sha256 } from "@bitauth/libauth";
import { toExtendedJson } from "@xo-cash/utils";
import type { WriteRequest } from "../types.js";
export type AuthenticatedRequestHeaders = {
'X-Public-Key': string;
'X-Timestamp': string;
'X-Signature': string;
};
export type SignedPayload = { publicKey: string; signature: string };
export abstract class SyncClient {
abstract connect(): Promise<void>;
abstract disconnect(): Promise<void>;
abstract write(resourceId: string, value: Record<string, unknown>): Promise<unknown>;
abstract read(resourceId: string): Promise<unknown>;
abstract subscribe(resourceId: string): Promise<void>;
abstract unsubscribe(resourceId: string): Promise<unknown>;
/**
* Derives the resource private key from the private key and resource ID
* @param privateKey - The private key to derive the resource private key from
* @param resourceId - The resource ID to derive the resource private key from
* @returns The resource private key
*/
static deriveResourcePrivateKey(privateKey: Uint8Array, resourceId: string): Uint8Array {
// Convert the resource ID to bytes
const resourceIdBytes = new TextEncoder().encode(resourceId);
// Hash the private key and resource ID
return sha256.hash(flattenBinArray([ privateKey, resourceIdBytes ]));
}
/**
* Signs a payload with the private key and returns the public key and signature
* @param privateKey - The private key to sign the payload with
* @param payload - The payload to sign
* @returns The public key and signature
*/
static signPayload(privateKey: Uint8Array, payload: string): SignedPayload {
// Convert the payload to Binary and hash it
const payloadHash = sha256.hash(new TextEncoder().encode(payload));
// Derive the public key
const publicKey = secp256k1.derivePublicKeyCompressed(privateKey);
// If the public key is a string, throw an error
if (typeof publicKey === 'string') {
throw new Error('Failed to derive public key');
}
// Sign the payload
const signature = secp256k1.signMessageHashDER(privateKey, payloadHash);
// If the signature is a string, throw an error
if (typeof signature === 'string') {
throw new Error('Failed to sign message');
}
return {
publicKey: binToHex(publicKey),
signature: binToHex(signature),
};
}
/**
* Signs a write request with the private key and returns the write request
* @param privateKey - The private key to sign the write request with
* @param resourceId - The resource ID to write to
* @param value - The value to write
* @returns The write request
*/
static signWriteRequest(privateKey: Uint8Array, resourceId: string, value: Uint8Array): WriteRequest {
// Derive the resource private key
const derivedKey = this.deriveResourcePrivateKey(privateKey, resourceId);
// Create the payload
const timestamp = Date.now();
const payload = `${timestamp}:${resourceId}:${toExtendedJson(value)}`;
// Sign the payload
const { publicKey, signature } = this.signPayload(derivedKey, payload);
return {
id: resourceId,
value,
publicKey,
timestamp,
signature,
};
}
/**
* Signs a request with the private key and returns the public key, timestamp, and signature
* @param privateKey - The private key to sign the request with
* @param path - The path of the request
* @param body - The body of the request
* @returns The public key, timestamp, and signature
*/
static signRequest(privateKey: Uint8Array, path: string, body: unknown): AuthenticatedRequestHeaders {
// Create the payload
const timestamp = Date.now();
// Convert the body to a string if it isn't already a string
const bodyStr = typeof body === 'string' ? body : toExtendedJson(body);
// Create the payload as `Timestamp:Path:Body`
const payload = `${timestamp}:${path}:${bodyStr}`;
// Sign the payload
const { publicKey, signature } = this.signPayload(privateKey, payload);
return {
'X-Public-Key': publicKey,
'X-Timestamp': timestamp.toString(),
'X-Signature': signature,
};
}
}