Skip to content

Commit

Permalink
Move sign logic to sign() methods in inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
MatthewLM committed Oct 12, 2023
1 parent aa183ca commit 42003bc
Show file tree
Hide file tree
Showing 22 changed files with 281 additions and 119 deletions.
17 changes: 9 additions & 8 deletions coinlib/lib/src/coinlib_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,18 @@ export 'package:coinlib/src/scripts/programs/p2witness.dart';
export 'package:coinlib/src/scripts/programs/p2wpkh.dart';
export 'package:coinlib/src/scripts/programs/p2wsh.dart';

export 'package:coinlib/src/tx/input.dart';
export 'package:coinlib/src/tx/input_signature.dart';
export 'package:coinlib/src/tx/transaction.dart';
export 'package:coinlib/src/tx/outpoint.dart';
export 'package:coinlib/src/tx/output.dart';
export 'package:coinlib/src/tx/p2pkh_input.dart';
export 'package:coinlib/src/tx/p2sh_multisig_input.dart';
export 'package:coinlib/src/tx/p2wpkh_input.dart';
export 'package:coinlib/src/tx/pkh_input.dart';
export 'package:coinlib/src/tx/raw_input.dart';
export 'package:coinlib/src/tx/witness_input.dart';

export 'package:coinlib/src/tx/inputs/input.dart';
export 'package:coinlib/src/tx/inputs/input_signature.dart';
export 'package:coinlib/src/tx/inputs/p2pkh_input.dart';
export 'package:coinlib/src/tx/inputs/p2sh_multisig_input.dart';
export 'package:coinlib/src/tx/inputs/p2wpkh_input.dart';
export 'package:coinlib/src/tx/inputs/pkh_input.dart';
export 'package:coinlib/src/tx/inputs/raw_input.dart';
export 'package:coinlib/src/tx/inputs/witness_input.dart';

export 'package:coinlib/src/tx/sighash/legacy_signature_hasher.dart';
export 'package:coinlib/src/tx/sighash/sighash_type.dart';
Expand Down
2 changes: 1 addition & 1 deletion coinlib/lib/src/scripts/operations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'dart:typed_data';
import 'package:coinlib/src/common/hex.dart';
import 'package:coinlib/src/common/serial.dart';
import 'package:coinlib/src/crypto/ec_public_key.dart';
import 'package:coinlib/src/tx/input_signature.dart';
import 'package:coinlib/src/tx/inputs/input_signature.dart';
import 'package:collection/collection.dart';
import 'codes.dart';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import 'dart:typed_data';
import 'package:coinlib/src/common/serial.dart';
import 'package:coinlib/src/scripts/script.dart';
import 'package:coinlib/src/tx/input_signature.dart';
import 'outpoint.dart';
import 'package:coinlib/src/tx/outpoint.dart';
import 'input_signature.dart';
import 'p2pkh_input.dart';
import 'p2sh_multisig_input.dart';
import 'raw_input.dart';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'dart:typed_data';
import 'package:coinlib/src/crypto/ecdsa_signature.dart';
import 'sighash/sighash_type.dart';
import 'package:coinlib/src/tx/sighash/sighash_type.dart';

class InvalidInputSignature implements Exception {}

Expand Down
51 changes: 51 additions & 0 deletions coinlib/lib/src/tx/inputs/legacy_input.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import 'package:coinlib/src/crypto/ec_private_key.dart';
import 'package:coinlib/src/crypto/ecdsa_signature.dart';
import 'package:coinlib/src/scripts/script.dart';
import 'package:coinlib/src/tx/sighash/legacy_signature_hasher.dart';
import 'package:coinlib/src/tx/sighash/sighash_type.dart';
import 'package:coinlib/src/tx/transaction.dart';
import 'input.dart';
import 'input_signature.dart';
import 'raw_input.dart';

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

LegacyInput({
required super.prevOut,
required super.scriptSig,
super.sequence = Input.sequenceFinal,
});

/// Signs the input given the [tx], input number ([inputN]) and a private
/// [key] using the specifified [hashType].
/// Implemented by specific subclasses.
LegacyInput sign({
required Transaction tx,
required int inputN,
required ECPrivateKey key,
hashType = const SigHashType.all(),
});

/// Creates a signature for the input. Used by subclasses to implement
/// signing.
InputSignature createInputSignature({
required Transaction tx,
required int inputN,
required ECPrivateKey key,
required Script scriptCode,
hashType = const SigHashType.all(),
}) => InputSignature(
ECDSASignature.sign(
key,
LegacySignatureHasher(
tx: tx,
inputN: inputN,
scriptCode: scriptCode,
hashType: hashType,
).hash,
),
hashType,
);

}
55 changes: 55 additions & 0 deletions coinlib/lib/src/tx/inputs/legacy_witness_input.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import 'package:coinlib/src/crypto/ec_private_key.dart';
import 'package:coinlib/src/crypto/ecdsa_signature.dart';
import 'package:coinlib/src/scripts/script.dart';
import 'package:coinlib/src/tx/sighash/sighash_type.dart';
import 'package:coinlib/src/tx/sighash/witness_signature_hasher.dart';
import 'package:coinlib/src/tx/transaction.dart';
import 'input.dart';
import 'input_signature.dart';
import 'witness_input.dart';

