Whereas broadcasting uncooked transaction has to chain, acquired the under error
RPC error: {“code”:-26,”message”:”non-mandatory-script-verify-flag (Invalid Schnorr signature)”} however signature verification methodology says its a legitimate signature for the given message.
I am making an attempt to do a easy taproot ship transaction (key path spending). Signature is given by exterior signer
My information factors are as under
{
message: '60ccb78b5936d80f63c32eeafe9b746909d7e716f0f96988f437972e633ec7e0',
signature: 'ed3061523f39137cdab4c9ca0bf9b62433b9642bb8165d996dcc120662866e8060e6f2314d7b03bab31f0a95b4574ddbed2892a176d2723810bd28fda66d1496',
publicKey: 'ddb666300b8d7ef23e61e2ff3af3c70b59f882d6de1deb601590bb4d15b5d5d3'
}
Transaction hash
020000000001012bd272fa216d2370e265f591907266a8e76a26bc0be4318c9b1b340eaa6134860000000000ffffffff021027000000000000225120ddb666300b8d7ef23e61e2ff3af3c70b59f882d6de1deb601590bb4d15b5d5d3a8c8fa0200000000225120ddb666300b8d7ef23e61e2ff3af3c70b59f882d6de1deb601590bb4d15b5d5d30141ed3061523f39137cdab4c9ca0bf9b62433b9642bb8165d996dcc120662866e8060e6f2314d7b03bab31f0a95b4574ddbed2892a176d2723810bd28fda66d14960100000000
That is my whole code
import sha256 from "sha256";
import { Psbt, networks, funds, Transaction } from "bitcoinjs-lib";
import dotenv from 'dotenv';
import { verifySchnorrSignature } from "./mpc/handle.js";
import { signBTCTransaction } from "./systemCallerUtils.js";
dotenv.config();
const UNCONFIRMED_HEIGHT = 4194303;
async operate btcSendTransaction() {
strive {
const handle = course of.env.BTC_ADDRESS;
return await createSendTransaction(handle);
} catch (err) {
console.error("Error in btcSendTransaction:", err);
throw err;
}
}
async operate createSendTransaction(handle) {
const txObj = await handleTxObj(handle);
const { preSignTxHashes, psbt } = await generatePreSignTxHash(txObj);
console.log("preSignTxHashes", preSignTxHashes);
// Generate signatures for every enter
const generatedSigns = [];
for (let i = 0; i < preSignTxHashes.size; i++) {
const generatedSign = await signBTCTransaction(preSignTxHashes[i], "HASH");
if (generatedSign) {
generatedSigns.push(generatedSign);
}
}
// Take away the '0x' prefix for verification
const hashToVerify = preSignTxHashes[0].exchange('0x', '');
console.log('Hash for verification:', hashToVerify);
strive {
const isValid = await verifySchnorrSignature(
hashToVerify,
generatedSigns[0].signal
);
console.log("Signature verification:", isValid);
if (!isValid) {
console.error('Verification failed:', {
hash: hashToVerify,
signature: generatedSigns[0].signal,
pubkey: course of.env.MPC_PUB_KEY
});
throw new Error('Invalid signature generated');
}
} catch (error) {
console.error('Verification error:', error);
throw error;
}
strive {
if (generatedSigns.size) {
const response = await broadCastTx(generatedSigns, psbt);
return response;
}
} catch (e) {
console.error('Broadcast error:', e);
throw e;
}
}
const handleTxObj = async (handle) => {
strive {
let utxos = await getUtxos(handle);
utxos = utxos.filter((v) => v.peak !== UNCONFIRMED_HEIGHT);
const outputAmount = 10000;
const feeRate = 200;
const outputs = [{
address: address,
amount: outputAmount,
}];
const { selectedUtxos, totalValue } = selectUtxos(utxos, outputAmount + feeRate);
console.log("selectedUtxos", selectedUtxos);
return {
fromAddress: handle,
utxos: selectedUtxos,
outputs,
feeRate,
toAddress: handle,
};
} catch (error) {
console.error("handleTxObj error:", error);
throw error;
}
}
export operate selectUtxos(utxos, targetAmount) {
utxos.type((a, b) => b.worth - a.worth);
const selectedUtxos = [];
let totalValue = 0;
for (const utxo of utxos) {
selectedUtxos.push(utxo);
totalValue += utxo.worth;
if (totalValue >= targetAmount) break;
}
if (totalValue < targetAmount) {
throw new Error('Inadequate UTXOs to cowl the goal quantity');
}
return { selectedUtxos, totalValue };
}
export const generatePreSignTxHash = async (txObject) => {
const community = networks.testnet;
const psbt = new Psbt({ community });
strive {
const { utxos, fromAddress, outputs, feeRate } = txObject;
let inputSum = 0;
const pubKey = course of.env.MPC_PUB_KEY;
const xOnlyPubKey = Buffer.from(pubKey, 'hex');
// Add inputs utilizing the precise UTXO script
for (const utxo of utxos) {
inputSum += utxo.worth;
// Get the precise UTXO particulars
const txDetails = await fetch(`https://esplora.signet.surge.dev/tx/${utxo.txid}/hex`).then(r => r.textual content());
const tx = Transaction.fromHex(txDetails);
const outputScript = tx.outs[utxo.vout].script;
console.log('UTXO particulars:', {
script: outputScript.toString('hex'),
worth: utxo.worth
});
psbt.addInput({
hash: utxo.txid,
index: utxo.vout,
witnessUtxo: {
script: outputScript,
worth: utxo.worth,
},
tapInternalKey: xOnlyPubKey,
});
}
// Add outputs
for (const output of outputs) {
psbt.addOutput({
handle: output.handle,
worth: output.quantity,
});
}
// Add change output
const totalOutputAmount = outputs.scale back((sum, output) => sum + output.quantity, 0);
const changeAmount = inputSum - totalOutputAmount - feeRate;
if (changeAmount > 546) {
psbt.addOutput({
handle: fromAddress,
worth: changeAmount,
});
}
// Generate signing hashes
const unsignedTx = Transaction.fromBuffer(psbt.information.globalMap.unsignedTx.toBuffer());
const preSignTxHashes = psbt.information.inputs.map((enter, index) => {
const hash = unsignedTx.hashForWitnessV1(
index,
[input.witnessUtxo.script],
[input.witnessUtxo.value],
Transaction.SIGHASH_ALL
);
console.log('Enter particulars:', {
script: enter.witnessUtxo.script.toString('hex'),
worth: enter.witnessUtxo.worth,
hash: hash.toString('hex')
});
return `0x${hash.toString('hex')}`;
});
return { preSignTxHashes, psbt };
} catch (error) {
console.error('Error in generatePreSignTxHash:', error);
throw error;
}
};
export const broadCastTx = async (signatures, psbt) => {
if (!signatures.size) {
throw new Error('No signatures supplied');
}
strive {
signatures.forEach((sig, index) => {
const sigBuffer = Buffer.from(sig.signal.exchange('0x', ''), 'hex');
console.log(`Uncooked signature for enter ${index}:`, sigBuffer.toString('hex'));
console.log('Signature size:', sigBuffer.size);
// For Taproot with SIGHASH_ALL
const sigWithHashtype = Buffer.concat([
sigBuffer,
Buffer.from([Transaction.SIGHASH_ALL])
]);
console.log(`Signature with hashtype for enter ${index}:`, sigWithHashtype.toString('hex'));
psbt.updateInput(index, {
tapKeySig: sigWithHashtype
});
});
psbt.finalizeAllInputs();
const tx = psbt.extractTransaction(true);
const rawTx = tx.toHex();
console.log('Last transaction:', rawTx);
console.log('Transaction particulars:', {
model: tx.model,
inputs: tx.ins.map(enter => ({
txid: enter.hash.reverse().toString('hex'),
vout: enter.index,
witness: enter.witness.map(w => w.toString('hex'))
})),
outputs: tx.outs.map(output => ({
worth: output.worth,
script: output.script.toString('hex')
}))
});
return await pushTx(rawTx);
} catch (error) {
console.error('Did not broadcast transaction:', error);
throw error;
}
};
export const pushTx = async (rawtx) => {
strive {
const endpoint="https://esplora.signet.surge.dev/tx";
const resp = await fetch(endpoint, {
methodology: "POST",
headers: { "Content material-Kind": "textual content/plain" },
physique: rawtx,
});
if (!resp.okay) {
const errorText = await resp.textual content();
throw new Error(`HTTP error! standing: ${resp.standing}, message: ${errorText}`);
}
const txid = await resp.textual content();
return { txid };
} catch (error) {
console.error('Push transaction error:', error);
throw error;
}
};
export const getUtxos = async (handle) => {
strive {
const endpoint = `https://esplora.signet.surge.dev/handle/${handle}/utxo`;
const resp = await fetch(endpoint, {
methodology: "GET",
headers: { "Content material-Kind": "software/json" },
});
if (!resp.okay) {
throw new Error(`HTTP error! standing: ${resp.standing}`);
}
return await resp.json();
} catch (error) {
console.error('Get UTXOs error:', error);
throw error;
}
};
export { btcSendTransaction };
Can anybody assist me with this?