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

TxWitnessSigner   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 120
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 15

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 15
c 1
b 0
f 0
lcom 1
cbo 15
dl 0
loc 120
rs 9.1666

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 2
C sign() 0 60 9
A get() 0 21 4
1
<?php
2
3
namespace BitWasp\Bitcoin\Transaction\Factory;
4
5
use BitWasp\Bitcoin\Collection\Transaction\TransactionWitnessCollection;
6
use BitWasp\Bitcoin\Crypto\EcAdapter\Adapter\EcAdapterInterface;
7
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PrivateKeyInterface;
8
use BitWasp\Bitcoin\Script\Classifier\OutputClassifier;
9
use BitWasp\Bitcoin\Script\Opcodes;
10
use BitWasp\Bitcoin\Script\Script;
11
use BitWasp\Bitcoin\Script\ScriptFactory;
12
use BitWasp\Bitcoin\Script\ScriptInterface;
13
use BitWasp\Bitcoin\Script\ScriptWitness;
14
use BitWasp\Bitcoin\Transaction\SignatureHash\SigHash;
15
use BitWasp\Bitcoin\Transaction\TransactionFactory;
16
use BitWasp\Bitcoin\Transaction\TransactionInterface;
17
use BitWasp\Buffertools\Buffer;
18
19
class TxWitnessSigner
20
{
21
    /**
22
     * @var EcAdapterInterface
23
     */
24
    private $ecAdapter;
25
26
    /**
27
     * @var TransactionInterface
28
     */
29
    private $tx;
30
31
    /**
32
     * @var SignatureData
33
     */
34
    private $signatures = [];
35
36
    /**
37
     * @var TxSigCreator
38
     */
39
    private $signatureCreator = [];
40
41
    /**
42
     * TxWitnessSigner constructor.
43
     * @param TransactionInterface $tx
44
     * @param EcAdapterInterface $ecAdapter
45
     */
46
    public function __construct(TransactionInterface $tx, EcAdapterInterface $ecAdapter)
47
    {
48
        $this->tx = $tx;
49
        $this->ecAdapter = $ecAdapter;
50
        $nInputs = count($tx->getInputs());
51
        for ($i = 0; $i < $nInputs; $i++) {
52
            $this->signatures[] = new SignatureData();
53
        }
54
    }
55
56
    public function sign($nInput, $amount, PrivateKeyInterface $privateKey, ScriptInterface $scriptPubKey, ScriptInterface $redeemScript = null, $sigHashType = SigHash::ALL)
57
    {
58
59
        if (!isset($this->signatureCreator[$nInput])) {
60
            $this->signatureCreator[$nInput] = new TxSigCreator($this->ecAdapter, $this->tx, $nInput, $amount);
61
        }
62
63
        /** @var TxSigCreator $sigCreator */
64
        $sigCreator = $this->signatureCreator[$nInput];
65
        $sigData = $this->signatures[$nInput];
66
        $sigVersion = 0;
67
        $scriptSig = $this->tx->getInput($nInput)->getScript();
68
        $witness = null;
69
70
        if ($sigData->scriptType === null) {
71
            $classifier = new OutputClassifier($scriptPubKey);
72
            $sigData->innerScriptType = $sigData->scriptType = $classifier->classify($sigData->solution);
73
        }
74
75
        if ($sigData->scriptType === OutputClassifier::PAYTOSCRIPTHASH) {
76
            $classifier = new OutputClassifier($redeemScript);
0 ignored issues
show
Bug introduced by
It seems like $redeemScript defined by parameter $redeemScript on line 56 can be null; however, BitWasp\Bitcoin\Script\C...assifier::__construct() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
77
            $sigData->innerScriptType = $classifier->classify();
78
        }
79
80
        if ($sigData->scriptType === OutputClassifier::WITNESS_V0_KEYHASH) {
81
            $sigVersion = 1;
82
            $scriptPubKey->isWitness($witness);
0 ignored issues
show
Documentation introduced by
$witness is of type null, but the function expects a object<BitWasp\Bitcoin\Script\WitnessProgram>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
83
            $witnessScript = ScriptFactory::sequence([Opcodes::OP_DUP, Opcodes::OP_HASH160, $witness->getProgram(), Opcodes::OP_EQUALVERIFY, Opcodes::OP_CHECKSIG]);
84
            $classifier = new OutputClassifier($witnessScript);
85
            $classifier->classify($sigData->solution);
86
87
            $sigData->witnessScript = $witnessScript;
88
            $scriptSig = new Script();
89
        }
90
91
        if ($sigData->scriptType === OutputClassifier::WITNESS_V0_SCRIPTHASH) {
92
            $sigVersion = 1;
93
            $scriptPubKey->isWitness($witness);
0 ignored issues
show
Bug introduced by
It seems like $witness defined by null on line 68 can be null; however, BitWasp\Bitcoin\Script\S...tInterface::isWitness() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
94
95
            $witnessScript = new Script(end($sigData->witnesses));
96
            $sigData->witnesses = $sigData->solution;
97
98
            $classifier = new OutputClassifier($witnessScript);
99
            $sigData->scriptType = $classifier->classify();
100
            $scriptSig = ScriptFactory::sequence($this->tx->getWitness($nInput)->all());
101
        }
102
103
        if ($sigData->scriptType === OutputClassifier::UNKNOWN) {
104
            throw new \RuntimeException('Unsupported scriptPubKey');
105
        }
106
107
        if ($sigData->signatures === null) {
108
            $sigCreator->extractSignatures($sigData, $scriptSig, $redeemScript);
109
110
        }
111
112
        $sigCreator->signInput($sigData, $privateKey, $redeemScript ?: $scriptPubKey, $sigHashType, $sigVersion);
0 ignored issues
show
Unused Code introduced by
The call to TxSigCreator::signInput() has too many arguments starting with $sigVersion.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
113
        //print_r($sigData);
114
        return $this;
115
    }
116
117
    public function get()
118
    {
119
        $mutable = TransactionFactory::mutate($this->tx);
120
        $witnesses = [];
121
        foreach ($mutable->inputsMutator() as $idx => $input) {
122
            $sigData = $this->signatures[$idx];
123
            $sigCreator = $this->signatureCreator[$idx];
124
            $witness = null;
125
            $sig = $sigCreator->serializeSig($sigData, $witness);
126
            echo $sig->getHex() . "\n";
127
            $input->script($sig);
128
            $witnesses[$idx] = $witness ?: new ScriptWitness([]);
129
        }
130
131
        if (count($witnesses) > 0) {
132
            $mutable->witness(new TransactionWitnessCollection($witnesses));
133
        }
134
135
        $new = $mutable->done();
136
        return $new;
137
    }
138
}
139