Completed
Pull Request — master (#446)
by thomas
157:14 queued 87:18
created

Transaction::getWitnessSerialization()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

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