Completed
Pull Request — master (#525)
by thomas
26:53
created

Checksig::getSolution()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.0416

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 0
dl 0
loc 10
ccs 5
cts 6
cp 0.8333
crap 3.0416
rs 9.4285
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 136
    public function __construct($info, TransactionSignatureSerializer $txSigSerializer, PublicKeySerializerInterface $pubKeySerializer)
58
    {
59 136
        if (!is_object($info)) {
60
            throw new \RuntimeException("First value to checksig must be an object");
61
        }
62
63 136
        $infoClass = get_class($info);
64
        switch ($infoClass) {
65 136
            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 102
            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 64
            case Multisig::class:
78
                /** @var Multisig $info */
79 64
                $this->scriptType = ScriptType::MULTISIG;
80 64
                $this->requiredSigs = $info->getRequiredSigCount();
81 64
                $this->keyCount = $info->getKeyCount();
82 64
                break;
83
            default:
84
                throw new \RuntimeException("Unsupported class passed to Checksig");
85
        }
86
87 136
        $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 136
        $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 136
        $this->info = $info;
90 136
    }
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 116
    public function isRequired()
113
    {
114 116
        return $this->required;
115
    }
116
117
    /**
118
     * @return Multisig|PayToPubkey|PayToPubkeyHash
119
     */
120 84
    public function getInfo()
121
    {
122 84
        return $this->info;
123
    }
124
125
    /**
126
     * @return string
127
     */
128 136
    public function getType()
129
    {
130 136
        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 120
    public function getRequiredSigs()
151
    {
152 120
        return $this->requiredSigs;
153
    }
154
155
    /**
156
     * @return bool
157
     */
158 122
    public function isFullySigned()
159
    {
160 122
        if ($this->required) {
161 122
            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 118
    public function hasSignature($idx)
172
    {
173 118
        if ($idx > $this->requiredSigs) {
174
            throw new \RuntimeException("Out of range signature queried");
175
        }
176
177 118
        return array_key_exists($idx, $this->signatures);
178
    }
179
180
    /**
181
     * @param int $idx
182
     * @param TransactionSignatureInterface $signature
183
     * @return $this
184
     */
185 116
    public function setSignature($idx, TransactionSignatureInterface $signature)
186
    {
187 116
        if ($idx < 0 || $idx > $this->keyCount) {
188
            throw new \RuntimeException("Out of range signature for operation");
189
        }
190
191 116
        $this->signatures[$idx] = $signature;
192 116
        return $this;
193
    }
194
195
    /**
196
     * @param int $idx
197
     * @return TransactionSignatureInterface|null
198
     */
199 116
    public function getSignature($idx)
200
    {
201 116
        if (!$this->hasSignature($idx)) {
202
            return null;
203
        }
204
205 116
        return $this->signatures[$idx];
206
    }
207
208
    /**
209
     * @return array
210
     */
211 116
    public function getSignatures()
212
    {
213 116
        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 128
    public function setKey($idx, $key)
244
    {
245 128
        if ($idx < 0 || $idx > $this->keyCount) {
246
            throw new \RuntimeException("Out of range index for public key");
247
        }
248
249 128
        $this->publicKeys[$idx] = $key;
250 128
        return $this;
251
    }
252
253
    /**
254
     * @return PublicKeyInterface[]
255
     */
256 118
    public function getKeys()
257
    {
258 118
        return $this->publicKeys;
259
    }
260
261 118
    public function isVerify()
262
    {
263 118
        return $this->info->isChecksigVerify();
264
    }
265
266
    /**
267
     * @return array
268
     */
269 116
    public function serialize()
270
    {
271 116
        $outputType = $this->getType();
272 116
        $result = [];
273
274 116
        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 86
        } 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 54
        } else if (ScriptType::MULTISIG === $outputType) {
291 54
            if (!$this->isRequired()) {
292 2
                $result = array_fill(0, 1 + $this->getRequiredSigs(), new Buffer());
293
            } else {
294 52
                $result[] = new Buffer();
295 54
                for ($i = 0, $nPubKeys = count($this->getKeys()); $i < $nPubKeys; $i++) {
296 52
                    if ($this->hasSignature($i)) {
297 52
                        $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 116
        return $result;
306
    }
307
}
308