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

TransactionSerializer   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 209
Duplicated Lines 0 %

Test Coverage

Coverage 98.53%

Importance

Changes 0
Metric Value
eloc 94
dl 0
loc 209
ccs 67
cts 68
cp 0.9853
rs 9.92
c 0
b 0
f 0
wmc 31

4 Methods

Rating   Name   Duplication   Size   Complexity  
B serialize() 0 53 11
A parse() 0 3 1
C fromParser() 0 55 12
B __construct() 0 30 7
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BitWasp\Bitcoin\Serializer\Transaction;
6
7
use BitWasp\Bitcoin\Script\Opcodes;
8
use BitWasp\Bitcoin\Serializer\Script\ScriptWitnessSerializer;
9
use BitWasp\Bitcoin\Serializer\ExtraPayload\ExtraPayloadSerializer;
10
use BitWasp\Bitcoin\Serializer\Types;
11
use BitWasp\Bitcoin\Transaction\Transaction;
12
use BitWasp\Bitcoin\Transaction\TransactionInterface;
13
use BitWasp\Buffertools\BufferInterface;
14
use BitWasp\Buffertools\Parser;
15
16
class TransactionSerializer implements TransactionSerializerInterface
17
{
18
    const NO_WITNESS = 1;
19
20
    /**
21
     * @var \BitWasp\Buffertools\Types\Int16
22
     */
23
    protected $int16le;
24
25
    /**
26
     * @var \BitWasp\Buffertools\Types\Uint16
27
     */
28
    protected $uint16le;
29
30
    /**
31
     * @var \BitWasp\Buffertools\Types\Int32
32
     */
33
    protected $int32le;
34
35
    /**
36
     * @var \BitWasp\Buffertools\Types\Uint32
37
     */
38
    protected $uint32le;
39
40
    /**
41
     * @var \BitWasp\Buffertools\Types\VarInt
42
     */
43
    protected $varint;
44
45
    /**
46
     * @var TransactionInputSerializer
47
     */
48
    protected $inputSerializer;
49 5237
50
    /**
51 5237
     * @var TransactionOutputSerializer
52 5237
     */
53 5237
    protected $outputSerializer;
54
55 5237
    /**
56 5236
     * @var ScriptWitnessSerializer
57 5236
     */
58 5236
    protected $witnessSerializer;
59
60 5236
    /**
61 5236
     * @var ExtraPayloadSerializer
62
     */
63
    protected $payloadSerializer;
64
65 5237
    public function __construct(TransactionInputSerializer $inputSerializer = null, TransactionOutputSerializer $outputSerializer = null, ScriptWitnessSerializer $witnessSerializer = null, ExtraPayloadSerializer $payloadSerializer = null)
66 5236
    {
67
        $this->int16le = Types::int16le();
68
        $this->uint16le = Types::uint16le();
69 5237
        $this->int32le = Types::int32le();
70 5237
        $this->uint32le = Types::uint32le();
71 5237
        $this->varint = Types::varint();
72 5237
73
        if ($inputSerializer === null || $outputSerializer === null) {
74
            $opcodes = new Opcodes();
75
            if (!$inputSerializer) {
76
                $inputSerializer = new TransactionInputSerializer(new OutPointSerializer(), $opcodes);
77
            }
78 60
            if (!$outputSerializer) {
79
                $outputSerializer = new TransactionOutputSerializer($opcodes);
80 60
            }
81
        }
82 60
83 60
        if (!$witnessSerializer) {
84 60
            $witnessSerializer = new ScriptWitnessSerializer();
85 49
        }
86
87
        if (!$payloadSerializer) {
88 60
            $payloadSerializer = new ExtraPayloadSerializer();
89 60
        }
90 60
91 11
        $this->inputSerializer = $inputSerializer;
92 11
        $this->outputSerializer = $outputSerializer;
93 11
        $this->witnessSerializer = $witnessSerializer;
94 11
        $this->payloadSerializer = $payloadSerializer;
95 11
    }
96
97
    /**
98 11
     * @param Parser $parser
99 11
     * @return TransactionInterface
100 11
     */
101
    public function fromParser(Parser $parser): TransactionInterface
102
    {
103
        $version = (int) $this->uint16le->read($parser);
104 49
        $type = (int) $this->uint16le->read($parser);
105 49
106 49
        $vin = [];
107
        $vinCount = $this->varint->read($parser);
108
        for ($i = 0; $i < $vinCount; $i++) {
109
            $vin[] = $this->inputSerializer->fromParser($parser);
110 59
        }
111 59
112 11
        $vout = [];
113 11
        $flags = 0;
114 11
        if (count($vin) === 0) {
115 11
            $flags = (int) $this->varint->read($parser);
116
            if ($flags !== 0) {
117
                $vinCount = $this->varint->read($parser);
118
                for ($i = 0; $i < $vinCount; $i++) {
119 59
                    $vin[] = $this->inputSerializer->fromParser($parser);
120
                }
121
122
                $voutCount = $this->varint->read($parser);
123 59
                for ($i = 0; $i < $voutCount; $i++) {
124
                    $vout[] = $this->outputSerializer->fromParser($parser);
125 59
                }
126
            }
127
        } else {
128
            $voutCount = $this->varint->read($parser);
129
            for ($i = 0; $i < $voutCount; $i++) {
130
                $vout[] = $this->outputSerializer->fromParser($parser);
131
            }
132 49
        }
133
134 49
        $vwit = [];
135
        if (($flags & 1)) {
136
            $flags ^= 1;
137
            $witCount = count($vin);
138
            for ($i = 0; $i < $witCount; $i++) {
139
                $vwit[] = $this->witnessSerializer->fromParser($parser);
140
            }
141
        }
142 5228
143
        if ($flags) {
144 5228
            throw new \RuntimeException('Flags byte was 0');
145 5228
        }
146
147 5228
        $lockTime = (int) $this->uint32le->read($parser);
148 5228
149 5228
        $extra_payload = null;
150 130
151
        if ($version >= 3 && $type != 0) {
152
            $extra_payload = $this->payloadSerializer->fromParser($parser);
153 5228
        }
154 130
155
        return new Transaction($version, $vin, $vout, $vwit, $lockTime, $type, $extra_payload);
156
    }
157 5228
158 5228
    /**
159 5227
     * @param BufferInterface $data
160
     * @return TransactionInterface
161
     */
162 5228
    public function parse(BufferInterface $data): TransactionInterface
163 5228
    {
164 5228
        return $this->fromParser(new Parser($data));
165
    }
166
167 5228
    /**
168 130
     * @param TransactionInterface $transaction
169 130
     * @param int $opt
170
     * @return BufferInterface
171
     */
172
    public function serialize(TransactionInterface $transaction, int $opt = 0): BufferInterface
173 5228
    {
174
        $version = $transaction->getVersion();
175 5228
        $type = $transaction->getType();
176
177
        $parser = new Parser();
178
        $parser->appendBinary($this->int16le->write($version));
179
        $parser->appendBinary($this->int16le->write($type));
180
181
        $flags = 0;
182
        $allowWitness = !($opt & self::NO_WITNESS);
183
        if ($allowWitness && $transaction->hasWitness()) {
184
            $flags |= 1;
185
        }
186
187
        if ($flags) {
188
            $parser->appendBinary(pack("CC", 0, $flags));
189
        }
190
191
        $parser->appendBinary($this->varint->write(count($transaction->getInputs())));
192
        foreach ($transaction->getInputs() as $input) {
193
            $parser->appendBuffer($this->inputSerializer->serialize($input));
194
        }
195
196
        $parser->appendBinary($this->varint->write(count($transaction->getOutputs())));
197
        foreach ($transaction->getOutputs() as $output) {
198
            $parser->appendBuffer($this->outputSerializer->serialize($output));
199
        }
200
201
        if ($flags & 1) {
202
            foreach ($transaction->getWitnesses() as $witness) {
203
                $parser->appendBuffer($this->witnessSerializer->serialize($witness));
204
            }
205
        }
206
207
        $parser->appendBinary($this->uint32le->write($transaction->getLockTime()));
208
209
        if ($version >= 3 && $type != 0) {
210
211
            $extraPayload = $transaction->getExtraPayload();
0 ignored issues
show
Bug introduced by
The method getExtraPayload() does not exist on BitWasp\Bitcoin\Transaction\TransactionInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to BitWasp\Bitcoin\Transaction\TransactionInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

211
            /** @scrutinizer ignore-call */ 
212
            $extraPayload = $transaction->getExtraPayload();
Loading history...
212
213
            $extra_payload_size = $extraPayload->getSize();
214
            $extra_payload = $extraPayload->getBuffer();
215
216
            if ($extra_payload_size >= 0) {
217
218
                $parser->appendBinary($this->varint->write($extra_payload_size));
219
                $parser->appendBuffer($extra_payload);
220
221
            }
222
        }
223
224
        return $parser->getBuffer();
225
    }
226
}
227