Completed
Pull Request — master (#213)
by thomas
25:17
created

MultisigHD::deriveFromList()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

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