/// Represents v0 witness program inputs
abstract class LegacyWitnessInput extends WitnessInput {

LegacyWitnessInput({
required super.prevOut,
required super.witness,
super.sequence = Input.sequenceFinal,
});

/// Signs the input given the [tx], input number ([inputN]), private
/// [key] and input [value] using the specifified [hashType]. Should throw
/// [CannotSignInput] if the key cannot sign the input.
/// Implemented by specific subclasses.
LegacyWitnessInput sign({
required Transaction tx,
required int inputN,
required ECPrivateKey key,
required BigInt value,
hashType = const SigHashType.all(),
});

/// Creates a signature for the input. Used by subclasses to implement
/// signing.
InputSignature createInputSignature({
required Transaction tx,
required int inputN,
required ECPrivateKey key,
required Script scriptCode,
required BigInt value,
hashType = const SigHashType.all(),
}) => InputSignature(
ECDSASignature.sign(
key,
WitnessSignatureHasher(
tx: tx,
inputN: inputN,
scriptCode: scriptCode,
value: value,
hashType: hashType,
).hash,
),
hashType,
);

}
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import 'package:coinlib/src/crypto/ec_private_key.dart';
import 'package:coinlib/src/crypto/ec_public_key.dart';
import 'package:coinlib/src/scripts/operations.dart';
import 'package:coinlib/src/tx/input_signature.dart';
import 'package:coinlib/src/scripts/programs/p2pkh.dart';
import 'package:coinlib/src/scripts/script.dart';
import 'package:coinlib/src/tx/outpoint.dart';
import 'package:coinlib/src/tx/pkh_input.dart';
import '../scripts/script.dart';
import 'package:coinlib/src/tx/sighash/sighash_type.dart';
import 'package:coinlib/src/tx/transaction.dart';
import 'input.dart';
import 'input_signature.dart';
import 'legacy_input.dart';
import 'pkh_input.dart';
import 'raw_input.dart';

