Completed
Push — master ( c42b07...45cd31 )
by thomas
21:37
created

Transaction::getBuffer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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