Skip to content

Commit

Permalink
extract evalChecksig method and fix checkPayToContract in both EcAdap…
Browse files Browse the repository at this point in the history
…ter implementations
  • Loading branch information
afk11 committed Nov 10, 2019
1 parent 5ee69f0 commit b08c28e
Show file tree
Hide file tree
Showing 11 changed files with 84 additions and 20 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"files": [
"src/Script/functions.php",
"src/Script/sighash_functions.php",
"src/Script/script_constants.php"
"src/Script/Interpreter/interpreter_constants.php"
]
},
"autoload-dev": {
Expand Down
5 changes: 3 additions & 2 deletions src/Crypto/EcAdapter/Impl/PhpEcc/Key/XOnlyPublicKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,10 @@ public function tweakAdd(BufferInterface $tweak32): XOnlyPublicKeyInterface
public function checkPayToContract(XOnlyPublicKeyInterface $base, BufferInterface $hash, bool $hasSquareY): bool
{
$pkExpected = $base->tweakAdd($hash);
$xEquals = gmp_cmp($pkExpected->getPoint()->getX(), $this->point->getX()) === 0;
$squareEquals = $pkExpected->hasSquareY() === !$hasSquareY;
/** @var XOnlyPublicKey $pkExpected */
return gmp_cmp($pkExpected->getPoint()->getX(), $this->point->getX()) === 0 &&
$pkExpected->hasSquareY() === $hasSquareY;
return $xEquals && $squareEquals;
}

public function getBuffer(): BufferInterface
Expand Down
5 changes: 4 additions & 1 deletion src/Crypto/EcAdapter/Impl/Secp256k1/Key/XOnlyPublicKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,10 @@ public function tweakAdd(BufferInterface $tweak32): XOnlyPublicKeyInterface

private function doCheckPayToContract(XOnlyPublicKey $base, BufferInterface $hash, bool $negated): bool
{
return secp256k1_xonly_pubkey_tweak_verify($this->context, $this->xonlyKey, (int) !$negated, $base->xonlyKey, $hash->getBinary());
if (1 !== secp256k1_xonly_pubkey_tweak_verify($this->context, $this->xonlyKey, (int) !$negated, $base->xonlyKey, $hash->getBinary())) {
return false;
}
return true;
}

public function checkPayToContract(XOnlyPublicKeyInterface $base, BufferInterface $hash, bool $hasSquareY): bool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,6 @@ public function parse(BufferInterface $sig): SchnorrSignatureInterface
throw new \RuntimeException('Unable to parse compact signature');
}
/** @var resource $sig_t */
return new SchnorrSignature($this->ecAdapter, $sig_t);
return new SchnorrSignature($this->ecAdapter->getContext(), $sig_t);
}
}
2 changes: 1 addition & 1 deletion src/Script/Interpreter/CheckerBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use BitWasp\Bitcoin\Exceptions\ScriptRuntimeException;
use BitWasp\Bitcoin\Exceptions\SignatureNotCanonical;
use BitWasp\Bitcoin\Locktime;
use BitWasp\Bitcoin\Script\ExecutionContext;
use BitWasp\Bitcoin\Script\Interpreter\ExecutionContext;
use BitWasp\Bitcoin\Script\PrecomputedData;
use BitWasp\Bitcoin\Script\ScriptInterface;
use BitWasp\Bitcoin\Serializer\Signature\TransactionSignatureSerializer;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php declare(strict_types=1);

namespace BitWasp\Bitcoin\Script;
namespace BitWasp\Bitcoin\Script\Interpreter;

use BitWasp\Buffertools\BufferInterface;

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

/**
* @return null|int
*/
public function getValidationWeightLeft()
{
return $this->validationWeightLeft;
}

public function setValidationWeightLeft(int $weight)
{
$this->validationWeightLeft = $weight;
Expand Down
65 changes: 58 additions & 7 deletions src/Script/Interpreter/Interpreter.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
use BitWasp\Bitcoin\Exceptions\ScriptRuntimeException;
use BitWasp\Bitcoin\Exceptions\SignatureNotCanonical;
use BitWasp\Bitcoin\Script\Classifier\OutputClassifier;
use BitWasp\Bitcoin\Script\ExecutionContext;
use BitWasp\Bitcoin\Script\Interpreter\ExecutionContext;
use BitWasp\Bitcoin\Script\Opcodes;
use BitWasp\Bitcoin\Script\Script;
use BitWasp\Bitcoin\Script\ScriptFactory;
Expand Down Expand Up @@ -196,7 +196,7 @@ private function verifyTaprootCommitment(BufferInterface $control, BufferInterfa
}
}
$t = Hash::taggedSha256("TapTweak", Buffertools::concat($p, $k));
return $Q->checkPayToContract($P, $t, ($control->getBinary()[0] & 1) == 1);
return $Q->checkPayToContract($P, $t, (ord($control->getBinary()[0]) & 1) == 1);
}

