open-libra-sdk
Version:
A minimalist Typescript library for interacting with the Open Libra blockchain.
141 lines (115 loc) • 3.97 kB
text/typescript
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;
}