Completed
Push — master ( 410ea8...0c9ae2 )
by thomas
105:23 queued 33:17
created

Transaction   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 210
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 14

Test Coverage

Coverage 100%

Importance

Changes 5
Bugs 0 Features 1
Metric Value
wmc 25
c 5
b 0
f 1
lcom 2
cbo 14
dl 0
loc 210
ccs 53
cts 53
cp 1
rs 10

16 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 35 8
A __clone() 0 5 1
A getTxHash() 0 4 1
A getTxId() 0 4 1
A getVersion() 0 4 1
A getInputs() 0 4 1
A getInput() 0 4 1
A getOutputs() 0 4 1
A getOutput() 0 4 1
A makeOutpoint() 0 5 1
A makeUtxo() 0 9 1
A getLockTime() 0 4 1
A getSignatureHash() 0 4 1
A getValueOut() 0 10 2
A isCoinbase() 0 4 2
A getBuffer() 0 4 1
1
<?php
2
3
namespace BitWasp\Bitcoin\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\Crypto\Hash;
9
use BitWasp\Bitcoin\Serializable;
10
use BitWasp\Bitcoin\Serializer\Transaction\TransactionSerializer;
11
use BitWasp\Bitcoin\Transaction\SignatureHash\Hasher;
12
use BitWasp\Bitcoin\Utxo\Utxo;
13
use BitWasp\Buffertools\Buffer;
14
use BitWasp\CommonTrait\FunctionAliasArrayAccess;
15
16
class Transaction extends Serializable implements TransactionInterface
17
{
18
    use FunctionAliasArrayAccess;
19
20
    /**
21
     * @var int|string
22
     */
23
    private $version;
24
25
    /**
26
     * @var TransactionInputCollection
27
     */
28
    private $inputs;
29
30
    /**
31
     * @var TransactionOutputCollection
32
     */
33
    private $outputs;
34
35
    /**
36
     * @var int|string
37
     */
38
    private $lockTime;
39
40
    /**
41
     * @param int|string $nVersion
42
     * @param TransactionInputCollection $inputs
43
     * @param TransactionOutputCollection $outputs
44
     * @param int|string $nLockTime
45
     * @throws \Exception
46 1220
     */
47
    public function __construct(
48
        $nVersion = TransactionInterface::DEFAULT_VERSION,
49
        TransactionInputCollection $inputs = null,
50
        TransactionOutputCollection $outputs = null,
51
        $nLockTime = '0'
52
    ) {
53 1220
54 6
        if (!is_numeric($nVersion)) {
55
            throw new \InvalidArgumentException('Transaction version must be numeric');
56
        }
57 1214
58 6
        if (!is_numeric($nLockTime)) {
59
            throw new \InvalidArgumentException('Transaction locktime must be numeric');
60
        }
61 1208
62 1208
        $math = Bitcoin::getMath();
63 6
        if ($math->cmp($nVersion, TransactionInterface::MAX_VERSION) > 0) {
64
            throw new \InvalidArgumentException('Version must be less than ' . TransactionInterface::MAX_VERSION);
65
        }
66 1202
67 6
        if ($math->cmp($nLockTime, 0) < 0 || $math->cmp($nLockTime, TransactionInterface::MAX_LOCKTIME) > 0) {
68
            throw new \InvalidArgumentException('Locktime must be positive and less than ' . TransactionInterface::MAX_LOCKTIME);
69
        }
70 1196
71 1196
        $this->version = $nVersion;
0 ignored issues
show
Documentation Bug introduced by
It seems like $nVersion can also be of type double. However, the property $version is declared as type integer|string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
72 1196
        $this->inputs = $inputs ?: new TransactionInputCollection();
73 1196
        $this->outputs = $outputs ?: new TransactionOutputCollection();
74
        $this->lockTime = $nLockTime;
0 ignored issues
show
Documentation Bug introduced by
It seems like $nLockTime can also be of type double. However, the property $lockTime is declared as type integer|string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
75 1196
76 1196
        $this
77 1196
            ->initFunctionAlias('version', 'getVersion')
78 1196
            ->initFunctionAlias('inputs', 'getInputs')
79 1196
            ->initFunctionAlias('outputs', 'getOutputs')
80 1196
            ->initFunctionAlias('locktime', 'getLockTime');
81
    }
82
83
    /**
84
     * @return Transaction
85 112
     */
86
    public function __clone()
87 112
    {
88 112
        $this->inputs = clone $this->inputs;
89 112
        $this->outputs = clone $this->outputs;
90
    }
91
92
    /**
93
     * @return Buffer
94 184
     */
95
    public function getTxHash()
96 184
    {
97
        return Hash::sha256d($this->getBuffer());
98
    }
99
100
    /**
101
     * @return Buffer
102 184
     */
103
    public function getTxId()
104 184
    {
105
        return $this->getTxHash()->flip();
106
    }
107
108
    /**
109
     * @return int|string
110 322
     */
111
    public function getVersion()
112 322
    {
113
        return $this->version;
114
    }
115
116
    /**
117
     * Get the array of inputs in the transaction
118
     *
119
     * @return TransactionInputCollection
120 998
     */
121
    public function getInputs()
122 998
    {
123
        return $this->inputs;
124
    }
125
126
    /**
127
     * @param int $index
128
     * @return TransactionInputInterface
129 106
     */
130
    public function getInput($index)
131 106
    {
132
        return $this->inputs[$index];
133
    }
134
135
    /**
136
     * Get Outputs
137
     *
138
     * @return TransactionOutputCollection
139 332
     */
140
    public function getOutputs()
141 332
    {
142
        return $this->outputs;
143
    }
144
145
    /**
146
     * @param int $vout
147
     * @return TransactionOutputInterface
148 130
     */
149
    public function getOutput($vout)
150 130
    {
151
        return $this->outputs[$vout];
152
    }
153
154
    /**
155
     * @param int $vout
156
     * @return OutPointInterface
157
     */
158 328
    public function makeOutpoint($vout)
159
    {
160 328
        $this->getOutput($vout);
0 ignored issues
show
Unused Code introduced by
The call to the method BitWasp\Bitcoin\Transact...ransaction::getOutput() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
161
        return new OutPoint($this->getTxId(), $vout);
162
    }
163
164
    /**
165
     * @param int $vout
166 98
     * @return Utxo
167
     */
168 98
    public function makeUtxo($vout)
169
    {
170
        $output = $this->getOutput($vout);
171
172
        return new Utxo(
173
            new OutPoint($this->getTxId(), $vout),
174 6
            $output
175
        );
176 6
    }
177 6
178 6
    /**
179 6
     * Get Lock Time
180 6
     *
181
     * @return int|string
182 6
     */
183
    public function getLockTime()
184
    {
185
        return $this->lockTime;
186
    }
187
188 6
    /**
189
     * @return \BitWasp\Bitcoin\Transaction\SignatureHash\SignatureHashInterface
190 6
     */
191
    public function getSignatureHash()
192
    {
193
        return new Hasher($this);
194
    }
195
196 292
    /**
197
     * @return int|string
198 292
     */
199
    public function getValueOut()
200
    {
201
        $math = Bitcoin::getMath();
202
        $value = 0;
203
        foreach ($this->outputs as $output) {
204
            $value = $math->add($value, $output->getValue());
205
        }
206
207
        return $value;
208
    }
209
210
    /**
211
     * @return bool
212
     */
213
    public function isCoinbase()
214
    {
215
        return count($this->inputs) === 1 && $this->getInput(0)->isCoinBase();
216
    }
217
218
    /**
219
     * @return Buffer
220
     */
221
    public function getBuffer()
222
    {
223
        return (new TransactionSerializer)->serialize($this);
224
    }
225
}
226