Skip to content

Commit 2d5de6e

Browse files
committed
Add InputSequence abstraction for input sequences
1 parent b19aec3 commit 2d5de6e

32 files changed

+311
-178
lines changed

coinlib/CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ secp256k1-coinlib fork.
55

66
- Adds MuSig2 support with `MuSigPublicKeys` and `MuSigStatefulSigningSession`.
77
- Includes adaptor signature support when using MuSig2 with
8-
- `SchnorrAdaptorSignature`.
8+
`SchnorrAdaptorSignature`.
9+
- Adds `InputSequence` abstraction for input sequences. Input sequences are now
10+
`0xfffffffe` by default and enforce locktimes.
11+
- Adds `locktimeIsEnforced` to `Transaction` to determine if the locktime is in
12+
effect.
913
- Moves to underlying secp256k1-coinlib.
1014
- Removed dependency to wasm_interop that had a broken js dependency.
1115
- Fixes `extraEntropy` being ignored for `Secp256k1Base.schnorrSign`.

coinlib/lib/src/coinlib_base.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export 'package:coinlib/src/tx/inputs/p2sh_multisig_input.dart';
5454
export 'package:coinlib/src/tx/inputs/p2wpkh_input.dart';
5555
export 'package:coinlib/src/tx/inputs/pkh_input.dart';
5656
export 'package:coinlib/src/tx/inputs/raw_input.dart';
57+
export 'package:coinlib/src/tx/inputs/sequence.dart';
5758
export 'package:coinlib/src/tx/inputs/taproot_input.dart';
5859
export 'package:coinlib/src/tx/inputs/taproot_key_input.dart';
5960
export 'package:coinlib/src/tx/inputs/taproot_script_input.dart';

coinlib/lib/src/tx/inputs/input.dart

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,23 @@ import 'input_signature.dart';
66
import 'p2pkh_input.dart';
77
import 'p2sh_multisig_input.dart';
88
import 'raw_input.dart';
9+
import 'sequence.dart';
910
import 'witness_input.dart';
1011

