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
|
|
|
* @var TransactionInputSerializer |
26
|
|
|
*/ |
27
|
|
|
private $inputSerializer; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* @var TransactionOutputSerializer |
31
|
|
|
*/ |
32
|
|
|
private $outputSerializer; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* @var ScriptWitnessSerializer |
36
|
|
|
*/ |
37
|
|
|
private $witnessSerializer; |
38
|
|
|
|
39
|
300 |
|
public function __construct() |
40
|
|
|
{ |
41
|
300 |
|
$this->inputSerializer = new TransactionInputSerializer(new OutPointSerializer()); |
42
|
300 |
|
$this->outputSerializer = new TransactionOutputSerializer; |
43
|
300 |
|
$this->witnessSerializer = new ScriptWitnessSerializer(); |
44
|
300 |
|
} |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @param Parser $parser |
48
|
|
|
* @return Transaction |
49
|
|
|
*/ |
50
|
228 |
|
public function fromParser(Parser $parser) |
51
|
|
|
{ |
52
|
228 |
|
$math = Bitcoin::getMath(); |
53
|
228 |
|
$int32le = new Int32($math, ByteOrder::LE); |
54
|
228 |
|
$uint32le = new Uint32($math, ByteOrder::LE); |
55
|
228 |
|
$varint = new VarInt($math, ByteOrder::LE); |
56
|
|
|
|
57
|
228 |
|
$version = $int32le->read($parser); |
58
|
|
|
|
59
|
228 |
|
$vin = []; |
60
|
228 |
|
$vinCount = $varint->read($parser); |
61
|
228 |
|
for ($i = 0; $i < $vinCount; $i++) { |
62
|
228 |
|
$vin[] = $this->inputSerializer->fromParser($parser); |
63
|
228 |
|
} |
64
|
|
|
|
65
|
228 |
|
$vout = []; |
66
|
228 |
|
$flags = 0; |
67
|
228 |
|
if (count($vin) == 0) { |
68
|
|
|
$flags = (int) $varint->read($parser); |
69
|
|
|
if ($flags != 0) { |
70
|
|
|
$vinCount = $varint->read($parser); |
71
|
|
|
for ($i = 0; $i < $vinCount; $i++) { |
72
|
|
|
$vin[] = $this->inputSerializer->fromParser($parser); |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
$voutCount = $varint->read($parser); |
76
|
|
|
for ($i = 0; $i < $voutCount; $i++) { |
77
|
|
|
$vout[] = $this->outputSerializer->fromParser($parser); |
78
|
|
|
} |
79
|
|
|
} |
80
|
|
|
} else { |
81
|
228 |
|
$voutCount = $varint->read($parser); |
82
|
228 |
|
for ($i = 0; $i < $voutCount; $i++) { |
83
|
228 |
|
$vout[] = $this->outputSerializer->fromParser($parser); |
84
|
228 |
|
} |
85
|
|
|
} |
86
|
|
|
|
87
|
228 |
|
$vwit = []; |
88
|
228 |
|
if (($flags & 1)) { |
89
|
|
|
$flags ^= 1; |
90
|
|
|
$witCount = count($vin); |
91
|
|
|
for ($i = 0; $i < $witCount; $i++) { |
92
|
|
|
$vectorCount = $varint->read($parser); |
93
|
|
|
$vwit[] = $this->witnessSerializer->fromParser($parser, $vectorCount); |
94
|
|
|
} |
95
|
|
|
} |
96
|
|
|
|
97
|
228 |
|
if ($flags) { |
98
|
|
|
throw new \RuntimeException('Flags byte was 0'); |
99
|
|
|
} |
100
|
|
|
|
101
|
228 |
|
$lockTime = $uint32le->read($parser); |
102
|
|
|
|
103
|
228 |
|
return new Transaction( |
104
|
228 |
|
$version, |
105
|
228 |
|
new TransactionInputCollection($vin), |
106
|
228 |
|
new TransactionOutputCollection($vout), |
107
|
228 |
|
new TransactionWitnessCollection($vwit), |
108
|
|
|
$lockTime |
109
|
228 |
|
); |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* @param string|BufferInterface $data |
114
|
|
|
* @return Transaction |
115
|
|
|
*/ |
116
|
156 |
|
public function parse($data) |
117
|
|
|
{ |
118
|
156 |
|
return $this->fromParser(new Parser($data)); |
|
|
|
|
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* @param TransactionInterface $transaction |
123
|
|
|
* @return BufferInterface |
124
|
|
|
*/ |
125
|
54 |
|
public function serialize(TransactionInterface $transaction) |
126
|
|
|
{ |
127
|
54 |
|
$math = Bitcoin::getMath(); |
128
|
54 |
|
$int8le = new Int8($math, ByteOrder::LE); |
129
|
54 |
|
$int32le = new Int32($math, ByteOrder::LE); |
130
|
54 |
|
$uint32le = new Uint32($math, ByteOrder::LE); |
131
|
54 |
|
$varint = new VarInt($math, ByteOrder::LE); |
132
|
54 |
|
$vector = new Vector($varint, function () { |
133
|
54 |
|
}); |
134
|
|
|
|
135
|
54 |
|
$binary = $int32le->write($transaction->getVersion()); |
136
|
54 |
|
$flags = 0; |
137
|
|
|
|
138
|
54 |
|
if (!$transaction->getWitnesses()->isNull()) { |
139
|
54 |
|
$flags |= 1; |
140
|
54 |
|
} |
141
|
|
|
|
142
|
54 |
|
if ($flags) { |
143
|
54 |
|
$binary .= $int8le->write(0); |
144
|
54 |
|
$binary .= $int8le->write($flags); |
145
|
54 |
|
} |
146
|
|
|
|
147
|
54 |
|
$binary .= $vector->write($transaction->getInputs()->all()); |
148
|
54 |
|
$binary .= $vector->write($transaction->getOutputs()->all()); |
149
|
|
|
|
150
|
54 |
|
if ($flags & 1) { |
151
|
54 |
|
foreach ($transaction->getWitnesses() as $witness) { |
152
|
54 |
|
$binary .= $witness->getBuffer()->getBinary(); |
153
|
54 |
|
} |
154
|
54 |
|
} |
155
|
|
|
|
156
|
54 |
|
$binary .= $uint32le->write($transaction->getLockTime()); |
157
|
|
|
|
158
|
54 |
|
return new Buffer($binary); |
159
|
|
|
} |
160
|
|
|
} |
161
|
|
|
|
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.