UNPKG

@unspent/phi

Version:

a collection of anyone can spend contracts

275 lines (234 loc) 8.06 kB
import { binToHex, binToNumberUintLE, decodeCashAddressFormat, decodeBase58Address, decodeCashAddress, decodeCashAddressFormatWithoutPrefix, cashAddressToLockingBytecode, CashAddressNetworkPrefix, hexToBin, instantiateSha256, instantiateRipemd160, isBech32CharacterSet, utf8ToBin, numberToBinUintLE, encodeCashAddress, encodeCashAddressFormat, CashAddressType, } from "@bitauth/libauth"; import { Op, encodeNullDataScript } from "@cashscript/utils"; /** * Helper function to convert an address to a public key hash * * @param address Address to convert to a hash * * @returns a public key hash corresponding to the passed address */ export function derivePublicKeyHash(address: string): Uint8Array { let result; // If the address has a prefix decode it as is if (address.includes(":")) { result = decodeCashAddressFormat(address); } // otherwise, derive the network from the address without prefix else { result = decodeCashAddressFormatWithoutPrefix(address); } if (typeof result === "string") throw new Error(result); // return the public key hash return result.payload; } export function derivePublicKeyHashHex(address: string): string { return binToHex(derivePublicKeyHash(address)); } export function deriveLockingBytecodeHex(address: string): string { const bytecode = deriveLockingBytecode(address); return binToHex(bytecode); } export function deriveLockingBytecode(address: string): Uint8Array { const lock = cashAddressToLockingBytecode(address); if (typeof lock === "string") throw lock; return lock.bytecode; } export async function sanitizeAddress(wildString: string) { if (typeof wildString != "string") throw Error("Cashaddress was not a string"); // If the address has a prefix decode it as is let r, cashAddrResult; // in case it comes with spaces wildString = wildString.trim() // Throw on segwit address if ( wildString.substring(0, 3) === "bc1" || wildString.substring(0, 3) === "tb1" ) throw Error("Refusing to convert segwit P2SH address to cashaddress"); if (wildString.includes(":")) { r = decodeCashAddressFormat(wildString); } else { // If not Bech32, try to decode a Base58 address if (!isBech32CharacterSet(wildString)) { if (wildString[0] === "3" || wildString[0] === "2") throw Error( "Refusing to convert a legacy P2SH address (possibly segwit) to cashaddress" ); r = decodeBase58Address(wildString); if (typeof r === "string") throw Error(r); let prefix; if (r.version === 0 || r.version === 5) { prefix = "bitcoincash"; } else if (r.version === 111) { prefix = "bchtest"; } else { throw Error("Couldn't identify type of legacy address"); } cashAddrResult = encodeCashAddress({payload: r.payload, prefix: prefix as CashAddressNetworkPrefix, throwErrors:true, type: "p2pkh"}); return cashAddrResult.address; } else { r = decodeCashAddressFormatWithoutPrefix(wildString); } } // otherwise, derive the network from the address without prefix if (typeof r === "string") throw Error(r); cashAddrResult = encodeCashAddressFormat({payload:r.payload, prefix: r.prefix, throwErrors:true, version:r.version}); return cashAddrResult.address; } export function getPrefixFromNetwork( network: string ): CashAddressNetworkPrefix { let prefix = !network ? CashAddressNetworkPrefix.mainnet : undefined; if (!prefix) { if (network == "mainnet") prefix = CashAddressNetworkPrefix.mainnet; if (network == "staging") prefix = CashAddressNetworkPrefix.testnet; if (network == "chipnet") prefix = CashAddressNetworkPrefix.testnet; if (network == "regtest") prefix = CashAddressNetworkPrefix.regtest; } if (!prefix) throw Error("unknown network"); return prefix; } export function createOpReturnData(opReturnData: string[]): Uint8Array { const script = [ Op.OP_RETURN, ...opReturnData.map((output: string) => toBin(output)), ]; return encodeNullDataScript(script); } export function toBin(output: string): Uint8Array { const data = output.replace(/^0x/, ""); const encode = data === output ? utf8ToBin : hexToBin; return encode(data); } export function toHex(num: number|bigint): string { num = Number(num) let hex = binToHex(numberToBinUintLE(num)).toUpperCase(); if (!hex) hex = "00"; return "0x" + hex; } export function binToNumber(data: Uint8Array): number { const h = binToNumberUintLE(data); return h; } export function binToBigInt(data: Uint8Array): bigint { const h = binToNumberUintLE(data); return BigInt(h); } // For decoding OP_RETURN data export function decodeNullDataScript(data: Uint8Array | string) { if (typeof data === "string") data = hexToBin(data); if (data.slice(0, 1)[0] !== 106) { throw Error( "Attempted to decode NullDataScript without a OP_RETURN code (106), not an OpReturn output?" ); } // skip the OP_RETURN code data[0] let i = 1; const r: Uint8Array[] = []; while (i < data.length) { if (data.slice(i, i + 1)[0] === 0x4c) { r.push(data.slice(i, i + 1)); i + 1; } else if (data.slice(i, i + 1)[0] === 0x4d) { throw Error("Not Implemented"); } else { const len = data.slice(i, i + 1)[0]!; const start = i + 1; const end = start + len; r.push(data.slice(start, end)); i = end; } } return r; } /** * hash160 - Calculate the sha256, ripemd160 hash of a value * * @param {message} Uint8Array value to hash as a binary array * * @returns a promise to the hash160 value of the input */ export async function hash160(message: Uint8Array) { const ripemd160 = await instantiateRipemd160(); const sha256 = await instantiateSha256(); return ripemd160.hash(sha256.hash(message)); } /** * sha256 - Calculate the sha256 a value * * @param {message} Uint8Array value to hash as a binary array * * @returns a promise to the sha256 value of the input */ export async function sha256(message: Uint8Array) { const sha256 = await instantiateSha256(); return sha256.hash(message); } // Simple function to get a random integer export function getRandomIntWeak(max: number) { return Math.floor(Math.random() * Math.floor(max)); } export function sum(previousValue: any, currentValue: any) { return BigInt(previousValue) + BigInt(currentValue); } export function sumNumber(previousValue: any, currentValue: any) { return (previousValue) + (currentValue); } export function parseBigInt(num:string):bigint{ return BigInt(parseInt(num)) } export function assurePkh(address: string){ const cashaddrInfo = decodeCashAddress(address) if(typeof cashaddrInfo === "string") throw Error(cashaddrInfo) if(cashaddrInfo.type!=CashAddressType.p2pkh) throw ("Provided address was not a pay to public key hash address") } /** * Helper function to convert an address to a locking script * * @param address Address to convert to locking script * * @returns a locking script corresponding to the passed address */ export function addressToLockScript(address: string): Uint8Array { const result = cashAddressToLockingBytecode(address); if (typeof result === 'string') throw new Error(result); return result.bytecode; } /** * Helper function to convert an address to an electrum-cash compatible scripthash. * This is necessary to support electrum versions lower than 1.4.3, which do not * support addresses, only script hashes. * * @param address Address to convert to an electrum scripthash * * @returns The corresponding script hash in an electrum-cash compatible format */ export async function addressToElectrumScriptHash(address: string): Promise<string> { // Retrieve locking script const lockScript = addressToLockScript(address); // Hash locking script const scriptHash = await sha256(lockScript); // Reverse scripthash scriptHash.reverse(); // Return scripthash as a hex string return binToHex(scriptHash); }