Completed
Push — master ( 460d12...eabe8c )
by thomas
24:52
created

src/Key/Deterministic/MultisigHD.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
declare(strict_types=1);
4
5
namespace BitWasp\Bitcoin\Key\Deterministic;
6
7
use BitWasp\Bitcoin\Address\ScriptHashAddress;
8
use BitWasp\Bitcoin\Script\P2shScript;
9
use BitWasp\Bitcoin\Script\ScriptFactory;
10
use BitWasp\Bitcoin\Script\ScriptInterface;
11
use BitWasp\Buffertools\BufferInterface;
12
use BitWasp\Buffertools\Buffertools;
13
14
class MultisigHD
15
{
16
    /**
17
     * @var int|string
18
     */
19
    private $m;
20
21
    /**
22
     * @var string
23
     */
24
    private $path;
25
26
    /**
27
     * @var HierarchicalKey[]
28
     */
29
    private $keys;
30
31
    /**
32
     * @var HierarchicalKeySequence
33
     */
34
    private $sequences;
35
36
    /**
37
     * @var P2shScript
38
     */
39
    private $redeemScript;
40
41
    /**
42
     * @var bool
43
     */
44
    private $sort;
45
46
    /**
47
     * @param int $m
48
     * @param string $path
49
     * @param array $keys
50
     * @param HierarchicalKeySequence $sequences
51
     * @param bool $sort
52
     */
53 10
    public function __construct(int $m, string $path, array $keys, HierarchicalKeySequence $sequences, bool $sort = false)
54
    {
55 10
        if (count($keys) < 1) {
56 1
            throw new \RuntimeException('Must have at least one HierarchicalKey for Multisig HD Script');
57
        }
58
59
        // Sort here to guarantee calls to getKeys() returns keys in the same order as the redeemScript.
60 9
        if ($sort) {
61 8
            $keys = $this->sortHierarchicalKeys($keys);
62
        }
63
64 9
        foreach ($keys as $key) {
65 9
            $this->keys[] = $key;
66
        }
67
68 9
        $this->m = $m;
69 9
        $this->path = $path;
70 9
        $this->sort = $sort;
71 9
        $this->sequences = $sequences;
72 9
        $this->redeemScript = new P2shScript(ScriptFactory::scriptPubKey()->multisig($m, array_map(
73 9
            function (HierarchicalKey $key) {
74 9
                return $key->getPublicKey();
75 9
            },
76 9
            $this->keys
77 9
        ), false));
78 9
    }
79
80
    /**
81
     * @param HierarchicalKey[] $keys
82
     * @return HierarchicalKey[]
83
     */
84
    private function sortHierarchicalKeys(array $keys): array
85
    {
86 8
        return Buffertools::sort($keys, function (HierarchicalKey $key): BufferInterface {
87 8
            return $key->getPublicKey()->getBuffer();
88 8
        });
89
    }
90
91
    /**
92
     * @return string
93
     */
94 1
    public function getPath(): string
95
    {
96 1
        return $this->path;
97
    }
98
99
    /**
100
     * Return the composite keys of this MultisigHD wallet entry.
101
     * This will strictly adhere to the choice on whether keys should be sorted, since this is done in the constructor.
102
     *
103
     * @return HierarchicalKey[]
104
     */
105 7
    public function getKeys(): array
106
    {
107 7
        return $this->keys;
108
    }
109
110
    /**
111
     * Returns the redeemScript. Note - keys are already sorted in the constructor, so this is not required in ScriptFactory.
112
     *
113
     * @return P2shScript
114
     */
115 4
    public function getRedeemScript(): P2shScript
116
    {
117 4
        return $this->redeemScript;
118
    }
119
120
    /**
121
     * @return ScriptInterface
122
     */
123
    public function getScriptPubKey(): ScriptInterface
124
    {
125
        return $this->redeemScript->getOutputScript();
126
    }
127
128
    /**
129
     * @return ScriptHashAddress
130
     */
131 4
    public function getAddress(): ScriptHashAddress
132
    {
133 4
        return $this->redeemScript->getAddress();
134
    }
135
136
    /**
137
     * Derive each HK child and produce a new MultisigHD object
138
     *
139
     * @param int $sequence
140
     * @return MultisigHD
141
     */
142 4
    public function deriveChild(int $sequence): MultisigHD
143
    {
144 4
        $keys = array_map(
145 4
            function (HierarchicalKey $hk) use ($sequence) {
146 4
                return $hk->deriveChild($sequence);
147 4
            },
148 4
            $this->keys
149
        );
150
151 4
        if ($this->sort) {
152 4
            $keys = $this->sortHierarchicalKeys($keys);
153
        }
154
155 4
        return new self(
156 4
            $this->m,
157 4
            $this->path . '/' . $this->sequences->getNode($sequence),
158 4
            $keys,
159 4
            $this->sequences,
160 4
            $this->sort
161
        );
162
    }
163
164
    /**
165
     * @param array|\stdClass|\Traversable $list
166
     * @return MultisigHD
167
     */
168 4
    public function deriveFromList($list): MultisigHD
169
    {
170 4
        HierarchicalKeySequence::validateListType($list);
171
172 4
        $account = $this;
173 4
        foreach ($list as $sequence) {
0 ignored issues
show
The expression $list of type array|object<stdClass>|object<Traversable> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
174 4
            $account = $account->deriveChild((int) $sequence);
175
        }
176
177 4
        return $account;
178
    }
179
180
    /**
181
     * Derive a path in the tree of available addresses.
182
     *
183
     * @param string $path
184
     * @return MultisigHD
185
     */
186 4
    public function derivePath($path): MultisigHD
187
    {
188 4
        return $this->deriveFromList($this->sequences->decodePath($path));
189
    }
190
}
191