package peergos.shared.merklebtree;
import peergos.shared.crypto.asymmetric.*;
import peergos.shared.io.ipfs.multihash.*;
import peergos.shared.storage.ContentAddressedStorage;
import peergos.shared.util.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
public class MerkleBTree
{
public static final int MAX_NODE_CHILDREN = 16;
public final ContentAddressedStorage storage;
public final int maxChildren;
public TreeNode root;
public MerkleBTree(TreeNode root, MaybeMultihash rootHash, ContentAddressedStorage storage, int maxChildren) {
this.storage = storage;
this.root = new TreeNode(root.keys, rootHash);
this.maxChildren = maxChildren;
}
public MerkleBTree(TreeNode root, Multihash rootHash, ContentAddressedStorage storage, int maxChildren) {
this(root, MaybeMultihash.of(rootHash), storage, maxChildren);
}
public static CompletableFuture<MerkleBTree> create(PublicSigningKey writer, Multihash rootHash, ContentAddressedStorage dht) {
return create(writer, MaybeMultihash.of(rootHash), dht);
}
public static CompletableFuture<MerkleBTree> create(PublicSigningKey writer, MaybeMultihash rootHash, ContentAddressedStorage dht) {
if (! rootHash.isPresent()) {
TreeNode newRoot = new TreeNode(new TreeSet<>());
return dht.put(writer, newRoot.serialize())
.thenApply(put -> new MerkleBTree(newRoot, put, dht, MAX_NODE_CHILDREN));
}
return dht.get(rootHash.get()).thenApply(rawOpt -> {
if (! rawOpt.isPresent())
throw new IllegalStateException("Null byte[] returned by DHT for hash: " + rootHash.get());
return new MerkleBTree(TreeNode.fromCbor(rawOpt.get()), rootHash, dht, MAX_NODE_CHILDREN);
});
}
/**
*
* @param rawKey
* @return value stored under rawKey
* @throws IOException
*/
public CompletableFuture<MaybeMultihash> get(byte[] rawKey) {
return root.get(new ByteArrayWrapper(rawKey), storage);
}
/**
*
* @param rawKey
* @param value
* @return hash of new tree root
* @throws IOException
*/
public CompletableFuture<Multihash> put(PublicSigningKey writer, byte[] rawKey, Multihash value) {
return root.put(writer, new ByteArrayWrapper(rawKey), value, storage, maxChildren)
.thenCompose(newRoot -> commit(writer, newRoot));
}
/**
*
* @param rawKey
* @return hash of new tree root
* @throws IOException
*/
public CompletableFuture<Multihash> delete(PublicSigningKey writer, byte[] rawKey) {
return root.delete(writer, new ByteArrayWrapper(rawKey), storage, maxChildren)
.thenCompose(newRoot -> commit(writer, newRoot));
}
private CompletableFuture<Multihash> commit(PublicSigningKey writer, TreeNode newRoot) {
if (newRoot.hash.isPresent()) {
root = newRoot;
return CompletableFuture.completedFuture(newRoot.hash.get());
}
return storage.put(writer, newRoot.serialize()).thenApply(newRootHash -> {
root = new TreeNode(newRoot.keys, newRootHash);
return newRootHash;
});
}
/**
*
* @return number of keys stored in tree
* @throws IOException
*/
public CompletableFuture<Integer> size() {
return root.size(storage);
}
public void print(PrintStream w) throws Exception {
root.print(w, 0, storage);
}
}