@unspent/phi
Version:
a collection of anyone can spend contracts
229 lines • 8.16 kB
JavaScript
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) {
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) {
return binToHex(derivePublicKeyHash(address));
}
export function deriveLockingBytecodeHex(address) {
const bytecode = deriveLockingBytecode(address);
return binToHex(bytecode);
}
export function deriveLockingBytecode(address) {
const lock = cashAddressToLockingBytecode(address);
if (typeof lock === "string")
throw lock;
return lock.bytecode;
}
export async function sanitizeAddress(wildString) {
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, 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) {
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) {
const script = [
Op.OP_RETURN,
...opReturnData.map((output) => toBin(output)),
];
return encodeNullDataScript(script);
}
export function toBin(output) {
const data = output.replace(/^0x/, "");
const encode = data === output ? utf8ToBin : hexToBin;
return encode(data);
}
export function toHex(num) {
num = Number(num);
let hex = binToHex(numberToBinUintLE(num)).toUpperCase();
if (!hex)
hex = "00";
return "0x" + hex;
}
export function binToNumber(data) {
const h = binToNumberUintLE(data);
return h;
}
export function binToBigInt(data) {
const h = binToNumberUintLE(data);
return BigInt(h);
}
// For decoding OP_RETURN data
export function decodeNullDataScript(data) {
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 = [];
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) {
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) {
const sha256 = await instantiateSha256();
return sha256.hash(message);
}
// Simple function to get a random integer
export function getRandomIntWeak(max) {
return Math.floor(Math.random() * Math.floor(max));
}
export function sum(previousValue, currentValue) {
return BigInt(previousValue) + BigInt(currentValue);
}
export function sumNumber(previousValue, currentValue) {
return (previousValue) + (currentValue);
}
export function parseBigInt(num) {
return BigInt(parseInt(num));
}
export function assurePkh(address) {
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) {
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) {
// 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);
}
//# sourceMappingURL=util.js.map