@ocap/wallet
Version:
Utility function to create and use an forge compatible crypto wallet
162 lines (161 loc) • 5.6 kB
JavaScript
import { toHex } from '@ocap/util';
import { getSigner, getHasher } from '@ocap/mcrypto';
import { toAddress, fromPublicKey as DIDFromPublicKey, toTypeInfo, DidType, } from '@arcblock/did';
export const WalletType = DidType;
/**
* Generate an wallet instance that can be used to sign a message or verify a signature
*/
export function Wallet(keyPair, t = 'default') {
const type = DidType(t);
const signer = getSigner(type.pk);
const hasher = getHasher(type.hash);
return {
type,
secretKey: keyPair.sk,
publicKey: keyPair.pk,
address: keyPair.pk ? DIDFromPublicKey(keyPair.pk, type) : keyPair.address,
// @ts-ignore
hash(data, round = 1, encoding = 'hex') {
return hasher(data, round, encoding);
},
// @ts-ignore
sign(data, hashBeforeSign = true, encoding = 'hex') {
if (!keyPair.sk) {
throw new Error('Cannot sign data without a secretKey');
}
if (hashBeforeSign) {
const hash = hasher(data, 1);
return signer.sign(hash, keyPair.sk, encoding);
}
return signer.sign(data, keyPair.sk, encoding);
},
// eslint-disable-next-line require-await
async verify(data, signature, hashBeforeVerify = true, extra) {
if (!keyPair.pk) {
throw new Error('Cannot verify data without a publicKey');
}
const hash = hashBeforeVerify ? hasher(data, 1) : data;
return signer.verify(hash, signature, keyPair.pk, extra);
},
ethHash(data) {
if (typeof signer.ethHash !== 'function') {
throw new Error('ethHash is not supported by signer');
}
return signer.ethHash(data);
},
ethSign(data, hashBeforeSign = true) {
if (!keyPair.sk) {
throw new Error('Cannot sign data without a secretKey');
}
if (typeof signer.ethHash !== 'function') {
throw new Error('ethSign is not supported by signer');
}
if (hashBeforeSign) {
return signer.ethSign(signer.ethHash(data), toHex(keyPair.sk));
}
return signer.ethSign(data, toHex(keyPair.sk));
},
ethVerify(data, signature, hashBeforeVerify = true) {
if (typeof signer.ethHash !== 'function') {
throw new Error('ethVerify is not supported by signer');
}
const hash = hashBeforeVerify ? signer.ethHash(data) : data;
return signer.ethRecover(hash, signature) === this.address;
},
// deprecated
toAddress() {
return keyPair.pk ? DIDFromPublicKey(keyPair.pk, type) : keyPair.address;
},
toJSON() {
return {
type: DidType.toJSON(type),
sk: toHex(keyPair.sk),
pk: toHex(keyPair.pk),
address: this.address,
};
},
};
}
/**
* Generate a wallet from secretKey
*
* @example
* const assert = require('assert');
* const { fromSecretKey } = require('@ocap/wallet');
*
* const sk =
* '0xD67C071B6F51D2B61180B9B1AA9BE0DD0704619F0E30453AB4A592B036EDE644E4852B7091317E3622068E62A5127D1FB0D4AE2FC50213295E10652D2F0ABFC7';
* const sig =
* '0x08a102851c38c072e42756c1cc70938b5499c8e9358dfe5f383823f56cdb282ffda60fcd581a02c6c673069e5afc0bf09abbe3639b61b84d64fd58ef9f083003';
*
* const wallet = fromSecretKey(sk, type);
* const message = 'data to sign';
* const signature = wallet.sign(message);
* assert.equal(signature, sig, "signature should match");
* assert.ok(wallet.verify(message, signature), "signature should be verified");
*/
export function fromSecretKey(sk, _type = 'default') {
const type = DidType(_type);
const keyPair = { sk, pk: getSigner(type.pk).getPublicKey(sk) };
return Wallet(keyPair, type);
}
/**
* Generate a wallet from publicKey
*/
export function fromPublicKey(pk, _type = 'default') {
return Wallet({ pk }, DidType(_type));
}
/**
* Generate a wallet from address (did)
*
* Since we do not know the publicKey and secretKey, this kind of wallet cannot be used for signing and verifying
*/
export function fromAddress(address) {
return Wallet({ address: toAddress(address) }, toTypeInfo(address));
}
/**
* Generate a wallet by generating a random secretKey
*/
export function fromRandom(_type = 'default') {
const type = DidType(_type);
const signer = getSigner(type.pk);
const keyPair = signer.genKeyPair();
return Wallet({ sk: keyPair.secretKey, pk: keyPair.publicKey }, type);
}
/**
* Generating a wallet from a serialized json presentation of another wallet
*/
export function fromJSON(json) {
const type = DidType.fromJSON(json.type);
// @ts-ignore
return Wallet(json, type);
}
/**
* Check if an object is valid wallet object
*/
export function isValid(wallet, canSign = true) {
if (!wallet || typeof wallet !== 'object') {
return false;
}
if (typeof wallet.verify !== 'function') {
return false;
}
if (typeof wallet.toAddress !== 'function') {
return false;
}
if (typeof wallet.toJSON !== 'function') {
return false;
}
if (!wallet.type || !wallet.publicKey) {
return false;
}
if (canSign) {
if (!wallet.secretKey) {
return false;
}
if (typeof wallet.sign !== 'function') {
return false;
}
}
return true;
}