Completed
Push — master ( b4333d...f02c0d )
by thomas
14s
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.31%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
dl 0
loc 139
ccs 61
cts 81
cp 0.7531
rs 9.1666
c 3
b 0
f 0
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 144
    public function __construct(TransactionInputSerializer $txInSer = null, TransactionOutputSerializer $txOutSer = null, ScriptWitnessSerializer $witSer = null)
37
    {
38 144
        $this->inputSerializer = $txInSer ?: new TransactionInputSerializer(new OutPointSerializer());
39 144
        $this->outputSerializer = $txOutSer ?: new TransactionOutputSerializer;
40 144
        $this->witnessSerializer = $witSer ?: new ScriptWitnessSerializer();
41 144
    }
42
43
    /**
44
     * @param Parser $parser
45
     * @return TransactionInterface
46
     */
47 108
    public function fromParser(Parser $parser)
48
    {
49 108
        $math = Bitcoin::getMath();
50 108
        $int32le = new Int32($math, ByteOrder::LE);
51 108
        $uint32le = new Uint32($math, ByteOrder::LE);
52 108
        $varint = new VarInt($math, ByteOrder::LE);
53
54 108
        $version = $int32le->read($parser);
55
56 108
        $vin = [];
57 108
        $vinCount = $varint->read($parser);
58 108
        for ($i = 0; $i < $vinCount; $i++) {
59 108
            $vin[] = $this->inputSerializer->fromParser($parser);
60 72
        }
61
62 108
        $vout = [];
63 108
        $flags = 0;
64 108
        if (count($vin) == 0) {
65
            $flags = (int) $varint->read($parser);
66
            if ($flags != 0) {
67
                $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 108
            $voutCount = $varint->read($parser);
79 108
            for ($i = 0; $i < $voutCount; $i++) {
80 108
                $vout[] = $this->outputSerializer->fromParser($parser);
81 72
            }
82
        }
83
84 108
        $vwit = [];
85 108
        if (($flags & 1)) {
86
            $flags ^= 1;
87
            $witCount = count($vin);
88
            for ($i = 0; $i < $witCount; $i++) {
89
                $vectorCount = $varint->read($parser);
90
                $vwit[] = $this->witnessSerializer->fromParser($parser, $vectorCount);
91
            }
92
        }
93
94 108
        if ($flags) {
95
            throw new \RuntimeException('Flags byte was 0');
96
        }
97
98 108
        $lockTime = $uint32le->read($parser);
99
100 108
        return new Transaction(
101 72
            $version,
102 72
            $vin,
103 72
            $vout,
104 72
            $vwit,
105
            $lockTime
106 72
        );
107
    }
108
109
    /**
110
     * @param string|BufferInterface $data
111
     * @return TransactionInterface
112
     */
113 75
    public function parse($data)
114
    {
115 75
        return $this->fromParser(new Parser($data));
116
    }
117
118
    /**
119
     * @param TransactionInterface $transaction
120
     * @return BufferInterface
121
     */
122 27
    public function serialize(TransactionInterface $transaction)
123
    {
124 27
        $math = Bitcoin::getMath();
125 27
        $int8le = new Int8($math, ByteOrder::LE);
126 27
        $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
132 27
        $binary = $int32le->write($transaction->getVersion());
133 27
        $flags = 0;
134
135 27
        if (!empty($transaction->getWitnesses())) {
136 27
            $flags |= 1;
137 18
        }
138
139 27
        if ($flags) {
140 27
            $binary .= $int8le->write(0);
141 27
            $binary .= $int8le->write($flags);
142 18
        }
143
144 27
        $binary .= $vector->write($transaction->getInputs());
145 27
        $binary .= $vector->write($transaction->getOutputs());
146
147 27
        if ($flags & 1) {
148 27
            foreach ($transaction->getWitnesses() as $witness) {
149 27
                $binary .= $witness->getBuffer()->getBinary();
150 18
            }
151 18
        }
152
153 27
        $binary .= $uint32le->write($transaction->getLockTime());
154
155 27
        return new Buffer($binary);
156
    }
157
}
158