Completed
Push — master ( 1989a1...e79d55 )
by thomas
37:47
created

Transaction   C

Complexity

Total Complexity 40

Size/Duplication

Total Lines 276
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 18

Test Coverage

Coverage 86.42%

Importance

Changes 4
Bugs 1 Features 1
Metric Value
dl 0
loc 276
ccs 70
cts 81
cp 0.8642
rs 6.2762
c 4
b 1
f 1
wmc 40
lcom 1
cbo 18

22 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 22 7
A __clone() 0 5 1
A getTxHash() 0 4 1
A getTxId() 0 4 1
A getWitnessTxId() 0 4 1
A getVersion() 0 4 1
A getInputs() 0 4 1
A getInput() 0 4 1
A getOutputs() 0 4 1
A getOutput() 0 4 1
A getWitnesses() 0 4 1
A getWitness() 0 4 1
A makeOutpoint() 0 5 1
A makeUtxo() 0 4 1
A getLockTime() 0 4 1
A getSignatureHash() 0 4 1
A getValueOut() 0 10 2
A isCoinbase() 0 4 2
C equals() 0 34 11
A validator() 0 4 1
A getBuffer() 0 4 1
A getWitnessBuffer() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Transaction often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Transaction, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace BitWasp\Bitcoin\Transaction;
4
5
use BitWasp\Bitcoin\Bitcoin;
6
use BitWasp\Bitcoin\Collection\Transaction\TransactionInputCollection;
7
use BitWasp\Bitcoin\Collection\Transaction\TransactionOutputCollection;
8
use BitWasp\Bitcoin\Collection\Transaction\TransactionWitnessCollection;
9
use BitWasp\Bitcoin\Crypto\Hash;
10
use BitWasp\Bitcoin\Script\ScriptWitnessInterface;
11
use BitWasp\Bitcoin\Serializable;
12
use BitWasp\Bitcoin\Serializer\Transaction\OldTransactionSerializer;
13
use BitWasp\Bitcoin\Serializer\Transaction\TransactionSerializer;
14
use BitWasp\Bitcoin\Transaction\SignatureHash\Hasher;
15
use BitWasp\Bitcoin\Utxo\Utxo;
16
use BitWasp\Buffertools\BufferInterface;
17
18
class Transaction extends Serializable implements TransactionInterface
19
{
20
    /**
21
     * @var int
22
     */
23
    private $version;
24
25
    /**
26
     * @var TransactionInputCollection
27
     */
28
    private $inputs;
29
30
    /**
31
     * @var TransactionOutputCollection
32
     */
33
    private $outputs;
34
35
    /**
36
     * @var TransactionWitnessCollection
37
     */
38
    private $witness;
39
40
    /**
41
     * @var int
42
     */
43
    private $lockTime;
44
45
    /**
46
     * Transaction constructor.
47
     *
48
     * @param int $nVersion
49
     * @param TransactionInputCollection|null $inputs
50
     * @param TransactionOutputCollection|null $outputs
51
     * @param TransactionWitnessCollection|null $witness
52
     * @param int $nLockTime
53
     */
54 2485
    public function __construct(
55
        $nVersion = TransactionInterface::DEFAULT_VERSION,
56
        TransactionInputCollection $inputs = null,
57
        TransactionOutputCollection $outputs = null,
58
        TransactionWitnessCollection $witness = null,
59
        $nLockTime = 0
60
    ) {
61 2485
        if ($nVersion > TransactionInterface::MAX_VERSION) {
62 3
            throw new \InvalidArgumentException('Version must be less than ' . TransactionInterface::MAX_VERSION);
63
        }
64
65 2482
        if ($nLockTime < 0 || $nLockTime > TransactionInterface::MAX_LOCKTIME) {
66 3
            throw new \InvalidArgumentException('Locktime must be positive and less than ' . TransactionInterface::MAX_LOCKTIME);
67
        }
68
69 2479
        $this->version = $nVersion;
70 2479
        $this->inputs = $inputs ?: new TransactionInputCollection();
71 2479
        $this->outputs = $outputs ?: new TransactionOutputCollection();
72 2479
        $this->witness = $witness ?: new TransactionWitnessCollection();
73 2479
        $this->lockTime = $nLockTime;
74
75 2479
    }
76
77
    /**
78
     * @return Transaction
79
     */
80 157
    public function __clone()
81
    {
82 157
        $this->inputs = clone $this->inputs;
83 157
        $this->outputs = clone $this->outputs;
84 157
    }
85
86
    /**
87
     * @return BufferInterface
88
     */
89 2254
    public function getTxHash()
90
    {
91 2254
        return Hash::sha256d($this->getBuffer());
92
    }
93
94
    /**
95
     * @return BufferInterface
96
     */
97 2254
    public function getTxId()
98
    {
99 2254
        return $this->getTxHash()->flip();
100
    }
101
102
    /**
103
     * @return BufferInterface
104
     */
105
    public function getWitnessTxId()
106
    {
107
        return Hash::sha256d($this->getWitnessBuffer())->flip();
108
    }
109
110
    /**
111
     * @return int
112
     */
113 2335
    public function getVersion()
114
    {
115 2335
        return $this->version;
116
    }
117
118
    /**
119
     * Get the array of inputs in the transaction
120
     *
121
     * @return TransactionInputCollection
122
     */
123 2335
    public function getInputs()
124
    {
125 2335
        return $this->inputs;
126
    }
127
128
    /**
129
     * @param int $index
130
     * @return TransactionInputInterface
131
     */
132 85
    public function getInput($index)
133
    {
134 85
        return $this->inputs[$index];
135
    }
136
137
    /**
138
     * Get Outputs
139
     *
140
     * @return TransactionOutputCollection
141
     */
142 2353
    public function getOutputs()
143
    {
144 2353
        return $this->outputs;
145
    }
146
147
    /**
148
     * @param int $vout
149
     * @return TransactionOutputInterface
150
     */
151 2245
    public function getOutput($vout)
152
    {
153 2245
        return $this->outputs[$vout];
154
    }
155
156
    /**
157
     * @return TransactionWitnessCollection
158
     */
159 148
    public function getWitnesses()
160
    {
161 148
        return $this->witness;
162
    }
163
164
    /**
165
     * @return ScriptWitnessInterface
166
     */
167
    public function getWitness($index)
168
    {
169
        return $this->witness[$index];
170
    }
171
172
    /**
173
     * @param int $vout
174
     * @return OutPointInterface
175
     */
176 2206
    public function makeOutpoint($vout)
177
    {
178 2206
        $this->getOutput($vout);
0 ignored issues
show
Unused Code introduced by
The call to the method BitWasp\Bitcoin\Transact...ransaction::getOutput() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
179 2206
        return new OutPoint($this->getTxId(), $vout);
180
    }
181
182
    /**
183
     * @param int $vout
184
     * @return Utxo
185
     */
186
    public function makeUtxo($vout)
187
    {
188
        return new Utxo(new OutPoint($this->getTxId(), $vout), $this->getOutput($vout));
189
    }
190
191
    /**
192
     * Get Lock Time
193
     *
194
     * @return int
195
     */
196 2338
    public function getLockTime()
197
    {
198 2338
        return $this->lockTime;
199
    }
200
201
    /**
202
     * @return Hasher
203
     */
204
    public function getSignatureHash()
205
    {
206
        return new Hasher($this);
207
    }
208
209
    /**
210
     * @return int|string
211
     */
212 3
    public function getValueOut()
213
    {
214 3
        $math = Bitcoin::getMath();
215 3
        $value = gmp_init(0);
216 3
        foreach ($this->outputs as $output) {
217 3
            $value = $math->add($value, gmp_init($output->getValue()));
218 2
        }
219
220 3
        return gmp_strval($value, 10);
221
    }
222
223
    /**
224
     * @return bool
225
     */
226 3
    public function isCoinbase()
227
    {
228 3
        return count($this->inputs) === 1 && $this->getInput(0)->isCoinBase();
229
    }
230
231
    /**
232
     * @param TransactionInterface $tx
233
     * @return bool
234
     */
235 3
    public function equals(TransactionInterface $tx)
236
    {
237 3
        $version = gmp_cmp($this->version, $tx->getVersion());
238 3
        if ($version !== 0) {
239 3
            return false;
240
        }
241
242 3
        $nIn = count($this->inputs);
243 3
        $nOut = count($this->outputs);
244 3
        $nWit = count($this->witness);
245 3
        if ($nIn !== count($tx->getInputs()) || $nOut !== count($tx->getOutputs()) || $nWit !== count($tx->getWitnesses())) {
246 3
            return false;
247
        }
248
249 3
        for ($i = 0; $i < $nIn; $i++) {
250 3
            if (false === $this->getInput($i)->equals($tx->getInput($i))) {
251 3
                return false;
252
            }
253 2
        }
254
255 3
        for ($i = 0; $i < $nOut; $i++) {
256 3
            if (false === $this->getOutput($i)->equals($tx->getOutput($i))) {
257 3
                return false;
258
            }
259 2
        }
260
261 3
        for ($i = 0; $i < $nWit; $i++) {
262
            if (false === $this->getWitness($i)->equals($tx->getWitness($i))) {
263
                return false;
264
            }
265
        }
266
267 3
        return gmp_cmp($this->lockTime, $tx->getLockTime()) === 0;
268
    }
269
270
    /**
271
     * @return Validator
272
     */
273 27
    public function validator()
274
    {
275 27
        return new Validator($this);
276
    }
277
278
    /**
279
     * @return BufferInterface
280
     */
281 2299
    public function getBuffer()
282
    {
283 2299
        return (new OldTransactionSerializer())->serialize($this);
284
    }
285
286
    /**
287
     * @return BufferInterface
288
     */
289 27
    public function getWitnessBuffer()
290
    {
291 27
        return (new TransactionSerializer())->serialize($this);
292
    }
293
}
294