Skip to content

Commit

Permalink
Add signedSize for inputs so fees can be predicted
Browse files Browse the repository at this point in the history
  • Loading branch information
MatthewLM committed Nov 8, 2023
1 parent e053348 commit 8ff580a
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 0 deletions.
6 changes: 6 additions & 0 deletions coinlib/lib/src/tx/inputs/input.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ abstract class Input with Writable {
/// True when the input is fully signed and ready for broadcast
bool get complete;

/// The maximum total size when fully signed via the default hash type
/// including any witness data of the input. If this is unknown, this is
/// null. The actual signed size may be lower according to the data being
/// encoded.
int? get signedSize => null;

Input();

/// Given a [RawInput] and witness data, the specific [Input] subclass is
Expand Down
2 changes: 2 additions & 0 deletions coinlib/lib/src/tx/inputs/p2pkh_input.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class P2PKHInput extends LegacyInput with PKHInput {
final ECPublicKey publicKey;
@override
final ECDSAInputSignature? insig;
@override
final int? signedSize = 147;

P2PKHInput({
required OutPoint prevOut,
Expand Down
13 changes: 13 additions & 0 deletions coinlib/lib/src/tx/inputs/p2sh_multisig_input.dart
Original file line number Diff line number Diff line change
Expand Up @@ -200,4 +200,17 @@ class P2SHMultisigInput extends LegacyInput {
@override
Script get script => super.script!;

int get _signedScriptSize
=> 1 // Extra 0
+ program.threshold*73 // Add 73 bytes per signature
// Determine the length of the program pushdata by actually compiling it.
// Not the most efficient but the simplest solution.
+ ScriptPushData(program.script.compiled).compiled.length;

@override
int? get signedSize
=> 40 // Outpoint plus sequence
+ _signedScriptSize
+ (_signedScriptSize < 0xfd ? 1 : 3); // Varint size

}
2 changes: 2 additions & 0 deletions coinlib/lib/src/tx/inputs/p2wpkh_input.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class P2WPKHInput extends LegacyWitnessInput with PKHInput {
final ECPublicKey publicKey;
@override
final ECDSAInputSignature? insig;
@override
final int? signedSize = 147;

P2WPKHInput({
required OutPoint prevOut,
Expand Down
4 changes: 4 additions & 0 deletions coinlib/lib/src/tx/inputs/taproot_key_input.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ class TaprootKeyInput extends TaprootInput {

final SchnorrInputSignature? insig;

@override
// 64-bit sig plus varint with default sighash type
final int? signedSize = 41 + 65;

TaprootKeyInput({
required OutPoint prevOut,
this.insig,
Expand Down
13 changes: 13 additions & 0 deletions coinlib/test/tx/transaction_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ void main() {
expect(mapToHex(tx.outputs), mapToHex(vec.obj.outputs));
}

expectInputSignedSize(Input input) => expect(
input.size, lessThanOrEqualTo(input.signedSize!),
);

test("valid txs", () {
for (final vec in validTxVecs) {

Expand Down Expand Up @@ -331,6 +335,7 @@ void main() {
key: keyVec.privateObj,
hashType: hashType,
);
expectInputSignedSize(signed.inputs[i]);
}

expect(tx.complete, false);
Expand Down Expand Up @@ -426,6 +431,7 @@ void main() {
inputN: 1, key: keyVec.privateObj, value: BigInt.from(3000000),
);
expect(signed.complete, true);
expectInputSignedSize(signed.inputs[1]);

expect(
signed.toHex(),
Expand Down Expand Up @@ -479,6 +485,9 @@ void main() {
);

expect(signed.complete, true);
for (final input in signed.inputs) {
expectInputSignedSize(input);
}

expect(
signed.toHex(),
Expand Down Expand Up @@ -602,6 +611,8 @@ void main() {
outputs: [exampleOutput, exampleOutput],
);

final signedSizeFromUnsigned = tx.inputs[1].signedSize;

// Sign first P2PKH input
var signed = tx.sign(inputN: 0, key: privkeys[0]);
expect(signed.complete, false);
Expand Down Expand Up @@ -638,6 +649,8 @@ void main() {

// Check final tx
expect(signed.complete, true);
expectInputSignedSize(signed.inputs[1]);
expect(signed.inputs[1].signedSize, signedSizeFromUnsigned);
expectMultisigSigs(
signed, [
SigHashType.single(),
Expand Down

0 comments on commit 8ff580a

Please sign in to comment.