UNPKG

open-libra-sdk

Version:

A minimalist Typescript library for interacting with the Open Libra blockchain.

141 lines (115 loc) 3.97 kB
import * as hkdf from "@noble/hashes/hkdf"; import { pbkdf2 } from "@noble/hashes/pbkdf2"; import { sha3_256 } from "@noble/hashes/sha3"; import { ed25519 } from "@noble/curves/ed25519"; import { AccountAddress, AuthenticationKey, Ed25519Account, Ed25519PrivateKey, Ed25519PublicKey, } from "@aptos-labs/ts-sdk"; import { entropyToMnemonic, validateMnemonic } from "@scure/bip39"; import { wordlist } from "@scure/bip39/wordlists/english"; import { webcrypto } from "node:crypto"; // Seed generation salting const INFO_PREFIX = Buffer.concat([ Buffer.from( "0L WALLET: US DEATHS NEAR 100,000, AN INCALCULABLE LOSS: derived key$", "ascii", ), Buffer.alloc(8), ]); const MNEMONIC_SALT_PREFIX = Buffer.from( "0L WALLET: UNREST, FIRES, AND VIOLENCE AS PROTESTS RAGE ACROSS US: mnemonic salt prefix$0L", "ascii", ); const MAIN_KEY_SALT = Buffer.from( "0L WALLET: 30 MILLION AMERICANS HAVE FILED INITIAL UNEMPLOYMENT CLAIMS: master key salt$", "ascii", ); //////// RECOVERING ACCOUNTS //////// // checks if the mnemonic string is valid export function isMnemString(mnemonic: string): boolean { // Validates mnemonic for being 12-24 words contained in `wordlist`. return validateMnemonic(mnemonic, wordlist); } // throw error if bad mnemonic function checkMnem(mnemonic: string) { if (!isMnemString(mnemonic)) { throw "ERROR: not a valid mnemonic string"; } } export function mnemonicToPrivateKey(mnemonic: string): Uint8Array { checkMnem(mnemonic); const ikm = pbkdf2(sha3_256, mnemonic, MNEMONIC_SALT_PREFIX, { c: 2048, dkLen: 32, }); const hkdfExtract = hkdf.extract(sha3_256, ikm, MAIN_KEY_SALT); return hkdf.expand(sha3_256, hkdfExtract.slice(0, 32), INFO_PREFIX, 32); } export function privateKeyToPublicKey(privateKey: Uint8Array) { return ed25519.getPublicKey(privateKey); } export function publicKeyBytesToAuthKey(publicKey: Uint8Array): Uint8Array { // Concatenate the public key with 0. 0 means Ed25519 which is the only Scheme supported for now. return sha3_256(new Uint8Array(Buffer.concat([publicKey, Buffer.from([0])]))); } export function publicKeyToAuthKey( publicKey: Ed25519PublicKey, ): AuthenticationKey { // Concatenate the public key with 0. 0 means Ed25519 which is the only Scheme supported for now. return publicKey.authKey(); } export function mnemonicToAuthKey(mnemonic: string) { checkMnem(mnemonic); return publicKeyBytesToAuthKey( privateKeyToPublicKey(mnemonicToPrivateKey(mnemonic)), ); } // OL legacy addresses (Pre V5) were only 32 bytes, the last 16 digits of an authkey export function deriveLegacyAddress(authKey: Uint8Array): Uint8Array { const lastHalf = authKey.slice(16); return lastHalf; } export function mnemonicToEd25519PrivateKey( mnemonic: string, ): Ed25519PrivateKey { const pk = mnemonicToPrivateKey(mnemonic); return new Ed25519PrivateKey(pk); } export function mnemonicToAccountObj( mnemonic: string, forceAddress?: AccountAddress, ): Ed25519Account { const privateKey = mnemonicToEd25519PrivateKey(mnemonic); const authkey = mnemonicToAuthKey(mnemonic); const address = forceAddress ?? AccountAddress.from(authkey); return newAccount(privateKey, address); } export function newAccount( privateKey: Ed25519PrivateKey, address: AccountAddress, ): Ed25519Account { return new Ed25519Account({ privateKey, address, }); } // gets an AccountAddress type from a string. // Will pad the string with zeros up to 32 characters. export function addressFromString(literal: string): AccountAddress { return AccountAddress.fromString(literal, { maxMissingChars: 63 }); } //////// CREATING ACCOUNTS //////// function getEntropyBytes(): Uint8Array { const buffer = new Uint8Array(32); webcrypto.getRandomValues(buffer); return buffer; } export function generateMnemonic(): string { const ent = getEntropyBytes(); const mnem = entropyToMnemonic(ent, wordlist); return mnem; }