/**
Expand Down Expand Up @@ -305,7 +305,6 @@ private function verifyWitnessProgram(WitnessProgram $witnessProgram, ScriptWitn
$witnessCount--;
}
$execContext->setAnnexCheckDone();

if ($witnessCount === 1) {
// key spend path - doesn't use the interpreter, directly checks signature
$signature = $scriptWitness[count($scriptWitness) - 1];
Expand Down Expand Up @@ -343,7 +342,7 @@ private function verifyWitnessProgram(WitnessProgram $witnessProgram, ScriptWitn
}

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

Expand Down Expand Up @@ -502,6 +501,54 @@ public function checkExec(Stack $vfStack, bool $value): bool
return (bool) $ret;
}

private function evalChecksigPreTapscript(BufferInterface $sig, BufferInterface $key, ScriptInterface $scriptPubKey, int $hashStartPos, int $flags, CheckerBase $checker, int $sigVersion, bool &$success): bool
{
assert($sigVersion === SigHash::V0 || $sigVersion === SigHash::V1);
$scriptCode = new Script($scriptPubKey->getBuffer()->slice($hashStartPos));
// encoding is checked in checker
$success = $checker->checkSig($scriptCode, $sig, $key, $sigVersion, $flags);
return true;
}

private function evalChecksigTapscript(BufferInterface $sig, BufferInterface $key, int $flags, CheckerBase $checker, int $sigVersion, ExecutionContext $execContext, bool &$success): bool
{
assert($sigVersion === SigHash::TAPSCRIPT);
$success = $sig->getSize() > 0;
if ($success) {
assert($execContext->hasValidationWeightSet());
$execContext->setValidationWeightLeft($execContext->getValidationWeightLeft() - VALIDATION_WEIGHT_OFFSET);
if ($execContext->getValidationWeightLeft() < 0) {
return false;
}
}
if ($key->getSize() === 0) {
return false;
} else if ($key->getSize() === 32) {
if ($success && !$checker->checkSigSchnorr($sig, $key, $sigVersion, $execContext)) {
return false;
}
} else {
if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE) {
return false;
}
}
return true;
}

private function evalChecksig(BufferInterface $sig, BufferInterface $key, ScriptInterface $scriptPubKey, int $hashStartPos, int $flags, CheckerBase $checker, int $sigVersion, ExecutionContext $execContext, bool &$success): bool
{
switch ($sigVersion) {
case SigHash::V0:
case SigHash::V1:
return $this->evalChecksigPreTapscript($sig, $key, $scriptPubKey, $hashStartPos, $flags, $checker, $sigVersion, $success);
case SigHash::TAPSCRIPT:
return $this->evalChecksigTapscript($sig, $key, $flags, $checker, $sigVersion, $execContext, $success);
case SigHash::TAPROOT:
break;
};
assert(false);
}

/**
* @param ScriptInterface $script
* @param Stack $mainStack
Expand All @@ -513,6 +560,9 @@ public function checkExec(Stack $vfStack, bool $value): bool
*/
public function evaluate(ScriptInterface $script, Stack $mainStack, int $sigVersion, int $flags, CheckerBase $checker, ExecutionContext $execContext = null): bool
{
if ($execContext === null) {
$execContext = new ExecutionContext();
}
$hashStartPos = 0;
$opCount = 0;
$zero = gmp_init(0, 10);
Expand Down Expand Up @@ -1033,9 +1083,10 @@ public function evaluate(ScriptInterface $script, Stack $mainStack, int $sigVers
$vchPubKey = $mainStack[-1];
$vchSig = $mainStack[-2];

$scriptCode = new Script($script->getBuffer()->slice($hashStartPos));

$success = $checker->checkSig($scriptCode, $vchSig, $vchPubKey, $sigVersion, $flags, $execContext);
$success = false;
if (!$this->evalChecksig($vchSig, $vchPubKey, $script, $hashStartPos, $flags, $checker, $sigVersion, $execContext, $success)) {
return false;
}

$mainStack->pop();
$mainStack->pop();
Expand Down
2 changes: 1 addition & 1 deletion src/Script/Interpreter/InterpreterInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ interface InterpreterInterface
const VERIFY_DISCOURAGE_UPGRADABLE_ANNEX = 1 << 19;

const VERIFY_DISCOURAGE_OP_SUCCESS = 1 << 20;

const VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = 1 << 21;
// Verify CHECKSEQUENCEVERIFY
//
// See BIP112 for details.
Expand Down
5 changes: 5 additions & 0 deletions src/Script/Interpreter/interpreter_constants.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php declare(strict_types=1);

namespace BitWasp\Bitcoin\Script\Interpreter;

const VALIDATION_WEIGHT_OFFSET = 50;
4 changes: 0 additions & 4 deletions src/Script/script_constants.php

This file was deleted.

2 changes: 1 addition & 1 deletion src/Transaction/SignatureHash/TaprootHasher.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace BitWasp\Bitcoin\Transaction\SignatureHash;

use BitWasp\Bitcoin\Crypto\Hash;
use BitWasp\Bitcoin\Script\ExecutionContext;
use BitWasp\Bitcoin\Script\Interpreter\ExecutionContext;
use BitWasp\Bitcoin\Script\PrecomputedData;
use BitWasp\Bitcoin\Script\ScriptInterface;
use BitWasp\Bitcoin\Serializer\Transaction\OutPointSerializer;
Expand Down

0 comments on commit b08c28e

Please sign in to comment.