@web5/agent
Version:
120 lines • 6.08 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { Sha256, CryptoUtils } from '@web5/crypto';
import { concatenateUrl } from './utils.js';
import { Convert } from '@web5/common';
/**
* A client for registering tenants with a DWN.
*/
export class DwnRegistrar {
/**
* Registers a new tenant with the given DWN.
* NOTE: Assumes the user has already accepted the terms of service.
* NOTE: Currently the DWN Server from `dwn-server` does not require user signature.
* TODO: bring in types from `dwn-server`.
*/
static registerTenant(dwnEndpoint, did) {
return __awaiter(this, void 0, void 0, function* () {
const registrationEndpoint = concatenateUrl(dwnEndpoint, 'registration');
const termsOfUseEndpoint = concatenateUrl(registrationEndpoint, 'terms-of-service');
const proofOfWorkEndpoint = concatenateUrl(registrationEndpoint, 'proof-of-work');
// fetch the terms-of-service
const termsOfServiceGetResponse = yield fetch(termsOfUseEndpoint, {
method: 'GET',
});
if (termsOfServiceGetResponse.status !== 200) {
const statusCode = termsOfServiceGetResponse.status;
const statusText = termsOfServiceGetResponse.statusText;
const errorText = yield termsOfServiceGetResponse.text();
throw new Error(`Failed fetching terms-of-service: ${statusCode} ${statusText}: ${errorText}`);
}
const termsOfServiceFetched = yield termsOfServiceGetResponse.text();
// fetch the proof-of-work challenge
const proofOfWorkChallengeGetResponse = yield fetch(proofOfWorkEndpoint, {
method: 'GET',
});
const { challengeNonce, maximumAllowedHashValue } = yield proofOfWorkChallengeGetResponse.json();
// create registration data based on the hash of the terms-of-service and the DID
const registrationData = {
did,
termsOfServiceHash: yield DwnRegistrar.hashAsHexString(termsOfServiceFetched),
};
// compute the proof-of-work response nonce based on the the proof-of-work challenge and the registration data.
const responseNonce = yield DwnRegistrar.findQualifiedResponseNonce({
challengeNonce,
maximumAllowedHashValue,
requestData: JSON.stringify(registrationData),
});
// send the registration request to the server
const registrationRequest = {
registrationData,
proofOfWork: {
challengeNonce,
responseNonce,
},
};
const registrationResponse = yield fetch(registrationEndpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(registrationRequest),
});
if (registrationResponse.status !== 200) {
const statusCode = registrationResponse.status;
const statusText = registrationResponse.statusText;
const errorText = yield registrationResponse.text();
throw new Error(`Registration failed: ${statusCode} ${statusText}: ${errorText}`);
}
});
}
/**
* Computes the SHA-256 hash of the given array of strings.
*/
static hashAsHexString(input) {
return __awaiter(this, void 0, void 0, function* () {
const hashAsBytes = yield Sha256.digest({ data: Convert.string(input).toUint8Array() });
const hashAsHex = Convert.uint8Array(hashAsBytes).toHex();
return hashAsHex;
});
}
/**
* Finds a response nonce that qualifies the difficulty requirement for the given proof-of-work challenge and request data.
*/
static findQualifiedResponseNonce(input) {
return __awaiter(this, void 0, void 0, function* () {
const startTime = Date.now();
const { maximumAllowedHashValue, challengeNonce, requestData } = input;
const maximumAllowedHashValueAsBigInt = BigInt(`0x${maximumAllowedHashValue}`);
let iterations = 1;
let responseNonce;
let qualifiedSolutionNonceFound = false;
do {
responseNonce = yield this.generateNonce();
const computedHash = yield DwnRegistrar.hashAsHexString(challengeNonce + responseNonce + requestData);
const computedHashAsBigInt = BigInt(`0x${computedHash}`);
qualifiedSolutionNonceFound = computedHashAsBigInt <= maximumAllowedHashValueAsBigInt;
iterations++;
} while (!qualifiedSolutionNonceFound);
// Log final/successful iteration.
console.log(`iterations: ${iterations}, time lapsed: ${Date.now() - startTime} ms`);
return responseNonce;
});
}
/**
* Generates 32 random bytes expressed as a HEX string.
*/
static generateNonce() {
return __awaiter(this, void 0, void 0, function* () {
const randomBytes = CryptoUtils.randomBytes(32);
const hexString = Convert.uint8Array(randomBytes).toHex().toUpperCase();
return hexString;
});
}
}
//# sourceMappingURL=dwn-registrar.js.map