Bit-Wasp /
bitcoin-php
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | namespace BitWasp\Bitcoin\Script; |
||
| 4 | |||
| 5 | use BitWasp\Bitcoin\Bitcoin; |
||
| 6 | use BitWasp\Bitcoin\Script\Classifier\OutputClassifier; |
||
| 7 | use BitWasp\Bitcoin\Script\Interpreter\InterpreterInterface; |
||
| 8 | use BitWasp\Bitcoin\Script\Parser\Parser; |
||
| 9 | use BitWasp\Buffertools\Buffer; |
||
| 10 | use BitWasp\Bitcoin\Crypto\Hash; |
||
| 11 | use BitWasp\Bitcoin\Serializable; |
||
| 12 | use BitWasp\Buffertools\BufferInterface; |
||
| 13 | |||
| 14 | class Script extends Serializable implements ScriptInterface |
||
| 15 | { |
||
| 16 | |||
| 17 | /** |
||
| 18 | * @var Opcodes |
||
| 19 | */ |
||
| 20 | protected $opCodes; |
||
| 21 | |||
| 22 | /** |
||
| 23 | * @var string |
||
| 24 | */ |
||
| 25 | protected $script; |
||
| 26 | |||
| 27 | /** |
||
| 28 | * @param BufferInterface $script |
||
| 29 | 1666 | * @param Opcodes|null $opCodes |
|
| 30 | */ |
||
| 31 | 1666 | public function __construct(BufferInterface $script = null, Opcodes $opCodes = null) |
|
| 32 | 1666 | { |
|
| 33 | 1666 | $this->script = $script instanceof BufferInterface ? $script->getBinary() : ''; |
|
| 34 | $this->opCodes = $opCodes ?: new Opcodes(); |
||
| 35 | } |
||
| 36 | |||
| 37 | /** |
||
| 38 | 1444 | * @return BufferInterface |
|
| 39 | */ |
||
| 40 | 1444 | public function getBuffer() |
|
| 41 | { |
||
| 42 | return new Buffer($this->script); |
||
| 43 | } |
||
| 44 | |||
| 45 | /** |
||
| 46 | 1114 | * @return Parser |
|
| 47 | */ |
||
| 48 | 1114 | public function getScriptParser() |
|
| 49 | { |
||
| 50 | return new Parser(Bitcoin::getMath(), $this); |
||
| 51 | } |
||
| 52 | |||
| 53 | /** |
||
| 54 | * Get all opcodes |
||
| 55 | * |
||
| 56 | 672 | * @return Opcodes |
|
| 57 | */ |
||
| 58 | 672 | public function getOpCodes() |
|
| 59 | { |
||
| 60 | return $this->opCodes; |
||
| 61 | } |
||
| 62 | |||
| 63 | /** |
||
| 64 | * Return a buffer containing the hash of this script. |
||
| 65 | * |
||
| 66 | 162 | * @return BufferInterface |
|
| 67 | */ |
||
| 68 | 162 | public function getScriptHash() |
|
| 69 | { |
||
| 70 | return Hash::sha256ripe160($this->getBuffer()); |
||
| 71 | } |
||
| 72 | |||
| 73 | /** |
||
| 74 | * @param bool|true $accurate |
||
| 75 | 48 | * @return int |
|
| 76 | */ |
||
| 77 | 48 | public function countSigOps($accurate = true) |
|
| 78 | 48 | { |
|
| 79 | $count = 0; |
||
| 80 | 48 | $parser = $this->getScriptParser(); |
|
| 81 | 48 | ||
| 82 | 36 | $lastOp = 0xff; |
|
| 83 | 36 | foreach ($parser as $exec) { |
|
| 84 | if ($exec->isPush()) { |
||
| 85 | continue; |
||
| 86 | 36 | } |
|
| 87 | 36 | ||
| 88 | $op = $exec->getOp(); |
||
| 89 | 36 | if ($op > Opcodes::OP_PUSHDATA4) { |
|
| 90 | 24 | // None of these are pushdatas, so just an opcode |
|
| 91 | 36 | if ($op === Opcodes::OP_CHECKSIG || $op === Opcodes::OP_CHECKSIGVERIFY) { |
|
| 92 | 30 | $count++; |
|
| 93 | 24 | } elseif ($op === Opcodes::OP_CHECKMULTISIG || $op === Opcodes::OP_CHECKMULTISIGVERIFY) { |
|
| 94 | 24 | if ($accurate && ($lastOp >= Opcodes::OP_1 && $lastOp <= Opcodes::OP_16)) { |
|
| 95 | 24 | $c = ($lastOp - (Opcodes::OP_1 - 1)); |
|
| 96 | 12 | $count += $c; |
|
| 97 | } else { |
||
| 98 | 30 | $count += 20; |
|
| 99 | } |
||
| 100 | 36 | } |
|
| 101 | 36 | ||
| 102 | 48 | $lastOp = $op; |
|
| 103 | } |
||
| 104 | 48 | } |
|
| 105 | |||
| 106 | return $count; |
||
| 107 | } |
||
| 108 | |||
| 109 | /** |
||
| 110 | * @param WitnessProgram $program |
||
| 111 | 18 | * @return int |
|
| 112 | */ |
||
| 113 | 18 | private function witnessSigOps(WitnessProgram $program) |
|
| 114 | 18 | { |
|
| 115 | 18 | if ($program->getVersion() == 0) { |
|
| 116 | 18 | return $program->getOutputScript()->countSigOps(true); |
|
| 117 | 6 | } |
|
| 118 | |||
| 119 | return 0; |
||
| 120 | 18 | } |
|
| 121 | |||
| 122 | 18 | /** |
|
| 123 | 18 | * @param ScriptInterface $scriptSig |
|
| 124 | 18 | * @param ScriptWitnessInterface $scriptWitness |
|
| 125 | 6 | * @param int $flags |
|
| 126 | * @return int |
||
| 127 | */ |
||
| 128 | 12 | public function countWitnessSigOps(ScriptInterface $scriptSig, ScriptWitnessInterface $scriptWitness, $flags) |
|
|
0 ignored issues
–
show
|
|||
| 129 | 12 | { |
|
| 130 | 12 | if ($flags & InterpreterInterface::VERIFY_WITNESS === 0) { |
|
| 131 | 18 | return 0; |
|
| 132 | } |
||
| 133 | 18 | ||
| 134 | 6 | $program = null; |
|
| 135 | if ($this->isWitness($program)) { |
||
| 136 | return $this->witnessSigOps($program); |
||
|
0 ignored issues
–
show
It seems like
$program defined by null on line 134 can be null; however, BitWasp\Bitcoin\Script\Script::witnessSigOps() does not accept null, maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
Loading history...
|
|||
| 137 | 12 | } |
|
| 138 | |||
| 139 | if ((new OutputClassifier($this))->isPayToScriptHash()) { |
||
| 140 | $parsed = $scriptSig->getScriptParser()->decode(); |
||
| 141 | $subscript = new Script(end($parsed)->getData()); |
||
| 142 | if ($subscript->isWitness($program)) { |
||
| 143 | 18 | return $this->witnessSigOps($program); |
|
|
0 ignored issues
–
show
It seems like
$program defined by null on line 134 can be null; however, BitWasp\Bitcoin\Script\Script::witnessSigOps() does not accept null, maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
Loading history...
|
|||
| 144 | } |
||
| 145 | 18 | } |
|
| 146 | 18 | ||
| 147 | 18 | return 0; |
|
| 148 | 18 | } |
|
| 149 | 18 | ||
| 150 | /** |
||
| 151 | * @param ScriptInterface $scriptSig |
||
| 152 | * @return int |
||
| 153 | */ |
||
| 154 | public function countP2shSigOps(ScriptInterface $scriptSig) |
||
| 155 | { |
||
| 156 | if (ScriptFactory::scriptPubKey() |
||
| 157 | ->classify($this) |
||
| 158 | ->isPayToScriptHash() === false |
||
| 159 | ) { |
||
| 160 | return $this->countSigOps(true); |
||
| 161 | } |
||
| 162 | |||
| 163 | $parser = $scriptSig->getScriptParser(); |
||
| 164 | |||
| 165 | $data = null; |
||
| 166 | foreach ($parser as $exec) { |
||
| 167 | if ($exec->getOp() > Opcodes::OP_16) { |
||
| 168 | return 0; |
||
| 169 | } |
||
| 170 | |||
| 171 | if ($exec->isPush()) { |
||
| 172 | $data = $exec->getData(); |
||
| 173 | } |
||
| 174 | } |
||
| 175 | |||
| 176 | if (!$data instanceof BufferInterface) { |
||
| 177 | return 0; |
||
| 178 | } |
||
| 179 | |||
| 180 | return (new Script($data))->countSigOps(true); |
||
| 181 | } |
||
| 182 | |||
| 183 | /** |
||
| 184 | * @return bool |
||
| 185 | */ |
||
| 186 | public function isPushOnly() |
||
| 187 | { |
||
| 188 | $pushOnly = true; |
||
| 189 | foreach ($this->getScriptParser()->decode() as $entity) { |
||
| 190 | $pushOnly &= $entity->isPush(); |
||
| 191 | } |
||
| 192 | return $pushOnly; |
||
| 193 | } |
||
| 194 | |||
| 195 | /** |
||
| 196 | * @param WitnessProgram|null $program |
||
| 197 | * @return bool |
||
| 198 | */ |
||
| 199 | public function isWitness(WitnessProgram & $program = null) |
||
| 200 | { |
||
| 201 | $buffer = $this->getBuffer(); |
||
| 202 | $size = $buffer->getSize(); |
||
| 203 | if ($size < 4 || $size > 34) { |
||
| 204 | return false; |
||
| 205 | } |
||
| 206 | |||
| 207 | $parser = $this->getScriptParser(); |
||
| 208 | $script = $parser->decode(); |
||
| 209 | if (!$script[1]->isPush()) { |
||
| 210 | return false; |
||
| 211 | } |
||
| 212 | |||
| 213 | $version = $script[0]->getOp(); |
||
| 214 | if ($version != Opcodes::OP_0 && ($version < Opcodes::OP_1 || $version > Opcodes::OP_16)) { |
||
| 215 | return false; |
||
| 216 | } |
||
| 217 | |||
| 218 | $witness = $script[1]; |
||
| 219 | if ($size === $witness->getDataSize() + 2) { |
||
| 220 | $program = new WitnessProgram(decodeOpN($version), $witness->getData()); |
||
| 221 | return true; |
||
| 222 | } |
||
| 223 | |||
| 224 | return false; |
||
| 225 | } |
||
| 226 | } |
||
| 227 |
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.