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

TransactionSerializer   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 139
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 15

Test Coverage

Coverage 75%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 139
ccs 60
cts 80
cp 0.75
rs 9.1666
wmc 20
lcom 1
cbo 15

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 4
C fromParser() 0 61 10
A parse() 0 4 1
B serialize() 0 35 5
1
<?php
2
3
namespace BitWasp\Bitcoin\Serializer\Transaction;
4
5
use BitWasp\Bitcoin\Bitcoin;
6
use BitWasp\Bitcoin\Serializer\Script\ScriptWitnessSerializer;
7
use BitWasp\Bitcoin\Transaction\Transaction;
8
use BitWasp\Bitcoin\Transaction\TransactionInterface;
9
use BitWasp\Buffertools\Buffer;
10
use BitWasp\Buffertools\BufferInterface;
11
use BitWasp\Buffertools\ByteOrder;
12
use BitWasp\Buffertools\Parser;
13
use BitWasp\Buffertools\Types\Int32;
14
use BitWasp\Buffertools\Types\Int8;
15
use BitWasp\Buffertools\Types\Uint32;
16
use BitWasp\Buffertools\Types\VarInt;
17
use BitWasp\Buffertools\Types\Vector;
18
19
class TransactionSerializer implements TransactionSerializerInterface
20
{
21
    /**
22
     * @var TransactionInputSerializer
23
     */
24
    private $inputSerializer;
25
26
    /**
27
     * @var TransactionOutputSerializer
28
     */
29
    private $outputSerializer;
30
31
    /**
32
     * @var ScriptWitnessSerializer
33
     */
34
    private $witnessSerializer;
35
36
    public function __construct(TransactionInputSerializer $txInSer = null, TransactionOutputSerializer $txOutSer = null, ScriptWitnessSerializer $witSer = null)
37
    {
38
        $this->inputSerializer = $txInSer ?: new TransactionInputSerializer(new OutPointSerializer());
39 144
        $this->outputSerializer = $txOutSer ?: new TransactionOutputSerializer;
40
        $this->witnessSerializer = $witSer ?: new ScriptWitnessSerializer();
41 144
    }
42 144
43 144
    /**
44 144
     * @param Parser $parser
45
     * @return TransactionInterface
46
     */
47
    public function fromParser(Parser $parser)
48
    {
49
        $math = Bitcoin::getMath();
50 108
        $int32le = new Int32($math, ByteOrder::LE);
51
        $uint32le = new Uint32($math, ByteOrder::LE);
52 108
        $varint = new VarInt($math, ByteOrder::LE);
53 108
54 108
        $version = $int32le->read($parser);
55 108
56
        $vin = [];
57 108
        $vinCount = $varint->read($parser);
58
        for ($i = 0; $i < $vinCount; $i++) {
59 108
            $vin[] = $this->inputSerializer->fromParser($parser);
60 108
        }
61 108
62 108
        $vout = [];
63 72
        $flags = 0;
64
        if (count($vin) == 0) {
65 108
            $flags = (int) $varint->read($parser);
66 108
            if ($flags != 0) {
67 108
                $vinCount = $varint->read($parser);
68
                for ($i = 0; $i < $vinCount; $i++) {
69
                    $vin[] = $this->inputSerializer->fromParser($parser);
70
                }
71
72
                $voutCount = $varint->read($parser);
73
                for ($i = 0; $i < $voutCount; $i++) {
74
                    $vout[] = $this->outputSerializer->fromParser($parser);
75
                }
76
            }
77
        } else {
78
            $voutCount = $varint->read($parser);
79
            for ($i = 0; $i < $voutCount; $i++) {
80
                $vout[] = $this->outputSerializer->fromParser($parser);
81 108
            }
82 108
        }
83 108
84 72
        $vwit = [];
85
        if (($flags & 1)) {
86
            $flags ^= 1;
87 108
            $witCount = count($vin);
88 108
            for ($i = 0; $i < $witCount; $i++) {
89
                $vectorCount = $varint->read($parser);
90
                $vwit[] = $this->witnessSerializer->fromParser($parser, $vectorCount);
91
            }
92
        }
93
94
        if ($flags) {
95
            throw new \RuntimeException('Flags byte was 0');
96
        }
97 108
98
        $lockTime = $uint32le->read($parser);
99
100
        return new Transaction(
101 108
            $version,
102
            $vin,
103 108
            $vout,
104 72
            $vwit,
105 108
            $lockTime
106 108
        );
107 108
    }
108
109 72
    /**
110
     * @param string|BufferInterface $data
111
     * @return TransactionInterface
112
     */
113
    public function parse($data)
114
    {
115
        return $this->fromParser(new Parser($data));
116 75
    }
117
118 75
    /**
119
     * @param TransactionInterface $transaction
120
     * @return BufferInterface
121
     */
122
    public function serialize(TransactionInterface $transaction)
123
    {
124
        $math = Bitcoin::getMath();
125 27
        $int8le = new Int8($math, ByteOrder::LE);
126
        $int32le = new Int32($math, ByteOrder::LE);
127 27
        $uint32le = new Uint32($math, ByteOrder::LE);
128 27
        $varint = new VarInt($math, ByteOrder::LE);
129 27
        $vector = new Vector($varint, function () {
130 27
        });
131 27
132 27
        $binary = $int32le->write($transaction->getVersion());
133 27
        $flags = 0;
134
135 27
        if (!empty($transaction->getWitnesses())) {
136 27
            $flags |= 1;
137
        }
138 27
139 27
        if ($flags) {
140 18
            $binary .= $int8le->write(0);
141
            $binary .= $int8le->write($flags);
142 27
        }
143 27
144 27
        $binary .= $vector->write($transaction->getInputs());
145 18
        $binary .= $vector->write($transaction->getOutputs());
146
147 27
        if ($flags & 1) {
148 27
            foreach ($transaction->getWitnesses() as $witness) {
149
                $binary .= $witness->getBuffer()->getBinary();
150 27
            }
151 27
        }
152 27
153 18
        $binary .= $uint32le->write($transaction->getLockTime());
154 18
155
        return new Buffer($binary);
156 27
    }
157
}
158