Update btcOperator.js

1. Added editFee_corewallet function to handle Bech32 multisig fee edits
2. Added btcOperator.extractLastHexStrings funtion to extract last item of witness to get RedeemScript
3. Exposed internal functions to btcOperator so that they can be accessed externally
This commit is contained in:
tripathyr 2023-09-30 13:47:34 +05:30 committed by GitHub
parent eb26e95545
commit 19441ef04e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -297,12 +297,15 @@
return key ? coinjs.segwitAddress(btcOperator.pubkey(key)).redeemscript : null;
case "bech32":
return decode.redeemscript;
case "'multisigBech32":
return decode.redeemscript; //Multisig-edit-fee-change1
case "bech32m":
return decode.outstring; //Maybe the redeemscript will come when input processing happens for bech32m
default:
return null;
}
}
btcOperator._redeemScript = _redeemScript;
function _sizePerInput(addr, rs) {
switch (coinjs.addressDecode(addr).type) {
@ -389,6 +392,7 @@
//return
return parameters;
}
btcOperator.validateTxParameters = validateTxParameters;
function createTransaction(senders, redeemScripts, receivers, amounts, fee, change_address, fee_from_receiver) {
return new Promise((resolve, reject) => {
@ -427,6 +431,7 @@
}).catch(error => reject(error))
})
}
btcOperator.createTransaction = createTransaction;
function addInputs(tx, senders, redeemScripts, total_amount, fee, output_size, fee_from_receiver) {
return new Promise((resolve, reject) => {
@ -451,6 +456,7 @@
}
})
}
btcOperator.addInputs = addInputs;
function addUTXOs(tx, senders, redeemScripts, required_amount, fee_rate, rec_args = {}) {
return new Promise((resolve, reject) => {
@ -505,6 +511,7 @@
}).catch(error => reject(error))
})
}
btcOperator.addUTXOs = addUTXOs;
function addOutputs(tx, receivers, amounts, change_address) {
let size = 0;
@ -516,6 +523,7 @@
size += _sizePerOutput(change_address);
return size;
}
btcOperator.addOutputs = addOutputs;
/*
function autoFeeCalc(tx) {
@ -563,7 +571,19 @@
} else resolve(deserializeTx(tx));
})
}
btcOperator.tx_fetch_for_editing = tx_fetch_for_editing;
const extractLastHexStrings = btcOperator.extractLastHexStrings = function (arr) {
const result = [];
for (let i = 0; i < arr.length; i++) {
const innerArray = arr[i];
if (innerArray.length > 0) {
const lastHexString = innerArray[innerArray.length - 1];
result.push(lastHexString);
}
}
return result;
}
btcOperator.editFee = function (tx_hex, new_fee, private_keys, change_only = true) {
return new Promise((resolve, reject) => {
@ -654,6 +674,115 @@
})
}
btcOperator.editFee_corewallet = function(tx_hex, new_fee, private_keys, change_only = true) {
return new Promise((resolve, reject) => {
if (!Array.isArray(private_keys))
private_keys = [private_keys];
tx_fetch_for_editing(tx_hex).then(tx => {
parseTransaction(tx).then(tx_parsed => {
if (tx_parsed.fee >= new_fee)
return reject("Fees can only be increased");
//editable addresses in output values (for fee increase)
var edit_output_address = new Set();
if (change_only === true) //allow only change values (ie, sender address) to be edited to inc fee
tx_parsed.inputs.forEach(inp => edit_output_address.add(inp.address));
else if (change_only === false) //allow all output values to be edited
tx_parsed.outputs.forEach(out => edit_output_address.add(out.address));
else if (typeof change_only == 'string') // allow only given receiver id output to be edited
edit_output_address.add(change_only);
else if (Array.isArray(change_only)) //allow only given set of receiver id outputs to be edited
change_only.forEach(id => edit_output_address.add(id));
//edit output values to increase fee
let inc_fee = util.BTC_to_Sat(new_fee - tx_parsed.fee);
if (inc_fee < MIN_FEE_UPDATE)
return reject(`Insufficient additional fee. Minimum increment: ${MIN_FEE_UPDATE}`);
for (let i = tx.outs.length - 1; i >= 0 && inc_fee > 0; i--) //reduce in reverse order
if (edit_output_address.has(tx_parsed.outputs[i].address)) {
let current_value = tx.outs[i].value;
if (current_value instanceof BigInteger) //convert BigInteger class to inv value
current_value = current_value.intValue();
//edit the value as required
if (current_value > inc_fee) {
tx.outs[i].value = current_value - inc_fee;
inc_fee = 0;
} else {
inc_fee -= current_value;
tx.outs[i].value = 0;
}
}
if (inc_fee > 0) {
let max_possible_fee = util.BTC_to_Sat(new_fee) - inc_fee; //in satoshi
return reject(`Insufficient output values to increase fee. Maximum fee possible: ${util.Sat_to_BTC(max_possible_fee)}`);
}
tx.outs = tx.outs.filter(o => o.value >= DUST_AMT); //remove all output with value less than DUST amount
//remove existing signatures and reset the scripts
let wif_keys = [];
let witness_position = 0;
for (let i in tx.ins) {
var addr = tx_parsed.inputs[i].address,
value = util.BTC_to_Sat(tx_parsed.inputs[i].value);
let addr_decode = coinjs.addressDecode(addr);
//find the correct key for addr
var privKey = private_keys.find(pk => verifyKey(addr, pk));
if (!privKey)
return reject(`Private key missing for ${addr}`);
//find redeemScript (if any)
const rs = _redeemScript(addr, privKey);
rs === false ? wif_keys.unshift(privKey) : wif_keys.push(privKey); //sorting private-keys (wif)
//reset the script for re-signing
var script;
if (!rs || !rs.length) {
//legacy script (derive from address)
let s = coinjs.script();
s.writeOp(118); //OP_DUP
s.writeOp(169); //OP_HASH160
s.writeBytes(addr_decode.bytes);
s.writeOp(136); //OP_EQUALVERIFY
s.writeOp(172); //OP_CHECKSIG
script = Crypto.util.bytesToHex(s.buffer);
} else if (((rs.match(/^00/) && rs.length == 44)) || (rs.length == 40 && rs.match(/^[a-f0-9]+$/gi))) {
//redeemScript for segwit/bech32
let s = coinjs.script();
s.writeBytes(Crypto.util.hexToBytes(rs));
s.writeOp(0);
s.writeBytes(coinjs.numToBytes(value.toFixed(0), 8));
script = Crypto.util.bytesToHex(s.buffer);
if (addr_decode == "bech32") {witness_position = witness_position + 1;} //bech32 has witness
} else if (addr_decode.type === 'multisigBech32') {
var rs_array = [];
rs_array = btcOperator.extractLastHexStrings(tx.witness);
let redeemScript = rs_array[witness_position];
witness_position = witness_position + 1;
//redeemScript multisig (bech32)
let s = coinjs.script();
s.writeBytes(Crypto.util.hexToBytes(redeemScript));
s.writeOp(0);
s.writeBytes(coinjs.numToBytes(value.toFixed(0), 8));
script = Crypto.util.bytesToHex(s.buffer);
} else //redeemScript for multisig (segwit)
script = rs;
tx.ins[i].script = coinjs.script(script);
}
tx.witness = false; //remove all witness signatures
console.debug("Unsigned:", tx.serialize());
//re-sign the transaction
new Set(wif_keys).forEach(key => tx.sign(key, 1 /*sighashtype*/ )); //Sign the tx using private key WIF
if (btcOperator.checkSigned(tx)) {
resolve(tx.serialize());
} else {
reject("All private keys not present");
}
}).catch(error => reject(error))
}).catch(error => reject(error))
})
}
btcOperator.sendTx = function (senders, privkeys, receivers, amounts, fee = null, options = {}) {
return new Promise((resolve, reject) => {
createSignedTx(senders, privkeys, receivers, amounts, fee, options).then(result => {
@ -915,7 +1044,7 @@
}).catch(error => reject(error))
});
getTx.hex = txid => new Promise((resolve, reject) => {
getTx.hex = btcOperator.geTx.hex = txid => new Promise((resolve, reject) => {
fetch_api(`rawtx/${txid}?format=hex`, false)
.then(result => resolve(result))
.catch(error => reject(error))