Completed
Pull Request — master (#240)
by thomas
73:04
created

WitnessTransactionSerializer   C

Complexity

Total Complexity 13

Size/Duplication

Total Lines 177
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 20

Importance

Changes 1
Bugs 0 Features 1
Metric Value
wmc 13
c 1
b 0
f 1
lcom 1
cbo 20
dl 0
loc 177
rs 6.4705

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getTemplate() 0 18 1
A s() 0 12 1
B serialize() 0 44 4
B fromParser() 0 54 5
A parse() 0 5 1
1
<?php
2
3
namespace BitWasp\Bitcoin\Serializer\Transaction;
4
5
use BitWasp\Bitcoin\Bitcoin;
6
use BitWasp\Bitcoin\Collection\Transaction\TransactionInputCollection;
7
use BitWasp\Bitcoin\Collection\Transaction\TransactionOutputCollection;
8
use BitWasp\Bitcoin\Collection\Transaction\TransactionWitnessCollection;
9
use BitWasp\Bitcoin\Script\ScriptWitnessInterface;
10
use BitWasp\Buffertools\Buffer;
11
use BitWasp\Buffertools\BufferInterface;
12
use BitWasp\Buffertools\ByteOrder;
13
use BitWasp\Buffertools\Parser;
14
use BitWasp\Bitcoin\Transaction\Transaction;
15
use BitWasp\Bitcoin\Transaction\TransactionInterface;
16
use BitWasp\Buffertools\TemplateFactory;
17
use BitWasp\Buffertools\Types\Int32;
18
use BitWasp\Buffertools\Types\Uint32;
19
use BitWasp\Buffertools\Types\Uint8;
20
use BitWasp\Buffertools\Types\VarInt;
21
use BitWasp\Buffertools\Types\VarString;
22
use BitWasp\Buffertools\Types\Vector;
23
24
class WitnessTransactionSerializer
25
{
26
    /**
27
     * @var TransactionInputSerializer
28
     */
29
    public $inputSerializer;
30
31
    /**
32
     * @var TransactionOutputSerializer
33
     */
34
    public $outputSerializer;
35
36
    /**
37
     *
38
     */
39
    public function __construct()
40
    {
41
        $this->inputSerializer = new TransactionInputSerializer(new OutPointSerializer());
42
        $this->outputSerializer = new TransactionOutputSerializer;
43
        $this->witnessSerializer = new ScriptWitnessSerializer();
0 ignored issues
show
Bug introduced by
The property witnessSerializer does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
44
    }
45
46
    /**
47
     * @return \BitWasp\Buffertools\Template
48
     */
49
    private function getTemplate()
50
    {
51
        return (new TemplateFactory())
52
            ->int32le()
53
            ->int8()
54
            ->int8()
55
            ->vector(function (Parser & $parser) {
56
                return $this->inputSerializer->fromParser($parser);
57
            })
58
            ->vector(function (Parser &$parser) {
59
                return $this->outputSerializer->fromParser($parser);
60
            })
61
            ->vector(function (Parser $parser) {
62
                return $this->witnessSerializer->fromParser($parser);
63
            })
64
            ->uint32le()
65
            ->getTemplate();
66
    }
67
68
    public function s(TransactionInterface $transaction)
69
    {
70
        return $this->getTemplate()->write([
71
            $transaction->getVersion(),
72
            0,
73
            1,
74
            $transaction->getInputs()->all(),
75
            $transaction->getOutputs()->all(),
76
            $transaction->getWitnesses()->all(),
77
            $transaction->getLockTime()
78
        ]);
79
    }
80
81
    /**
82
     * @param TransactionInterface $transaction
83
     * @return BufferInterface
84
     */
85
    public function serialize(TransactionInterface $transaction)
86
    {
87
        $math = Bitcoin::getMath();
88
        $int32le = new Int32($math, ByteOrder::LE);
89
        $uint32le = new Uint32($math, ByteOrder::LE);
90
91
        $varint = new VarInt($math, ByteOrder::LE);
92
        $inputsSerializer = new Vector($varint, function (Parser $parser) {
93
            return $this->inputSerializer->fromParser($parser);
94
        });
95
        $flagsSerializer = new Uint8($math, ByteOrder::BE);
96
        $outputsSerializer = new Vector($varint, function (Parser $parser) {
97
            return $this->outputSerializer->fromParser($parser);
98
        });
99
        $witnessSerializer = new Vector($varint, function (Parser $parser) {
100
            return $this->witnessSerializer->fromParser($parser);
101
        });
102
103
        $binary = $int32le->write($transaction->getVersion());
104
        $flags = 0;
105
        if (!$transaction->getWitnesses()->isNull()) {
106
            $flags |= 1;
107
        }
108
109
        if ($flags) {
110
            // Dummy
111
            $binary .= $flagsSerializer->write(0);
112
            $binary .= $flagsSerializer->write($flags);
113
        }
114
115
        $binary .= $inputsSerializer->write($transaction->getInputs()->all());
116
        $binary .= $outputsSerializer->write($transaction->getOutputs()->all());
117
118
        if ($flags & 1) {
119
            $w = array_map(function (ScriptWitnessInterface $scriptWitness) {
120
                return $this->witnessSerializer->serialize($scriptWitness);
121
            }, $transaction->getWitnesses()->all());
122
            $binary .= $witnessSerializer->write($w);
123
        }
124
125
        $binary .= $uint32le->write($transaction->getLockTime());
126
127
        return new Buffer($binary, null, $math);
128
    }
129
130
    /**
131
     * @param Parser $parser
132
     * @return Transaction
133
     * @throws \BitWasp\Buffertools\Exceptions\ParserOutOfRange
134
     * @throws \Exception
135
     */
136
    public function fromParser(Parser $parser)
137
    {
138
        $math = Bitcoin::getMath();
139
        $int32le = new Int32($math, ByteOrder::LE);
140
        $uint32le = new Uint32($math, ByteOrder::LE);
141
142
        $varint = new VarInt($math, ByteOrder::LE);
143
        $inputsSerializer = new Vector($varint, function (Parser $parser) {
144
            return $this->inputSerializer->fromParser($parser);
145
        });
146
        $flagsSerializer = new Uint8($math, ByteOrder::BE);
147
        $outputsSerializer = new Vector($varint, function (Parser $parser) {
148
            return $this->outputSerializer->fromParser($parser);
149
        });
150
151
        $vcharSerializer = new VarString($varint);
152
        $witnessSerializer = new Vector($varint, function (Parser $parser) use ($vcharSerializer) {
153
            return $vcharSerializer->read($parser);
154
        });
155
156
        $version = $int32le->read($parser);
157
        $dummy = (int) $flagsSerializer->read($parser);
158
        if ($dummy !== 0) {
159
            throw new \RuntimeException('Non-zero dummy in witness-bearing transaction');
160
        }
161
162
        $flags = (int) $flagsSerializer->read($parser);
163
        if ($math->cmp($flags, 0) != 0) {
164
            $vInputs = $inputsSerializer->read($parser);
165
            $vOutputs = $outputsSerializer->read($parser);
166
        } else {
167
            throw new \RuntimeException('Unknown flag');
168
        }
169
170
        $vWitness = [];
171
        if ($flags & 1) {
172
            $flags ^= 1;
173
            $vWitness = $witnessSerializer->read($parser);
174
        }
175
176
        if ($flags) {
177
            throw new \RuntimeException('Unknown optional data');
178
        }
179
180
        $locktime = $uint32le->read($parser);
181
182
        return new Transaction(
183
            $version,
184
            new TransactionInputCollection($vInputs),
185
            new TransactionOutputCollection($vOutputs),
186
            new TransactionWitnessCollection($vWitness),
187
            $locktime
188
        );
189
    }
190
191
    /**
192
     * @param $hex
193
     * @return Transaction
194
     */
195
    public function parse($hex)
196
    {
197
        $parser = new Parser($hex);
198
        return $this->fromParser($parser);
199
    }
200
}
201