-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
25 changed files
with
815 additions
and
90 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import 'dart:typed_data'; | ||
import 'package:coinlib/src/crypto/ec_public_key.dart'; | ||
import 'package:coinlib/src/scripts/script.dart'; | ||
import 'package:coinlib/src/scripts/operations.dart'; | ||
import 'taproot.dart'; | ||
|
||
/// A TapLeaf containing a single CHECKSIG operation for a key | ||
class TapLeafChecksig extends TapLeaf { | ||
|
||
/// The 1-byte APO key, referring to the internal Taproot key that must be | ||
/// be tweaked prior to signing | ||
static final apoInternal = TapLeafChecksig._(ScriptOpCode.number1); | ||
|
||
TapLeafChecksig._(ScriptOp keyPush) : super( | ||
Script([keyPush, ScriptOpCode.checksig]), | ||
); | ||
|
||
/// Regular key | ||
TapLeafChecksig(ECPublicKey key) : this._(ScriptPushData(key.x)); | ||
|
||
/// A specified APO key | ||
TapLeafChecksig.apo( | ||
ECPublicKey key, | ||
) : this._( | ||
ScriptPushData(Uint8List.fromList([1, ...key.x])), | ||
); | ||
|
||
static TapLeafChecksig? match(Script script) { | ||
|
||
final ops = script.ops; | ||
if (ops.length != 2 || !ops.last.match(ScriptOpCode.checksig)) return null; | ||
|
||
final first = ops.first; | ||
if (first.match(ScriptOpCode.number1)) return apoInternal; | ||
if (first is! ScriptPushData) return null; | ||
|
||
final data = first.data; | ||
final pubkeyData = switch(data.length) { | ||
32 => data, | ||
33 => data.first != 1 ? null : data.sublist(1), | ||
_ => null, | ||
}; | ||
if (pubkeyData == null) return null; | ||
|
||
try { | ||
ECPublicKey.fromXOnly(pubkeyData); | ||
} on InvalidPublicKey { | ||
return null; | ||
} | ||
|
||
return TapLeafChecksig._(first); | ||
|
||
} | ||
|
||
bool get isApo { | ||
final push = script.ops.first; | ||
return push.match(ScriptOpCode.number1) || ( | ||
push is ScriptPushData | ||
&& push.data.length == 33 | ||
&& push.data.first == 1 | ||
); | ||
} | ||
|
||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
172 changes: 172 additions & 0 deletions
172
coinlib/lib/src/tx/inputs/taproot_single_script_sig_input.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
import 'dart:typed_data'; | ||
import 'package:coinlib/src/crypto/ec_private_key.dart'; | ||
import 'package:coinlib/src/taproot/leaves.dart'; | ||
import 'package:coinlib/src/taproot/taproot.dart'; | ||
import 'package:coinlib/src/tx/outpoint.dart'; | ||
import 'package:coinlib/src/tx/sign_details.dart'; | ||
import 'package:coinlib/src/tx/transaction.dart'; | ||
import 'input.dart'; | ||
import 'input_signature.dart'; | ||
import 'raw_input.dart'; | ||
import 'taproot_input.dart'; | ||
import 'taproot_script_input.dart'; | ||
|
||
/// An input that provides a single signature to satisfy a tapscript [leaf]. | ||
class TaprootSingleScriptSigInput extends TaprootInput { | ||
|
||
final TapLeafChecksig? leaf; | ||
final SchnorrInputSignature? insig; | ||
|
||
TaprootSingleScriptSigInput._({ | ||
OutPoint? prevOut, | ||
this.leaf, | ||
Uint8List? controlBlock, | ||
this.insig, | ||
required super.sequence, | ||
}) : super( | ||
prevOut: prevOut ?? OutPoint.nothing, | ||
witness: [ | ||
if (insig != null) insig.bytes, | ||
if (leaf != null) leaf.script.compiled, | ||
if (controlBlock != null) controlBlock, | ||
], | ||
); | ||
|
||
/// Constructs an input with all the information for signing with any sighash | ||
/// type. | ||
TaprootSingleScriptSigInput({ | ||
required OutPoint prevOut, | ||
required Taproot taproot, | ||
required TapLeafChecksig leaf, | ||
SchnorrInputSignature? insig, | ||
int sequence = Input.sequenceFinal, | ||
}) : this._( | ||
prevOut: prevOut, | ||
controlBlock: taproot.controlBlockForLeaf(leaf), | ||
leaf: leaf, | ||
insig: insig, | ||
sequence: sequence, | ||
); | ||
|
||
/// Create an APO input with no information that can only be signed with | ||
/// ANYPREVOUTANYSCRIPT. | ||
TaprootSingleScriptSigInput.anyPrevOutAnyScript({ | ||
SchnorrInputSignature? insig, | ||
int sequence = Input.sequenceFinal, | ||
}) : this._(insig: insig, sequence: sequence); | ||
|
||
/// Create an APO input specifying a [Taproot] and [TapLeaf] that can be | ||
/// signed using ANYPREVOUT or ANYPREVOUTANYSCRIPT. ANYPREVOUTANYSCRIPT may | ||
/// also ommit the taproot information using [anyPrevOutAnyScript()]. | ||
TaprootSingleScriptSigInput.anyPrevOut({ | ||
required Taproot taproot, | ||
required TapLeafChecksig leaf, | ||
SchnorrInputSignature? insig, | ||
int sequence = Input.sequenceFinal, | ||
}) : this._( | ||
leaf: leaf, | ||
controlBlock: taproot.controlBlockForLeaf(leaf), | ||
insig: insig, | ||
sequence: sequence, | ||
); | ||
|
||
/// Matches a [RawInput] as a [TaprootSingleScriptSigInput] if it contains the | ||
/// control block and [TapLeafChecksig] leaf script. | ||
static TaprootSingleScriptSigInput? match( | ||
RawInput raw, List<Uint8List> witness, | ||
) { | ||
|
||
// Only match up-to 3 witness items including signature | ||
if (witness.length > 3) return null; | ||
|
||
// Try to match as generic script input | ||
final scriptIn = TaprootScriptInput.match(raw, witness); | ||
if (scriptIn == null) return null; | ||
|
||
// Check if the script is a match | ||
final leaf = TapLeafChecksig.match(scriptIn.tapscript); | ||
if (leaf == null) return null; | ||
|
||
try { | ||
return TaprootSingleScriptSigInput._( | ||
prevOut: raw.prevOut, | ||
leaf: leaf, | ||
controlBlock: scriptIn.controlBlock, | ||
insig: witness.length == 2 | ||
? null | ||
: SchnorrInputSignature.fromBytes(witness[0]), | ||
sequence: raw.sequence, | ||
); | ||
} on InvalidInputSignature { | ||
return null; | ||
} | ||
|
||
} | ||
|
||
/// Add the [Taproot] and [TapLeaf] required to complete the input for | ||
/// ANYPREVOUTANYSCRIPT. The [prevOut] may optionally be added. | ||
/// | ||
/// The signature is not invalidated for ANYPREVOUTANYSCRIPT. | ||
TaprootSingleScriptSigInput addTaproot({ | ||
required Taproot taproot, | ||
required TapLeafChecksig leaf, | ||
OutPoint? prevOut, | ||
}) => TaprootSingleScriptSigInput._( | ||
prevOut: prevOut ?? this.prevOut, | ||
leaf: leaf, | ||
controlBlock: taproot.controlBlockForLeaf(leaf), | ||
insig: (insig != null && insig!.hashType.anyPrevOutAnyScript) | ||
? insig : null, | ||
sequence: sequence, | ||
); | ||
|
||
/// Complete the input by adding (or replacing) the [OutPoint]. | ||
/// | ||
/// A signature is not invalidated if ANYPREVOUT or ANYPREVOUTANYSCRIPT is | ||
/// used. | ||
TaprootSingleScriptSigInput addPrevOut( | ||
OutPoint prevOut, | ||
) => TaprootSingleScriptSigInput._( | ||
prevOut: prevOut, | ||
leaf: leaf, | ||
controlBlock: leaf == null ? null : witness.last, | ||
insig: (insig != null && insig!.hashType.requiresApo) ? insig : null, | ||
sequence: sequence, | ||
); | ||
|
||
/// Add a preprepared input signature. | ||
TaprootSingleScriptSigInput addSignature( | ||
SchnorrInputSignature insig, | ||
) => TaprootSingleScriptSigInput._( | ||
prevOut: prevOut, | ||
leaf: leaf, | ||
controlBlock: leaf == null ? null : witness.last, | ||
insig: insig, | ||
sequence: sequence, | ||
); | ||
|
||
/// Sign the input for the tapscript key. | ||
TaprootSingleScriptSigInput sign({ | ||
required TaprootScriptSignDetails details, | ||
required ECPrivateKey key, | ||
}) { | ||
|
||
if (leaf != null && !leaf!.isApo && details.hashType.requiresApo) { | ||
throw CannotSignInput( | ||
"Cannot sign with ${details.hashType} for non-APO key", | ||
); | ||
} | ||
|
||
return addSignature( | ||
createInputSignature( | ||
key: key, | ||
details: leaf == null ? details : details.addLeafHash(leaf!.hash), | ||
), | ||
); | ||
|
||
} | ||
|
||
@override | ||
bool get complete => witness.length == 3 && !prevOut.isNothing; | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.