Passed
Pull Request — master (#776)
by
unknown
40:03
created

Transaction::getType()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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