UNPKG

iso-filecoin

Version:

Isomorphic filecoin abstractions for RPC, signatures, address, token and wallet

417 lines (379 loc) 11.6 kB
'use strict'; var bls12381 = require('../node_modules/.pnpm/@noble_curves@1.9.4/node_modules/@noble/curves/esm/bls12-381.cjs'); var secp256k1 = require('../node_modules/.pnpm/@noble_curves@1.9.4/node_modules/@noble/curves/esm/secp256k1.cjs'); var blake2b = require('../node_modules/.pnpm/@noble_hashes@1.8.0/node_modules/@noble/hashes/esm/blake2b.cjs'); var index$1 = require('../node_modules/.pnpm/@scure_bip32@1.7.0/node_modules/@scure/bip32/lib/esm/index.cjs'); var index = require('../node_modules/.pnpm/@scure_bip39@1.6.0/node_modules/@scure/bip39/esm/index.cjs'); var english = require('../node_modules/.pnpm/@scure_bip39@1.6.0/node_modules/@scure/bip39/esm/wordlists/english.cjs'); var rfc4648 = require('../node_modules/.pnpm/iso-base@4.1.0/node_modules/iso-base/src/rfc4648.cjs'); var utf8 = require('../node_modules/.pnpm/iso-base@4.1.0/node_modules/iso-base/src/utf8.cjs'); var utils = require('../node_modules/.pnpm/iso-base@4.1.0/node_modules/iso-base/src/utils.cjs'); var src_address = require('./address.cjs'); var src_message = require('./message.cjs'); var src_signature = require('./signature.cjs'); var src_utils = require('./utils.cjs'); var schemas = require('../node_modules/.pnpm/zod@4.0.10/node_modules/zod/v4/classic/schemas.cjs'); /** * @import {SetRequired} from 'type-fest' */ /** * FRC-102 prefix * * @see https://github.com/filecoin-project/FIPs/blob/master/FRCs/frc-0102.md */ const FRC_102_PREFIX = '\x19Filecoin Signed Message:\n'; /** * Schemas */ const Schemas = { lotusPrivateKey: schemas.object({ Type: schemas.union([schemas.literal('bls'), schemas.literal('secp256k1')]), /** * Lotus BLS private key is little endian so you need to reverse the byte order. * base64pad(private-key) */ PrivateKey: schemas.string(), }), }; /** * Generate mnemonic */ function generateMnemonic() { return index.generateMnemonic(english.wordlist, 256) } /** * Get seed from mnemonic * * @param {string} mnemonic * @param {string} [password] */ function mnemonicToSeed(mnemonic, password) { return index.mnemonicToSeedSync(mnemonic, password) } /** * Get HD account from mnemonic * * @param {string} mnemonic * @param {import('./types.js').SignatureType} type * @param {string} path * @param {string} [password] * @param {import('./types.js').Network} [network] */ function accountFromMnemonic(mnemonic, type, path, password, network) { const seed = mnemonicToSeed(mnemonic, password); return accountFromSeed(seed, type, path, network) } /** * Get HD account from seed * * @param {Uint8Array} seed * @param {import('./types.js').SignatureType} type * @param {string} path * @param {import('./types.js').Network} [network] * @returns {SetRequired<import('./types.js').IAccount, 'privateKey' | 'path'>} */ function accountFromSeed(seed, type, path, network) { const masterKey = index$1.HDKey.fromMasterSeed(seed); const privateKey = masterKey.derive(path).privateKey; if (!privateKey) { throw new Error('Private key could not be generated.') } if (!network) { network = src_utils.getNetworkFromPath(path); } const { address, publicKey } = getPublicKey(privateKey, network, type); return { type, privateKey, publicKey, address, path, } } /** * Get account from private key * * Lotus BLS private key is little endian so you need to reverse the byte order. Use `lotusBlsPrivateKeyToBytes` to convert. * * @param {Uint8Array} privateKey * @param {import('./types.js').SignatureType} type * @param {import('./types.js').Network} network * @param {string} [path] * @returns {SetRequired<import('./types.js').IAccount, 'privateKey'>} */ function accountFromPrivateKey(privateKey, type, network, path) { if (privateKey.length !== 32) { throw new Error('Private key should be 32 bytes.') } const { address, publicKey } = getPublicKey(privateKey, network, type); return { type, privateKey, publicKey, address, path, } } /** * Get account from lotus private key export * * @param {string} lotusHex - Lotus hex encoded private key .ie `hex({"Type":"bls","PrivateKey":"base64pad(private-key)"})` * @param {import('./types.js').Network} network - Network * @returns {import('./types.js').IAccount} */ function accountFromLotus(lotusHex, network) { const lotusJson = Schemas.lotusPrivateKey.parse( JSON.parse(utf8.utf8.encode(rfc4648.hex.decode(lotusHex))) ); if (lotusJson.Type === 'bls') { return accountFromPrivateKey( lotusBlsPrivateKeyToBytes(lotusJson.PrivateKey), 'BLS', network ) } return accountFromPrivateKey( rfc4648.base64pad.decode(lotusJson.PrivateKey), 'SECP256K1', network ) } /** * Create account * * @param {import('./types.js').SignatureType} type * @param {import('./types.js').Network} network * @returns {SetRequired<import('./types.js').IAccount, 'privateKey'>} */ function create(type, network) { switch (type) { case 'SECP256K1': { return accountFromPrivateKey(secp256k1.secp256k1.utils.randomPrivateKey(), type, network) } case 'BLS': { return accountFromPrivateKey(bls12381.bls12_381.utils.randomPrivateKey(), type, network) } default: { throw new Error( `Create does not support "${type}" type. Use SECP256K1 or BLS.` ) } } } /** * Get public key from private key * * @param {Uint8Array} privateKey * @param {import('./types.js').Network} network * @param {import('./types.js').SignatureType} type * @returns {import('./types.js').IAccount} */ function getPublicKey(privateKey, network, type) { switch (type) { case 'SECP256K1': { const publicKey = secp256k1.secp256k1.getPublicKey(privateKey, false); return { type: 'SECP256K1', publicKey, address: src_address.fromPublicKey(publicKey, network, 'SECP256K1'), } } case 'BLS': { const publicKey = bls12381.bls12_381.getPublicKey(privateKey); return { type: 'BLS', publicKey, address: src_address.fromPublicKey(publicKey, network, 'BLS'), } } default: { throw new Error( `getPublicKey does not support "${type}" type. Use SECP256K1 or BLS.` ) } } } /** * Sign filecoin message * * @param {Uint8Array} privateKey * @param {import('./types.js').SignatureType} type * @param {import('./types.js').MessageObj} message * @returns */ function signMessage(privateKey, type, message) { const cid = new src_message.Message(message).cidBytes(); return sign(privateKey, type, cid) } /** * Sign arbitary bytes similar to `lotus wallet sign` * * Lotus BLS private key is little endian so you need to reverse the byte order. Use `lotusBlsPrivateKeyToBytes` to convert. * * @param {Uint8Array} privateKey * @param {import('./types.js').SignatureType} type * @param {Uint8Array} data */ function sign(privateKey, type, data) { switch (type) { case 'SECP256K1': { const signature = secp256k1.secp256k1.sign( blake2b.blake2b(data, { dkLen: 32, }), privateKey ); return new src_signature.Signature({ type: 'SECP256K1', data: utils.concat([ signature.toCompactRawBytes(), Uint8Array.from([signature.recovery]), ]), }) } case 'BLS': { const signature = bls12381.bls12_381.sign(data, privateKey); return new src_signature.Signature({ type: 'BLS', data: signature, }) } default: { throw new Error( `Sign does not support "${type}" type. Use SECP256K1 or BLS.` ) } } } /** * Personal sign using FRC-102 * * @see https://github.com/filecoin-project/FIPs/blob/master/FRCs/frc-0102.md * * @param {Uint8Array} privateKey * @param {import('./types.js').SignatureType} type * @param {Uint8Array} data */ function personalSign(privateKey, type, data) { const prefix = utf8.utf8.decode(`${FRC_102_PREFIX}${data.length}`); return sign(privateKey, type, utils.concat([prefix, data])) } /** * Personal verify using FRC-102 * * @see https://github.com/filecoin-project/FIPs/blob/master/FRCs/frc-0102.md * * @param {import('./signature.js').Signature} signature * @param {Uint8Array} data * @param {Uint8Array} publicKey */ function personalVerify(signature, data, publicKey) { const prefix = utf8.utf8.decode(`${FRC_102_PREFIX}${data.length}`); return verify(signature, utils.concat([prefix, data]), publicKey) } /** * Verify signatures * * @param {import('./signature.js').Signature} signature * @param {Uint8Array} data * @param {Uint8Array} publicKey */ function verify(signature, data, publicKey) { switch (signature.type) { case 'SECP256K1': { return secp256k1.secp256k1.verify( secp256k1.secp256k1.Signature.fromCompact(signature.data.subarray(0, 64)), blake2b.blake2b(data, { dkLen: 32, }), publicKey ) } case 'BLS': { return bls12381.bls12_381.verify(signature.data, data, publicKey) } default: { throw new Error( `Verify does not support "${signature.type}" type. Use SECP256K1 or BLS.` ) } } } /** * Lotus BLS base64 private key to bytes * Lotus BLS private key is little endian so you need to reverse the byte order. * * @param {string} priv */ function lotusBlsPrivateKeyToBytes(priv) { return rfc4648.base64pad.decode(priv).reverse() } /** * * @param {Signature} signature * @param {Uint8Array} data */ function recoverPublicKey(signature, data) { if (signature.type === 'BLS') { throw new Error('Recover public key is not supported for BLS') } const hash = blake2b.blake2b(data, { dkLen: 32, }); return secp256k1.secp256k1.Signature.fromCompact(signature.data.subarray(0, 64)) .addRecoveryBit(signature.data[64]) .recoverPublicKey(hash) .toRawBytes(false) } /** * * @param {Signature} signature * @param {Uint8Array} data * @param {import('./types.js').Network} network */ function recoverAddress(signature, data, network) { const publicKey = recoverPublicKey(signature, data); return src_address.fromPublicKey(publicKey, network, 'SECP256K1') } /** * Export account to lotus private key export format (hex) * * @param {import('./types.js').IAccount} account */ function accountToLotus(account) { if (account.privateKey == null) { throw new Error('Private key not found') } if (account.type === 'BLS') { return rfc4648.hex.encode( JSON.stringify({ Type: 'bls', PrivateKey: rfc4648.base64pad.encode(account.privateKey?.reverse()), }) ) } return rfc4648.hex.encode( JSON.stringify({ Type: 'secp256k1', PrivateKey: rfc4648.base64pad.encode(account.privateKey), }) ) } exports.Schemas = Schemas; exports.accountFromLotus = accountFromLotus; exports.accountFromMnemonic = accountFromMnemonic; exports.accountFromPrivateKey = accountFromPrivateKey; exports.accountFromSeed = accountFromSeed; exports.accountToLotus = accountToLotus; exports.create = create; exports.generateMnemonic = generateMnemonic; exports.getPublicKey = getPublicKey; exports.lotusBlsPrivateKeyToBytes = lotusBlsPrivateKeyToBytes; exports.mnemonicToSeed = mnemonicToSeed; exports.personalSign = personalSign; exports.personalVerify = personalVerify; exports.recoverAddress = recoverAddress; exports.recoverPublicKey = recoverPublicKey; exports.sign = sign; exports.signMessage = signMessage; exports.verify = verify;