Completed
Pull Request — master (#248)
by thomas
58:32 queued 33:01
created

MerkleRoot::calculateHash()   C

Complexity

Conditions 7
Paths 9

Size

Total Lines 42
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 7

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 42
ccs 24
cts 24
cp 1
rs 6.7272
cc 7
eloc 21
nc 9
nop 1
crap 7
1
<?php
2
3
namespace BitWasp\Bitcoin\Block;
4
5
use BitWasp\Bitcoin\Math\Math;
6
use BitWasp\Bitcoin\Collection\Transaction\TransactionCollection;
7
use BitWasp\Buffertools\Buffer;
8
use BitWasp\Buffertools\BufferInterface;
9
use BitWasp\Bitcoin\Exceptions\MerkleTreeEmpty;
10
use Pleo\Merkle\FixedSizeTree;
11
12
class MerkleRoot
13
{
14
    /**
15
     * @var TransactionCollection
16
     */
17
    private $transactions;
18
19
    /**
20
     * @var Math
21
     */
22
    private $math;
23
24
    /**
25
     * @var BufferInterface
26
     */
27
    private $lastHash;
28
29
    /**
30
     * Instantiate the class when given a block
31
     *
32
     * @param Math $math
33
     * @param TransactionCollection $txCollection
34
     */
35 48
    public function __construct(Math $math, TransactionCollection $txCollection)
36
    {
37 48
        $this->math = $math;
38 48
        $this->transactions = $txCollection;
39 48
    }
40
41
    /**
42
     * @param callable|null $hashFunction
43
     * @return BufferInterface
44
     * @throws MerkleTreeEmpty
45
     */
46 48
    public function calculateHash(callable $hashFunction = null)
47
    {
48 48
        if ($this->lastHash instanceof BufferInterface) {
49 6
            return $this->lastHash;
50
        }
51
52 48
        $hashFxn = $hashFunction ?: function ($value) {
53 42
            return hash('sha256', hash('sha256', $value, true), true);
54 48
        };
55
56 48
        $txCount = count($this->transactions);
57
58 48
        if ($txCount === 0) {
59
            // TODO: Probably necessary. Should always have a coinbase at least.
60 6
            throw new MerkleTreeEmpty('Cannot compute Merkle root of an empty tree');
61
        }
62
63 42
        if ($txCount === 1) {
64 30
            $binary = $hashFxn($this->transactions[0]->getBinary());
65
66 30
        } else {
67
            // Create a fixed size Merkle Tree
68 12
            $tree = new FixedSizeTree($txCount + ($txCount % 2), $hashFxn);
69
70
            // Compute hash of each transaction
71 12
            $last = '';
72 12
            foreach ($this->transactions as $i => $transaction) {
73 12
                $last = $transaction->getBinary();
74 12
                $tree->set($i, $last);
75 12
            }
76
77
            // Check if we need to repeat the last hash (odd number of transactions)
78 12
            if (!$this->math->isEven($txCount)) {
79 6
                $tree->set($txCount, $last);
80 6
            }
81
82 12
            $binary = $tree->hash();
83
        }
84
85 42
        $this->lastHash = (new Buffer($binary))->flip();
86 42
        return $this->lastHash;
87
    }
88
}
89