|
20 | 20 | import java.io.ByteArrayOutputStream;
|
21 | 21 | import java.io.IOException;
|
22 | 22 | import java.math.BigInteger;
|
| 23 | +import java.util.Arrays; |
23 | 24 | import java.util.HashMap;
|
24 | 25 | import java.util.Map;
|
25 | 26 | import java.util.Objects;
|
26 | 27 |
|
27 | 28 | public class Bech32 {
|
28 |
| - |
| 29 | + public static final String SEPARATOR = ":"; |
| 30 | + public static final String MAIN_NET_PREFIX = "bitcoincash"; |
29 | 31 | public static final String CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
30 | 32 |
|
31 | 33 | private static final BigInteger[] POLYMOD_GENERATORS = new BigInteger[] {
|
@@ -123,6 +125,59 @@ public static String encodeHashToBech32Address(String humanPart, int version, by
|
123 | 125 | return encodeToCharset(payloadData);
|
124 | 126 | }
|
125 | 127 |
|
| 128 | + |
| 129 | + public static byte[] decodeCashAddress(String bitcoinCashAddress) { |
| 130 | + if (!isValidCashAddress(bitcoinCashAddress)) { |
| 131 | + throw new RuntimeException("Address wasn't valid: " + bitcoinCashAddress); |
| 132 | + } |
| 133 | + |
| 134 | + String[] addressParts = bitcoinCashAddress.split(SEPARATOR); |
| 135 | + int datapos = 0; |
| 136 | + if (addressParts.length == 2) { |
| 137 | + String prefix = addressParts[0]; |
| 138 | + datapos++; |
| 139 | + } |
| 140 | + |
| 141 | + byte[] addressData = Bech32.decodeFromCharset(addressParts[datapos]); |
| 142 | + addressData = Arrays.copyOfRange(addressData, 0, addressData.length - 8); |
| 143 | + addressData = convertBits(addressData, 5, 8, true); |
| 144 | + byte versionByte = addressData[0]; |
| 145 | + return Arrays.copyOfRange(addressData, 1, addressData.length); |
| 146 | + } |
| 147 | + |
| 148 | + public static boolean isValidCashAddress(String bitcoinCashAddress) { |
| 149 | + try { |
| 150 | + if (bitcoinCashAddress == null || bitcoinCashAddress.length() == 0) { |
| 151 | + return false; |
| 152 | + } |
| 153 | + String prefix; |
| 154 | + if (bitcoinCashAddress.contains(SEPARATOR)) { |
| 155 | + String[] split = bitcoinCashAddress.split(SEPARATOR); |
| 156 | + if (split.length != 2) { |
| 157 | + return false; |
| 158 | + } |
| 159 | + prefix = split[0]; |
| 160 | + bitcoinCashAddress = split[1]; |
| 161 | + } else { |
| 162 | + prefix = MAIN_NET_PREFIX; |
| 163 | + } |
| 164 | + if (!isSingleCase(bitcoinCashAddress)) |
| 165 | + return false; |
| 166 | + bitcoinCashAddress = bitcoinCashAddress.toLowerCase(); |
| 167 | + byte[] checksumData = concatenateByteArrays( |
| 168 | + concatenateByteArrays(encodePrefixToUInt5(prefix), new byte[]{0x00}), |
| 169 | + Bech32.decodeFromCharset(bitcoinCashAddress)); |
| 170 | + byte[] calculateChecksumBytesPolymod = calculateChecksumBytesPolymod(checksumData); |
| 171 | + return new BigInteger(calculateChecksumBytesPolymod).compareTo(BigInteger.ZERO) == 0; |
| 172 | + } catch (RuntimeException re) { |
| 173 | + return false; |
| 174 | + } |
| 175 | + } |
| 176 | + |
| 177 | + private static boolean isSingleCase(String s) { |
| 178 | + return s.equals(s.toLowerCase()) || s.equals(s.toUpperCase()); |
| 179 | + } |
| 180 | + |
126 | 181 | private static byte[] convertBits(byte[] bytes8Bits, int from, int to, boolean strictMode) {
|
127 | 182 | int length = (int) (strictMode ? Math.floor((double) bytes8Bits.length * from / to)
|
128 | 183 | : Math.ceil((double) bytes8Bits.length * from / to));
|
|
0 commit comments