Coverage Summary for Class: NodeReference (co.rsk.trie)
Class |
Class, %
|
Method, %
|
Line, %
|
NodeReference |
100%
(1/1)
|
91.7%
(11/12)
|
91.3%
(42/46)
|
1 /*
2 * This file is part of RskJ
3 * Copyright (C) 2019 RSK Labs Ltd.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 package co.rsk.trie;
19
20 import co.rsk.core.types.ints.Uint8;
21 import co.rsk.crypto.Keccak256;
22 import org.ethereum.crypto.Keccak256Helper;
23
24 import javax.annotation.Nullable;
25 import java.nio.ByteBuffer;
26 import java.util.Optional;
27
28 public class NodeReference {
29
30
31 private final TrieStore store;
32
33 private Trie lazyNode;
34 private Keccak256 lazyHash;
35
36 public NodeReference(TrieStore store, @Nullable Trie node, @Nullable Keccak256 hash) {
37 this.store = store;
38 if (node != null && node.isEmptyTrie()) {
39 this.lazyNode = null;
40 this.lazyHash = null;
41 } else {
42 this.lazyNode = node;
43 this.lazyHash = hash;
44 }
45 }
46
47 public boolean isEmpty() {
48 return lazyHash == null && lazyNode == null;
49 }
50
51 /**
52 * The node or empty if this is an empty reference.
53 * If the node is not present but its hash is known, it will be retrieved from the store.
54 */
55 public Optional<Trie> getNode() {
56 if (lazyNode != null) {
57 return Optional.of(lazyNode);
58 }
59
60 if (lazyHash == null) {
61 return Optional.empty();
62 }
63
64 Optional<Trie> node = store.retrieve(lazyHash.getBytes());
65
66 lazyNode = node.orElse(null);
67 return node;
68 }
69
70 /**
71 * The hash or empty if this is an empty reference.
72 * If the hash is not present but its node is known, it will be calculated.
73 */
74 public Optional<Keccak256> getHash() {
75 if (lazyHash != null) {
76 return Optional.of(lazyHash);
77 }
78
79 if (lazyNode == null) {
80 return Optional.empty();
81 }
82
83 lazyHash = lazyNode.getHash();
84 return Optional.of(lazyHash);
85 }
86
87 /**
88 * The hash or empty if this is an empty reference.
89 * If the hash is not present but its node is known, it will be calculated.
90 */
91 public Optional<Keccak256> getHashOrchid(boolean isSecure) {
92 return getNode().map(trie -> trie.getHashOrchid(isSecure));
93 }
94
95 @SuppressWarnings("squid:S2384") // private method knows it can avoid copying the byte[] field
96 private byte[] getSerialized() {
97 return lazyNode.toMessage();
98 }
99
100 public boolean isEmbeddable() {
101 // if the node is embeddable then this reference must have a reference in memory
102 if (lazyNode == null) {
103 return false;
104 }
105 return lazyNode.isEmbeddable();
106
107 }
108
109 // This method should only be called from save()
110 public int serializedLength() {
111 if (!isEmpty()) {
112 if (isEmbeddable()) {
113 return lazyNode.getMessageLength() + 1;
114 }
115
116 return Keccak256Helper.DEFAULT_SIZE_BYTES;
117 }
118
119 return 0;
120 }
121
122 public void serializeInto(ByteBuffer buffer) {
123 if (!isEmpty()) {
124 if (isEmbeddable()) {
125 byte[] serialized = getSerialized();
126 buffer.put(new Uint8(serialized.length).encode());
127 buffer.put(serialized);
128 } else {
129 byte[] hash = getHash().map(Keccak256::getBytes)
130 .orElseThrow(() -> new IllegalStateException("The hash should always exists at this point"));
131 buffer.put(hash);
132 }
133 }
134 }
135
136 /**
137 * @return the tree size in bytes as specified in RSKIP107 plus the actual serialized size
138 *
139 * This method will EXPAND internal encoding caches without removing them afterwards.
140 * Do not use.
141 */
142 public long referenceSize() {
143 return getNode().map(this::nodeSize).orElse(0L);
144 }
145
146 private long nodeSize(Trie trie) {
147 long externalValueLength = trie.hasLongValue() ? trie.getValueLength().intValue() : 0L;
148 return trie.getChildrenSize().value + externalValueLength + trie.getMessageLength();
149 }
150
151 public static NodeReference empty() {
152 return new NodeReference(null, null, null);
153 }
154 }