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

TransactionSerializer::serialize()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 35
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 35
rs 8.439
ccs 0
cts 0
cp 0
cc 5
eloc 21
nc 8
nop 1
crap 30
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\Serializer\Script\ScriptWitnessSerializer;
10
use BitWasp\Bitcoin\Transaction\Transaction;
11
use BitWasp\Bitcoin\Transaction\TransactionInterface;
12
use BitWasp\Buffertools\Buffer;
13
use BitWasp\Buffertools\BufferInterface;
14
use BitWasp\Buffertools\ByteOrder;
15
use BitWasp\Buffertools\Parser;
16
use BitWasp\Buffertools\Types\Int32;
17
use BitWasp\Buffertools\Types\Int8;
18
use BitWasp\Buffertools\Types\Uint32;
19
use BitWasp\Buffertools\Types\VarInt;
20
use BitWasp\Buffertools\Types\Vector;
21
22
class TransactionSerializer
23
{
24
    /**
25
     *
26 334
     */
27
    public function __construct()
28 334
    {
29 334
        $this->inputSerializer = new TransactionInputSerializer(new OutPointSerializer());
0 ignored issues
show
Bug introduced by
The property inputSerializer 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...
30 334
        $this->outputSerializer = new TransactionOutputSerializer;
0 ignored issues
show
Bug introduced by
The property outputSerializer 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...
31
        $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...
32
    }
33
34
    /**
35 316
     * @param Parser $parser
36
     * @return Transaction
37 316
     */
38 316
    public function fromParser(Parser $parser)
39
    {
40 228
        $math = Bitcoin::getMath();
41 316
        $int32le = new Int32($math, ByteOrder::LE);
42 316
        $uint32le = new Uint32($math, ByteOrder::LE);
43 228
        $varint = new VarInt($math, ByteOrder::LE);
44 316
45 316
        $version = $int32le->read($parser);
46 316
47
        $vin = [];
48
        $vinCount = $varint->read($parser);
49
        for ($i = 0; $i < $vinCount; $i++) {
50
            $vin[] = $this->inputSerializer->fromParser($parser);
51
        }
52
53 292
        $vout = [];
54
        $flags = 0;
55 292
        if (count($vin) == 0) {
56 292
            $flags = (int) $varint->read($parser);
57 292
            if ($flags != 0) {
58 292
                $vinCount = $varint->read($parser);
59 292
                for ($i = 0; $i < $vinCount; $i++) {
60 292
                    $vin[] = $this->inputSerializer->fromParser($parser);
61
                }
62
63
                $voutCount = $varint->read($parser);
64
                for ($i = 0; $i < $voutCount; $i++) {
65
                    $vout[] = $this->outputSerializer->fromParser($parser);
66
                }
67
            }
68
        } else {
69 228
            $voutCount = $varint->read($parser);
70
            for ($i = 0; $i < $voutCount; $i++) {
71 228
                $vout[] = $this->outputSerializer->fromParser($parser);
72
            }
73 228
        }
74
75 228
        $vwit = [];
76 228
        if (($flags & 1)) {
77 228
            $flags ^= 1;
78 228
            $witCount = count($vin);
79 228
            for ($i = 0; $i < $witCount; $i++) {
80 228
                $vectorCount = $varint->read($parser);
81
                $vwit[] = $this->witnessSerializer->fromParser($parser, $vectorCount);
82
            }
83
        }
84
85
        if ($flags) {
86
            throw new \RuntimeException('Flags byte was 0');
87 156
        }
88
89 156
        $lockTime = $uint32le->read($parser);
90 156
91
        return new Transaction(
92
            $version,
93
            new TransactionInputCollection($vin),
94
            new TransactionOutputCollection($vout),
95
            new TransactionWitnessCollection($vwit),
96
            $lockTime
97
        );
98
    }
99
100
    /**
101
     * @param string|BufferInterface $data
102
     * @return Transaction
103
     */
104
    public function parse($data)
105
    {
106
        return $this->fromParser(new Parser($data));
0 ignored issues
show
Bug introduced by
It seems like $data defined by parameter $data on line 104 can also be of type object<BitWasp\Buffertools\BufferInterface>; however, BitWasp\Buffertools\Parser::__construct() does only seem to accept null|string|object<BitWasp\Buffertools\Buffer>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
107
    }
108
109
    /**
110
     * @param TransactionInterface $transaction
111
     * @return Buffer
112
     */
113
    public function serialize(TransactionInterface $transaction)
114
    {
115
        $math = Bitcoin::getMath();
116
        $int8le = new Int8($math, ByteOrder::LE);
117
        $int32le = new Int32($math, ByteOrder::LE);
118
        $uint32le = new Uint32($math, ByteOrder::LE);
119
        $varint = new VarInt($math, ByteOrder::LE);
120
        $vector = new Vector($varint, function () {
121
        });
122
123
        $binary = $int32le->write($transaction->getVersion());
124
        $flags = 0;
125
126
        if (!$transaction->getWitnesses()->isNull()) {
127
            $flags |= 1;
128
        }
129
130
        if ($flags) {
131
            $binary .= $int8le->write(0);
132
            $binary .= $int8le->write($flags);
133
        }
134
135
        $binary .= $vector->write($transaction->getInputs()->all());
136
        $binary .= $vector->write($transaction->getOutputs()->all());
137
138
        if ($flags & 1) {
139
            foreach ($transaction->getWitnesses() as $witness) {
140
                $binary .= $witness->getBuffer()->getBinary();
141
            }
142
        }
143
144
        $binary .= $uint32le->write($transaction->getLockTime());
145
146
        return new Buffer($binary);
147
    }
148
}
149