package net.quux00;import java.nio.ByteBuffer;import java.nio.charset.StandardCharsets;import java.util.ArrayDeque;import java.util.ArrayList;import java.util.List;import java.util.Queue;import java.util.zip.Adler32;/**
* MerkleTree is an implementation of a Merkle binary hash tree where the leaves
* are signatures (hashes, digests, CRCs, etc.) of some underlying data structure
* that is not explicitly part of the tree.
*
* The internal leaves of the tree are signatures of its two child nodes. If an
* internal node has only one child, the the signature of the child node is
* adopted ("promoted").
*
* MerkleTree knows how to serialize itself to a binary format, but does not
* implement the Java Serializer interface. The {@link #serialize()} method
* returns a byte array, which should be passed to
* {@link MerkleDeserializer#deserialize(byte[])} in order to hydrate into
* a MerkleTree in memory.
*
* This MerkleTree is intentionally ignorant of the hashing/checksum algorithm
* used to generate the leaf signatures. It uses Adler32 CRC to generate
* signatures for all internal node signatures (other than those "promoted"
* that have only one child).
*
* The Adler32 CRC is not cryptographically secure, so this implementation
* should NOT be used in scenarios where the data is being received from
* an untrusted source.
*/publicclassMerkleTree{publicstaticfinalint MAGIC_HDR =0xcdaace99;publicstaticfinalint INT_BYTES =4;publicstaticfinalint LONG_BYTES =8;publicstaticfinalbyte LEAF_SIG_TYPE =0x0;publicstaticfinalbyte INTERNAL_SIG_TYPE =0x01;privatefinal Adler32 crc =newAdler32();private List<String> leafSigs;private Node root;privateint depth;privateint nnodes;/**
* Use this constructor to create a MerkleTree from a list of leaf signatures.
* The Merkle tree is built from the bottom up.
* @param leafSignatures
*/publicMerkleTree(List<String> leafSignatures){constructTree(leafSignatures);}/**
* Use this constructor when you have already constructed the tree of Nodes
* (from deserialization).
* @param treeRoot
* @param numNodes
* @param height
* @param leafSignatures
*/publicMerkleTree(Node treeRoot,int numNodes,int height, List<String> leafSignatures){
root = treeRoot;
nnodes = numNodes;
depth = height;
leafSigs = leafSignatures;}/**
* Serialization format:
* (magicheader:int)(numnodes:int)[(nodetype:byte)(siglength:int)(signature:[]byte)]
* @return
*/publicbyte[]serialize(){int magicHeaderSz = INT_BYTES;int nnodesSz = INT_BYTES;int hdrSz = magicHeaderSz + nnodesSz;int typeByteSz =1;int siglength = INT_BYTES;int parentSigSz = LONG_BYTES;int leafSigSz = leafSigs.get(0).getBytes(StandardCharsets.UTF_8).length;// some of the internal nodes may use leaf signatures (when "promoted")// so ensure that the ByteBuffer overestimates how much space is needed// since ByteBuffer does not expand on demandint maxSigSz = leafSigSz;if(parentSigSz > maxSigSz){
maxSigSz = parentSigSz;}int spaceForNodes =(typeByteSz + siglength + maxSigSz)* nnodes;int cap = hdrSz + spaceForNodes;
ByteBuffer buf = ByteBuffer.allocate(cap);
buf.putInt(MAGIC_HDR).putInt(nnodes);// headerserializeBreadthFirst(buf);// the ByteBuf allocated space is likely more than was needed// so copy to a byte array of the exact size necesssarybyte[] serializedTree =newbyte[buf.position()];
buf.rewind();
buf.get(serializedTree);return serializedTree;}/**
* Serialization format after the header section:
* [(nodetype:byte)(siglength:int)(signature:[]byte)]
* @param buf
*/voidserializeBreadthFirst(ByteBuffer buf){
Queue<Node> q =newArrayDeque<Node>((nnodes /2)+1);
q.add(root);while(!q.isEmpty()){
Node nd = q.remove();
buf.put(nd.type).putInt(nd.sig.length).put(nd.sig);if(nd.left != null){
q.add(nd.left);}if(nd.right != null){
q.add(nd.right);}}}/**
* Create a tree from the bottom up starting from the leaf signatures.
* @param signatures
*/voidconstructTree(List<String> signatures){if(signatures.size()<=1){thrownewIllegalArgumentException("Must be at least two signatures to construct a Merkle tree");}
leafSigs = signatures;
nnodes = signatures.size();
List<Node> parents =bottomLevel(signatures);
nnodes += parents.size();
depth =1;while(parents.size()>1){
parents =internalLevel(parents);
depth++;
nnodes += parents.size();}
root = parents.get(0);}publicintgetNumNodes(){return nnodes;}public Node getRoot(){return root;}publicintgetHeight(){return depth;}/**
* Constructs an internal level of the tree
*/
List<Node>internalLevel(List<Node> children){
List<Node> parents =newArrayList<Node>(children.size()/2);for(int i =0; i < children.size()-1; i +=2){
Node child1 = children.get(i);
Node child2 = children.get(i+1);
Node parent =constructInternalNode(child1, child2);
parents.add(parent);}if(children.size()%2!=0){
Node child = children.get(children.size()-1);
Node parent =constructInternalNode(child, null);
parents.add(parent);}return parents;}/**
* Constructs the bottom part of the tree - the leaf nodes and their
* immediate parents. Returns a list of the parent nodes.
*/
List<Node>bottomLevel(List<String> signatures){
List<Node> parents =newArrayList<Node>(signatures.size()/2);for(int i =0; i < signatures.size()-1; i +=2){
Node leaf1 =constructLeafNode(signatures.get(i));
Node leaf2 =constructLeafNode(signatures.get(i+1));
Node parent =constructInternalNode(leaf1, leaf2);
parents.add(parent);}// if odd number of leafs, handle last entryif(signatures.size()%2!=0){
Node leaf =constructLeafNode(signatures.get(signatures.size()-1));
Node parent =constructInternalNode(leaf, null);
parents.add(parent);}return parents;}private Node constructInternalNode(Node child1, Node child2){
Node parent =newNode();
parent.type = INTERNAL_SIG_TYPE;if(child2 == null){
parent.sig = child1.sig;}else{
parent.sig =internalHash(child1.sig, child2.sig);}
parent.left = child1;
parent.right = child2;return parent;}privatestatic Node constructLeafNode(String signature){
Node leaf =newNode();
leaf.type = LEAF_SIG_TYPE;
leaf.sig = signature.getBytes(StandardCharsets.UTF_8);return leaf;}byte[]internalHash(byte[] leftChildSig,byte[] rightChildSig){
crc.reset();
crc.update(leftChildSig);
crc.update(rightChildSig);returnlongToByteArray(crc.getValue());}/* ---[ Node class ]--- *//**
* The Node class should be treated as immutable, though immutable
* is not enforced in the current design.
*
* A Node knows whether it is an internal or leaf node and its signature.
*
* Internal Nodes will have at least one child (always on the left).
* Leaf Nodes will have no children (left = right = null).
*/staticclassNode{publicbyte type;// INTERNAL_SIG_TYPE or LEAF_SIG_TYPEpublicbyte[] sig;// signature of the nodepublic Node left;public Node right;@Overridepublic String toString(){
String leftType ="<null>";
String rightType ="<null>";if(left != null){
leftType = String.valueOf(left.type);}if(right != null){
rightType = String.valueOf(right.type);}return String.format("MerkleTree.Node<type:%d, sig:%s, left (type): %s, right (type): %s>",
type,sigAsString(), leftType, rightType);}private String sigAsString(){
StringBuffer sb =newStringBuffer();
sb.append('[');for(int i =0; i < sig.length; i++){
sb.append(sig[i]).append(' ');}
sb.insert(sb.length()-1,']');return sb.toString();}}/**
* Big-endian conversion
*/publicstaticbyte[]longToByteArray(long value){returnnewbyte[]{(byte)(value >>56),(byte)(value >>48),(byte)(value >>40),(byte)(value >>32),(byte)(value >>24),(byte)(value >>16),(byte)(value >>8),(byte) value
};}}
package net.quux00;importstatic net.quux00.MerkleTree.LEAF_SIG_TYPE;import java.nio.ByteBuffer;import java.nio.charset.StandardCharsets;import java.util.ArrayDeque;import java.util.ArrayList;import java.util.List;import java.util.Queue;import net.quux00.MerkleTree.Node;/**
* The Deserialization code was separated from the MerkleTree class.
*/publicfinalclassMerkleDeserializer{privateMerkleDeserializer(){}/**
* Deserialize works from a byte array generated by {@link MerkleTree#serialize()}.
* Serialization format:
* (magicheader:int)(numnodes:int)[(nodetype:byte)(siglength:int)(signature:[]byte)]
* @param serializedTree
* @return
*/publicstatic MerkleTree deserialize(byte[] serializedTree){
ByteBuffer buf = ByteBuffer.wrap(serializedTree);/* ---[ read header ]--- */if(buf.getInt()!= MerkleTree.MAGIC_HDR){thrownewIllegalArgumentException("serialized byte array does not start with appropriate Magic Header");}int totnodes = buf.getInt();/* ---[ read data ]--- */
List<String> leafSigs =newArrayList<>((totnodes /2)+1);// read root
Node root =newNode();
root.type = buf.get();if(root.type == LEAF_SIG_TYPE){thrownewIllegalStateException("First serialized node is a leaf");}readNextSignature(buf, root);
Queue<Node> q =newArrayDeque<>((totnodes /2)+1);
Node curr = root;int height =0;int expNumNodes =2;int nodesThisLevel =0;for(int i =1; i < totnodes; i++){
Node child =newNode();
child.type = buf.get();readNextSignature(buf, child);
q.add(child);if(child.type == LEAF_SIG_TYPE){
leafSigs.add(newString(child.sig, StandardCharsets.UTF_8));}// handles incomplete tree where a node has been "promoted"if(signaturesEqual(child.sig,curr.sig)){
curr.left = child;
curr = q.remove();
expNumNodes *=2;
nodesThisLevel =0;
height++;continue;}
nodesThisLevel++;if(curr.left == null){
curr.left = child;}else{
curr.right = child;
curr = q.remove();if(nodesThisLevel >= expNumNodes){
expNumNodes *=2;
nodesThisLevel =0;
height++;}}}returnnewMerkleTree(root, totnodes, height, leafSigs);}/**
* Returns two if the two byte arrays passed in are exactly identical.
*/staticbooleansignaturesEqual(byte[] sig,byte[] sig2){if(sig.length != sig2.length){returnfalse;}for(int i =0; i < sig.length; i++){if(sig[i]!= sig2[i]){returnfalse;}}returntrue;}staticvoidreadNextSignature(ByteBuffer buf, Node nd){byte[] sigBytes =newbyte[buf.getInt()];
buf.get(sigBytes);
nd.sig = sigBytes;}}