8
8
use BitWasp \Bitcoin \Crypto \EcAdapter \EcSerializer ;
9
9
use BitWasp \Bitcoin \Crypto \EcAdapter \Impl \PhpEcc \Key \PublicKey ;
10
10
use BitWasp \Bitcoin \Crypto \EcAdapter \Serializer \Key \PublicKeySerializerInterface ;
11
+ use BitWasp \Bitcoin \Crypto \EcAdapter \Serializer \Key \XOnlyPublicKeySerializerInterface ;
11
12
use BitWasp \Bitcoin \Crypto \EcAdapter \Serializer \Signature \DerSignatureSerializerInterface ;
13
+ use BitWasp \Bitcoin \Crypto \EcAdapter \Serializer \Signature \SchnorrSignatureSerializerInterface ;
12
14
use BitWasp \Bitcoin \Exceptions \ScriptRuntimeException ;
13
15
use BitWasp \Bitcoin \Exceptions \SignatureNotCanonical ;
14
16
use BitWasp \Bitcoin \Locktime ;
15
17
use BitWasp \Bitcoin \Script \ScriptInterface ;
16
18
use BitWasp \Bitcoin \Serializer \Signature \TransactionSignatureSerializer ;
17
19
use BitWasp \Bitcoin \Signature \TransactionSignature ;
18
20
use BitWasp \Bitcoin \Transaction \SignatureHash \SigHash ;
21
+ use BitWasp \Bitcoin \Transaction \SignatureHash \TaprootHasher ;
19
22
use BitWasp \Bitcoin \Transaction \TransactionInput ;
20
23
use BitWasp \Bitcoin \Transaction \TransactionInputInterface ;
21
24
use BitWasp \Bitcoin \Transaction \TransactionInterface ;
25
+ use BitWasp \Bitcoin \Transaction \TransactionOutputInterface ;
26
+ use BitWasp \Buffertools \Buffer ;
22
27
use BitWasp \Buffertools \BufferInterface ;
23
28
24
29
abstract class CheckerBase
@@ -48,6 +53,16 @@ abstract class CheckerBase
48
53
*/
49
54
protected $ sigCache = [];
50
55
56
+ /**
57
+ * @var array
58
+ */
59
+ protected $ schnorrSigHashCache = [];
60
+
61
+ /**
62
+ * @var TransactionOutputInterface[]
63
+ */
64
+ protected $ spentOutputs = [];
65
+
51
66
/**
52
67
* @var TransactionSignatureSerializer
53
68
*/
@@ -58,30 +73,60 @@ abstract class CheckerBase
58
73
*/
59
74
private $ pubKeySerializer ;
60
75
76
+ /**
77
+ * @var XOnlyPublicKeySerializerInterface
78
+ */
79
+ private $ xonlyKeySerializer ;
80
+
81
+ /**
82
+ * @var SchnorrSignatureSerializerInterface
83
+ */
84
+ private $ schnorrSigSerializer ;
85
+
61
86
/**
62
87
* @var int
63
88
*/
64
89
protected $ sigHashOptionalBits = SigHash::ANYONECANPAY ;
65
90
66
91
/**
67
- * Checker constructor.
92
+ * CheckerBase constructor.
68
93
* @param EcAdapterInterface $ecAdapter
69
94
* @param TransactionInterface $transaction
70
95
* @param int $nInput
71
96
* @param int $amount
72
97
* @param TransactionSignatureSerializer|null $sigSerializer
73
98
* @param PublicKeySerializerInterface|null $pubKeySerializer
99
+ * @param XOnlyPublicKeySerializerInterface|null $xonlyKeySerializer
100
+ * @param SchnorrSignatureSerializerInterface|null $schnorrSigSerializer
74
101
*/
75
- public function __construct (EcAdapterInterface $ ecAdapter , TransactionInterface $ transaction , int $ nInput , int $ amount , TransactionSignatureSerializer $ sigSerializer = null , PublicKeySerializerInterface $ pubKeySerializer = null )
76
- {
102
+ public function __construct (
103
+ EcAdapterInterface $ ecAdapter ,
104
+ TransactionInterface $ transaction ,
105
+ int $ nInput ,
106
+ int $ amount ,
107
+ TransactionSignatureSerializer $ sigSerializer = null ,
108
+ PublicKeySerializerInterface $ pubKeySerializer = null ,
109
+ XOnlyPublicKeySerializerInterface $ xonlyKeySerializer = null ,
110
+ SchnorrSignatureSerializerInterface $ schnorrSigSerializer = null
111
+ ) {
77
112
$ this ->sigSerializer = $ sigSerializer ?: new TransactionSignatureSerializer (EcSerializer::getSerializer (DerSignatureSerializerInterface::class, true , $ ecAdapter ));
78
113
$ this ->pubKeySerializer = $ pubKeySerializer ?: EcSerializer::getSerializer (PublicKeySerializerInterface::class, true , $ ecAdapter );
114
+ $ this ->xonlyKeySerializer = $ xonlyKeySerializer ?: EcSerializer::getSerializer (XOnlyPublicKeySerializerInterface::class, true , $ ecAdapter );
115
+ $ this ->schnorrSigSerializer = $ schnorrSigSerializer ?: EcSerializer::getSerializer (SchnorrSignatureSerializerInterface::class, true , $ ecAdapter );
79
116
$ this ->adapter = $ ecAdapter ;
80
117
$ this ->transaction = $ transaction ;
81
118
$ this ->nInput = $ nInput ;
82
119
$ this ->amount = $ amount ;
83
120
}
84
121
122
+ public function setSpentOutputs (array $ txOuts )
123
+ {
124
+ if (count ($ txOuts ) !== count ($ this ->transaction ->getInputs ())) {
125
+ throw new \RuntimeException ("number of spent txouts should equal number of inputs " );
126
+ }
127
+ $ this ->spentOutputs = $ txOuts ;
128
+ }
129
+
85
130
/**
86
131
* @param ScriptInterface $script
87
132
* @param int $hashType
@@ -225,6 +270,53 @@ public function checkSig(ScriptInterface $script, BufferInterface $sigBuf, Buffe
225
270
}
226
271
}
227
272
273
+ public function getTaprootSigHash (int $ sigHashType , int $ sigVersion ): BufferInterface
274
+ {
275
+ $ cacheCheck = $ sigVersion . $ sigHashType ;
276
+ if (!isset ($ this ->schnorrSigHashCache [$ cacheCheck ])) {
277
+ $ hasher = new TaprootHasher ($ this ->transaction , $ this ->amount , $ this ->spentOutputs );
278
+
279
+ $ hash = $ hasher ->calculate ($ this ->spentOutputs [$ this ->nInput ]->getScript (), $ this ->nInput , $ sigHashType );
280
+ $ this ->schnorrSigHashCache [$ cacheCheck ] = $ hash ->getBinary ();
281
+ } else {
282
+ $ hash = new Buffer ($ this ->schnorrSigHashCache [$ cacheCheck ], 32 );
283
+ }
284
+
285
+ return $ hash ;
286
+ }
287
+
288
+ public function checkSigSchnorr (BufferInterface $ sig64 , BufferInterface $ key32 , int $ sigVersion ): bool
289
+ {
290
+ if ($ sig64 ->getSize () === 0 ) {
291
+ return false ;
292
+ }
293
+ if ($ key32 ->getSize () !== 32 ) {
294
+ return false ;
295
+ }
296
+
297
+ $ hashType = SigHash::TAPDEFAULT ;
298
+ if ($ sig64 ->getSize () === 65 ) {
299
+ $ hashType = $ sig64 ->slice (64 , 1 );
300
+ if ($ hashType == SigHash::TAPDEFAULT ) {
301
+ return false ;
302
+ }
303
+ $ sig64 = $ sig64 ->slice (0 , 64 );
304
+ }
305
+
306
+ if ($ sig64 ->getSize () !== 64 ) {
307
+ return false ;
308
+ }
309
+
310
+ try {
311
+ $ sig = $ this ->schnorrSigSerializer ->parse ($ sig64 );
312
+ $ pubKey = $ this ->xonlyKeySerializer ->parse ($ key32 );
313
+ $ sigHash = $ this ->getTaprootSigHash ($ hashType , $ sigVersion );
314
+ return $ pubKey ->verifySchnorr ($ sigHash , $ sig );
315
+ } catch (\Exception $ e ) {
316
+ return false ;
317
+ }
318
+ }
319
+
228
320
/**
229
321
* @param \BitWasp\Bitcoin\Script\Interpreter\Number $scriptLockTime
230
322
* @return bool
0 commit comments