Completed
Push — master ( c42b07...45cd31 )
by thomas
21:37
created

MultisigHD   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 177
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 7

Test Coverage

Coverage 96.15%

Importance

Changes 0
Metric Value
dl 0
loc 177
ccs 50
cts 52
cp 0.9615
rs 10
c 0
b 0
f 0
wmc 15
lcom 2
cbo 7

10 Methods

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