burrow/src/main/java/io/rudefox/burrow/InteractiveEntropyGenerator...

155 lines
5.3 KiB
Java

package io.rudefox.burrow;
import com.bjdweck.bitcoin.mnemonic.Entropy;
import com.diogonunes.jcolor.AnsiFormat;
import java.util.Scanner;
import static com.diogonunes.jcolor.Attribute.*;
public abstract class InteractiveEntropyGenerator {
public static final int WORDS_PER_EVENT_SET = 3;
public static final int BITS_PER_WORD = 11;
public static final int BITS_PER_EVENT_SET = WORDS_PER_EVENT_SET * BITS_PER_WORD;
public static final AnsiFormat RED_STYLE = new AnsiFormat(WHITE_TEXT(), RED_BACK(), BOLD());
public static final AnsiFormat GREEN_STYLE = new AnsiFormat(BLACK_TEXT(), GREEN_BACK(), BOLD());
public static final AnsiFormat BLUE_STYLE = new AnsiFormat(WHITE_TEXT(), BLUE_BACK(), BOLD());
private Entropy entropy = new Entropy();
protected final int targetBitsOfEntropy;
protected final int bitsPerEvent;
protected final int eventsPerEventSet;
protected final Scanner inputScanner;
protected final boolean ansiColorOutput;
protected InteractiveEntropyGenerator(Scanner inputScanner, boolean ansiColorOutput, int targetBitsOfEntropy, int bitsPerEvent) {
this.targetBitsOfEntropy = targetBitsOfEntropy;
this.bitsPerEvent = bitsPerEvent;
this.eventsPerEventSet = BITS_PER_EVENT_SET / this.bitsPerEvent;
this.inputScanner = inputScanner;
this.ansiColorOutput = ansiColorOutput;
}
Entropy generate() {
entropy = new Entropy();
for (int eventSetIndex = 0; entropy.getBitLength() < targetBitsOfEntropy; eventSetIndex++) {
String eventString = readNextEventSetString();
StringBuilder eventValuesLine = processEventSetString(eventString);
System.out.println();
outputEventValuesLine(eventValuesLine);
System.out.println(getBitsLine());
System.out.println(getSeedWordsLine(eventSetIndex));
System.out.println();
}
Entropy truncatedEntropy = entropy.truncate(targetBitsOfEntropy);
return truncatedEntropy.appendChecksum();
}
private StringBuilder processEventSetString(String eventString) {
StringBuilder eventValuesLine = new StringBuilder("|");
for (int eventIndexWithinSet = 0; eventIndexWithinSet < eventsPerEventSet; eventIndexWithinSet++) {
int eventDecimalValue = Integer.parseInt(eventString.charAt(eventIndexWithinSet) + "");
eventValuesLine.append(String.format(getEventValueFormatString(eventIndexWithinSet), eventDecimalValue));
entropy = entropy.appendBits(toZeroBasedInteger(eventDecimalValue), bitsPerEvent);
}
return eventValuesLine;
}
private StringBuilder getBitsLine() {
String entropyBitString = entropy.toString();
String eventSetBitString = entropyBitString.substring(entropyBitString.length() - BITS_PER_EVENT_SET);
StringBuilder bitsLine = getInitialBitsLine();
for (int bitIndexWithinEventSet = 0; bitIndexWithinEventSet < eventSetBitString.length(); bitIndexWithinEventSet++) {
int binIndexWithinEntropy = entropyBitString.length() - eventSetBitString.length() + bitIndexWithinEventSet;
if (binIndexWithinEntropy < targetBitsOfEntropy)
bitsLine.append(styleBit("" + eventSetBitString.charAt(bitIndexWithinEventSet), binIndexWithinEntropy, bitIndexWithinEventSet));
else
bitsLine.append("-");
padBitsLine(bitsLine, bitIndexWithinEventSet, binIndexWithinEntropy);
}
return bitsLine;
}
String styleBit(String bit, int bitIndexWithinEntropy, int bitIndexWithinEventSet) {
if (!ansiColorOutput || bitIndexWithinEntropy >= targetBitsOfEntropy)
return bit;
return getBitColor(bitIndexWithinEventSet).format(bit);
}
private AnsiFormat getBitColor(int bitIndexWithinEventSet) {
int bitIndexWithinWord = bitIndexWithinEventSet % BITS_PER_WORD;
if (bitIndexWithinWord == 0)
return RED_STYLE;
if (bitIndexWithinWord <= 6)
return GREEN_STYLE;
return BLUE_STYLE;
}
private String getSeedWordsLine(int eventSetIndex) {
int firstWordIndex = eventSetIndex * WORDS_PER_EVENT_SET;
return String.format(
getSeedWordLineFormatString(),
getFormattedSeedWord(firstWordIndex),
getFormattedSeedWord(firstWordIndex + 1),
getFormattedSeedWord(firstWordIndex + 2));
}
private String getFormattedSeedWord(int wordIndex) {
int lastWordIndex = WORDS_PER_EVENT_SET * targetBitsOfEntropy / (BITS_PER_EVENT_SET - 1) - 1;
String seedWord = wordIndex != lastWordIndex ? entropy.getWord(wordIndex) : "CHECKWORD";
return String.format(" %2d. %s", wordIndex + 1, seedWord);
}
protected abstract String readNextEventSetString();
protected abstract String getEventValueFormatString(int eventIndexWithinSet);
protected abstract int toZeroBasedInteger(int eventDecimalValue);
protected abstract void outputEventValuesLine(StringBuilder eventValuesLine);
protected abstract void padBitsLine(StringBuilder eventSetBitsLine, int bitIndexWithinEventSet, int bitIndexWithinEntropy);
protected abstract StringBuilder getInitialBitsLine();
protected abstract String getSeedWordLineFormatString();
}