Completed
Pull Request — master (#524)
by thomas
40:24 queued 38:13
created

Checksig::getRequiredSigs()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace BitWasp\Bitcoin\Transaction\Factory;
4
5
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PublicKeyInterface;
6
use BitWasp\Bitcoin\Crypto\EcAdapter\Serializer\Key\PublicKeySerializerInterface;
7
use BitWasp\Bitcoin\Script\ScriptInfo\Multisig;
8
use BitWasp\Bitcoin\Script\ScriptInfo\PayToPubkey;
9
use BitWasp\Bitcoin\Script\ScriptInfo\PayToPubkeyHash;
10
use BitWasp\Bitcoin\Script\ScriptType;
11
use BitWasp\Bitcoin\Serializer\Signature\TransactionSignatureSerializer;
12
use BitWasp\Bitcoin\Signature\TransactionSignatureInterface;
13
use BitWasp\Buffertools\Buffer;
14
use BitWasp\Buffertools\BufferInterface;
15
16
class Checksig
17
{
18
    /**
19
     * @var string
20
     */
21
    private $scriptType;
22
23
    /**
24
     * @var bool
25
     */
26
    private $required = true;
27
28
    /**
29
     * @var PayToPubkeyHash|PayToPubkey|Multisig
30
     */
31
    private $info;
32
33
    /**
34
     * @var int
35
     */
36
    protected $requiredSigs;
37
38
    /**
39
     * @var int
40
     */
41
    protected $keyCount;
42
43
    /**
44
     * @var TransactionSignatureInterface[]
45
     */
46
    protected $signatures = [];
47
48
    /**
49
     * @var PublicKeyInterface[]|null[]
50
     */
51
    protected $publicKeys = [];
52
53
    /**
54
     * Checksig constructor.
55
     * @param $info
56
     */
57 108
    public function __construct($info, TransactionSignatureSerializer $txSigSerializer, PublicKeySerializerInterface $pubKeySerializer)
58
    {
59 108
        if (!is_object($info)) {
60
            throw new \RuntimeException("First value to checksig must be an object");
61
        }
62
63 108
        $infoClass = get_class($info);
64
        switch ($infoClass) {
65 108
            case PayToPubkey::class:
66
                /** @var PayToPubkey $info */
67 50
                $this->scriptType = $info->getType();
68 50
                $this->requiredSigs = $info->getRequiredSigCount();
69 50
                $this->keyCount = 1;
70 50
                break;
71 74
            case PayToPubkeyHash::class:
72
                /** @var PayToPubkeyHash $info */
73 42
                $this->scriptType = ScriptType::P2PKH;
74 42
                $this->requiredSigs = $info->getRequiredSigCount();
75 42
                $this->keyCount = 1;
76 42
                break;
77 36
            case Multisig::class:
78
                /** @var Multisig $info */
79 36
                $this->scriptType = ScriptType::MULTISIG;
80 36
                $this->requiredSigs = $info->getRequiredSigCount();
81 36
                $this->keyCount = $info->getKeyCount();
82 36
                break;
83
            default:
84
                throw new \RuntimeException("Unsupported class passed to Checksig");
85
        }
86
87 108
        $this->txSigSerializer = $txSigSerializer;
0 ignored issues
show
Bug introduced by
The property txSigSerializer does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
88 108
        $this->pubKeySerializer = $pubKeySerializer;
0 ignored issues
show
Bug introduced by
The property pubKeySerializer does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
89 108
        $this->info = $info;
90 108
    }
91
92
    public function receivesValue(Conditional $conditional)
93
    {
94
        if (!$conditional->hasValue()) {
95
            throw new \RuntimeException("Sanity check, conditional requires value");
96
        }
97
98
        if ($conditional->getValue() === false) {
99
            $this->setRequired(false);
100
        }
101
    }
102
103 4
    public function setRequired($setting)
104
    {
105 4
        if (!is_bool($setting)) {
106
            throw new \RuntimeException("Invalid input to setRequired");
107
        }
108 4
        $this->required = $setting;
109 4
        return $this;
110
    }
111
112 88
    public function isRequired()
113
    {
114 88
        return $this->required;
115
    }
116
117
    /**
118
     * @return Multisig|PayToPubkey|PayToPubkeyHash
119
     */
120 56
    public function getInfo()
121
    {
122 56
        return $this->info;
123
    }
124
125
    /**
126
     * @return string
127
     */
128 108
    public function getType()
129
    {
130 108
        return $this->scriptType;
131
    }
132
133
    /**
134
     * @return array|BufferInterface|BufferInterface[]
135
     */
136 82
    public function getSolution()
137
    {
138 82
        if ($this->info instanceof Multisig) {
139
            return $this->info->getKeyBuffers();
140 82
        } else if ($this->info instanceof PayToPubkey) {
141 50
            return $this->info->getKeyBuffer();
142
        } else {
143 38
            return $this->info->getPubKeyHash();
144
        }
145
    }
146
147
    /**
148
     * @return int
149
     */
150 88
    public function getRequiredSigs()
151
    {
152 88
        return $this->requiredSigs;
153
    }
154
155
    /**
156
     * @return bool
157
     */
158 94
    public function isFullySigned()
159
    {
160 94
        if ($this->required) {
161 94
            return $this->requiredSigs === count($this->signatures);
162
        } else {
163 4
            return true;
164
        }
165
    }
166
167
    /**
168
     * @param int $idx
169
     * @return bool
170
     */
171 90
    public function hasSignature($idx)
172
    {
173 90
        if ($idx > $this->requiredSigs) {
174
            throw new \RuntimeException("Out of range signature queried");
175
        }
176
177 90
        return array_key_exists($idx, $this->signatures);
178
    }
179
180
    /**
181
     * @param int $idx
182
     * @param TransactionSignatureInterface $signature
183
     * @return $this
184
     */
185 88
    public function setSignature($idx, TransactionSignatureInterface $signature)
186
    {
187 88
        if ($idx < 0 || $idx > $this->keyCount) {
188
            throw new \RuntimeException("Out of range signature for operation");
189
        }
190
191 88
        $this->signatures[$idx] = $signature;
192 88
        return $this;
193
    }
194
195
    /**
196
     * @param int $idx
197
     * @return TransactionSignatureInterface|null
198
     */
199 88
    public function getSignature($idx)
200
    {
201 88
        if (!$this->hasSignature($idx)) {
202
            return null;
203
        }
204
205 88
        return $this->signatures[$idx];
206
    }
207
208
    /**
209
     * @return array
210
     */
211 88
    public function getSignatures()
212
    {
213 88
        return $this->signatures;
214
    }
215
216
    /**
217
     * @param int $idx
218
     * @return bool
219
     */
220 68
    public function hasKey($idx)
221
    {
222 68
        return array_key_exists($idx, $this->publicKeys);
223
    }
224
225
    /**
226
     * @param int $idx
227
     * @return PublicKeyInterface|null
228
     */
229 68
    public function getKey($idx)
230
    {
231 68
        if (!$this->hasKey($idx)) {
232
            return null;
233
        }
234
235 68
        return $this->publicKeys[$idx];
236
    }
237
238
    /**
239
     * @param $idx
240
     * @param PublicKeyInterface|null $key
241
     * @return $this
242
     */
243 100
    public function setKey($idx, $key)
244
    {
245 100
        if ($idx < 0 || $idx > $this->keyCount) {
246
            throw new \RuntimeException("Out of range index for public key");
247
        }
248
249 100
        $this->publicKeys[$idx] = $key;
250 100
        return $this;
251
    }
252
253
    /**
254
     * @return PublicKeyInterface[]
255
     */
256 90
    public function getKeys()
257
    {
258 90
        return $this->publicKeys;
259
    }
260
261 90
    public function isVerify()
262
    {
263 90
        return $this->info->isChecksigVerify();
264
    }
265
266
    /**
267
     * @return array
268
     */
269 88
    public function serialize()
270
    {
271 88
        $outputType = $this->getType();
272 88
        $result = [];
273
274 88
        if (ScriptType::P2PK === $outputType) {
275 46
            if (!$this->required) {
276 2
                $result[0] = new Buffer();
277
            } else {
278 46
                if ($this->hasSignature(0)) {
279 46
                    $result = [$this->txSigSerializer->serialize($this->getSignature(0))];
280
                }
281
            }
282 58
        } else if (ScriptType::P2PKH === $outputType) {
283 36
            if (!$this->required && $this->hasKey(0)) {
284
                $result = [new Buffer(), $this->pubKeySerializer->serialize($this->getKey(0))];
285
            } else {
286 36
                if ($this->hasSignature(0) && $this->hasKey(0)) {
287 36
                    $result = [$this->txSigSerializer->serialize($this->getSignature(0)), $this->pubKeySerializer->serialize($this->getKey(0))];
288
                }
289
            }
290 26
        } else if (ScriptType::MULTISIG === $outputType) {
291 26
            if (!$this->isRequired()) {
292 2
                $result = array_fill(0, 1 + $this->getRequiredSigs(), new Buffer());
293
            } else {
294 24
                $result[] = new Buffer();
295 26
                for ($i = 0, $nPubKeys = count($this->getKeys()); $i < $nPubKeys; $i++) {
296 24
                    if ($this->hasSignature($i)) {
297 24
                        $result[] = $this->txSigSerializer->serialize($this->getSignature($i));
298
                    }
299
                }
300
            }
301
        } else {
302
            throw new \RuntimeException('Parameter 0 for serializeSolution was a non-standard input type');
303
        }
304
305 88
        return $result;
306
    }
307
}
308