1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace BitWasp\Bitcoin\Transaction\Factory; |
6
|
|
|
|
7
|
|
|
use BitWasp\Bitcoin\Address\AddressInterface; |
8
|
|
|
use BitWasp\Bitcoin\Locktime; |
9
|
|
|
use BitWasp\Bitcoin\Script\Script; |
10
|
|
|
use BitWasp\Bitcoin\Script\ScriptFactory; |
11
|
|
|
use BitWasp\Bitcoin\Script\ScriptInterface; |
12
|
|
|
use BitWasp\Bitcoin\Script\ScriptWitnessInterface; |
13
|
|
|
use BitWasp\Bitcoin\Transaction\Bip69\Bip69; |
14
|
|
|
use BitWasp\Bitcoin\Transaction\OutPoint; |
15
|
|
|
use BitWasp\Bitcoin\Transaction\OutPointInterface; |
16
|
|
|
use BitWasp\Bitcoin\Transaction\Transaction; |
17
|
|
|
use BitWasp\Bitcoin\Transaction\TransactionInput; |
18
|
|
|
use BitWasp\Bitcoin\Transaction\TransactionInputInterface; |
19
|
|
|
use BitWasp\Bitcoin\Transaction\TransactionInterface; |
20
|
|
|
use BitWasp\Bitcoin\Transaction\TransactionOutput; |
21
|
|
|
use BitWasp\Bitcoin\Transaction\TransactionOutputInterface; |
22
|
|
|
use BitWasp\Buffertools\Buffer; |
23
|
|
|
use BitWasp\Buffertools\BufferInterface; |
24
|
|
|
|
25
|
|
|
class TxBuilder |
26
|
|
|
{ |
27
|
|
|
/** |
28
|
|
|
* @var int |
29
|
|
|
*/ |
30
|
|
|
private $nVersion; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @var array |
34
|
|
|
*/ |
35
|
|
|
private $inputs; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @var array |
39
|
|
|
*/ |
40
|
|
|
private $outputs; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var array |
44
|
|
|
*/ |
45
|
|
|
private $witness; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @var int |
49
|
|
|
*/ |
50
|
|
|
private $nLockTime; |
51
|
|
|
|
52
|
35 |
|
public function __construct() |
53
|
|
|
{ |
54
|
35 |
|
$this->reset(); |
55
|
35 |
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @return $this |
59
|
|
|
*/ |
60
|
35 |
|
public function reset() |
61
|
|
|
{ |
62
|
35 |
|
$this->nVersion = 1; |
63
|
35 |
|
$this->inputs = []; |
64
|
35 |
|
$this->outputs = []; |
65
|
35 |
|
$this->witness = []; |
66
|
35 |
|
$this->nLockTime = 0; |
67
|
35 |
|
return $this; |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* @return TransactionInterface |
72
|
|
|
*/ |
73
|
84 |
|
private function makeTransaction(): TransactionInterface |
74
|
|
|
{ |
75
|
84 |
|
return new Transaction($this->nVersion, $this->inputs, $this->outputs, $this->witness, $this->nLockTime); |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* @return TransactionInterface |
80
|
|
|
*/ |
81
|
83 |
|
public function get(): TransactionInterface |
82
|
|
|
{ |
83
|
83 |
|
return $this->makeTransaction(); |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* @return TransactionInterface |
88
|
|
|
*/ |
89
|
2 |
|
public function getAndReset(): TransactionInterface |
90
|
|
|
{ |
91
|
2 |
|
$transaction = $this->makeTransaction(); |
92
|
2 |
|
$this->reset(); |
93
|
2 |
|
return $transaction; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* @param int $nVersion |
98
|
|
|
* @return $this |
99
|
|
|
*/ |
100
|
5 |
|
public function version(int $nVersion) |
101
|
|
|
{ |
102
|
5 |
|
$this->nVersion = $nVersion; |
103
|
5 |
|
return $this; |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* @param BufferInterface|string $hashPrevOut - hex or BufferInterface |
108
|
|
|
* @param int $nPrevOut |
109
|
|
|
* @param Script|null $script |
110
|
|
|
* @param int $nSequence |
111
|
|
|
* @return $this |
112
|
|
|
*/ |
113
|
16 |
|
public function input($hashPrevOut, int $nPrevOut, Script $script = null, int $nSequence = TransactionInputInterface::SEQUENCE_FINAL) |
114
|
|
|
{ |
115
|
16 |
|
if ($hashPrevOut instanceof BufferInterface) { |
116
|
3 |
|
if ($hashPrevOut->getSize() !== 32) { |
117
|
3 |
|
throw new \InvalidArgumentException("Invalid size for txid buffer"); |
118
|
|
|
} |
119
|
13 |
|
} else if (is_string($hashPrevOut)) { |
120
|
13 |
|
$hashPrevOut = Buffer::hex($hashPrevOut, 32); |
121
|
|
|
} else { |
122
|
|
|
throw new \InvalidArgumentException("Invalid value for hashPrevOut in TxBuilder::input"); |
123
|
|
|
} |
124
|
|
|
|
125
|
16 |
|
$this->inputs[] = new TransactionInput( |
126
|
16 |
|
new OutPoint($hashPrevOut, $nPrevOut), |
127
|
16 |
|
$script ?: new Script(), |
128
|
16 |
|
$nSequence |
129
|
|
|
); |
130
|
|
|
|
131
|
16 |
|
return $this; |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* @param TransactionInputInterface[] $inputs |
136
|
|
|
* @return $this |
137
|
|
|
*/ |
138
|
|
|
public function inputs(array $inputs) |
139
|
|
|
{ |
140
|
7 |
|
array_walk($inputs, function (TransactionInputInterface $input) { |
141
|
7 |
|
$this->inputs[] = $input; |
142
|
7 |
|
}); |
143
|
|
|
|
144
|
7 |
|
return $this; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* @param integer $value |
149
|
|
|
* @param ScriptInterface $script |
150
|
|
|
* @return $this |
151
|
|
|
*/ |
152
|
24 |
|
public function output(int $value, ScriptInterface $script) |
153
|
|
|
{ |
154
|
24 |
|
$this->outputs[] = new TransactionOutput($value, $script); |
155
|
24 |
|
return $this; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* @param TransactionOutputInterface[] $outputs |
160
|
|
|
* @return $this |
161
|
|
|
*/ |
162
|
|
|
public function outputs(array $outputs) |
163
|
|
|
{ |
164
|
7 |
|
array_walk($outputs, function (TransactionOutputInterface $output) { |
165
|
7 |
|
$this->outputs[] = $output; |
166
|
7 |
|
}); |
167
|
|
|
|
168
|
7 |
|
return $this; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* @param ScriptWitnessInterface[] $witness |
173
|
|
|
* @return $this |
174
|
|
|
*/ |
175
|
|
|
public function witnesses(array $witness) |
176
|
|
|
{ |
177
|
|
|
array_walk($witness, function (ScriptWitnessInterface $witness) { |
178
|
|
|
$this->witness[] = $witness; |
179
|
|
|
}); |
180
|
|
|
|
181
|
|
|
return $this; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* @param int $locktime |
186
|
|
|
* @return $this |
187
|
|
|
*/ |
188
|
4 |
|
public function locktime(int $locktime) |
189
|
|
|
{ |
190
|
4 |
|
$this->nLockTime = $locktime; |
191
|
4 |
|
return $this; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* @param Locktime $lockTime |
196
|
|
|
* @param int $nTimestamp |
197
|
|
|
* @return $this |
198
|
|
|
* @throws \Exception |
199
|
|
|
*/ |
200
|
1 |
|
public function lockToTimestamp(Locktime $lockTime, int $nTimestamp) |
201
|
|
|
{ |
202
|
1 |
|
$this->locktime($lockTime->fromTimestamp($nTimestamp)); |
203
|
1 |
|
return $this; |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* @param Locktime $lockTime |
208
|
|
|
* @param int $blockHeight |
209
|
|
|
* @return $this |
210
|
|
|
* @throws \Exception |
211
|
|
|
*/ |
212
|
1 |
|
public function lockToBlockHeight(Locktime $lockTime, int $blockHeight) |
213
|
|
|
{ |
214
|
1 |
|
$this->locktime($lockTime->fromBlockHeight($blockHeight)); |
215
|
1 |
|
return $this; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* @param OutPointInterface $outpoint |
220
|
|
|
* @param ScriptInterface|null $script |
221
|
|
|
* @param int $nSequence |
222
|
|
|
* @return $this |
223
|
|
|
*/ |
224
|
6 |
|
public function spendOutPoint(OutPointInterface $outpoint, ScriptInterface $script = null, int $nSequence = TransactionInputInterface::SEQUENCE_FINAL) |
225
|
|
|
{ |
226
|
6 |
|
$this->inputs[] = new TransactionInput( |
227
|
6 |
|
$outpoint, |
228
|
6 |
|
$script ?: new Script(), |
229
|
6 |
|
$nSequence |
230
|
|
|
); |
231
|
|
|
|
232
|
6 |
|
return $this; |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
/** |
236
|
|
|
* @param TransactionInterface $transaction |
237
|
|
|
* @param int $outputToSpend |
238
|
|
|
* @param ScriptInterface|null $script |
239
|
|
|
* @param int $nSequence |
240
|
|
|
* @return $this |
241
|
|
|
*/ |
242
|
1 |
|
public function spendOutputFrom(TransactionInterface $transaction, int $outputToSpend, ScriptInterface $script = null, int $nSequence = TransactionInputInterface::SEQUENCE_FINAL) |
243
|
|
|
{ |
244
|
|
|
// Check TransactionOutput exists in $tx |
245
|
1 |
|
$transaction->getOutput($outputToSpend); |
246
|
1 |
|
$this->input( |
247
|
1 |
|
$transaction->getTxId(), |
248
|
1 |
|
$outputToSpend, |
249
|
1 |
|
$script, |
|
|
|
|
250
|
1 |
|
$nSequence |
251
|
|
|
); |
252
|
|
|
|
253
|
1 |
|
return $this; |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
/** |
257
|
|
|
* Create an output paying $value to an Address. |
258
|
|
|
* |
259
|
|
|
* @param int $value |
260
|
|
|
* @param AddressInterface $address |
261
|
|
|
* @return $this |
262
|
|
|
*/ |
263
|
8 |
|
public function payToAddress(int $value, AddressInterface $address) |
264
|
|
|
{ |
265
|
|
|
// Create Script from address, then create an output. |
266
|
8 |
|
$this->output( |
267
|
8 |
|
$value, |
268
|
8 |
|
$address->getScriptPubKey() |
269
|
|
|
); |
270
|
|
|
|
271
|
8 |
|
return $this; |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
/** |
275
|
|
|
* Sorts the transaction inputs and outputs lexicographically, |
276
|
|
|
* according to BIP69 |
277
|
|
|
* |
278
|
|
|
* @param Bip69 $bip69 |
279
|
|
|
* @return $this |
280
|
|
|
*/ |
281
|
|
|
public function bip69(Bip69 $bip69) |
282
|
|
|
{ |
283
|
|
|
list ($inputs, $witness) = $bip69->sortInputsAndWitness($this->inputs, $this->witness); |
284
|
|
|
|
285
|
|
|
$this->inputs = $inputs; |
286
|
|
|
$this->outputs = $bip69->sortOutputs($this->outputs); |
287
|
|
|
$this->witness = $witness; |
288
|
|
|
|
289
|
|
|
return $this; |
290
|
|
|
} |
291
|
|
|
} |
292
|
|
|
|
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.