Completed
Pull Request — master (#446)
by thomas
105:00 queued 101:56
created

MerkleRoot::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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