Skip to content

Commit b08c28e

Browse files
committed
extract evalChecksig method and fix checkPayToContract in both EcAdapter implementations
1 parent 5ee69f0 commit b08c28e

File tree

11 files changed

+84
-20
lines changed

11 files changed

+84
-20
lines changed

composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"files": [
1919
"src/Script/functions.php",
2020
"src/Script/sighash_functions.php",
21-
"src/Script/script_constants.php"
21+
"src/Script/Interpreter/interpreter_constants.php"
2222
]
2323
},
2424
"autoload-dev": {

src/Crypto/EcAdapter/Impl/PhpEcc/Key/XOnlyPublicKey.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,10 @@ public function tweakAdd(BufferInterface $tweak32): XOnlyPublicKeyInterface
6161
public function checkPayToContract(XOnlyPublicKeyInterface $base, BufferInterface $hash, bool $hasSquareY): bool
6262
{
6363
$pkExpected = $base->tweakAdd($hash);
64+
$xEquals = gmp_cmp($pkExpected->getPoint()->getX(), $this->point->getX()) === 0;
65+
$squareEquals = $pkExpected->hasSquareY() === !$hasSquareY;
6466
/** @var XOnlyPublicKey $pkExpected */
65-
return gmp_cmp($pkExpected->getPoint()->getX(), $this->point->getX()) === 0 &&
66-
$pkExpected->hasSquareY() === $hasSquareY;
67+
return $xEquals && $squareEquals;
6768
}
6869

6970
public function getBuffer(): BufferInterface

src/Crypto/EcAdapter/Impl/Secp256k1/Key/XOnlyPublicKey.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,10 @@ public function tweakAdd(BufferInterface $tweak32): XOnlyPublicKeyInterface
9696

9797
private function doCheckPayToContract(XOnlyPublicKey $base, BufferInterface $hash, bool $negated): bool
9898
{
99-
return secp256k1_xonly_pubkey_tweak_verify($this->context, $this->xonlyKey, (int) !$negated, $base->xonlyKey, $hash->getBinary());
99+
if (1 !== secp256k1_xonly_pubkey_tweak_verify($this->context, $this->xonlyKey, (int) !$negated, $base->xonlyKey, $hash->getBinary())) {
100+
return false;
101+
}
102+
return true;
100103
}
101104

102105
public function checkPayToContract(XOnlyPublicKeyInterface $base, BufferInterface $hash, bool $hasSquareY): bool

src/Crypto/EcAdapter/Impl/Secp256k1/Serializer/Signature/SchnorrSignatureSerializer.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,6 @@ public function parse(BufferInterface $sig): SchnorrSignatureInterface
6464
throw new \RuntimeException('Unable to parse compact signature');
6565
}
6666
/** @var resource $sig_t */
67-
return new SchnorrSignature($this->ecAdapter, $sig_t);
67+
return new SchnorrSignature($this->ecAdapter->getContext(), $sig_t);
6868
}
6969
}

src/Script/Interpreter/CheckerBase.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
use BitWasp\Bitcoin\Exceptions\ScriptRuntimeException;
1515
use BitWasp\Bitcoin\Exceptions\SignatureNotCanonical;
1616
use BitWasp\Bitcoin\Locktime;
17-
use BitWasp\Bitcoin\Script\ExecutionContext;
17+
use BitWasp\Bitcoin\Script\Interpreter\ExecutionContext;
1818
use BitWasp\Bitcoin\Script\PrecomputedData;
1919
use BitWasp\Bitcoin\Script\ScriptInterface;
2020
use BitWasp\Bitcoin\Serializer\Signature\TransactionSignatureSerializer;

src/Script/ExecutionContext.php renamed to src/Script/Interpreter/ExecutionContext.php

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php declare(strict_types=1);
22

3-
namespace BitWasp\Bitcoin\Script;
3+
namespace BitWasp\Bitcoin\Script\Interpreter;
44

55
use BitWasp\Buffertools\BufferInterface;
66

