io.bitsquare.gui.util.validation.altcoins.ByteballAddressValidator.java Source code

Java tutorial

Introduction

Here is the source code for io.bitsquare.gui.util.validation.altcoins.ByteballAddressValidator.java

Source

/*
 * This file is part of Bitsquare.
 *
 * Bitsquare is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 *
 * Bitsquare is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
 */

package io.bitsquare.gui.util.validation.altcoins;

import io.bitsquare.gui.util.validation.InputValidator;
import org.apache.commons.codec.binary.Base32;
import org.apache.commons.codec.binary.Base64;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Created by DevAlexey on 19.12.2016.
 */
public class ByteballAddressValidator {
    private static final Base32 base32 = new Base32();
    private static final Base64 base64 = new Base64();
    private static final String PI = "14159265358979323846264338327950288419716939937510";
    private static final String[] arrRelativeOffsets = PI.split("");
    private static Integer[] arrOffsets160, arrOffsets288;

    static {
        try {
            arrOffsets160 = calcOffsets(160);
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            arrOffsets288 = calcOffsets(288);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static InputValidator.ValidationResult validate(String input) {
        return new InputValidator.ValidationResult(isValidAddress(input));
    }

    private static boolean isValidAddress(String address) {
        return isValidChash(address, 32);
    }

    private static boolean isValidChash(String str, int len) {
        return (isStringOfLength(str, len) && isChashValid(str));
    }

    private static boolean isStringOfLength(String str, int len) {
        return str.length() == len;
    }

    private static void checkLength(int chash_length) throws Exception {
        if (chash_length != 160 && chash_length != 288)
            throw new Exception("unsupported c-hash length: " + chash_length);
    }

    private static Integer[] calcOffsets(int chash_length) throws Exception {
        checkLength(chash_length);
        List<Integer> arrOffsets = new ArrayList<>(chash_length);
        int offset = 0;
        int index = 0;

        for (int i = 0; offset < chash_length; i++) {
            int relative_offset = Integer.parseInt(arrRelativeOffsets[i]);
            if (relative_offset == 0)
                continue;
            offset += relative_offset;
            if (chash_length == 288)
                offset += 4;
            if (offset >= chash_length)
                break;
            arrOffsets.add(offset);
            //console.log("index="+index+", offset="+offset);
            index++;
        }

        if (index != 32)
            throw new Exception("wrong number of checksum bits");

        return arrOffsets.toArray(new Integer[0]);
    }

    private static SeparatedData separateIntoCleanDataAndChecksum(String bin) throws Exception {
        int len = bin.length();
        Integer[] arrOffsets;
        if (len == 160)
            arrOffsets = arrOffsets160;
        else if (len == 288)
            arrOffsets = arrOffsets288;
        else
            throw new Exception("bad length");
        StringBuilder arrFrags = new StringBuilder();
        StringBuilder arrChecksumBits = new StringBuilder();
        int start = 0;
        for (int i = 0; i < arrOffsets.length; i++) {
            arrFrags.append(bin.substring(start, arrOffsets[i]));
            arrChecksumBits.append(bin.substring(arrOffsets[i], arrOffsets[i] + 1));
            start = arrOffsets[i] + 1;
        }
        // add last frag
        if (start < bin.length())
            arrFrags.append(bin.substring(start));
        String binCleanData = arrFrags.toString();
        String binChecksum = arrChecksumBits.toString();
        return new SeparatedData(binCleanData, binChecksum);
    }

    private static String buffer2bin(byte[] buf) {
        StringBuffer bytes = new StringBuffer();
        for (int i = 0; i < buf.length; i++) {
            String bin = String.format("%8s", Integer.toBinaryString(buf[i] & 0xFF)).replace(' ', '0');
            bytes.append(bin);
        }
        return bytes.toString();
    }

    private static byte[] bin2buffer(String bin) {
        int len = bin.length() / 8;
        byte[] buf = new byte[len];
        for (int i = 0; i < len; i++)
            buf[i] = (byte) Integer.parseInt(bin.substring(i * 8, (i + 1) * 8), 2);
        return buf;
    }

    private static boolean isChashValid(String encoded) {
        int encoded_len = encoded.length();
        if (encoded_len != 32 && encoded_len != 48) // 160/5 = 32, 288/6 = 48
            return false;
        byte[] chash = (encoded_len == 32) ? base32.decode(encoded) : base64.decode(encoded);
        String binChash = buffer2bin(chash);
        SeparatedData separated = null;
        try {
            separated = separateIntoCleanDataAndChecksum(binChash);
        } catch (Exception e) {
            return false;
        }
        byte[] clean_data = bin2buffer(separated.clean_data);
        byte[] checksum = bin2buffer(separated.checksum);
        return Arrays.equals(getChecksum(clean_data), checksum);
    }

    private static byte[] getChecksum(byte[] clean_data) {

        try {
            byte[] full_checksum = MessageDigest.getInstance("SHA-256").digest(clean_data);
            byte[] checksum = { full_checksum[5], full_checksum[13], full_checksum[21], full_checksum[29] };
            return checksum;
        } catch (NoSuchAlgorithmException e) {
            return null;
        }
    }

    private static class SeparatedData {
        String clean_data, checksum;

        public SeparatedData(String clean_data, String checksum) {
            this.clean_data = clean_data;
            this.checksum = checksum;
        }
    }
}