Completed
Pull Request — master (#222)
by thomas
24:34
created

MultisigHD::__construct()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 27
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 4

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 27
ccs 19
cts 19
cp 1
rs 8.5806
cc 4
eloc 15
nc 5
nop 5
crap 4
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->sort = $sort;
61 36
        $this->sequences = $sequences;
62 36
        $this->redeemScript = ScriptFactory::p2sh()->multisig($m, array_map(
0 ignored issues
show
Bug introduced by
The property redeemScript 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...
63
            function (HierarchicalKey $key) {
64 36
                return $key->getPublicKey();
65 36
            },
66 36
            $this->keys
67 36
        ));
68
69 36
    }
70
71
    /**
72
     * @param HierarchicalKey[] $keys
73
     * @return HierarchicalKey[]
74
     */
75 30
    private function sortHierarchicalKeys(array $keys)
76
    {
77
        return Buffertools::sort($keys, function (HierarchicalKey $key) {
78 30
            return $key->getPublicKey()->getBuffer();
79 30
        });
80
    }
81
82
    /**
83
     * @return string
84
     */
85 6
    public function getPath()
86
    {
87 6
        return $this->path;
88
    }
89
90
    /**
91
     * Return the composite keys of this MultisigHD wallet entry.
92
     * This will strictly adhere to the choice on whether keys should be sorted, since this is done in the constructor.
93
     *
94
     * @return HierarchicalKey[]
95
     */
96 30
    public function getKeys()
97
    {
98 30
        return $this->keys;
99
    }
100
101
    /**
102
     * Returns the redeemScript. Note - keys are already sorted in the constructor, so this is not required in ScriptFactory.
103
     *
104
     * @return \BitWasp\Bitcoin\Script\ScriptInterface
105
     */
106 18
    public function getRedeemScript()
107
    {
108 18
        return $this->redeemScript;
109
    }
110
111
    /**
112
     * @return \BitWasp\Bitcoin\Address\ScriptHashAddress
113
     */
114 12
    public function getAddress()
115
    {
116 12
        return AddressFactory::fromScript($this->getRedeemScript());
117
    }
118
119
    /**
120
     * Derive each HK child and produce a new MultisigHD object
121
     *
122
     * @param int|string $sequence
123
     * @return MultisigHD
124
     */
125 12
    public function deriveChild($sequence)
126
    {
127 12
        $keys = array_map(
128 12
            function (HierarchicalKey $hk) use ($sequence) {
129 12
                return $hk->deriveChild($sequence);
130 12
            },
131 12
            $this->keys
132 12
        );
133
134 12
        if ($this->sort) {
135 12
            $keys = $this->sortHierarchicalKeys($keys);
136 12
        }
137
138 12
        return new self(
139 12
            $this->m,
140 12
            $this->path . '/' . $this->sequences->getNode($sequence),
141 12
            $keys,
142 12
            $this->sequences,
143 12
            $this->sort
144 12
        );
145
    }
146
147
    /**
148
     * @param array|\stdClass|\Traversable $list
149
     * @return MultisigHD
150
     */
151 12
    public function deriveFromList($list)
152
    {
153 12
        HierarchicalKeySequence::validateListType($list);
154
155 12
        $account = $this;
156 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...
157 12
            $account = $account->deriveChild($sequence);
158 12
        }
159
160 12
        return $account;
161
    }
162
163
    /**
164
     * Derive a path in the tree of available addresses.
165
     *
166
     * @param string $path
167
     * @return MultisigHD
168
     */
169 12
    public function derivePath($path)
170
    {
171 12
        return $this->deriveFromList($this->sequences->decodePath($path));
172
    }
173
}
174