UNPKG

@ocap/wallet

Version:

Utility function to create and use an forge compatible crypto wallet

162 lines (161 loc) 5.6 kB
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; }