/// An input for a Pay-to-Public-Key-Hash output ([P2PKH]). This contains the
/// public key that should match the hash in the associated output. It is either
/// signed or unsigned and the [addSignature] method can be used to add a signature.
class P2PKHInput extends RawInput with PKHInput {
/// signed or unsigned. The [sign] method can be used to sign the input with the
/// corresponding [ECPrivateKey] or a signature can be added without checks
/// using [addSignature].
class P2PKHInput extends LegacyInput with PKHInput {

@override
final ECPublicKey publicKey;
Expand Down Expand Up @@ -56,6 +63,22 @@ class P2PKHInput extends RawInput with PKHInput {

}

@override
P2PKHInput sign({
required Transaction tx,
required int inputN,
required ECPrivateKey key,
hashType = const SigHashType.all(),
}) => addSignature(
createInputSignature(
tx: tx,
inputN: inputN,
key: checkKey(key),
scriptCode: scriptCode,
hashType: hashType,
),
);

@override
/// Returns a new [P2PKHInput] with the [InputSignature] added. Any existing
/// signature is replaced.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import 'dart:typed_data';
import 'package:coinlib/src/common/serial.dart';
import 'package:coinlib/src/crypto/ec_private_key.dart';
import 'package:coinlib/src/crypto/ec_public_key.dart';
import 'package:coinlib/src/scripts/operations.dart';
import 'package:coinlib/src/scripts/program.dart';
import 'package:coinlib/src/scripts/programs/multisig.dart';
import 'package:coinlib/src/scripts/script.dart';
import 'package:coinlib/src/tx/input_signature.dart';
import 'package:coinlib/src/tx/outpoint.dart';
import 'package:coinlib/src/tx/sighash/legacy_signature_hasher.dart';
import 'package:coinlib/src/tx/sighash/sighash_type.dart';
import 'package:coinlib/src/tx/transaction.dart';
import 'input.dart';
import 'input_signature.dart';
import 'legacy_input.dart';
import 'raw_input.dart';
import 'sighash/sighash_type.dart';

/// An input for a Pay-to-Script-Hash output ([P2SH]) with a multisig
/// redeemScript and any number of required signatures that may be provided with
/// [replaceSignatures].
class P2SHMultisigInput extends RawInput {
/// redeemScript and any number of required signatures. It can be signed with
/// one of the associated [ECPrivateKey] objects using [sign] or an existing
/// signature can be inserted with [insertSignature].
class P2SHMultisigInput extends LegacyInput {

final MultisigProgram program;
final List<InputSignature> sigs;
Expand Down Expand Up @@ -87,6 +92,37 @@ class P2SHMultisigInput extends RawInput {

}

@override
LegacyInput sign({
required Transaction tx,
required int inputN,
required ECPrivateKey key,
hashType = const SigHashType.all(),
}) {

if (!program.pubkeys.contains(key.pubkey)) {
throw CannotSignInput("Key doesn't exist for multisig input");
}

return insertSignature(
createInputSignature(
tx: tx,
inputN: inputN,
key: key,
scriptCode: program.script,
hashType: hashType,
),
key.pubkey,
(hashType) => LegacySignatureHasher(
tx: tx,
inputN: inputN,
scriptCode: program.script,
hashType: hashType,
).hash,
);

}

/// Returns a new [P2SHMultisigInput] with the new signature added in order.
/// The [pubkey] should be the public key for the signature to ensure that it
/// matches. [getSigHash] obtains the signature hash for a given type so that
Expand Down Expand Up @@ -156,5 +192,4 @@ class P2SHMultisigInput extends RawInput {
@override
Script get script => super.script!;


}
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import 'dart:typed_data';
import 'package:coinlib/src/crypto/ec_private_key.dart';
import 'package:coinlib/src/crypto/ec_public_key.dart';
import 'package:coinlib/src/tx/input.dart';
import 'package:coinlib/src/tx/input_signature.dart';
import 'package:coinlib/src/tx/outpoint.dart';
import 'package:coinlib/src/tx/sighash/sighash_type.dart';
import 'package:coinlib/src/tx/transaction.dart';
import 'input.dart';
import 'input_signature.dart';
import 'pkh_input.dart';
import 'raw_input.dart';
import 'witness_input.dart';
import 'legacy_witness_input.dart';

/// An input for a Pay-to-Witness-Public-Key-Hash output ([P2WPKH]). This
/// contains the public key that should match the hash in the associated output.
/// It is either signed or unsigned and the [addSignature] method can be used to
/// add a signature. Signature and public key data is stored in the witness
/// data.
class P2WPKHInput extends WitnessInput with PKHInput {
/// It is either signed or unsigned. The [sign] method can be used to sign the
/// input with the corresponding [ECPrivateKey] or a signature can be added
/// without checks using [addSignature]. Signature and public key data is
/// stored in the witness data.
class P2WPKHInput extends LegacyWitnessInput with PKHInput {

@override
final ECPublicKey publicKey;
Expand Down Expand Up @@ -63,6 +67,24 @@ class P2WPKHInput extends WitnessInput with PKHInput {

}

@override
LegacyWitnessInput sign({
required Transaction tx,
required int inputN,
required ECPrivateKey key,
required BigInt value,
hashType = const SigHashType.all(),
}) => addSignature(
createInputSignature(
tx: tx,
inputN: inputN,
key: checkKey(key),
scriptCode: scriptCode,
value: value,
hashType: hashType,
),
);

@override
/// Returns a new [P2WPKHInput] with the [InputSignature] added. Any existing
/// signature is replaced.
Expand Down
25 changes: 25 additions & 0 deletions coinlib/lib/src/tx/inputs/pkh_input.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'package:coinlib/src/crypto/ec_private_key.dart';
import 'package:coinlib/src/crypto/ec_public_key.dart';
import 'package:coinlib/src/scripts/programs/p2pkh.dart';
import 'package:coinlib/src/scripts/script.dart';
import 'package:coinlib/src/tx/transaction.dart';
import 'input_signature.dart';

/// A mixin for Public Key Hash input types, providing the [ECPublicKey] and
/// [InputSignature] required in these inputs.
abstract mixin class PKHInput {

ECPublicKey get publicKey;
InputSignature? get insig;
PKHInput addSignature(InputSignature insig);
bool get complete => insig != null;
Script get scriptCode => P2PKH.fromPublicKey(publicKey).script;

ECPrivateKey checkKey(ECPrivateKey key) {
if (key.pubkey != publicKey) {
throw CannotSignInput("Incorrect key for input");
}
return key;
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'dart:typed_data';
import 'package:coinlib/src/common/checks.dart';
import 'package:coinlib/src/common/serial.dart';
import 'package:coinlib/src/tx/input_signature.dart';
import 'package:coinlib/src/tx/outpoint.dart';
import 'input.dart';
import 'outpoint.dart';
import 'input_signature.dart';

/// A transaction input without any associated witness data that acts as the
/// base for all other inputs as all inputs include a outpoint, script and
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'dart:typed_data';
import 'package:coinlib/src/tx/outpoint.dart';
import 'input.dart';
import 'outpoint.dart';
import 'raw_input.dart';
import 'p2wpkh_input.dart';

Expand Down
Loading

0 comments on commit 42003bc

Please sign in to comment.