1112
/// The base class for all inputs, providing the [Input.match] factory
1213
/// constructor to determine the appropriate subclass from a [RawInput]
1314
abstract class Input with Writable {
1415

15-
static const sequenceFinal = 0xffffffff;
16-
1716
OutPoint get prevOut;
1817
Uint8List get scriptSig;
19-
int get sequence;
20-
21-
/// True when the input is fully signed and ready for broadcast
18+
InputSequence get sequence;
19+
20+
/// True when the input is fully signed where it is possible to determine so.
21+
///
22+
/// This doesn't mean the input is valid.
23+
///
24+
/// [RawInput]s will set this to true as it is not known if they are
25+
/// completed.
2226
bool get complete;
2327

2428
/// The maximum total size when fully signed via the default hash type

coinlib/lib/src/tx/inputs/legacy_input.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@ import 'package:coinlib/src/crypto/ec_private_key.dart';
22
import 'package:coinlib/src/crypto/ecdsa_signature.dart';
33
import 'package:coinlib/src/tx/sighash/legacy_signature_hasher.dart';
44
import 'package:coinlib/src/tx/sign_details.dart';
5-
import 'input.dart';
65
import 'input_signature.dart';
76
import 'p2pkh_input.dart';
87
import 'p2sh_multisig_input.dart';
98
import 'raw_input.dart';
9+
import 'sequence.dart';
1010

1111
/// Inputs that are not witness inputs: [P2PKHInput] and [P2SHMultisigInput].
1212
abstract class LegacyInput extends RawInput {
1313

1414
LegacyInput({
1515
required super.prevOut,
1616
required super.scriptSig,
17-
super.sequence = Input.sequenceFinal,
17+
super.sequence = InputSequence.enforceLocktime,
1818
});
1919

2020
/// Signs the input given the sign [details] and [key].

coinlib/lib/src/tx/inputs/legacy_witness_input.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import 'package:coinlib/src/crypto/ecdsa_signature.dart';
33
import 'package:coinlib/src/tx/sighash/witness_signature_hasher.dart';
44
import 'package:coinlib/src/tx/sign_details.dart';
55
import 'package:coinlib/src/tx/transaction.dart';
6-
import 'input.dart';
76
import 'input_signature.dart';
7+
import 'sequence.dart';
88
import 'witness_input.dart';
99

1010
/// Represents v0 witness program inputs
@@ -13,7 +13,7 @@ abstract class LegacyWitnessInput extends WitnessInput {
1313
LegacyWitnessInput({
1414
required super.prevOut,
1515
required super.witness,
16-
super.sequence = Input.sequenceFinal,
16+
super.sequence = InputSequence.enforceLocktime,
1717
});
1818

1919
/// Signs the input given the [details] and [key]. Should throw

coinlib/lib/src/tx/inputs/p2pkh_input.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import 'package:coinlib/src/scripts/operations.dart';
44
import 'package:coinlib/src/scripts/programs/p2pkh.dart';
55
import 'package:coinlib/src/scripts/script.dart';
66
import 'package:coinlib/src/tx/sign_details.dart';
7-
import 'input.dart';
87
import 'input_signature.dart';
98
import 'legacy_input.dart';
109
import 'pkh_input.dart';
1110
import 'raw_input.dart';
11+
import 'sequence.dart';
1212

1313
/// An input for a Pay-to-Public-Key-Hash output ([P2PKH]). This contains the
1414
/// public key that should match the hash in the associated output. It is either
@@ -28,7 +28,7 @@ class P2PKHInput extends LegacyInput with PKHInput {
2828
required super.prevOut,
2929
required this.publicKey,
3030
this.insig,
31-
super.sequence = Input.sequenceFinal,
31+
super.sequence = InputSequence.enforceLocktime,
3232
}) : super(
3333
scriptSig: Script([
3434
if (insig != null) ScriptPushData(insig.bytes),

coinlib/lib/src/tx/inputs/p2sh_multisig_input.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import 'package:coinlib/src/tx/sighash/legacy_signature_hasher.dart';
1111
import 'package:coinlib/src/tx/sighash/sighash_type.dart';
1212
import 'package:coinlib/src/tx/sign_details.dart';
1313
import 'package:coinlib/src/tx/transaction.dart';
14-
import 'input.dart';
1514
import 'input_signature.dart';
1615
import 'legacy_input.dart';
1716
import 'raw_input.dart';
17+
import 'sequence.dart';
1818

1919
/// An input for a Pay-to-Script-Hash output ([P2SH]) with a multisig
2020
/// redeemScript and any number of required signatures. It can be signed with
@@ -29,7 +29,7 @@ class P2SHMultisigInput extends LegacyInput {
2929
required super.prevOut,
3030
required this.program,
3131
Iterable<ECDSAInputSignature> sigs = const [],
32-
super.sequence = Input.sequenceFinal,
32+
super.sequence = InputSequence.enforceLocktime,
3333
}) : sigs = List.unmodifiable(sigs), super(
3434
scriptSig: Script([
3535
ScriptOp.fromNumber(0),

coinlib/lib/src/tx/inputs/p2wpkh_input.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import 'package:coinlib/src/crypto/ec_private_key.dart';
33
import 'package:coinlib/src/crypto/ec_public_key.dart';
44
import 'package:coinlib/src/scripts/programs/p2wpkh.dart';
55
import 'package:coinlib/src/tx/sign_details.dart';
6-
import 'input.dart';
76
import 'input_signature.dart';
87
import 'pkh_input.dart';
98
import 'raw_input.dart';
109
import 'legacy_witness_input.dart';
10+
import 'sequence.dart';
1111

1212
/// An input for a Pay-to-Witness-Public-Key-Hash output ([P2WPKH]). This
1313
/// contains the public key that should match the hash in the associated output.
@@ -28,7 +28,7 @@ class P2WPKHInput extends LegacyWitnessInput with PKHInput {
2828
required super.prevOut,
2929
required this.publicKey,
3030
this.insig,
31-
super.sequence = Input.sequenceFinal,
31+
super.sequence = InputSequence.enforceLocktime,
3232
}) : super(
3333
witness: [
3434
if (insig != null) insig.bytes,

coinlib/lib/src/tx/inputs/raw_input.dart

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import 'dart:typed_data';
2-
import 'package:coinlib/src/common/checks.dart';
32
import 'package:coinlib/src/common/serial.dart';
43
import 'package:coinlib/src/tx/outpoint.dart';
54
import 'package:coinlib/src/tx/sighash/sighash_type.dart';
65
import 'package:coinlib/src/tx/transaction.dart';
76
import 'input.dart';
87
import 'input_signature.dart';
8+
import 'sequence.dart';
99

1010
/// A transaction input without any associated witness data that acts as the
1111
/// base for all other inputs as all inputs include a outpoint, script and
@@ -17,7 +17,7 @@ class RawInput extends Input {
1717
@override
1818
final Uint8List scriptSig;
1919
@override
20-
final int sequence;
20+
final InputSequence sequence;
2121

2222
static SigHashType checkHashTypeNotSchnorr(SigHashType type) {
2323
if (type.schnorrDefault) {
@@ -31,21 +31,19 @@ class RawInput extends Input {
3131
RawInput({
3232
required this.prevOut,
3333
required this.scriptSig,
34-
this.sequence = Input.sequenceFinal,
35-
}) {
36-
checkUint32(sequence, "this.sequence");
37-
}
34+
this.sequence = InputSequence.enforceLocktime,
35+
});
3836

3937
RawInput.fromReader(BytesReader reader)
4038
: prevOut = OutPoint.fromReader(reader),
4139
scriptSig = reader.readVarSlice(),
42-
sequence = reader.readUInt32();
40+
sequence = InputSequence.fromValue(reader.readUInt32());
4341

4442
@override
4543
void write(Writer writer) {
4644
prevOut.write(writer);
4745
writer.writeVarSlice(scriptSig);
48-
writer.writeUInt32(sequence);
46+
writer.writeUInt32(sequence.value);
4947
}
5048

5149
/// Always true as a simple [RawInput] is assumed to be fully signed as there
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import 'package:coinlib/src/common/checks.dart';
2+
import 'package:coinlib/src/tx/transaction.dart';
3+
4+
/// Represents the sequence field of an input. Inputs should generally use
5+
/// [enforceLocktime] to ensure that any given locktime will function correctly.
6+
/// This is also the default behaviour of the Peercoin client.
7+
///
8+
/// To set BIP68 relative locktimes, use [InputSequence.fromValue]. There is no
9+
/// high-level abstraction for this yet.
10+
class InputSequence {
11+
12+
final int value;
13+
14+
const InputSequence._(this.value);
15+
16+
/// The usual sequence value that requires the [Transaction.locktime] to be
17+
/// enforced.
18+
static const enforceLocktime = InputSequence._(0xfffffffe);
19+
20+
/// An input which does not enforce the [Transaction.locktime]. If all inputs
21+
/// have this sequence, the transaction locktime will be ignored.
22+
static const finalWithoutLocktime = InputSequence._(0xffffffff);
23+
24+
/// Set a specific value for the sequence.
25+
InputSequence.fromValue(this.value) {
26+
checkUint32(value, "this.value");
27+
}
28+
29+
/// True if this input requires enforcement of the [Transaction.locktime].
30+
bool get locktimeIsEnforced => value < finalWithoutLocktime.value;
31+
32+
@override
33+
String toString() => value.toString();
34+
35+
@override
36+
bool operator ==(Object other)
37+
=> (other is InputSequence) && value == other.value;
38+
39+
@override
40+
int get hashCode => value;
41+
42+
}

0 commit comments

Comments
 (0)