Update to latest engine. Add sync-v2. Various fixes.
This commit is contained in:
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user