Completed
Pull Request — master (#348)
by thomas
70:36
created

Transaction::equals()   C

Complexity

Conditions 11
Paths 17

Size

Total Lines 34
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 11.7975

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 11
eloc 19
c 1
b 0
f 1
nc 17
nop 1
dl 0
loc 34
ccs 13
cts 16
cp 0.8125
crap 11.7975
rs 5.2653

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
namespace BitWasp\Bitcoin\Transaction;
4
5
use BitWasp\Bitcoin\Bitcoin;
6
use BitWasp\Bitcoin\Crypto\Hash;
7
use BitWasp\Bitcoin\Script\ScriptWitnessInterface;
8
use BitWasp\Bitcoin\Serializable;
9
use BitWasp\Bitcoin\Serializer\Transaction\OldTransactionSerializer;
10
use BitWasp\Bitcoin\Serializer\Transaction\TransactionSerializer;
11
use BitWasp\Bitcoin\Utxo\Utxo;
12
use BitWasp\Buffertools\BufferInterface;
13
14
class Transaction extends Serializable implements TransactionInterface
15
{
16
    /**
17
     * @var int
18
     */
19
    private $version;
20
21
    /**
22
     * @var TransactionInputInterface[]
23
     */
24
    private $inputs;
25
26
    /**
27
     * @var TransactionOutputInterface[]
28
     */
29
    private $outputs;
30
31
    /**
32
     * @var ScriptWitnessInterface[]
33
     */
34
    private $witness;
35
36
    /**
37
     * @var int
38
     */
39
    private $lockTime;
40
41
    /**
42
     * Transaction constructor.
43
     *
44
     * @param int $nVersion
45
     * @param TransactionInputInterface[] $vin
46
     * @param TransactionOutputInterface[] $vout
47
     * @param ScriptWitnessInterface[] $vwit
48
     * @param int $nLockTime
49
     */
50
    public function __construct(
51
        $nVersion = TransactionInterface::DEFAULT_VERSION,
52
        array $vin = [],
53 1427
        array $vout = [],
54
        array $vwit = [],
55
        $nLockTime = 0
56
    ) {
57
        if ($nVersion > TransactionInterface::MAX_VERSION) {
58
            throw new \InvalidArgumentException('Version must be less than ' . TransactionInterface::MAX_VERSION);
59
        }
60 1427
61 3
        if ($nLockTime < 0 || $nLockTime > TransactionInterface::MAX_LOCKTIME) {
62
            throw new \InvalidArgumentException('Locktime must be positive and less than ' . TransactionInterface::MAX_LOCKTIME);
63
        }
64 1424
65 3
        $this->version = $nVersion;
66
        $this->lockTime = $nLockTime;
67
68 1421
        $this->inputs = array_map(function (TransactionInputInterface $input) {
69 1421
            return $input;
70 1421
        }, $vin);
71 1421
        $this->outputs = array_map(function (TransactionOutputInterface $output) {
72 1421
            return $output;
73
        }, $vout);
74 1421
        $this->witness = array_map(function (ScriptWitnessInterface $scriptWitness) {
75
            return $scriptWitness;
76
        }, $vwit);
77
78
    }
79 113
80
    /**
81 113
     * @return Transaction
82 113
     */
83 113
    public function __clone()
84
    {
85
        //$this->inputs = clone $this->inputs;
86
        //$this->outputs = clone $this->outputs;
87
    }
88 1196
89
    /**
90 1196
     * @return BufferInterface
91
     */
92
    public function getTxHash()
93
    {
94
        return Hash::sha256d($this->getBuffer());
95
    }
96 1196
97
    /**
98 1196
     * @return BufferInterface
99
     */
100
    public function getTxId()
101
    {
102
        return $this->getTxHash()->flip();
103
    }
104
105
    /**
106
     * @return BufferInterface
107
     */
108
    public function getWitnessTxId()
109
    {
110
        return Hash::sha256d($this->getWitnessBuffer())->flip();
111
    }
112 1277
113
    /**
114 1277
     * @return int
115
     */
116
    public function getVersion()
117
    {
118
        return $this->version;
119
    }
120
121
    /**
122 1277
     * Get the array of inputs in the transaction
123
     *
124 1277
     * @return TransactionInputInterface[]
125
     */
126
    public function getInputs()
127
    {
128
        return $this->inputs;
129
    }
130
131 71
    /**
132
     * @param int $index
133 71
     * @return TransactionInputInterface
134
     */
135
    public function getInput($index)
136
    {
137
        if (!isset($this->inputs[$index])) {
138
            throw new \RuntimeException('No input at this index');
139
        }
140
        return $this->inputs[$index];
141 1295
    }
142
143 1295
    /**
144
     * Get Outputs
145
     *
146
     * @return TransactionOutputInterface[]
147
     */
148
    public function getOutputs()
149
    {
150 1187
        return $this->outputs;
151 18
    }
152 1187
153
    /**
154
     * @param int $vout
155
     * @return TransactionOutputInterface
156
     */
157
    public function getOutput($vout)
158 104
    {
159
        if (!isset($this->outputs[$vout])) {
160 104
            throw new \RuntimeException('No output at this index');
161
        }
162
        return $this->outputs[$vout];
163
    }
164
165
    /**
166
     * @return ScriptWitnessInterface[]
167
     */
168
    public function getWitnesses()
169
    {
170
        return $this->witness;
171
    }
172
173
    /**
174
     * @return ScriptWitnessInterface
175 1148
     */
176
    public function getWitness($index)
177 1148
    {
178 1148
        if (!isset($this->witness[$index])) {
179
            throw new \RuntimeException('No witness at this index');
180
        }
181
        return $this->witness[$index];
182
    }
183
184
    /**
185
     * @param int $vout
186
     * @return OutPointInterface
187
     */
188
    public function makeOutpoint($vout)
189
    {
190
        $this->getOutput($vout);
191
        return new OutPoint($this->getTxId(), $vout);
192
    }
193
194
    /**
195 1280
     * @param int $vout
196
     * @return Utxo
197 1280
     */
198
    public function makeUtxo($vout)
199
    {
200
        return new Utxo(new OutPoint($this->getTxId(), $vout), $this->getOutput($vout));
201
    }
202
203 3
    /**
204
     * Get Lock Time
205 3
     *
206 3
     * @return int
207 3
     */
208 3
    public function getLockTime()
209 2
    {
210
        return $this->lockTime;
211 3
    }
212
213
    /**
214
     * @return int|string
215
     */
216
    public function getValueOut()
217 3
    {
218
        $math = Bitcoin::getMath();
219 3
        $value = gmp_init(0);
220
        foreach ($this->outputs as $output) {
221
            $value = $math->add($value, gmp_init($output->getValue()));
222
        }
223
224
        return gmp_strval($value, 10);
225
    }
226 3
227
    /**
228 3
     * @return bool
229 3
     */
230 3
    public function isCoinbase()
231
    {
232
        return count($this->inputs) === 1 && $this->getInput(0)->isCoinBase();
233 3
    }
234 3
235 3
    /**
236 3
     * @param TransactionInterface $tx
237 3
     * @return bool
238
     */
239
    public function equals(TransactionInterface $tx)
240 3
    {
241 3
        $version = gmp_cmp($this->version, $tx->getVersion());
242 3
        if ($version !== 0) {
243
            return false;
244 2
        }
245
246 3
        $nIn = count($this->inputs);
247 3
        $nOut = count($this->outputs);
248 3
        $nWit = count($this->witness);
249
        if ($nIn !== count($tx->getInputs()) || $nOut !== count($tx->getOutputs()) || $nWit !== count($tx->getWitnesses())) {
250 2
            return false;
251
        }
252 3
253
        for ($i = 0; $i < $nIn; $i++) {
254
            if (false === $this->getInput($i)->equals($tx->getInput($i))) {
255
                return false;
256
            }
257
        }
258 3
259
        for ($i = 0; $i < $nOut; $i++) {
260
            if (false === $this->getOutput($i)->equals($tx->getOutput($i))) {
261
                return false;
262
            }
263
        }
264 27
265
        for ($i = 0; $i < $nWit; $i++) {
266 27
            if (false === $this->getWitness($i)->equals($tx->getWitness($i))) {
267
                return false;
268
            }
269
        }
270
271
        return gmp_cmp($this->lockTime, $tx->getLockTime()) === 0;
272 1241
    }
273
274 1241
    /**
275
     * @return Validator
276
     */
277
    public function validator()
278
    {
279
        return new Validator($this);
280 27
    }
281
282 27
    /**
283
     * @return BufferInterface
284
     */
285
    public function getBuffer()
286
    {
287
        return (new OldTransactionSerializer())->serialize($this);
288
    }
289
290
    /**
291
     * @return BufferInterface
292
     */
293
    public function getWitnessBuffer()
294
    {
295
        return (new TransactionSerializer())->serialize($this);
296
    }
297
}
298