Completed
Pull Request — master (#524)
by thomas
71:45
created

Checksig::serialize()   C

Complexity

Conditions 13
Paths 9

Size

Total Lines 38
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 13
eloc 26
nc 9
nop 0
dl 0
loc 38
rs 5.1234
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
    public function __construct($info, TransactionSignatureSerializer $txSigSerializer, PublicKeySerializerInterface $pubKeySerializer)
58
    {
59
        if (!is_object($info)) {
60
            throw new \RuntimeException("First value to checksig must be an object");
61
        }
62
63
        $infoClass = get_class($info);
64
        switch ($infoClass) {
65
            case PayToPubkey::class:
66
                /** @var PayToPubkey $info */
67
                $this->scriptType = $info->getType();
68
                $this->requiredSigs = $info->getRequiredSigCount();
69
                $this->keyCount = 1;
70
                break;
71
            case PayToPubkeyHash::class:
72
                /** @var PayToPubkeyHash $info */
73
                $this->scriptType = ScriptType::P2PKH;
74
                $this->requiredSigs = $info->getRequiredSigCount();
75
                $this->keyCount = 1;
76
                break;
77
            case Multisig::class:
78
                /** @var Multisig $info */
79
                $this->scriptType = ScriptType::MULTISIG;
80
                $this->requiredSigs = $info->getRequiredSigCount();
81
                $this->keyCount = $info->getKeyCount();
82
                break;
83
            default:
84
                throw new \RuntimeException("Unsupported class passed to Checksig");
85
        }
86
87
        $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
        $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
        $this->info = $info;
90
    }
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
    public function setRequired($setting)
104
    {
105
        if (!is_bool($setting)) {
106
            throw new \RuntimeException("Invalid input to setRequired");
107
        }
108
        $this->required = $setting;
109
        return $this;
110
    }
111
112
    public function isRequired()
113
    {
114
        return $this->required;
115
    }
116
117
    /**
118
     * @return Multisig|PayToPubkey|PayToPubkeyHash
119
     */
120
    public function getInfo()
121
    {
122
        return $this->info;
123
    }
124
125
    /**
126
     * @return string
127
     */
128
    public function getType()
129
    {
130
        return $this->scriptType;
131
    }
132
133
    /**
134
     * @return array|BufferInterface|BufferInterface[]
135
     */
136
    public function getSolution()
137
    {
138
        if ($this->info instanceof Multisig) {
139
            return $this->info->getKeyBuffers();
140
        } else if ($this->info instanceof PayToPubkey) {
141
            return $this->info->getKeyBuffer();
142
        } else {
143
            return $this->info->getPubKeyHash();
144
        }
145
    }
146
147
    /**
148
     * @return int
149
     */
150
    public function getRequiredSigs()
151
    {
152
        return $this->requiredSigs;
153
    }
154
155
    /**
156
     * @return bool
157
     */
158
    public function isFullySigned()
159
    {
160
        if ($this->required) {
161
            return $this->requiredSigs === count($this->signatures);
162
        } else {
163
            return true;
164
        }
165
    }
166
167
    /**
168
     * @param int $idx
169
     * @return bool
170
     */
171
    public function hasSignature($idx)
172
    {
173
        if ($idx > $this->requiredSigs) {
174
            throw new \RuntimeException("Out of range signature queried");
175
        }
176
177
        return array_key_exists($idx, $this->signatures);
178
    }
179
180
    /**
181
     * @param int $idx
182
     * @param TransactionSignatureInterface $signature
183
     * @return $this
184
     */
185
    public function setSignature($idx, TransactionSignatureInterface $signature)
186
    {
187
        if ($idx < 0 || $idx > $this->keyCount) {
188
            throw new \RuntimeException("Out of range signature for operation");
189
        }
190
191
        $this->signatures[$idx] = $signature;
192
        return $this;
193
    }
194
195
    /**
196
     * @param int $idx
197
     * @return TransactionSignatureInterface|null
198
     */
199
    public function getSignature($idx)
200
    {
201
        if (!$this->hasSignature($idx)) {
202
            return null;
203
        }
204
205
        return $this->signatures[$idx];
206
    }
207
208
    /**
209
     * @return array
210
     */
211
    public function getSignatures()
212
    {
213
        return $this->signatures;
214
    }
215
216
    /**
217
     * @param int $idx
218
     * @return bool
219
     */
220
    public function hasKey($idx)
221
    {
222
        return array_key_exists($idx, $this->publicKeys);
223
    }
224
225
    /**
226
     * @param int $idx
227
     * @return PublicKeyInterface|null
228
     */
229
    public function getKey($idx)
230
    {
231
        if (!$this->hasKey($idx)) {
232
            return null;
233
        }
234
235
        return $this->publicKeys[$idx];
236
    }
237
238
    /**
239
     * @param $idx
240
     * @param PublicKeyInterface|null $key
241
     * @return $this
242
     */
243
    public function setKey($idx, $key)
244
    {
245
        if ($idx < 0 || $idx > $this->keyCount) {
246
            throw new \RuntimeException("Out of range index for public key");
247
        }
248
249
        $this->publicKeys[$idx] = $key;
250
        return $this;
251
    }
252
253
    /**
254
     * @return PublicKeyInterface[]
255
     */
256
    public function getKeys()
257
    {
258
        return $this->publicKeys;
259
    }
260
261
    public function isVerify()
262
    {
263
        return $this->info->isChecksigVerify();
264
    }
265
266
    /**
267
     * @return array
268
     */
269
    public function serialize()
270
    {
271
        $outputType = $this->getType();
272
        $result = [];
273
274
        if (ScriptType::P2PK === $outputType) {
275
            if (!$this->required) {
276
                $result[0] = new Buffer();
277
            } else {
278
                if ($this->hasSignature(0)) {
279
                    $result = [$this->txSigSerializer->serialize($this->getSignature(0))];
280
                }
281
            }
282
        } else if (ScriptType::P2PKH === $outputType) {
283
            if (!$this->required && $this->hasKey(0)) {
284
                $result = [new Buffer(), $this->pubKeySerializer->serialize($this->getKey(0))];
285
            } else {
286
                if ($this->hasSignature(0) && $this->hasKey(0)) {
287
                    $result = [$this->txSigSerializer->serialize($this->getSignature(0)), $this->pubKeySerializer->serialize($this->getKey(0))];
288
                }
289
            }
290
        } else if (ScriptType::MULTISIG === $outputType) {
291
            if (!$this->isRequired()) {
292
                $result = array_fill(0, 1 + $this->getRequiredSigs(), new Buffer());
293
            } else {
294
                $result[] = new Buffer();
295
                for ($i = 0, $nPubKeys = count($this->getKeys()); $i < $nPubKeys; $i++) {
296
                    if ($this->hasSignature($i)) {
297
                        $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
        return $result;
306
    }
307
}
308