All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.crums.util.mrkl.hashing.ProofStatement Maven / Gradle / Ivy

/*
 * Copyright 2021 Babak Farhang
 */
package io.crums.util.mrkl.hashing;


import static io.crums.model.hashing.ExpressionSymbols.*;
import static io.crums.util.IntegralStrings.*;

import java.util.List;
import java.util.Objects;

import io.crums.model.hashing.Entity;
import io.crums.model.hashing.Parser;
//import io.crums.model.hashing.Entity;
import io.crums.model.hashing.Statement;
import io.crums.util.hash.Digests;
import io.crums.util.mrkl.Proof;
import io.crums.util.mrkl.Tree;
import io.crums.util.mrkl.index.AbstractNode;
import io.crums.util.mrkl.index.TreeIndex;


/**
 * Generates {@linkplain Statement}s derived from Merkle {@linkplain Proof}s.
 * 
 * 

Structure

*

* The general structure of returned statements is as follows. *

*
    *
  1. LHS. A hex literal equal to the value of the root node in the Merkle tree. * (A proof redundantly contains the root value of the tree.)
  2. *
  3. RHS. Expresses a path of hash pointers from the proof-item, to the root of the * Merkle tree. The proof-item may be used as is, or may be substituted with an expression * that evaluates to the proof-item (typically the hash of another byte string).
  4. *
*

* This way, the intent of such a statement is more transparent to a human reader; * to validate the statement of course, a piece of software (such as provided in this library) * must be used. *

*/ public class ProofStatement { private ProofStatement() { } public final static String LEAF_PAD = toHex(Tree.LEAF_PAD); public final static String BRANCH_PAD = toHex(Tree.BRANCH_PAD); /** * Creates and returns a Statement for the given proof. */ public static Statement createStatement(Proof proof) { return createItemStatement(proof, null); } /** * Creates and returns a Statement for the given proof and optional * item definition itemDef. If provided, this must {@linkplain Parser#parse(String) parse} * to an {@linkplain Entity entity} that evaluates to the value of the {@linkplain Proof#item() item} * in the proof; if not provided (null), then a hex literal equal to the proof's item itself * is used. */ public static Statement createItemStatement(Proof proof, CharSequence itemDef) { Objects.requireNonNull(proof, "null proof"); if (!Digests.SHA_256.hashAlgo().equals(proof.getHashAlgo())) throw new IllegalArgumentException("proof hash algo: " + proof.getHashAlgo()); if (itemDef == null) itemDef = toHex(proof.item()); return generateStatement(proof, itemDef); } private static Statement generateStatement(Proof proof, CharSequence itemDef) { if (itemDef == null) itemDef = toHex(proof.item()); try { List chain = proof.hashChain(); TreeIndex tree = TreeIndex.newGeneric(proof.leafCount()); StringBuilder expr = new StringBuilder(70 * chain.size()); expr.append(itemDef).append(' ').append(FLIP).append(LEAF_PAD); AbstractNode node = tree.getSibling(0, proof.leafIndex()); String sibHex = toHex(chain.get(1)); if (node.isLeaf()) { if (node.isRight()) { expr.append(' ').append(LEAF_PAD).append(' ').append(sibHex); } else { group(expr).append(' ').append(FLIP).append(' ') .append(LFT_BRKT).append(LEAF_PAD).append(' ').append(sibHex).append(RGT_BRKT); } } else { assert node.isLeft(); group(expr).append(' ').append(FLIP).append(' ') .append(LFT_BRKT).append(BRANCH_PAD).append(' ').append(sibHex).append(RGT_BRKT); } node = tree.getParent(node); hash(expr); int cindex = 2; for (; node.level() != tree.height(); node = tree.getParent(node), ++cindex) { // invariant: *expr*, if evaluated, is the value of *node*; // the values in the chain are the *siblings of each computed parent sibHex = toHex(chain.get(cindex)); expr.append(FLIP).append(BRANCH_PAD).append(' '); AbstractNode sib = tree.getSibling(node); String sibPadding = sib.isLeaf() ? LEAF_PAD : BRANCH_PAD; if (sib.isLeft()) { expr.append(FLIP).append(LFT_BRKT).append(sibPadding).append(' ') .append(sibHex).append(RGT_BRKT); } else { expr.append(sibPadding).append(' ').append(sibHex); } hash(expr); } if (cindex != chain.size() - 1) throw new IllegalArgumentException( "proof hash chain is *structurally* invalid (too long): expected " + (cindex + 1) + " elements; actual given is " + chain.size()); // expr.append(' ').append(EQU).append(' ').append(toHex(proof.rootHash())); // Group all values of interest to a human at the start of the expresssion return Statement.parse(toHex(proof.rootHash()) + " " + EQU + " " + expr); } catch (IndexOutOfBoundsException chainTooShort) { throw new IllegalArgumentException( "proof hash chain is *structurally* invalid (too short)", chainTooShort); } } private static StringBuilder hash(StringBuilder expression) { return expression.insert(0, LFT_PRNS).append(RGT_PRNS); } private static StringBuilder group(StringBuilder expression) { return expression.insert(0, LFT_BRKT).append(RGT_BRKT); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy