Issues (130)

src/Block/MerkleRoot.php (1 issue)

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