Changeset View
Changeset View
Standalone View
Standalone View
modules/ecash-lib/src/txBuilder.ts
| Show First 20 Lines • Show All 114 Lines • ▼ Show 20 Lines | private prepareOutputs(): { | ||||
| } | } | ||||
| return { fixedOutputSum, leftoverIdx, outputs }; | return { fixedOutputSum, leftoverIdx, outputs }; | ||||
| } | } | ||||
| /** Sign the tx built by this builder and return a Tx */ | /** Sign the tx built by this builder and return a Tx */ | ||||
| public sign(ecc: Ecc, feePerKb?: number, dustLimit?: number): Tx { | public sign(ecc: Ecc, feePerKb?: number, dustLimit?: number): Tx { | ||||
| const { fixedOutputSum, leftoverIdx, outputs } = this.prepareOutputs(); | const { fixedOutputSum, leftoverIdx, outputs } = this.prepareOutputs(); | ||||
| const inputs = this.inputs.map(input => copyTxInput(input.input)); | const inputs = this.inputs.map(input => copyTxInput(input.input)); | ||||
| const updateSignatories = (ecc: Ecc, unsignedTx: UnsignedTx) => { | |||||
| for (let idx = 0; idx < this.inputs.length; ++idx) { | |||||
| const signatory = this.inputs[idx].signatory; | |||||
| const input = inputs[idx]; | |||||
| if (signatory !== undefined) { | |||||
| input.script = signatory( | |||||
| ecc, | |||||
| new UnsignedTxInput({ | |||||
| inputIdx: idx, | |||||
| unsignedTx, | |||||
| }), | |||||
| ); | |||||
| } | |||||
| } | |||||
| }; | |||||
| if (leftoverIdx !== undefined) { | if (leftoverIdx !== undefined) { | ||||
| const inputSum = this.inputSum(); | const inputSum = this.inputSum(); | ||||
| if (inputSum === undefined) { | if (inputSum === undefined) { | ||||
| throw new Error( | throw new Error( | ||||
| 'Using a leftover output requires setting SignData.value for all inputs', | 'Using a leftover output requires setting SignData.value for all inputs', | ||||
| ); | ); | ||||
| } | } | ||||
| if (feePerKb === undefined) { | if (feePerKb === undefined) { | ||||
| Show All 12 Lines | public sign(ecc: Ecc, feePerKb?: number, dustLimit?: number): Tx { | ||||
| const dummyUnsignedTx = UnsignedTx.dummyFromTx( | const dummyUnsignedTx = UnsignedTx.dummyFromTx( | ||||
| new Tx({ | new Tx({ | ||||
| version: this.version, | version: this.version, | ||||
| inputs, | inputs, | ||||
| outputs, | outputs, | ||||
| locktime: this.locktime, | locktime: this.locktime, | ||||
| }), | }), | ||||
| ); | ); | ||||
| for (let inputIdx = 0; inputIdx < this.inputs.length; ++inputIdx) { | // Must use dummy here because ECDSA sigs could be too small for fee calc | ||||
| const signatory = this.inputs[inputIdx].signatory; | updateSignatories(new EccDummy(), dummyUnsignedTx); | ||||
| const input = inputs[inputIdx]; | |||||
| if (signatory !== undefined) { | |||||
| // Must use dummy here because ECDSA sigs could be too small | |||||
| // for fee calc | |||||
| input.script = signatory( | |||||
| new EccDummy(), | |||||
| new UnsignedTxInput({ | |||||
| inputIdx, | |||||
| unsignedTx: dummyUnsignedTx, | |||||
| }), | |||||
| ); | |||||
| } | |||||
| } | |||||
| let txSize = dummyUnsignedTx.tx.serSize(); | let txSize = dummyUnsignedTx.tx.serSize(); | ||||
| let txFee = calcTxFee(txSize, feePerKb); | let txFee = calcTxFee(txSize, feePerKb); | ||||
| const leftoverValue = inputSum - (fixedOutputSum + txFee); | const leftoverValue = inputSum - (fixedOutputSum + txFee); | ||||
| if (leftoverValue < dustLimit) { | if (leftoverValue < dustLimit) { | ||||
| // inputs cannot pay for a dust leftover -> remove & recalc | // inputs cannot pay for a dust leftover -> remove & recalc | ||||
| outputs.splice(leftoverIdx, 1); | outputs.splice(leftoverIdx, 1); | ||||
| dummyUnsignedTx.tx.outputs = outputs; | dummyUnsignedTx.tx.outputs = outputs; | ||||
| // Must update signatories again as they might depend on outputs | |||||
| updateSignatories(new EccDummy(), dummyUnsignedTx); | |||||
| txSize = dummyUnsignedTx.tx.serSize(); | txSize = dummyUnsignedTx.tx.serSize(); | ||||
| txFee = calcTxFee(txSize, feePerKb); | txFee = calcTxFee(txSize, feePerKb); | ||||
| } else { | } else { | ||||
| outputs[leftoverIdx].value = leftoverValue; | outputs[leftoverIdx].value = leftoverValue; | ||||
| } | } | ||||
| if (inputSum < fixedOutputSum + txFee) { | if (inputSum < fixedOutputSum + txFee) { | ||||
| throw new Error( | throw new Error( | ||||
| `Insufficient input value (${inputSum}): Can only pay for ${ | `Insufficient input value (${inputSum}): Can only pay for ${ | ||||
| inputSum - fixedOutputSum | inputSum - fixedOutputSum | ||||
| } fees, but ${txFee} required`, | } fees, but ${txFee} required`, | ||||
| ); | ); | ||||
| } | } | ||||
| } | } | ||||
| const unsignedTx = UnsignedTx.fromTx( | const unsignedTx = UnsignedTx.fromTx( | ||||
| new Tx({ | new Tx({ | ||||
| version: this.version, | version: this.version, | ||||
| inputs, | inputs, | ||||
| outputs, | outputs, | ||||
| locktime: this.locktime, | locktime: this.locktime, | ||||
| }), | }), | ||||
| ); | ); | ||||
| for (let inputIdx = 0; inputIdx < this.inputs.length; ++inputIdx) { | updateSignatories(ecc, unsignedTx); | ||||
| const signatory = this.inputs[inputIdx].signatory; | |||||
| const input = inputs[inputIdx]; | |||||
| if (signatory !== undefined) { | |||||
| input.script = signatory( | |||||
| ecc, | |||||
| new UnsignedTxInput({ | |||||
| inputIdx, | |||||
| unsignedTx, | |||||
| }), | |||||
| ); | |||||
| } | |||||
| } | |||||
| return unsignedTx.tx; | return unsignedTx.tx; | ||||
| } | } | ||||
| } | } | ||||
| /** Calculate the required tx fee for the given txSize and feePerKb, | /** Calculate the required tx fee for the given txSize and feePerKb, | ||||
| * rounding up */ | * rounding up */ | ||||
| export function calcTxFee(txSize: number, feePerKb: number): bigint { | export function calcTxFee(txSize: number, feePerKb: number): bigint { | ||||
| return (BigInt(txSize) * BigInt(feePerKb) + 999n) / 1000n; | return (BigInt(txSize) * BigInt(feePerKb) + 999n) / 1000n; | ||||
| Show All 36 Lines | |||||