Multisig::getKeyCount()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BitWasp\Bitcoin\Script\ScriptInfo;
6
7
use BitWasp\Bitcoin\Bitcoin;
8
use BitWasp\Bitcoin\Crypto\EcAdapter\EcSerializer;
9
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\PhpEcc\Key\PublicKey;
10
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PublicKeyInterface;
11
use BitWasp\Bitcoin\Crypto\EcAdapter\Serializer\Key\PublicKeySerializerInterface;
12
use BitWasp\Bitcoin\Script\Opcodes;
13
use BitWasp\Bitcoin\Script\Parser\Operation;
14
use BitWasp\Bitcoin\Script\ScriptInterface;
15
use BitWasp\Bitcoin\Script\ScriptType;
16
use BitWasp\Buffertools\BufferInterface;
17
18
class Multisig
19
{
20
    /**
21
     * @var int
22
     */
23
    private $m;
24
25
    /**
26
     * @var int
27
     */
28
    private $n;
29
30
    /**
31
     * @var bool
32
     */
33
    private $verify = false;
34
35
    /**
36
     * @var BufferInterface[]
37
     */
38
    private $keyBuffers = [];
39
40
    /**
41
     * @var PublicKeySerializerInterface
42
     */
43
    private $pubKeySerializer;
44
45
    /**
46
     * Multisig constructor.
47
     * @param int $requiredSigs
48
     * @param BufferInterface[] $keys
49
     * @param int $opcode
50
     * @param bool $allowVerify
51
     * @param PublicKeySerializerInterface|null $pubKeySerializer
52
     */
53 41
    public function __construct(int $requiredSigs, array $keys, int $opcode, $allowVerify = false, PublicKeySerializerInterface $pubKeySerializer = null)
54
    {
55 41
        if ($opcode === Opcodes::OP_CHECKMULTISIG) {
56 36
            $verify = false;
57 5
        } else if ($allowVerify && $opcode === Opcodes::OP_CHECKMULTISIGVERIFY) {
58 5
            $verify = true;
59
        } else {
60
            throw new \InvalidArgumentException('Malformed multisig script');
61
        }
62
63 41
        foreach ($keys as $key) {
64 41
            if (!PublicKey::isCompressedOrUncompressed($key)) {
65
                throw new \RuntimeException("Malformed public key");
66
            }
67
        }
68
69 41
        $keyCount = count($keys);
70 41
        if ($requiredSigs < 0 || $requiredSigs > $keyCount) {
71
            throw new \RuntimeException("Invalid number of required signatures");
72
        }
73
74 41
        if ($keyCount < 1 || $keyCount > 16) {
75
            throw new \RuntimeException("Invalid number of public keys");
76
        }
77
78 41
        if (null === $pubKeySerializer) {
79 3
            $pubKeySerializer = EcSerializer::getSerializer(PublicKeySerializerInterface::class, true, Bitcoin::getEcAdapter());
80
        }
81
82 41
        $this->verify = $verify;
83 41
        $this->m = $requiredSigs;
84 41
        $this->n = $keyCount;
85 41
        $this->keyBuffers = $keys;
86 41
        $this->pubKeySerializer = $pubKeySerializer;
87 41
    }
88
89
    /**
90
     * @param Operation[] $decoded
91
     * @param PublicKeySerializerInterface|null $pubKeySerializer
92
     * @param bool $allowVerify
93
     * @return Multisig
94
     */
95 120
    public static function fromDecodedScript(array $decoded, PublicKeySerializerInterface $pubKeySerializer = null, $allowVerify = false)
96
    {
97 120
        if (count($decoded) < 4) {
98 117
            throw new \InvalidArgumentException('Malformed multisig script');
99
        }
100
101 84
        $mCode = $decoded[0]->getOp();
102 84
        $nCode = $decoded[count($decoded) - 2]->getOp();
103 84
        $opCode = end($decoded)->getOp();
104
105 84
        $requiredSigs = \BitWasp\Bitcoin\Script\decodeOpN($mCode);
106 41
        $publicKeyBuffers = [];
107 41
        foreach (array_slice($decoded, 1, -2) as $key) {
108
            /** @var \BitWasp\Bitcoin\Script\Parser\Operation $key */
109 41
            if (!$key->isPush()) {
110
                throw new \RuntimeException('Malformed multisig script');
111
            }
112
113 41
            $buffer = $key->getData();
114 41
            $publicKeyBuffers[] = $buffer;
115
        }
116
117 41
        $keyCount = \BitWasp\Bitcoin\Script\decodeOpN($nCode);
118 41
        if ($keyCount !== count($publicKeyBuffers)) {
119
            throw new \LogicException('No public keys found in script');
120
        }
121
122 41
        return new Multisig($requiredSigs, $publicKeyBuffers, $opCode, $allowVerify, $pubKeySerializer);
123
    }
124
125
    /**
126
     * @param ScriptInterface $script
127
     * @param PublicKeySerializerInterface|null $pubKeySerializer
128
     * @param bool $allowVerify
129
     * @return Multisig
130
     */
131 3
    public static function fromScript(ScriptInterface $script, PublicKeySerializerInterface $pubKeySerializer = null, bool $allowVerify = false)
132
    {
133 3
        return static::fromDecodedScript($script->getScriptParser()->decode(), $pubKeySerializer, $allowVerify);
134
    }
135
136
    /**
137
     * @return string
138
     */
139 8
    public function getType(): string
140
    {
141 8
        return ScriptType::MULTISIG;
142
    }
143
144
    /**
145
     * @return int
146
     */
147 40
    public function getRequiredSigCount(): int
148
    {
149 40
        return $this->m;
150
    }
151
152
    /**
153
     * @return int
154
     */
155 39
    public function getKeyCount(): int
156
    {
157 39
        return $this->n;
158
    }
159
160
    /**
161
     * @return bool
162
     */
163 35
    public function isChecksigVerify(): bool
164
    {
165 35
        return $this->verify;
166
    }
167
168
    /**
169
     * @param PublicKeyInterface $publicKey
170
     * @return bool
171
     */
172 1
    public function checkInvolvesKey(PublicKeyInterface $publicKey): bool
173
    {
174 1
        $buffer = $this->pubKeySerializer->serialize($publicKey);
175 1
        foreach ($this->keyBuffers as $key) {
176 1
            if ($key->equals($buffer)) {
177 1
                return true;
178
            }
179
        }
180
181 1
        return false;
182
    }
183
184
    /**
185
     * @return array|BufferInterface[]
186
     */
187 40
    public function getKeyBuffers(): array
188
    {
189 40
        return $this->keyBuffers;
190
    }
191
}
192