@@ -100,6 +100,14 @@ public function hasValidationWeightSet(): bool
100100
return null !== $this->validationWeightLeft;
101101
}
102102

103+
/**
104+
* @return null|int
105+
*/
106+
public function getValidationWeightLeft()
107+
{
108+
return $this->validationWeightLeft;
109+
}
110+
103111
public function setValidationWeightLeft(int $weight)
104112
{
105113
$this->validationWeightLeft = $weight;

src/Script/Interpreter/Interpreter.php

+58-7
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
use BitWasp\Bitcoin\Exceptions\ScriptRuntimeException;
1313
use BitWasp\Bitcoin\Exceptions\SignatureNotCanonical;
1414
use BitWasp\Bitcoin\Script\Classifier\OutputClassifier;
15-
use BitWasp\Bitcoin\Script\ExecutionContext;
15+
use BitWasp\Bitcoin\Script\Interpreter\ExecutionContext;
1616
use BitWasp\Bitcoin\Script\Opcodes;
1717
use BitWasp\Bitcoin\Script\Script;
1818
use BitWasp\Bitcoin\Script\ScriptFactory;
@@ -196,7 +196,7 @@ private function verifyTaprootCommitment(BufferInterface $control, BufferInterfa
196196
}
197197
}
198198
$t = Hash::taggedSha256("TapTweak", Buffertools::concat($p, $k));
199-
return $Q->checkPayToContract($P, $t, ($control->getBinary()[0] & 1) == 1);
199+
return $Q->checkPayToContract($P, $t, (ord($control->getBinary()[0]) & 1) == 1);
200200
}
201201

