@btc-vision/transaction
Version:
OPNet transaction library allows you to create and sign transactions for the OPNet network.
271 lines • 9.79 kB
JavaScript
import { address, fromHex, payments } from '@btc-vision/bitcoin';
import { EcKeyPair } from './EcKeyPair.js';
import { BitcoinUtils } from '../utils/BitcoinUtils.js';
import { P2WDADetector } from '../p2wda/P2WDADetector.js';
import { MLDSASecurityLevel } from '@btc-vision/bip32';
export var AddressTypes;
(function (AddressTypes) {
AddressTypes["P2PKH"] = "P2PKH";
AddressTypes["P2OP"] = "P2OP";
AddressTypes["P2SH_OR_P2SH_P2WPKH"] = "P2SH_OR_P2SH-P2WPKH";
AddressTypes["P2PK"] = "P2PK";
AddressTypes["P2TR"] = "P2TR";
AddressTypes["P2MR"] = "P2MR";
AddressTypes["P2WPKH"] = "P2WPKH";
AddressTypes["P2WSH"] = "P2WSH";
AddressTypes["P2WDA"] = "P2WDA";
})(AddressTypes || (AddressTypes = {}));
export class AddressVerificator {
static isValidP2TRAddress(inAddress, network) {
if (!inAddress || inAddress.length < 50)
return false;
let isValidTapRootAddress = false;
try {
address.toOutputScript(inAddress, network);
const decodedAddress = address.fromBech32(inAddress);
isValidTapRootAddress = decodedAddress.version === 1;
}
catch { }
return isValidTapRootAddress;
}
/**
* Validates that the given address is a valid P2MR (BIP 360) address.
* Checks for witness version 2 with a 32-byte Merkle root program.
*/
static isValidP2MRAddress(inAddress, network) {
if (!inAddress || inAddress.length < 50)
return false;
let isValidP2MR = false;
try {
address.toOutputScript(inAddress, network);
const decodedAddress = address.fromBech32(inAddress);
isValidP2MR = decodedAddress.version === 2 && decodedAddress.data.length === 32;
}
catch { }
return isValidP2MR;
}
static isP2WPKHAddress(inAddress, network) {
if (!inAddress || inAddress.length < 20 || inAddress.length > 50)
return false;
let isValidSegWitAddress = false;
try {
const decodedAddress = address.fromBech32(inAddress);
address.toOutputScript(inAddress, network);
isValidSegWitAddress =
decodedAddress.version === 0 && decodedAddress.data.length === 20;
}
catch { }
return isValidSegWitAddress;
}
static isP2WDAWitnessScript(witnessScript) {
return P2WDADetector.isP2WDAWitnessScript(witnessScript);
}
static isP2PKHOrP2SH(addy, network) {
try {
const decodedBase58 = address.fromBase58Check(addy);
if (decodedBase58.version === network.pubKeyHash) {
return true;
}
return decodedBase58.version === network.scriptHash;
}
catch (error) {
return false;
}
}
static isValidPublicKey(input, network) {
try {
if (input.startsWith('0x')) {
input = input.slice(2);
}
if (!BitcoinUtils.isValidHex(input)) {
return false;
}
if (input.length === 64) {
return true;
}
const pubKeyBuffer = fromHex(input);
if ((input.length === 130 && pubKeyBuffer[0] === 0x06) || pubKeyBuffer[0] === 0x07) {
return true;
}
if (input.length === 66 || input.length === 130) {
EcKeyPair.fromPublicKey(pubKeyBuffer, network);
return true;
}
}
catch (e) {
return false;
}
return false;
}
static isValidMLDSAPublicKey(input) {
try {
let byteLength;
if (typeof input !== 'string' && input instanceof Uint8Array) {
byteLength = input.length;
}
else {
if (input.startsWith('0x')) {
input = input.slice(2);
}
if (!BitcoinUtils.isValidHex(input)) {
return null;
}
byteLength = input.length / 2;
}
switch (byteLength) {
case 1312:
return MLDSASecurityLevel.LEVEL2;
case 1952:
return MLDSASecurityLevel.LEVEL3;
case 2592:
return MLDSASecurityLevel.LEVEL5;
default:
return null;
}
}
catch (e) {
return null;
}
}
static isValidP2OPAddress(inAddress, network) {
if (!inAddress || inAddress.length < 20)
return false;
try {
const decodedAddress = address.fromBech32(inAddress);
const validPrefix = decodedAddress.prefix === network.bech32 ||
decodedAddress.prefix === network.bech32Opnet;
if (!validPrefix) {
return false;
}
return decodedAddress.version === 16 && decodedAddress.data.length === 21;
}
catch {
return false;
}
}
static requireRedeemScript(addy, network) {
try {
const decodedBase58 = address.fromBase58Check(addy);
if (decodedBase58.version === network.pubKeyHash) {
return false;
}
return decodedBase58.version === network.scriptHash;
}
catch {
return false;
}
}
static detectAddressType(addy, network) {
if (AddressVerificator.isValidPublicKey(addy, network)) {
return AddressTypes.P2PK;
}
try {
const decodedBase58 = address.fromBase58Check(addy);
if (decodedBase58.version === network.pubKeyHash) {
return AddressTypes.P2PKH;
}
if (decodedBase58.version === network.scriptHash) {
return AddressTypes.P2SH_OR_P2SH_P2WPKH;
}
}
catch { }
try {
const decodedBech32 = address.fromBech32(addy);
if ((decodedBech32.prefix === network.bech32Opnet ||
decodedBech32.prefix === network.bech32) &&
decodedBech32.version === 16 &&
decodedBech32.data.length === 21) {
return AddressTypes.P2OP;
}
if (decodedBech32.prefix === network.bech32) {
if (decodedBech32.version === 0 && decodedBech32.data.length === 20) {
return AddressTypes.P2WPKH;
}
if (decodedBech32.version === 0 && decodedBech32.data.length === 32) {
return AddressTypes.P2WSH;
}
if (decodedBech32.version === 1 && decodedBech32.data.length === 32) {
return AddressTypes.P2TR;
}
if (decodedBech32.version === 2 && decodedBech32.data.length === 32) {
return AddressTypes.P2MR;
}
}
}
catch { }
return null;
}
static detectAddressTypeWithWitnessScript(addy, network, witnessScript) {
const baseType = AddressVerificator.detectAddressType(addy, network);
if (baseType === AddressTypes.P2WSH && witnessScript) {
if (AddressVerificator.isP2WDAWitnessScript(witnessScript)) {
return AddressTypes.P2WDA;
}
}
return baseType;
}
static validateP2WDAAddress(address, network, witnessScript) {
try {
const addressType = AddressVerificator.detectAddressType(address, network);
if (addressType !== AddressTypes.P2WSH) {
return {
isValid: false,
isPotentiallyP2WDA: false,
isDefinitelyP2WDA: false,
error: 'Not a P2WSH address',
};
}
if (!witnessScript) {
return {
isValid: true,
isPotentiallyP2WDA: true,
isDefinitelyP2WDA: false,
};
}
if (!AddressVerificator.isP2WDAWitnessScript(witnessScript)) {
return {
isValid: true,
isPotentiallyP2WDA: true,
isDefinitelyP2WDA: false,
error: 'Witness script does not match P2WDA pattern',
};
}
const p2wsh = payments.p2wsh({
redeem: { output: witnessScript },
network,
});
if (p2wsh.address !== address) {
return {
isValid: false,
isPotentiallyP2WDA: false,
isDefinitelyP2WDA: false,
error: 'Witness script does not match address',
};
}
const publicKey = P2WDADetector.extractPublicKeyFromP2WDA(witnessScript);
if (!publicKey) {
return {
isValid: false,
isPotentiallyP2WDA: false,
isDefinitelyP2WDA: false,
error: 'Failed to extract public key from witness script',
};
}
return {
isValid: true,
isPotentiallyP2WDA: true,
isDefinitelyP2WDA: true,
publicKey,
};
}
catch (error) {
return {
isValid: false,
isPotentiallyP2WDA: false,
isDefinitelyP2WDA: false,
error: error.message,
};
}
}
}
//# sourceMappingURL=AddressVerificator.js.map