Transaction::equals()   B
last analyzed

Complexity

Conditions 11
Paths 17

Size

Total Lines 36
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 11.1412

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 18
c 1
b 0
f 0
nc 17
nop 1
dl 0
loc 36
ccs 17
cts 19
cp 0.8947
crap 11.1412
rs 7.3166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace BitWasp\Bitcoin\Transaction;
6
7
use BitWasp\Bitcoin\Bitcoin;
8
use BitWasp\Bitcoin\Crypto\Hash;
9
use BitWasp\Bitcoin\Script\ScriptWitnessInterface;
10
use BitWasp\Bitcoin\Serializable;
11
use BitWasp\Bitcoin\Serializer\Transaction\TransactionSerializer;
12
use BitWasp\Bitcoin\Util\IntRange;
13
use BitWasp\Bitcoin\Utxo\Utxo;
14
use BitWasp\Buffertools\BufferInterface;
15
16
class Transaction extends Serializable implements TransactionInterface
17
{
18
    /**
19
     * @var int
20
     */
21
    private $version;
22
23
    /**
24
     * @var TransactionInputInterface[]
25
     */
26
    private $inputs;
27
28
    /**
29
     * @var TransactionOutputInterface[]
30
     */
31
    private $outputs;
32
33
    /**
34
     * @var ScriptWitnessInterface[]
35
     */
36
    private $witness;
37
38
    /**
39
     * @var int
40
     */
41
    private $lockTime;
42
43
    /**
44
     * @var BufferInterface
45
     */
46
    private $wtxid;
47
48
    /**
49
     * @var BufferInterface
50
     */
51
    private $hash;
52
53
    /**
54
     * Transaction constructor.
55
     *
56
     * @param int $nVersion
57
     * @param TransactionInputInterface[] $vin
58
     * @param TransactionOutputInterface[] $vout
59
     * @param ScriptWitnessInterface[] $vwit
60
     * @param int $nLockTime
61
     */
62 5302
    public function __construct(
63
        int $nVersion = TransactionInterface::DEFAULT_VERSION,
64
        array $vin = [],
65
        array $vout = [],
66
        array $vwit = [],
67
        int $nLockTime = 0
68
    ) {
69 5302
        if ($nVersion < IntRange::I32_MIN || $nVersion > IntRange::I32_MAX) {
70 1
            throw new \InvalidArgumentException('Transaction version is outside valid range');
71
        }
72
73 5301
        if ($nLockTime < 0 || $nLockTime > TransactionInterface::MAX_LOCKTIME) {
74 1
            throw new \InvalidArgumentException('Locktime must be positive and less than ' . TransactionInterface::MAX_LOCKTIME);
75
        }
76
77 5300
        $this->version = $nVersion;
78 5300
        $this->lockTime = $nLockTime;
79
80 5300
        $this->inputs = array_map(function (TransactionInputInterface $input) {
81 5264
            return $input;
82 5300
        }, $vin);
83 5300
        $this->outputs = array_map(function (TransactionOutputInterface $output) {
84 5268
            return $output;
85 5300
        }, $vout);
86 5300
        $this->witness = array_map(function (ScriptWitnessInterface $scriptWitness) {
87 5192
            return $scriptWitness;
88 5300
        }, $vwit);
89 5300
    }
90
91
    /**
92
     * @return BufferInterface
93
     */
94 5105
    public function getTxHash(): BufferInterface
95
    {
96 5105
        if (null === $this->hash) {
97 5105
            $this->hash = Hash::sha256d($this->getBaseSerialization());
98
        }
99 5105
        return $this->hash;
100
    }
101
102
    /**
103
     * @return BufferInterface
104
     */
105 5105
    public function getTxId(): BufferInterface
106
    {
107 5105
        return $this->getTxHash()->flip();
108
    }
109
110
    /**
111
     * @return BufferInterface
112
     */
113
    public function getWitnessTxId(): BufferInterface
114
    {
115
        if (null === $this->wtxid) {
116
            $this->wtxid = Hash::sha256d($this->getBuffer())->flip();
117
        }
118
119
        return $this->wtxid;
120
    }
121
122
    /**
123
     * @return int
124
     */
125 5258
    public function getVersion(): int
126
    {
127 5258
        return $this->version;
128
    }
129
130
    /**
131
     * Get the array of inputs in the transaction
132
     *
133
     * @return TransactionInputInterface[]
134
     */
135 5252
    public function getInputs(): array
136
    {
137 5252
        return $this->inputs;
138
    }
139
140
    /**
141
     * @param int $index
142
     * @return TransactionInputInterface
143
     */
144 383
    public function getInput(int $index): TransactionInputInterface
145
    {
146 383
        if (!isset($this->inputs[$index])) {
147 2
            throw new \RuntimeException('No input at this index');
148
        }
149 381
        return $this->inputs[$index];
150
    }
151
152
    /**
153
     * Get Outputs
154
     *
155
     * @return TransactionOutputInterface[]
156
     */
157 5252
    public function getOutputs(): array
158
    {
159 5252
        return $this->outputs;
160
    }
161
162
    /**
163
     * @param int $vout
164
     * @return TransactionOutputInterface
165
     */
166 5158
    public function getOutput(int $vout): TransactionOutputInterface
167
    {
168 5158
        if (!isset($this->outputs[$vout])) {
169 1
            throw new \RuntimeException('No output at this index');
170
        }
171 5157
        return $this->outputs[$vout];
172
    }
173
174
    /**
175
     * @return bool
176
     */
177 218
    public function hasWitness(): bool
178
    {
179 218
        for ($l = count($this->inputs), $i = 0; $i < $l; $i++) {
180 218
            if (isset($this->witness[$i]) && count($this->witness[$i]) > 0) {
181 130
                return true;
182
            }
183
        }
184
185 96
        return false;
186
    }
187
188
    /**
189
     * @return ScriptWitnessInterface[]
190
     */
191 2562
    public function getWitnesses(): array
192
    {
193 2562
        return $this->witness;
194
    }
195
196
    /**
197
     * @param int $index
198
     * @return ScriptWitnessInterface
199
     */
200 2331
    public function getWitness(int $index): ScriptWitnessInterface
201
    {
202 2331
        if (!isset($this->witness[$index])) {
203
            throw new \RuntimeException('No witness at this index');
204
        }
205 2331
        return $this->witness[$index];
206
    }
207
208
    /**
209
     * @param int $vout
210
     * @return OutPointInterface
211
     */
212 5094
    public function makeOutpoint(int $vout): OutPointInterface
213
    {
214 5094
        $this->getOutput($vout);
215 5094
        return new OutPoint($this->getTxId(), $vout);
216
    }
217
218
    /**
219
     * @param int $vout
220
     * @return Utxo
221
     */
222
    public function makeUtxo(int $vout): Utxo
223
    {
224
        return new Utxo(new OutPoint($this->getTxId(), $vout), $this->getOutput($vout));
225
    }
226
227
    /**
228
     * Get Lock Time
229
     *
230
     * @return int
231
     */
232 5259
    public function getLockTime(): int
233
    {
234 5259
        return $this->lockTime;
235
    }
236
237
    /**
238
     * @return int
239
     */
240 1
    public function getValueOut(): int
241
    {
242 1
        $value = 0;
243 1
        foreach ($this->outputs as $output) {
244 1
            $value = $value + $output->getValue();
245
        }
246
247 1
        return $value;
248
    }
249
250
    /**
251
     * @return bool
252
     */
253 1
    public function isCoinbase(): bool
254
    {
255 1
        return count($this->inputs) === 1 && $this->getInput(0)->isCoinBase();
256
    }
257
258
    /**
259
     * @param TransactionInterface $tx
260
     * @return bool
261
     */
262 2
    public function equals(TransactionInterface $tx): bool
263
    {
264 2
        $version = gmp_cmp($this->version, $tx->getVersion());
265 2
        if ($version !== 0) {
266 1
            return false;
267
        }
268
269 2
        $nIn = count($this->inputs);
270 2
        $nOut = count($this->outputs);
271 2
        $nWit = count($this->witness);
272
273
        // Check the length of each field is equal
274 2
        if ($nIn !== count($tx->getInputs()) || $nOut !== count($tx->getOutputs()) || $nWit !== count($tx->getWitnesses())) {
275 1
            return false;
276
        }
277
278
        // Check each field
279 2
        for ($i = 0; $i < $nIn; $i++) {
280 2
            if (false === $this->getInput($i)->equals($tx->getInput($i))) {
281 1
                return false;
282
            }
283
        }
284
285 2
        for ($i = 0; $i < $nOut; $i++) {
286 2
            if (false === $this->getOutput($i)->equals($tx->getOutput($i))) {
287 1
                return false;
288
            }
289
        }
290
291 2
        for ($i = 0; $i < $nWit; $i++) {
292
            if (false === $this->getWitness($i)->equals($tx->getWitness($i))) {
293
                return false;
294
            }
295
        }
296
297 2
        return gmp_cmp($this->lockTime, $tx->getLockTime()) === 0;
298
    }
299
300
    /**
301
     * @return BufferInterface
302
     */
303 183
    public function getBuffer(): BufferInterface
304
    {
305 183
        return (new TransactionSerializer())->serialize($this);
306
    }
307
308
    /**
309
     * @return BufferInterface
310
     */
311 5149
    public function getBaseSerialization(): BufferInterface
312
    {
313 5149
        return (new TransactionSerializer())->serialize($this, TransactionSerializer::NO_WITNESS);
314
    }
315
316
    /**
317
     * @return BufferInterface
318
     */
319 50
    public function getWitnessSerialization(): BufferInterface
320
    {
321 50
        if (!$this->hasWitness()) {
322 28
            throw new \RuntimeException('Cannot get witness serialization for transaction without witnesses');
323
        }
324
325 22
        return $this->getBuffer();
326
    }
327
328
    /**
329
     * {@inheritdoc}
330
     * @see TransactionInterface::getWitnessBuffer()
331
     * @see TransactionInterface::getWitnessSerialization()
332
     * @deprecated
333
     */
334
    public function getWitnessBuffer(): BufferInterface
335
    {
336
        return $this->getWitnessSerialization();
337
    }
338
}
339