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

Transaction   B

Complexity

Total Complexity 39

Size/Duplication

Total Lines 284
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 14

Test Coverage

Coverage 88.75%

Importance

Changes 6
Bugs 1 Features 1
Metric Value
c 6
b 1
f 1
dl 0
loc 284
ccs 71
cts 80
cp 0.8875
rs 8.2857
wmc 39
lcom 1
cbo 14

21 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 29 4
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 7 2
A getOutputs() 0 4 1
A getOutput() 0 7 2
A getWitnesses() 0 4 1
A getWitness() 0 7 2
A makeOutpoint() 0 5 1
A makeUtxo() 0 4 1
A getLockTime() 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
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