Completed
Pull Request — master (#244)
by thomas
201:55 queued 131:14
created

Script::isWitness()   D

Complexity

Conditions 9
Paths 5

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 27
rs 4.909
ccs 0
cts 0
cp 0
cc 9
eloc 17
nc 5
nop 1
crap 90
1
<?php
2
3
namespace BitWasp\Bitcoin\Script;
4
5
use BitWasp\Bitcoin\Bitcoin;
6
use BitWasp\Bitcoin\Script\Parser\Parser;
7
use BitWasp\Buffertools\Buffer;
8
use BitWasp\Bitcoin\Crypto\Hash;
9
use BitWasp\Bitcoin\Serializable;
10
use BitWasp\Buffertools\BufferInterface;
11
12
class Script extends Serializable implements ScriptInterface
13
{
14
15
    /**
16
     * @var Opcodes
17
     */
18
    protected $opCodes;
19
20
    /**
21
     * @var string
22
     */
23
    protected $script;
24
25
    /**
26
     * @param BufferInterface $script
27
     * @param Opcodes|null $opCodes
28
     */
29 1666
    public function __construct(BufferInterface $script = null, Opcodes $opCodes = null)
30
    {
31 1666
        $this->script = $script instanceof BufferInterface ? $script->getBinary() : '';
32 1666
        $this->opCodes = $opCodes ?: new Opcodes();
33 1666
    }
34
35
    /**
36
     * @return BufferInterface
37
     */
38 1444
    public function getBuffer()
39
    {
40 1444
        return new Buffer($this->script);
41
    }
42
43
    /**
44
     * @return Parser
45
     */
46 1114
    public function getScriptParser()
47
    {
48 1114
        return new Parser(Bitcoin::getMath(), $this);
49
    }
50
51
    /**
52
     * Get all opcodes
53
     *
54
     * @return Opcodes
55
     */
56 672
    public function getOpCodes()
57
    {
58 672
        return $this->opCodes;
59
    }
60
61
    /**
62
     * Return a buffer containing the hash of this script.
63
     *
64
     * @return BufferInterface
65
     */
66 162
    public function getScriptHash()
67
    {
68 162
        return Hash::sha256ripe160($this->getBuffer());
69
    }
70
71
    /**
72
     * @param bool|true $accurate
73
     * @return int
74
     */
75 48
    public function countSigOps($accurate = true)
76
    {
77 48
        $count = 0;
78 48
        $parser = $this->getScriptParser();
79
80 48
        $lastOp = 0xff;
81 48
        foreach ($parser as $exec) {
82 36
            if ($exec->isPush()) {
83 36
                continue;
84
            }
85
86 36
            $op = $exec->getOp();
87 36
            if ($op > Opcodes::OP_PUSHDATA4) {
88
                // None of these are pushdatas, so just an opcode
89 36
                if ($op === Opcodes::OP_CHECKSIG || $op === Opcodes::OP_CHECKSIGVERIFY) {
90 24
                    $count++;
91 36
                } elseif ($op === Opcodes::OP_CHECKMULTISIG || $op === Opcodes::OP_CHECKMULTISIGVERIFY) {
92 30
                    if ($accurate && ($lastOp >= Opcodes::OP_1 && $lastOp <= Opcodes::OP_16)) {
93 24
                        $c = ($lastOp - (Opcodes::OP_1 - 1));
94 24
                        $count += $c;
95 24
                    } else {
96 12
                        $count += 20;
97
                    }
98 30
                }
99
100 36
                $lastOp = $op;
101 36
            }
102 48
        }
103
104 48
        return $count;
105
    }
106
107
    /**
108
     * @param ScriptInterface $scriptSig
109
     * @return int
110
     */
111 18
    public function countP2shSigOps(ScriptInterface $scriptSig)
112
    {
113 18
        if (ScriptFactory::scriptPubKey()
114 18
            ->classify($this)
115 18
            ->isPayToScriptHash() === false
116 18
        ) {
117 6
            return $this->countSigOps(true);
118
        }
119
120 18
        $parser = $scriptSig->getScriptParser();
121
122 18
        $data = null;
123 18
        foreach ($parser as $exec) {
124 18
            if ($exec->getOp() > Opcodes::OP_16) {
125 6
                return 0;
126
            }
127
128 12
            if ($exec->isPush()) {
129 12
                $data = $exec->getData();
130 12
            }
131 18
        }
132
133 18
        if (!$data instanceof BufferInterface) {
134 6
            return 0;
135
        }
136
137 12
        return (new Script($data))->countSigOps(true);
138
    }
139
140
    /**
141
     * @return bool
142
     */
143 18
    public function isPushOnly()
144
    {
145 18
        $pushOnly = true;
146 18
        foreach ($this->getScriptParser()->decode() as $entity) {
147 18
            $pushOnly &= $entity->isPush();
148 18
        }
149 18
        return $pushOnly;
150
    }
151
152
    /**
153
     * @param WitnessProgram|null $program
154
     * @return bool
155
     */
156
    public function isWitness(WitnessProgram & $program = null)
157
    {
158
        $buffer = $this->getBuffer();
159
        $size = $buffer->getSize();
160
        if ($size < 4 || $size > 34) {
161
            return false;
162
        }
163
164
        $parser = $this->getScriptParser();
165
        $script = $parser->decode();
166
        if ($script[0]->isPush() || !$script[1]->isPush()) {
167
            return false;
168
        }
169
170
        $version = $script[0]->getOp();
171
        if ($version != Opcodes::OP_0 && ($version < Opcodes::OP_1 || $version > Opcodes::OP_16)) {
172
            return false;
173
        }
174
175
        $witness = $script[1];
176
        if ($size === $witness->getDataSize() + 2) {
177
            $program = new WitnessProgram(decodeOpN($version), $witness->getData());
178
            return true;
179
        }
180
181
        return false;
182
    }
183
}
184