@civic/sol-did-client
Version:
A powerful DID-method on Solana
240 lines • 12.6 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
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());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isStringDID = exports.getKeyDataFromVerificationMethod = exports.getPublicKey = exports.makeKeypair = exports.privateKeyIsUint8Array = exports.privateKeyIsBuffer = exports.privateKeyIsString = exports.privateKeyIsArray = exports.getBinarySize = exports.mapControllers = exports.mapServices = exports.mapVerificationMethodsToDidComponents = exports.defaultVerificationMethod = exports.validateAndSplitControllers = exports.isDidSol = exports.isValidDid = exports.ethSignPayload = exports.findLegacyProgramAddress = exports.findProgramAddress = exports.fetchProgram = void 0;
const anchor = __importStar(require("@coral-xyz/anchor"));
const anchor_1 = require("@coral-xyz/anchor");
const sol_did_idl_1 = require("@civic/sol-did-idl");
const web3_js_1 = require("@solana/web3.js");
const address_1 = require("@ethersproject/address");
const bytes_1 = require("@ethersproject/bytes");
const bs58_1 = require("bs58");
const types_1 = require("./types");
const const_1 = require("./const");
const DidSolIdentifier_1 = require("../DidSolIdentifier");
const wrappers_1 = require("./wrappers");
const fetchProgram = (provider) => {
return new anchor_1.Program(sol_did_idl_1.IDL, provider);
};
exports.fetchProgram = fetchProgram;
const findProgramAddress = (authority) => web3_js_1.PublicKey.findProgramAddressSync([anchor.utils.bytes.utf8.encode(const_1.DEFAULT_SEED_STRING), authority.toBuffer()], const_1.DID_SOL_PROGRAM);
exports.findProgramAddress = findProgramAddress;
const findLegacyProgramAddress = (authority) => web3_js_1.PublicKey.findProgramAddressSync([authority.toBuffer(), anchor.utils.bytes.utf8.encode('sol')], const_1.LEGACY_DID_SOL_PROGRAM);
exports.findLegacyProgramAddress = findLegacyProgramAddress;
const ethSignPayload = (instruction, nonce, signer) => __awaiter(void 0, void 0, void 0, function* () {
// Anchor 8 bytes prefix, Option<T> byte suffix
const nonceBytes = Buffer.from(nonce.toArray('le', 8));
const message = Buffer.concat([instruction.data.subarray(8, -1), nonceBytes]);
const signatureFull = yield signer.signMessage(message);
// add signature to payload
const signatureBytes = (0, bytes_1.arrayify)(signatureFull);
const signature = Array.from(signatureBytes.slice(0, -1));
// // map [0x1b, 0x1c] to [0, 1]
// https://docs.ethers.io/v4/api-utils.html#signatures
// @ts-ignore // can never be 0
const recoveryId = signatureBytes.at(-1) - 27;
// const rawMessage = concat([
// toUtf8Bytes(messagePrefix),
// toUtf8Bytes(String(message.length)),
// message
// ])
// const hash = ethersUtils.hashMessage(message);
// console.log(`rawMessage: ${ethersUtils.hexlify(rawMessage)} length: ${rawMessage.length}`)
// console.log(`message: ${ethersUtils.hexlify(message)} length: ${message.length}`)
// console.log(`hash: ${ethersUtils.hexlify(hash)} hash: ${hash.length}`)
// console.log(`signature: ${ethersUtils.hexlify(signature)} length: ${signature.length}`)
// console.log(`recoveryId: ${ethersUtils.hexlify(recoveryId)}`)
// console.log("Eth Address: ", ethersUtils.arrayify(signer.address))
// console.log("Eth Address (full): ", ethersUtils.arrayify(signer.publicKey))
// const recPubKey = ethersUtils.verifyMessage(message, signature)
// console.log("Recovered Eth Address (full): ", ethersUtils.arrayify(recPubKey))
// update data & return instruction
instruction.data = Buffer.concat([
instruction.data.slice(0, -1), // Remove Option<T> == None
new Uint8Array([1]), // Add Option<T> == Some
new Uint8Array(signature),
new Uint8Array([recoveryId]),
]);
// return { signature, recoveryId };
return instruction;
});
exports.ethSignPayload = ethSignPayload;
const isValidDid = (did) => const_1.VALID_DID_REGEX.test(did);
exports.isValidDid = isValidDid;
const isDidSol = (did) => did.startsWith(const_1.DID_SOL_PREFIX);
exports.isDidSol = isDidSol;
const validateAndSplitControllers = (controllerDids) => {
if (controllerDids.some((did) => !(0, exports.isValidDid)(did))) {
throw new Error('Invalid DID found in controllers');
}
const nativeControllers = [];
const otherControllers = [];
controllerDids.forEach((did) => {
if ((0, exports.isDidSol)(did)) {
const id = DidSolIdentifier_1.DidSolIdentifier.parse(did);
nativeControllers.push(id.authority);
}
else {
otherControllers.push(did);
}
});
return {
nativeControllers,
otherControllers,
};
};
exports.validateAndSplitControllers = validateAndSplitControllers;
const defaultVerificationMethod = (authority) => wrappers_1.VerificationMethod.from({
fragment: const_1.DEFAULT_KEY_ID,
methodType: types_1.VerificationMethodType.Ed25519VerificationKey2018,
flags: types_1.BitwiseVerificationMethodFlag.CapabilityInvocation |
types_1.BitwiseVerificationMethodFlag.OwnershipProof,
keyData: authority.toBuffer(),
});
exports.defaultVerificationMethod = defaultVerificationMethod;
// Note: BitwiseVerificationMethodFlag.OwnershipProof is not mapped to DID components
const mapVerificationMethodsToDidComponents = (methods, identifier) => {
const didComponents = {
verificationMethod: new Array(),
authentication: new Array(),
assertionMethod: new Array(),
keyAgreement: new Array(),
capabilityInvocation: new Array(),
capabilityDelegation: new Array(),
};
for (const method of methods) {
// skip hidden methods
if (method.flags.has(types_1.BitwiseVerificationMethodFlag.DidDocHidden)) {
continue;
}
if (method.flags.has(types_1.BitwiseVerificationMethodFlag.Authentication)) {
didComponents.authentication.push(`${identifier.toString()}#${method.fragment}`);
}
if (method.flags.has(types_1.BitwiseVerificationMethodFlag.Assertion)) {
didComponents.assertionMethod.push(`${identifier.toString()}#${method.fragment}`);
}
if (method.flags.has(types_1.BitwiseVerificationMethodFlag.KeyAgreement)) {
didComponents.keyAgreement.push(`${identifier.toString()}#${method.fragment}`);
}
if (method.flags.has(types_1.BitwiseVerificationMethodFlag.CapabilityInvocation)) {
didComponents.capabilityInvocation.push(`${identifier.toString()}#${method.fragment}`);
}
if (method.flags.has(types_1.BitwiseVerificationMethodFlag.CapabilityDelegation)) {
didComponents.capabilityDelegation.push(`${identifier.toString()}#${method.fragment}`);
}
let vm = {
id: identifier.withUrl(method.fragment).toString(),
type: types_1.VerificationMethodType[method.methodType],
controller: identifier.toString(),
};
switch (method.methodType) {
case types_1.VerificationMethodType.Ed25519VerificationKey2018:
vm.publicKeyBase58 = new web3_js_1.PublicKey(method.keyData).toBase58();
break;
case types_1.VerificationMethodType.EcdsaSecp256k1RecoveryMethod2020:
vm.ethereumAddress = (0, address_1.getAddress)((0, bytes_1.hexlify)(method.keyData));
break;
case types_1.VerificationMethodType.EcdsaSecp256k1VerificationKey2019:
vm.publicKeyHex = (0, bytes_1.hexlify)(method.keyData).replace('0x', '');
break;
default:
throw new Error(`Verification method type '${method.methodType}' not recognized`);
}
didComponents.verificationMethod.push(vm);
}
return didComponents;
};
exports.mapVerificationMethodsToDidComponents = mapVerificationMethodsToDidComponents;
const mapServices = (services, identifier) => services.map((service) => ({
id: `${identifier.toString()}#${service.fragment}`,
type: service.serviceType,
serviceEndpoint: service.serviceEndpoint,
}));
exports.mapServices = mapServices;
const mapControllers = (nativeControllers, otherControllers, clusterType) => {
return [
...nativeControllers.map((key) => DidSolIdentifier_1.DidSolIdentifier.create(key, clusterType).toString()),
...otherControllers,
];
};
exports.mapControllers = mapControllers;
const getBinarySize = (input) => Buffer.byteLength(input, 'utf8');
exports.getBinarySize = getBinarySize;
const privateKeyIsArray = (privateKey) => Array.isArray(privateKey);
exports.privateKeyIsArray = privateKeyIsArray;
const privateKeyIsString = (privateKey) => typeof privateKey === 'string';
exports.privateKeyIsString = privateKeyIsString;
const privateKeyIsBuffer = (privateKey) => Buffer.isBuffer(privateKey);
exports.privateKeyIsBuffer = privateKeyIsBuffer;
const privateKeyIsUint8Array = (privateKey) => privateKey instanceof Uint8Array;
exports.privateKeyIsUint8Array = privateKeyIsUint8Array;
/**
* Create a Solana account object from an x25519 private key
* @param privateKey
*/
const makeKeypair = (privateKey) => {
if ((0, exports.privateKeyIsArray)(privateKey)) {
return web3_js_1.Keypair.fromSecretKey(Buffer.from(privateKey));
}
if ((0, exports.privateKeyIsBuffer)(privateKey) || (0, exports.privateKeyIsUint8Array)(privateKey))
return web3_js_1.Keypair.fromSecretKey(privateKey);
if ((0, exports.privateKeyIsString)(privateKey)) {
const privateKeyHex = (0, bs58_1.decode)(privateKey);
return web3_js_1.Keypair.fromSecretKey(privateKeyHex);
}
throw new Error('Incompatible private key format');
};
exports.makeKeypair = makeKeypair;
/**
* Given a private key on the x25519 curve, get its public key
* @param privateKey
*/
const getPublicKey = (privateKey) => (0, exports.makeKeypair)(privateKey).publicKey;
exports.getPublicKey = getPublicKey;
const getKeyDataFromVerificationMethod = (vm) => {
switch (vm.type) {
case types_1.VerificationMethodType[types_1.VerificationMethodType.Ed25519VerificationKey2018]:
return new web3_js_1.PublicKey(vm.publicKeyBase58).toBuffer();
case types_1.VerificationMethodType[types_1.VerificationMethodType.EcdsaSecp256k1RecoveryMethod2020]:
return Buffer.from((0, bytes_1.arrayify)(vm.ethereumAddress));
case types_1.VerificationMethodType[types_1.VerificationMethodType.EcdsaSecp256k1VerificationKey2019]:
return Buffer.from((0, bytes_1.arrayify)(vm.publicKeyHex));
default:
throw new Error(`Verification method type '${vm.type}' not recognized`);
}
};
exports.getKeyDataFromVerificationMethod = getKeyDataFromVerificationMethod;
const isStringDID = (identifier) => typeof identifier === 'string';
exports.isStringDID = isStringDID;
//# sourceMappingURL=utils.js.map