@btc-vision/transaction
Version:
OPNet transaction library allows you to create and sign transactions for the OPNet network.
227 lines • 9.05 kB
JavaScript
import {} from '@btc-vision/ecpair';
import { backend } from '../ecc/backend.js';
import { crypto, fromHex, toHex, toXOnly } from '@btc-vision/bitcoin';
import { TweakedSigner } from '../signer/TweakedSigner.js';
import { EcKeyPair } from './EcKeyPair.js';
import { MLDSASecurityLevel } from '@btc-vision/bip32';
import { isOPWallet } from '../transaction/browser/types/OPWallet.js';
import { SignatureType } from '../transaction/browser/types/Unisat.js';
class MessageSignerBase {
sha256(message) {
return crypto.sha256(message);
}
async trySignSchnorrWithOPWallet(message) {
const wallet = this.getOPWallet();
if (!wallet) {
return null;
}
const messageBuffer = typeof message === 'string' ? new TextEncoder().encode(message) : message;
const hashedMessage = this.sha256(messageBuffer);
const messageHex = toHex(hashedMessage);
const signatureHex = await wallet.signData(messageHex, SignatureType.schnorr, typeof message === 'string' ? message : undefined);
return {
signature: fromHex(signatureHex),
message: hashedMessage,
};
}
async trySignECDSAWithOPWallet(message) {
const wallet = this.getOPWallet();
if (!wallet) {
return null;
}
const messageBuffer = typeof message === 'string' ? new TextEncoder().encode(message) : message;
const hashedMessage = this.sha256(messageBuffer);
const messageHex = toHex(hashedMessage);
const signatureHex = await wallet.signData(messageHex, SignatureType.ecdsa, typeof message === 'string' ? message : undefined);
return {
signature: fromHex(signatureHex),
message: hashedMessage,
};
}
async trySignMLDSAWithOPWallet(message) {
const wallet = this.getOPWallet();
if (!wallet) {
return null;
}
const messageBuffer = typeof message === 'string' ? new TextEncoder().encode(message) : message;
const hashedMessage = this.sha256(messageBuffer);
const messageHex = toHex(hashedMessage);
const result = await wallet.web3.signMLDSAMessage(messageHex, typeof message === 'string' ? message : undefined);
return {
signature: fromHex(result.signature),
message: hashedMessage,
publicKey: fromHex(result.publicKey),
securityLevel: result.securityLevel,
};
}
async signMessageAuto(message, keypair) {
if (!keypair) {
const walletResult = await this.trySignSchnorrWithOPWallet(message);
if (walletResult) {
return walletResult;
}
throw new Error('No keypair provided and OP_WALLET is not available.');
}
return this.signMessage(keypair, message);
}
async signMessageECDSAAuto(message, keypair) {
if (!keypair) {
const walletResult = await this.trySignECDSAWithOPWallet(message);
if (walletResult) {
return walletResult;
}
throw new Error('No keypair provided and OP_WALLET is not available.');
}
return this.signECDSA(keypair, message);
}
async tweakAndSignMessageAuto(message, keypair, network) {
if (!keypair) {
const walletResult = await this.trySignSchnorrWithOPWallet(message);
if (walletResult) {
return walletResult;
}
throw new Error('No keypair provided and OP_WALLET is not available.');
}
if (!network) {
throw new Error('Network is required when signing with a local keypair.');
}
return this.tweakAndSignMessage(keypair, message, network);
}
async signMLDSAMessageAuto(message, mldsaKeypair) {
if (!mldsaKeypair) {
const walletResult = await this.trySignMLDSAWithOPWallet(message);
if (walletResult) {
return walletResult;
}
throw new Error('No ML-DSA keypair provided and OP_WALLET is not available.');
}
return this.signMLDSAMessage(mldsaKeypair, message);
}
async verifyMLDSAWithOPWallet(message, signature) {
const wallet = this.getOPWallet();
if (!wallet) {
return null;
}
const messageBuffer = typeof message === 'string' ? new TextEncoder().encode(message) : message;
const hashedMessage = this.sha256(messageBuffer);
const mldsaSignature = {
signature: toHex(signature.signature),
publicKey: toHex(signature.publicKey),
securityLevel: signature.securityLevel,
messageHash: toHex(hashedMessage),
};
return wallet.web3.verifyMLDSASignature(toHex(hashedMessage), mldsaSignature);
}
async getMLDSAPublicKeyFromOPWallet() {
const wallet = this.getOPWallet();
if (!wallet) {
return null;
}
const publicKeyHex = await wallet.web3.getMLDSAPublicKey();
return fromHex(publicKeyHex);
}
tweakAndSignMessage(keypair, message, network) {
const tweaked = TweakedSigner.tweakSigner(keypair, { network });
return this.signMessage(tweaked, message);
}
signMessage(keypair, message) {
if (typeof message === 'string') {
message = new TextEncoder().encode(message);
}
if (!keypair.privateKey) {
throw new Error('Private key not found in keypair.');
}
const hashedMessage = this.sha256(message);
if (!backend.signSchnorr) {
throw new Error('backend.signSchnorr is not available.');
}
return {
signature: backend.signSchnorr(hashedMessage, keypair.privateKey),
message: hashedMessage,
};
}
signECDSA(keypair, message) {
if (typeof message === 'string') {
message = new TextEncoder().encode(message);
}
if (!keypair.privateKey) {
throw new Error('Private key not found in keypair.');
}
const hashedMessage = this.sha256(message);
if (!backend.sign) {
throw new Error('backend.signSchnorr is not available.');
}
return {
signature: backend.sign(hashedMessage, keypair.privateKey),
message: hashedMessage,
};
}
verifyECDSASignature(publicKey, message, signature) {
if (typeof message === 'string') {
message = new TextEncoder().encode(message);
}
if (signature.length !== 64) {
throw new Error('Invalid signature length.');
}
const hashedMessage = this.sha256(message);
if (!backend.verify) {
throw new Error('backend.verifySchnorr is not available.');
}
return backend.verify(hashedMessage, publicKey, signature);
}
verifySignature(publicKey, message, signature) {
if (typeof message === 'string') {
message = new TextEncoder().encode(message);
}
if (signature.length !== 64) {
throw new Error('Invalid signature length.');
}
const hashedMessage = this.sha256(message);
if (!backend.verifySchnorr) {
throw new Error('backend.verifySchnorr is not available.');
}
return backend.verifySchnorr(hashedMessage, toXOnly(publicKey), signature);
}
tweakAndVerifySignature(publicKey, message, signature) {
const tweakedPublicKey = EcKeyPair.tweakPublicKey(publicKey);
return this.verifySignature(tweakedPublicKey, message, signature);
}
signMLDSAMessage(mldsaKeypair, message) {
if (typeof message === 'string') {
message = new TextEncoder().encode(message);
}
if (!mldsaKeypair.privateKey) {
throw new Error('ML-DSA private key not found in keypair.');
}
const hashedMessage = this.sha256(message);
const signature = mldsaKeypair.sign(hashedMessage);
return {
signature: new Uint8Array(signature),
message: hashedMessage,
publicKey: new Uint8Array(mldsaKeypair.publicKey),
securityLevel: mldsaKeypair.securityLevel,
};
}
verifyMLDSASignature(mldsaKeypair, message, signature) {
if (typeof message === 'string') {
message = new TextEncoder().encode(message);
}
const hashedMessage = this.sha256(message);
return mldsaKeypair.verify(hashedMessage, signature);
}
isOPWalletAvailable() {
return this.getOPWallet() !== null;
}
getOPWallet() {
if (typeof window === 'undefined') {
return null;
}
const _window = window;
if (!_window.opnet || !isOPWallet(_window.opnet)) {
return null;
}
return _window.opnet;
}
}
export const MessageSigner = new MessageSignerBase();
//# sourceMappingURL=MessageSigner.js.map