Skip to content

Commit de6cd36

Browse files
author
Thomas Kerin
committed
More tests
1 parent 557da8c commit de6cd36

File tree

9 files changed

+448
-124
lines changed

9 files changed

+448
-124
lines changed

composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@
3737
},
3838
"require-dev": {
3939
"ext-json": "*",
40-
"phpunit/phpunit": "^5.4.0",
40+
"phpunit/phpunit": "^6.0.0",
4141
"squizlabs/php_codesniffer": "^2.0.0",
4242
"nbobtc/bitcoind-php": "v2.0.2",
43-
"bitwasp/secp256k1-php": "^v0.2.0",
44-
"bitwasp/bitcoinconsensus": "v3.0.0"
43+
"bitwasp/secp256k1-php": "^0.2.0",
44+
"bitwasp/bitcoinconsensus": "^3.0.0"
4545
}
4646
}

src/Transaction/PSBT/PSBT.php

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
use BitWasp\Buffertools\Buffer;
1212
use BitWasp\Buffertools\BufferInterface;
1313
use BitWasp\Buffertools\Parser;
14-
use BitWasp\Buffertools\Types\VarString;
1514

1615
class PSBT
1716
{
@@ -65,7 +64,7 @@ public function __construct(TransactionInterface $tx, array $unknowns, array $in
6564
}
6665
foreach ($unknowns as $key => $unknown) {
6766
if (!is_string($key) || !($unknown instanceof BufferInterface)) {
68-
throw new InvalidPSBTException("Unknowns must be a map of string keys to Buffer values");
67+
throw new \InvalidArgumentException("Unknowns must be a map of string keys to Buffer values");
6968
}
7069
}
7170
$this->tx = $tx;
@@ -96,7 +95,6 @@ public static function fromBuffer(BufferInterface $in): PSBT
9695

9796
$tx = null;
9897
$unknown = [];
99-
10098
try {
10199
do {
102100
$key = $vs->read($parser);
@@ -148,12 +146,13 @@ public static function fromBuffer(BufferInterface $in): PSBT
148146
$outputs = [];
149147
for ($i = 0; $parser->getPosition() < $parser->getSize() && $i < $numOutputs; $i++) {
150148
try {
151-
$output = PSBTOutput::fromKeyValues($parser, $vs);
149+
$output = PSBTOutput::fromParser($parser, $vs);
152150
$outputs[] = $output;
153151
} catch (\Exception $e) {
154152
throw new InvalidPSBTException("Failed to parse outputs section", 0, $e);
155153
}
156154
}
155+
157156
if ($numOutputs !== count($outputs)) {
158157
throw new InvalidPSBTException("Missing outputs");
159158
}
@@ -198,34 +197,27 @@ public function updateInput(int $input, \Closure $modifyPsbtIn)
198197
throw new \RuntimeException("No input at this index");
199198
}
200199

201-
$result = $modifyPsbtIn(new UpdatableInput($this, $input, $this->inputs[$input]));
202-
if (!($result instanceof UpdatableInput)) {
203-
throw new \RuntimeException("Invalid result for update");
204-
}
205-
$this->inputs[$input] = $result->input();
200+
$updatable = new UpdatableInput($this, $input, $this->inputs[$input]);
201+
$modifyPsbtIn($updatable);
202+
$this->inputs[$input] = $updatable->input();
206203
}
207204

208-
public function writeToParser(Parser $parser, VarString $vs)
205+
/**
206+
* @return BufferInterface
207+
*/
208+
public function getBuffer(): BufferInterface
209209
{
210+
$vs = Types::varstring();
211+
$parser = new Parser();
212+
$parser->appendBinary("psbt\xff");
210213
$parser->appendBinary($vs->write(new Buffer(chr(self::PSBT_GLOBAL_UNSIGNED_TX))));
211214
$parser->appendBinary($vs->write($this->tx->getBuffer()));
212-
213215
foreach ($this->unknown as $key => $value) {
214216
$parser->appendBinary($vs->write(new Buffer($key)));
215217
$parser->appendBinary($vs->write($value));
216218
}
217219
$parser->appendBinary($vs->write(new Buffer()));
218-
}
219220

220-
/**
221-
* @return BufferInterface
222-
*/
223-
public function getBuffer(): BufferInterface
224-
{
225-
$vs = Types::varstring();
226-
$parser = new Parser();
227-
$parser->appendBinary("psbt\xff");
228-
$this->writeToParser($parser, $vs);
229221
$numInputs = count($this->tx->getInputs());
230222
for ($i = 0; $i < $numInputs; $i++) {
231223
$this->inputs[$i]->writeToParser($parser, $vs);

src/Transaction/PSBT/PSBTOutput.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public function __construct(
7272
* @throws \BitWasp\Bitcoin\Exceptions\WitnessScriptException
7373
* @throws \BitWasp\Buffertools\Exceptions\ParserOutOfRange
7474
*/
75-
public static function fromKeyValues(Parser $parser, VarString $vs): PSBTOutput
75+
public static function fromParser(Parser $parser, VarString $vs): PSBTOutput
7676
{
7777
$redeemScript = null;
7878
$witnessScript = null;
@@ -198,5 +198,4 @@ public function writeToParser(Parser $parser, VarString $vs): array
198198
$parser->appendBinary($vs->write(new Buffer()));
199199
return $map;
200200
}
201-
202201
}

src/Transaction/PSBT/UpdatableInput.php

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PublicKeyInterface;
88
use BitWasp\Bitcoin\Script\ScriptInterface;
9-
use BitWasp\Bitcoin\Transaction\OutPointInterface;
109
use BitWasp\Bitcoin\Transaction\TransactionInterface;
1110
use BitWasp\Bitcoin\Transaction\TransactionOutputInterface;
1211

@@ -31,43 +30,20 @@ public function input(): PSBTInput
3130
return $this->input;
3231
}
3332

34-
private function findOurOutPoint(TransactionInterface $tx, OutPointInterface &$o = null)
33+
public function addNonWitnessTx(TransactionInterface $tx)
3534
{
3635
$outPoint = $this->psbt->getTransaction()->getInputs()[$this->nIn]->getOutPoint();
3736
if (!$outPoint->getTxId()->equals($tx->getTxId())) {
3837
throw new \RuntimeException("Non-witness txid differs from unsigned tx input {$this->nIn}");
3938
}
40-
if ($outPoint->getVout() >= count($this->psbt->getTransaction()->getOutputs())) {
39+
if ($outPoint->getVout() > count($tx->getOutputs()) - 1) {
4140
throw new \RuntimeException("unsigned tx outpoint does not exist in this transaction");
4241
}
43-
$o = $outPoint;
44-
}
45-
46-
public function addNonWitnessTx(TransactionInterface $tx)
47-
{
48-
if ($this->input->hasNonWitnessTx()) {
49-
return;
50-
}
51-
$this->findOurOutPoint($tx);
5242
$this->input = $this->input->withNonWitnessTx($tx);
5343
}
5444

55-
public function addWitnessTx(TransactionInterface $tx)
56-
{
57-
if ($this->input->hasWitnessTxOut()) {
58-
return;
59-
}
60-
/** @var OutPointInterface $outPoint */
61-
$outPoint = null;
62-
$this->findOurOutPoint($tx, $outPoint);
63-
$this->input = $this->input->withWitnessTxOut($tx->getOutput($outPoint->getVout()));
64-
}
65-
6645
public function addWitnessTxOut(TransactionOutputInterface $txOut)
6746
{
68-
if ($this->input->hasWitnessTxOut()) {
69-
return;
70-
}
7147
$this->input = $this->input->withWitnessTxOut($txOut);
7248
}
7349

tests/Script/Parser/ParserTest.php

Lines changed: 0 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -18,73 +18,6 @@ class ParserTest extends AbstractTestCase
1818
*/
1919
private $script;
2020

21-
public function getInvalidScripts()
22-
{
23-
$start = array(
24-
['',255, null, false],
25-
['0200',2,null, false],
26-
['4c',76,null, false]
27-
);
28-
29-
$s = '';
30-
for ($j = 1; $j < 250; $j++) {
31-
$s .= '41';
32-
}
33-
$start[] = ['4cff'.$s, 76, null, false];
34-
35-
return $start;
36-
}
37-
38-
public function getValidPushScripts()
39-
{
40-
$s = '';
41-
for ($j = 1; $j < 256; $j++) {
42-
$s .= '41';
43-
}
44-
$s1 = '4cff'.$s;
45-
46-
$t = '';
47-
for ($j = 1; $j < 260; $j++) {
48-
$t .= '41';
49-
}
50-
//$t1 = pack("cvH*", 0x4d, 260, $t);
51-
52-
$start = [
53-
['0100', 1, chr(0), true],
54-
[$s1, 76, pack("H*", $s), true],
55-
//[bin2hex($t1), 77, pack("H*", $t), true]
56-
];
57-
return $start;
58-
}
59-
60-
public function getTestPushScripts()
61-
{
62-
return array_merge($this->getValidPushScripts(), $this->getInvalidScripts());
63-
}
64-
65-
/**
66-
* @dataProvider getTestPushScripts
67-
* @param string $script
68-
* @param int $expectedOp
69-
* @param string $expectedPushData
70-
* @param bool $result
71-
*/
72-
public function testPush(string $script, int $expectedOp, $expectedPushData, bool $result)
73-
{
74-
$parser = ScriptFactory::fromHex($script)->getScriptParser();
75-
76-
$result = $parser->next();
77-
78-
if ($result !== null) {
79-
if ($result->isPush()) {
80-
$data = $result->getData();
81-
if ($data->getSize() > 0) {
82-
$this->assertSame($expectedPushData, $data->getBinary());
83-
}
84-
}
85-
}
86-
}
87-
8821
public function testParse()
8922
{
9023
$buf = Buffer::hex('0f9947c2b0fdd82ef3153232ee23d5c0bed84a02');
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace BitWasp\Bitcoin\Tests\Transaction\PSBT;
6+
7+
use BitWasp\Bitcoin\Tests\AbstractTestCase;
8+
use BitWasp\Bitcoin\Transaction\PSBT\PSBTBip32Derivation;
9+
use BitWasp\Buffertools\Buffer;
10+
11+
class PSBTBip32DerivationUnitTest extends AbstractTestCase
12+
{
13+
public function testGetParams()
14+
{
15+
$rawKey = Buffer::hex("03e495306fca12c490e63353320b38d24786a68794384f0a6cea6838c976b2ce58");
16+
$fpr = 0xa1b2c3d4;
17+
$path = [0, 1, 2, 3];
18+
19+
$deriv = new PSBTBip32Derivation($rawKey, $fpr, ...$path);
20+
$this->assertSame($rawKey, $deriv->getRawPublicKey());
21+
$this->assertSame($fpr, $deriv->getMasterKeyFpr());
22+
$this->assertSame($path, $deriv->getPath());
23+
$this->assertSame($rawKey->getHex(), $deriv->getPublicKey()->getHex());
24+
}
25+
}

tests/Transaction/PSBT/PSBTTest.php

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use BitWasp\Bitcoin\Tests\AbstractTestCase;
99
use BitWasp\Bitcoin\Transaction\OutPoint;
1010
use BitWasp\Bitcoin\Transaction\PSBT\PSBT;
11-
use BitWasp\Bitcoin\Transaction\PSBT\PSBTGlobals;
1211
use BitWasp\Bitcoin\Transaction\PSBT\PSBTInput;
1312
use BitWasp\Bitcoin\Transaction\PSBT\PSBTOutput;
1413
use BitWasp\Bitcoin\Transaction\PSBT\UpdatableInput;
@@ -103,6 +102,16 @@ public function getInvalidFixtures(): array
103102
/*$base64=*/ 'cHNidP8BAFUCAAAAASeaIyOl37UfxF8iD6WLD8E+HjNCeSqF1+Ns1jM7XLw5AAAAAAD/////AaBa6gsAAAAAGXapFP/pwAYQl8w7Y28ssEYPpPxCfStFiKwAAAAAAAEBIJVe6gsAAAAAF6kUY0UgD2jRieGtwN8cTRbqjxTA2+uHIgIEsTQcy6doO2r08SOM1ul+cWfVafrEfx5I1HVBhENVvUZGMEMCIAQktY7/qqaU4VWepck7v9SokGQiQFXN8HC2dxRpRC0HAh9cjrD+plFtYLisszrWTt5g6Hhb+zqpS5m9+GFR25qaAQEEIgAgdx/RitRZZm3Unz1WTj28QvTIR3TjYK2haBao7UiNVoEBBUdSIQSxNBzLp2g7avTxI4zW6X5xZ9Vp+sR/HkjUdUGEQ1W9RiED3lXR4drIBeP4pYwfv5uUwC89uq/hJ/78pJlfJvggg71SriIGA7E0HMunaDtq9PEjjNbpfnFn1Wn6xH8eSNR1QYRDVb1GELSmumcAAACAAAAAgAQAAIAiBgPeVdHh2sgF4/iljB+/m5TALz26r+En/vykmV8m+CCDvRC0prpnAAAAgAAAAIAFAACAAAA=',
104103
],
105104

105+
[ // PSBT with duplicate global tx
106+
/*$hex=*/ '70736274ff01004501000000013412cdab3412cdab3412cdab3412cdab3412cdab3412cdab3412cdab3412cdab0000000000ffffffff020100000000000000000200000000000000000000000001004501000000013412cdab3412cdab3412cdab3412cdab3412cdab3412cdab3412cdab3412cdab0000000000ffffffff0201000000000000000002000000000000000000000000000000',
107+
/*$base64=*/ 'cHNidP8BAEUBAAAAATQSzas0Es2rNBLNqzQSzas0Es2rNBLNqzQSzas0Es2rAAAAAAD/////AgEAAAAAAAAAAAIAAAAAAAAAAAAAAAABAEUBAAAAATQSzas0Es2rNBLNqzQSzas0Es2rNBLNqzQSzas0Es2rAAAAAAD/////AgEAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAA=',
108+
],
109+
110+
[ // PSBT with duplicate global unknown key
111+
/*$hex=*/ '70736274ff023431023635023431023635000000',
112+
/*$base64=*/ 'cHNidP8CNDECNjUCNDECNjUAAAA==',
113+
],
114+
106115
];
107116
}
108117

@@ -149,6 +158,13 @@ public function getValidFixtures(): array
149158
/*$hex=*/ '70736274ff01003f0200000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000ffffffff010000000000000000036a010000000000000a0f0102030405060708090f0102030405060708090a0b0c0d0e0f0000',
150159
/*$base64=*/ 'cHNidP8BAD8CAAAAAf//////////////////////////////////////////AAAAAAD/////AQAAAAAAAAAAA2oBAAAAAAAACg8BAgMEBQYHCAkPAQIDBAUGBwgJCgsMDQ4PAAA=',
151160
],
161+
162+
// my own
163+
164+
[ // PSBT with a global unknown key
165+
/*$hex=*/ '70736274ff01004501000000013412cdab3412cdab3412cdab3412cdab3412cdab3412cdab3412cdab3412cdab0000000000ffffffff0201000000000000000002000000000000000000000000014102363500000000',
166+
/*$base64=*/ 'cHNidP8BAEUBAAAAATQSzas0Es2rNBLNqzQSzas0Es2rNBLNqzQSzas0Es2rAAAAAAD/////AgEAAAAAAAAAAAIAAAAAAAAAAAAAAAABQQI2NQAAAAA=',
167+
],
152168
];
153169
}
154170

