Skip to content

Commit 8c29f58

Browse files
committed
Update MerkleNode.
1 parent 37e93cc commit 8c29f58

File tree

4 files changed

+121
-42
lines changed

4 files changed

+121
-42
lines changed

src/Modifier/FulfillCapacity.php

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace drupol\phptree\Modifier;
6+
7+
use drupol\phptree\Node\NodeInterface;
8+
9+
/**
10+
* Class FulfillCapacity.
11+
*/
12+
class FulfillCapacity implements ModifierInterface
13+
{
14+
/**
15+
* {@inheritdoc}
16+
*/
17+
public function modify(NodeInterface $tree): NodeInterface
18+
{
19+
/** @var \drupol\phptree\Node\MerkleNode $item */
20+
foreach ($tree->all() as $item) {
21+
$capacity = $item->capacity();
22+
23+
if (false === $item->isLeaf()) {
24+
$children = iterator_to_array($item->children());
25+
26+
while ($item->degree() !== $capacity) {
27+
$item->add(current($children)->clone());
28+
}
29+
}
30+
}
31+
32+
return $tree;
33+
}
34+
}

src/Modifier/RemoveNullNode.php

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace drupol\phptree\Modifier;
6+
7+
use drupol\phptree\Node\NodeInterface;
8+
9+
/**
10+
* Class RemoveNullNode.
11+
*/
12+
class RemoveNullNode implements ModifierInterface
13+
{
14+
/**
15+
* {@inheritdoc}
16+
*/
17+
public function modify(NodeInterface $tree): NodeInterface
18+
{
19+
/** @var \drupol\phptree\Node\MerkleNode $item */
20+
foreach ($tree->all() as $item) {
21+
if (null === $parent = $item->getParent()) {
22+
continue;
23+
}
24+
25+
if (true !== $item->isLeaf()) {
26+
continue;
27+
}
28+
29+
if (null !== $item->getValue()) {
30+
continue;
31+
}
32+
33+
$parent->remove($item);
34+
}
35+
36+
return $tree;
37+
}
38+
}

src/Node/MerkleNode.php

+33-42
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,24 @@
66

77
use drupol\phpmerkle\Hasher\DoubleSha256;
88
use drupol\phpmerkle\Hasher\HasherInterface;
9+
use drupol\phptree\Modifier\FulfillCapacity;
10+
use drupol\phptree\Modifier\RemoveNullNode;
911

1012
/**
1113
* Class MerkleNode.
1214
*/
13-
class MerkleNode extends ValueNode
15+
class MerkleNode extends ValueNode implements MerkleNodeInterface
1416
{
1517
/**
1618
* @var \drupol\phpmerkle\Hasher\HasherInterface
1719
*/
1820
private $hasher;
1921

22+
/**
23+
* @var \drupol\phptree\Modifier\ModifierInterface[]
24+
*/
25+
private $modifiers;
26+
2027
/**
2128
* MerkleNode constructor.
2229
*
@@ -32,6 +39,10 @@ public function __construct(
3239
parent::__construct($value, $capacity, null, null);
3340

3441
$this->hasher = $hasher ?? new DoubleSha256();
42+
$this->modifiers = [
43+
new RemoveNullNode(),
44+
new FulfillCapacity(),
45+
];
3546
}
3647

3748
/**
@@ -43,60 +54,40 @@ public function getValue()
4354
return parent::getValue();
4455
}
4556

46-
return $this->clone()->hash();
57+
return $this->hash();
4758
}
4859

4960
/**
5061
* {@inheritdoc}
5162
*/
52-
private function doHash(): string
63+
public function normalize(): MerkleNodeInterface
5364
{
54-
// If node is a leaf, then compute its hash from its value.
55-
if (true === $this->isLeaf()) {
56-
$value = $this->getValue();
57-
58-
if (null === $value) {
59-
return '';
60-
}
65+
$tree = $this->clone();
6166

62-
return $this->hasher->hash($value);
67+
foreach ($this->modifiers as $modifier) {
68+
$tree = $modifier->modify($tree);
6369
}
6470

65-
// Remove all nodes with null value.
66-
if (null !== $parent = $this->getParent()) {
67-
/** @var \drupol\phptree\Node\MerkleNode $node */
68-
foreach ($parent->all() as $node) {
69-
if (false === $node->isLeaf()) {
70-
continue;
71-
}
72-
73-
if (null !== $node->getValue()) {
74-
continue;
75-
}
76-
77-
$node->getParent()->remove($node);
71+
/** @var \drupol\phptree\Node\MerkleNodeInterface $tree */
72+
return $tree;
73+
}
7874

79-
return $this->hash();
80-
}
75+
/**
76+
* {@inheritdoc}
77+
*/
78+
private function doHash(MerkleNodeInterface $node): string
79+
{
80+
// If node is a leaf, then compute its hash from its value.
81+
if (true === $node->isLeaf()) {
82+
return $this->hasher->hash($node->getValue());
8183
}
8284

83-
// If node with children is not fulfilled, make sure it is complete.
84-
if ($this->degree() !== $this->capacity()) {
85-
$children = iterator_to_array($this->children());
86-
87-
if ([] !== $children) {
88-
$this->add(current($children)->clone());
89-
}
85+
$hash = '';
86+
/** @var \drupol\phptree\Node\MerkleNodeInterface $child */
87+
foreach ($node->children() as $child) {
88+
$hash .= $this->doHash($child);
9089
}
9190

92-
$hash = array_reduce(
93-
iterator_to_array($this->children()),
94-
static function ($carry, MerkleNode $node): string {
95-
return $carry . $node->doHash();
96-
},
97-
''
98-
);
99-
10091
return $this->hasher->hash($hash);
10192
}
10293

@@ -105,6 +96,6 @@ static function ($carry, MerkleNode $node): string {
10596
*/
10697
private function hash(): string
10798
{
108-
return $this->hasher->unpack($this->doHash());
99+
return $this->hasher->unpack($this->doHash($this->normalize()));
109100
}
110101
}

src/Node/MerkleNodeInterface.php

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace drupol\phptree\Node;
6+
7+
/**
8+
* Interface MerkleNodeInterface.
9+
*/
10+
interface MerkleNodeInterface extends ValueNodeInterface
11+
{
12+
/**
13+
* @return \drupol\phptree\Node\MerkleNodeInterface
14+
*/
15+
public function normalize(): MerkleNodeInterface;
16+
}

0 commit comments

Comments
 (0)