Completed
Pull Request — master (#270)
by thomas
69:50
created

Bip69   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 104
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 3
Bugs 1 Features 1
Metric Value
wmc 13
c 3
b 1
f 1
lcom 1
cbo 10
dl 0
loc 104
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A sortInputs() 0 5 1
A compareInputs() 0 9 2
A sortOutputs() 0 5 1
A compareOutputs() 0 6 2
A check() 0 7 2
A sortInputsAndWitness() 0 16 3
A mutate() 0 15 2
1
<?php
2
3
namespace BitWasp\Bitcoin\Transaction\Bip69;
4
5
use BitWasp\Bitcoin\Collection\Transaction\TransactionInputCollection;
6
use BitWasp\Bitcoin\Collection\Transaction\TransactionOutputCollection;
7
use BitWasp\Bitcoin\Collection\Transaction\TransactionWitnessCollection;
8
use BitWasp\Bitcoin\Transaction\Mutator\TxMutator;
9
use BitWasp\Bitcoin\Transaction\TransactionInputInterface;
10
use BitWasp\Bitcoin\Transaction\TransactionInterface;
11
use BitWasp\Bitcoin\Transaction\TransactionOutputInterface;
12
13
class Bip69
14
{
15
    /**
16
     * @param array $vTxin
17
     * @return array
18
     */
19
    public function sortInputs(array $vTxin)
20
    {
21
        usort($vTxin, [$this, 'compareInputs']);
22
        return $vTxin;
23
    }
24
25
    /**
26
     * @param TransactionInputInterface $vin1
27
     * @param TransactionInputInterface $vin2
28
     * @return bool
29
     */
30
    public function compareInputs(TransactionInputInterface $vin1, TransactionInputInterface $vin2)
31
    {
32
        $outpoint1 = $vin1->getOutPoint();
33
        $outpoint2 = $vin2->getOutPoint();
34
35
        $cmpTxId = strcmp($outpoint1->getTxId()->getBinary(), $outpoint2->getTxId()->getBinary());
36
37
        return ($cmpTxId !== 0) ? $cmpTxId : $outpoint1->getVout() - $outpoint2->getVout();
38
    }
39
40
    /**
41
     * @param TransactionOutputInterface[] $vTxout
42
     * @return TransactionOutputInterface[]
43
     */
44
    public function sortOutputs($vTxout)
45
    {
46
        usort($vTxout, [$this, 'compareOutputs']);
47
        return $vTxout;
48
    }
49
50
    /**
51
     * @param TransactionOutputInterface $vout1
52
     * @param TransactionOutputInterface $vout2
53
     * @return bool
54
     */
55
    public function compareOutputs(TransactionOutputInterface $vout1, TransactionOutputInterface $vout2)
56
    {
57
        $value = $vout1->getValue() - $vout2->getValue();
58
59
        return ($value !== 0) ? $value : strcmp($vout1->getScript()->getBinary(), $vout2->getScript()->getBinary());
60
    }
61
62
    /**
63
     * @param TransactionInterface $tx
64
     * @return bool
65
     */
66
    public function check(TransactionInterface $tx)
67
    {
68
        $inputs = $tx->getInputs()->all();
69
        $outputs = $tx->getOutputs()->all();
70
71
        return $this->sortInputs($inputs) === $inputs && $this->sortOutputs($outputs) === $outputs;
72
    }
73
74
    /**
75
     * @param TransactionInputCollection $inputs
76
     * @param TransactionWitnessCollection $witnesses
77
     * @return array
78
     * @throws \Exception
79
     */
80
    public function sortInputsAndWitness(TransactionInputCollection $inputs, TransactionWitnessCollection $witnesses)
81
    {
82
        if (count($inputs) !== count($witnesses)) {
83
            throw new \Exception('Number of inputs must match witnesses');
84
        }
85
86
        $inputs = $inputs->all();
87
        uasort($inputs, [$this, 'compareInputs']);
88
89
        $vWitness = [];
90
        foreach ($inputs as $key => $input) {
91
            $vWitness[] = $witnesses[$key];
92
        }
93
94
        return [new TransactionInputCollection($inputs), new TransactionWitnessCollection($vWitness)];
95
    }
96
97
    /**
98
     * @param TransactionInterface $tx
99
     * @return TransactionInterface
100
     */
101
    public function mutate(TransactionInterface $tx)
102
    {
103
        if (count($tx->getWitnesses()) > 0) {
104
            list ($vTxin, $vWit) = $this->sortInputsAndWitness($tx->getInputs(), $tx->getWitnesses());
105
        } else {
106
            $vTxin = new TransactionInputCollection($this->sortInputs($tx->getInputs()->all()));
107
            $vWit = new TransactionWitnessCollection([]);
108
        }
109
110
        return (new TxMutator($tx))
111
            ->inputs($vTxin)
112
            ->outputs(new TransactionOutputCollection($this->sortOutputs($tx->getOutputs()->all())))
113
            ->witness($vWit)
114
            ->done();
115
    }
116
}
117