@@ -203,11 +219,91 @@ public function testUpdate()
203219
$psbt = new PSBT($unsignedTx, $unknowns, $inputs, $outputs);
204220
$this->assertFalse($psbt->getInputs()[0]->hasWitnessTxOut());
205221
$txOut = new TransactionOutput(1, new Script());
206-
$psbt->updateInput(0, function(UpdatableInput $input) use ($txOut): UpdatableInput {
222+
$psbt->updateInput(0, function (UpdatableInput $input) use ($txOut): UpdatableInput {
207223
$input->addWitnessTxOut($txOut);
208224
return $input;
209225
});
210226
$this->assertTrue($psbt->getInputs()[0]->hasWitnessTxOut());
211227
$this->assertSame($txOut, $psbt->getInputs()[0]->getWitnessTxOut());
212228
}
229+
230+
/**
231+
* @expectedException \BitWasp\Bitcoin\Exceptions\InvalidPSBTException
232+
* @expectedExceptionMessage Invalid number of inputs
233+
*/
234+
public function testChecksNumInputsMatchesGreaterThan()
235+
{
236+
$unsignedTx = new Transaction(0, [
237+
new TransactionInput(new OutPoint(Buffer::hex('', 32), 0xffffffff), new Script()),
238+
], []);
239+
240+
new PSBT($unsignedTx, [], [new PSBTInput(), new PSBTInput()], []);
241+
}
242+
243+
/**
244+
* @expectedException \BitWasp\Bitcoin\Exceptions\InvalidPSBTException
245+
* @expectedExceptionMessage Invalid number of inputs
246+
*/
247+
public function testChecksNumInputsMatchesLessThan()
248+
{
249+
$unsignedTx = new Transaction(0, [
250+
new TransactionInput(new OutPoint(Buffer::hex('', 32), 0xffffffff), new Script()),
251+
], []);
252+
253+
new PSBT($unsignedTx, [], [], []);
254+
}
255+
256+
/**
257+
* @expectedException \BitWasp\Bitcoin\Exceptions\InvalidPSBTException
258+
* @expectedExceptionMessage Invalid number of outputs
259+
*/
260+
public function testChecksNumOutputsMatchesGreaterThan()
261+
{
262+
$unsignedTx = new Transaction(0, [
263+
new TransactionInput(new OutPoint(Buffer::hex('', 32), 0xffffffff), new Script()),
264+
], [new TransactionOutput(1, new Script()), new TransactionOutput(2, new Script())]);
265+
266+
new PSBT($unsignedTx, [], [new PSBTInput()], [new PSBTOutput()]);
267+
}
268+
269+
/**
270+
* @expectedException \BitWasp\Bitcoin\Exceptions\InvalidPSBTException
271+
* @expectedExceptionMessage Invalid number of outputs
272+
*/
273+
public function testChecksNumOutputsMatchesLessThan()
274+
{
275+
$unsignedTx = new Transaction(0, [
276+
new TransactionInput(new OutPoint(Buffer::hex('', 32), 0xffffffff), new Script()),
277+
], [new TransactionOutput(1, new Script()), new TransactionOutput(2, new Script())]);
278+
279+
new PSBT($unsignedTx, [], [new PSBTInput()], [new PSBTOutput(), new PSBTOutput(), new PSBTOutput()]);
280+
}
281+
282+
/**
283+
* @expectedException \RuntimeException
284+
* @expectedExceptionMessage No input at this index
285+
*/
286+
public function testUpdateInputChecksInputNum()
287+
{
288+
$unsignedTx = new Transaction(0, [
289+
new TransactionInput(new OutPoint(Buffer::hex('', 32), 0xffffffff), new Script()),
290+
], [new TransactionOutput(1, new Script()), new TransactionOutput(2, new Script())]);
291+
292+
$psbt = new PSBT($unsignedTx, [], [new PSBTInput()], [new PSBTOutput(), new PSBTOutput()]);
293+
$psbt->updateInput(10, function () {
294+
});
295+
}
296+
297+
/**
298+
* @expectedException \InvalidArgumentException
299+
* @expectedExceptionMessage Unknowns must be a map of string keys to Buffer values
300+
*/
301+
public function testChecksUnknownsFormat()
302+
{
303+
$unsignedTx = new Transaction(0, [
304+
new TransactionInput(new OutPoint(Buffer::hex('', 32), 0xffffffff), new Script()),
305+
], [new TransactionOutput(1, new Script()), new TransactionOutput(2, new Script())]);
306+
307+
new PSBT($unsignedTx, [1 => $unsignedTx], [new PSBTInput()], [new PSBTOutput(), new PSBTOutput()]);
308+
}
213309
}

0 commit comments

Comments
 (0)