202202
/**
@@ -305,7 +305,6 @@ private function verifyWitnessProgram(WitnessProgram $witnessProgram, ScriptWitn
305305
$witnessCount--;
306306
}
307307
$execContext->setAnnexCheckDone();
308-
309308
if ($witnessCount === 1) {
310309
// key spend path - doesn't use the interpreter, directly checks signature
311310
$signature = $scriptWitness[count($scriptWitness) - 1];
@@ -343,7 +342,7 @@ private function verifyWitnessProgram(WitnessProgram $witnessProgram, ScriptWitn
343342
}
344343

345344
// return true at this stage, need further work to proceed
346-
return $this->executeWitnessProgram($scriptWitness, $scriptPubKey, SigHash::TAPSCRIPT, $flags, $checker, $execContext);
345+
return $this->executeWitnessProgram($scriptWitness, new Script($scriptPubKey), SigHash::TAPSCRIPT, $flags, $checker, $execContext);
347346
}
348347
}
349348

@@ -502,6 +501,54 @@ public function checkExec(Stack $vfStack, bool $value): bool
502501
return (bool) $ret;
503502
}
504503

504+
private function evalChecksigPreTapscript(BufferInterface $sig, BufferInterface $key, ScriptInterface $scriptPubKey, int $hashStartPos, int $flags, CheckerBase $checker, int $sigVersion, bool &$success): bool
505+
{
506+
assert($sigVersion === SigHash::V0 || $sigVersion === SigHash::V1);
507+
$scriptCode = new Script($scriptPubKey->getBuffer()->slice($hashStartPos));
508+
// encoding is checked in checker
509+
$success = $checker->checkSig($scriptCode, $sig, $key, $sigVersion, $flags);
510+
return true;
511+
}
512+
513+
private function evalChecksigTapscript(BufferInterface $sig, BufferInterface $key, int $flags, CheckerBase $checker, int $sigVersion, ExecutionContext $execContext, bool &$success): bool
514+
{
515+
assert($sigVersion === SigHash::TAPSCRIPT);
516+
$success = $sig->getSize() > 0;
517+
if ($success) {
518+
assert($execContext->hasValidationWeightSet());
519+
$execContext->setValidationWeightLeft($execContext->getValidationWeightLeft() - VALIDATION_WEIGHT_OFFSET);
520+
if ($execContext->getValidationWeightLeft() < 0) {
521+
return false;
522+
}
523+
}
524+
if ($key->getSize() === 0) {
525+
return false;
526+
} else if ($key->getSize() === 32) {
527+
if ($success && !$checker->checkSigSchnorr($sig, $key, $sigVersion, $execContext)) {
528+
return false;
529+
}
530+
} else {
531+
if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE) {
532+
return false;
533+
}
534+
}
535+
return true;
536+
}
537+
538+
private function evalChecksig(BufferInterface $sig, BufferInterface $key, ScriptInterface $scriptPubKey, int $hashStartPos, int $flags, CheckerBase $checker, int $sigVersion, ExecutionContext $execContext, bool &$success): bool
539+
{
540+
switch ($sigVersion) {
541+
case SigHash::V0:
542+
case SigHash::V1:
543+
return $this->evalChecksigPreTapscript($sig, $key, $scriptPubKey, $hashStartPos, $flags, $checker, $sigVersion, $success);
544+
case SigHash::TAPSCRIPT:
545+
return $this->evalChecksigTapscript($sig, $key, $flags, $checker, $sigVersion, $execContext, $success);
546+
case SigHash::TAPROOT:
547+
break;
548+
};
549+
assert(false);
550+
}
551+
505552
/**
506553
* @param ScriptInterface $script
507554
* @param Stack $mainStack
@@ -513,6 +560,9 @@ public function checkExec(Stack $vfStack, bool $value): bool
513560
*/
514561
public function evaluate(ScriptInterface $script, Stack $mainStack, int $sigVersion, int $flags, CheckerBase $checker, ExecutionContext $execContext = null): bool
515562
{
563+
if ($execContext === null) {
564+
$execContext = new ExecutionContext();
565+
}
516566
$hashStartPos = 0;
517567
$opCount = 0;
518568
$zero = gmp_init(0, 10);
@@ -1033,9 +1083,10 @@ public function evaluate(ScriptInterface $script, Stack $mainStack, int $sigVers
10331083
$vchPubKey = $mainStack[-1];
10341084
$vchSig = $mainStack[-2];
10351085

1036-
$scriptCode = new Script($script->getBuffer()->slice($hashStartPos));
1037-
1038-
$success = $checker->checkSig($scriptCode, $vchSig, $vchPubKey, $sigVersion, $flags, $execContext);
1086+
$success = false;
1087+
if (!$this->evalChecksig($vchSig, $vchPubKey, $script, $hashStartPos, $flags, $checker, $sigVersion, $execContext, $success)) {
1088+
return false;
1089+
}
10391090

10401091
$mainStack->pop();
10411092
$mainStack->pop();

src/Script/Interpreter/InterpreterInterface.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ interface InterpreterInterface
8181
const VERIFY_DISCOURAGE_UPGRADABLE_ANNEX = 1 << 19;
8282

8383
const VERIFY_DISCOURAGE_OP_SUCCESS = 1 << 20;
84-
84+
const VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = 1 << 21;
8585
// Verify CHECKSEQUENCEVERIFY
8686
//
8787
// See BIP112 for details.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace BitWasp\Bitcoin\Script\Interpreter;
4+
5+
const VALIDATION_WEIGHT_OFFSET = 50;

src/Script/script_constants.php

-4
This file was deleted.

src/Transaction/SignatureHash/TaprootHasher.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace BitWasp\Bitcoin\Transaction\SignatureHash;
66

77
use BitWasp\Bitcoin\Crypto\Hash;
8-
use BitWasp\Bitcoin\Script\ExecutionContext;
8+
use BitWasp\Bitcoin\Script\Interpreter\ExecutionContext;
99
use BitWasp\Bitcoin\Script\PrecomputedData;
1010
use BitWasp\Bitcoin\Script\ScriptInterface;
1111
use BitWasp\Bitcoin\Serializer\Transaction\OutPointSerializer;

0 commit comments

Comments
 (0)