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 }