区块链Base58格式的地址生成
一、背景
前段时间无意中了解到了Base58,分析后发现比Base64更适合做为UUID主键的编码方式。
二、Base58简介
Base58采用的字符集合为“123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ”,从这不难看出,Base58是纯数字与字母组成而且去掉了容易引起视觉混淆的字符(0:数字零,O:大写O,I:大写i,l:小写L)。9个数字+49个字母=58个。由于没有特殊字符所以在采用鼠标双击或移动设备选择时可以自动识别全选。
Base58本身就是URLSafe。Base64的URFSafe模式虽然已经对URL支持的比较好,但UUID中还是包含“-或_”。
目前流行的比特币,采用的就是Base58Check编码,是在Base58基础上又增加了安全效验机制。
Base58采用的字符集合为“123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ”,从这不难看出,Base58是纯数字与字母组成而且去掉了容易引起视觉混淆的字符(0:数字零,O:大写O,I:大写i,l:小写L)。9个数字+49个字母=58个。由于没有特殊字符所以在采用鼠标双击或移动设备选择时可以自动识别全选。
三、Base58编码器程序
- package org.noahx.uuid.utils;
- import java.io.UnsupportedEncodingException;
- import java.math.BigInteger;
- /**
- * Created with IntelliJ IDEA.
- * User: noah
- * Date: 8/2/13
- * Time: 10:36 AM
- * To change this template use File | Settings | File Templates.
- */
- public class Base58 {
- public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
- private static final int[] INDEXES = new int[128];
- static {
- for (int i = 0; i < INDEXES.length; i++) {
- INDEXES[i] = -1;
- }
- for (int i = 0; i < ALPHABET.length; i++) {
- INDEXES[ALPHABET[i]] = i;
- }
- }
- /**
- * Encodes the given bytes in base58. No checksum is appended.
- */
- public static String encode(byte[] input) {
- if (input.length == 0) {
- return "";
- }
- input = copyOfRange(input, 0, input.length);
- // Count leading zeroes.
- int zeroCount = 0;
- while (zeroCount < input.length && input[zeroCount] == 0) {
- ++zeroCount;
- }
- // The actual encoding.
- byte[] temp = new byte[input.length * 2];
- int j = temp.length;
- int startAt = zeroCount;
- while (startAt < input.length) {
- byte mod = divmod58(input, startAt);
- if (input[startAt] == 0) {
- ++startAt;
- }
- temp[--j] = (byte) ALPHABET[mod];
- }
- // Strip extra '1' if there are some after decoding.
- while (j < temp.length && temp[j] == ALPHABET[0]) {
- ++j;
- }
- // Add as many leading '1' as there were leading zeros.
- while (--zeroCount >= 0) {
- temp[--j] = (byte) ALPHABET[0];
- }
- byte[] output = copyOfRange(temp, j, temp.length);
- try {
- return new String(output, "US-ASCII");
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException(e); // Cannot happen.
- }
- }
- public static byte[] decode(String input) throws IllegalArgumentException {
- if (input.length() == 0) {
- return new byte[0];
- }
- byte[] input58 = new byte[input.length()];
- // Transform the String to a base58 byte sequence
- for (int i = 0; i < input.length(); ++i) {
- char c = input.charAt(i);
- int digit58 = -1;
- if (c >= 0 && c < 128) {
- digit58 = INDEXES[c];
- }
- if (digit58 < 0) {
- throw new IllegalArgumentException("Illegal character " + c + " at " + i);
- }
- input58[i] = (byte) digit58;
- }
- // Count leading zeroes
- int zeroCount = 0;
- while (zeroCount < input58.length && input58[zeroCount] == 0) {
- ++zeroCount;
- }
- // The encoding
- byte[] temp = new byte[input.length()];
- int j = temp.length;
- int startAt = zeroCount;
- while (startAt < input58.length) {
- byte mod = divmod256(input58, startAt);
- if (input58[startAt] == 0) {
- ++startAt;
- }
- temp[--j] = mod;
- }
- // Do no add extra leading zeroes, move j to first non null byte.
- while (j < temp.length && temp[j] == 0) {
- ++j;
- }
- return copyOfRange(temp, j - zeroCount, temp.length);
- }
- public static BigInteger decodeToBigInteger(String input) throws IllegalArgumentException {
- return new BigInteger(1, decode(input));
- }
- //
- // number -> number / 58, returns number % 58
- //
- private static byte divmod58(byte[] number, int startAt) {
- int remainder = 0;
- for (int i = startAt; i < number.length; i++) {
- int digit256 = (int) number[i] & 0xFF;
- int temp = remainder * 256 + digit256;
- number[i] = (byte) (temp / 58);
- remainder = temp % 58;
- }
- return (byte) remainder;
- }
- //
- // number -> number / 256, returns number % 256
- //
- private static byte divmod256(byte[] number58, int startAt) {
- int remainder = 0;
- for (int i = startAt; i < number58.length; i++) {
- int digit58 = (int) number58[i] & 0xFF;
- int temp = remainder * 58 + digit58;
- number58[i] = (byte) (temp / 256);
- remainder = temp % 256;
- }
- return (byte) remainder;
- }
- private static byte[] copyOfRange(byte[] source, int from, int to) {
- byte[] range = new byte[to - from];
- System.arraycopy(source, from, range, 0, range.length);
- return range;
- }
- }
UUID生成程序
- package org.noahx.uuid.util;
- import org.apache.commons.codec.binary.Base64;
- import java.nio.ByteBuffer;
- import java.util.UUID;
- public abstract class UuidUtils {
- public static String uuid() {
- UUID uuid = UUID.randomUUID();
- return uuid.toString();
- }
- public static String base64Uuid() {
- UUID uuid = UUID.randomUUID();
- return base64Uuid(uuid);
- }
- protected static String base64Uuid(UUID uuid) {
- ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
- bb.putLong(uuid.getMostSignificantBits());
- bb.putLong(uuid.getLeastSignificantBits());
- return Base64.encodeBase64URLSafeString(bb.array());
- }
- public static String encodeBase64Uuid(String uuidString) {
- UUID uuid = UUID.fromString(uuidString);
- return base64Uuid(uuid);
- }
- public static String decodeBase64Uuid(String compressedUuid) {
- byte[] byUuid = Base64.decodeBase64(compressedUuid);
- ByteBuffer bb = ByteBuffer.wrap(byUuid);
- UUID uuid = new UUID(bb.getLong(), bb.getLong());
- return uuid.toString();
- }
- public static String base58Uuid() {
- UUID uuid = UUID.randomUUID();
- return base58Uuid(uuid);
- }
- protected static String base58Uuid(UUID uuid) {
- ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
- bb.putLong(uuid.getMostSignificantBits());
- bb.putLong(uuid.getLeastSignificantBits());
- return Base58.encode(bb.array());
- }
- public static String encodeBase58Uuid(String uuidString) {
- UUID uuid = UUID.fromString(uuidString);
- return base58Uuid(uuid);
- }
- public static String decodeBase58Uuid(String base58uuid) {
- byte[] byUuid = Base58.decode(base58uuid);
- ByteBuffer bb = ByteBuffer.wrap(byUuid);
- UUID uuid = new UUID(bb.getLong(), bb.getLong());
- return uuid.toString();
- }
- }
1、Base64的效果
M0ISICCxQi6sP-KIq3kFOw 11YozyYYTvKmuUXpRDvoJA KlZnS-MuT2m3d-the2chxg 8J3SC10AQzqZr6Im8V2xYA ES1UiFTGTHqn6ADU5YW0aw 1usa208oT1q7FitKbQHH5Q 53aDQZxKTGyqmKCzDnBwYQ SVVjViEoQXayWB9_JknKqQ fP6znJIAT1uGMN9HW5o8cw YR-2-kKmSOubhGr2LpFCgQ
可以看到有-与_字符。大家可以双击上面包含-的UUID,得到只选中部分的效果。
2、Base58的效果
MqJqC2rtZLkuHys6ed2Eai QrS5w2t5etpRY3zTR1BAEJ
Qd6wcFFVz2ZSQb3voGGj8P
75bJdWMcEh6NhT51D5Uyju
2L7kTgsktxMBKLkfAo2iWC
UX2Twhbt1kstRziqc7iwCR
9tZNKCeR93taLHU6PVy8hN
HSn6JMibca4nG9URWokpwg
8eL4SNz2a4puEW8fD4njsG
GThFxPsdVUoZMfmKoEHwQX
没有混淆字符一眼看上去更清晰更可读。