mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 09:52:23 +01:00
No functional change, pure refactoring
Move to convertToRawHex CryptoNoteUtils as well as the classes inside the validator.
This commit is contained in:
parent
5143b1ed59
commit
77c203e87e
@ -17,16 +17,6 @@
|
|||||||
|
|
||||||
package bisq.asset;
|
package bisq.asset;
|
||||||
|
|
||||||
import org.bitcoinj.core.Utils;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
|
|
||||||
import java.math.BigInteger;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link AddressValidator} for Base58-encoded Cryptonote addresses.
|
* {@link AddressValidator} for Base58-encoded Cryptonote addresses.
|
||||||
*
|
*
|
||||||
@ -49,7 +39,7 @@ public class CryptoNoteAddressValidator implements AddressValidator {
|
|||||||
@Override
|
@Override
|
||||||
public AddressValidationResult validate(String address) {
|
public AddressValidationResult validate(String address) {
|
||||||
try {
|
try {
|
||||||
long prefix = MoneroBase58.decodeAddress(address, this.validateChecksum);
|
long prefix = CryptoNoteUtils.MoneroBase58.decodeAddress(address, this.validateChecksum);
|
||||||
for (long validPrefix : this.validPrefixes) {
|
for (long validPrefix : this.validPrefixes) {
|
||||||
if (prefix == validPrefix) {
|
if (prefix == validPrefix) {
|
||||||
return AddressValidationResult.validAddress();
|
return AddressValidationResult.validAddress();
|
||||||
@ -60,221 +50,4 @@ public class CryptoNoteAddressValidator implements AddressValidator {
|
|||||||
return AddressValidationResult.invalidStructure();
|
return AddressValidationResult.invalidStructure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String convertToRawHex(String address) {
|
|
||||||
try {
|
|
||||||
byte[] decoded = MoneroBase58.decode(address);
|
|
||||||
// omit the type (1st byte) and checksum (last 4 byte)
|
|
||||||
byte[] slice = Arrays.copyOfRange(decoded, 1, decoded.length - 4);
|
|
||||||
return Utils.HEX.encode(slice);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Keccak {
|
|
||||||
|
|
||||||
private static final int BLOCK_SIZE = 136;
|
|
||||||
private static final int LONGS_PER_BLOCK = BLOCK_SIZE / 8;
|
|
||||||
private static final int KECCAK_ROUNDS = 24;
|
|
||||||
private static final long[] KECCAKF_RNDC = {
|
|
||||||
0x0000000000000001L, 0x0000000000008082L, 0x800000000000808aL,
|
|
||||||
0x8000000080008000L, 0x000000000000808bL, 0x0000000080000001L,
|
|
||||||
0x8000000080008081L, 0x8000000000008009L, 0x000000000000008aL,
|
|
||||||
0x0000000000000088L, 0x0000000080008009L, 0x000000008000000aL,
|
|
||||||
0x000000008000808bL, 0x800000000000008bL, 0x8000000000008089L,
|
|
||||||
0x8000000000008003L, 0x8000000000008002L, 0x8000000000000080L,
|
|
||||||
0x000000000000800aL, 0x800000008000000aL, 0x8000000080008081L,
|
|
||||||
0x8000000000008080L, 0x0000000080000001L, 0x8000000080008008L
|
|
||||||
};
|
|
||||||
private static final int[] KECCAKF_ROTC = {
|
|
||||||
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
|
|
||||||
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44
|
|
||||||
};
|
|
||||||
private static final int[] KECCAKF_PILN = {
|
|
||||||
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
|
|
||||||
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1
|
|
||||||
};
|
|
||||||
|
|
||||||
private static long rotateLeft(long value, int shift) {
|
|
||||||
return (value << shift) | (value >>> (64 - shift));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void keccakf(long[] st, int rounds) {
|
|
||||||
long[] bc = new long[5];
|
|
||||||
|
|
||||||
for (int round = 0; round < rounds; ++round) {
|
|
||||||
for (int i = 0; i < 5; ++i) {
|
|
||||||
bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 5; i++) {
|
|
||||||
long t = bc[(i + 4) % 5] ^ rotateLeft(bc[(i + 1) % 5], 1);
|
|
||||||
for (int j = 0; j < 25; j += 5) {
|
|
||||||
st[j + i] ^= t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
long t = st[1];
|
|
||||||
for (int i = 0; i < 24; ++i) {
|
|
||||||
int j = KECCAKF_PILN[i];
|
|
||||||
bc[0] = st[j];
|
|
||||||
st[j] = rotateLeft(t, KECCAKF_ROTC[i]);
|
|
||||||
t = bc[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int j = 0; j < 25; j += 5) {
|
|
||||||
for (int i = 0; i < 5; i++) {
|
|
||||||
bc[i] = st[j + i];
|
|
||||||
}
|
|
||||||
for (int i = 0; i < 5; i++) {
|
|
||||||
st[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
st[0] ^= KECCAKF_RNDC[round];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ByteBuffer keccak1600(ByteBuffer input) {
|
|
||||||
input.order(ByteOrder.LITTLE_ENDIAN);
|
|
||||||
|
|
||||||
int fullBlocks = input.remaining() / BLOCK_SIZE;
|
|
||||||
long[] st = new long[25];
|
|
||||||
for (int block = 0; block < fullBlocks; ++block) {
|
|
||||||
for (int index = 0; index < LONGS_PER_BLOCK; ++index) {
|
|
||||||
st[index] ^= input.getLong();
|
|
||||||
}
|
|
||||||
keccakf(st, KECCAK_ROUNDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteBuffer lastBlock = ByteBuffer.allocate(144).order(ByteOrder.LITTLE_ENDIAN);
|
|
||||||
lastBlock.put(input);
|
|
||||||
lastBlock.put((byte)1);
|
|
||||||
int paddingOffset = BLOCK_SIZE - 1;
|
|
||||||
lastBlock.put(paddingOffset, (byte)(lastBlock.get(paddingOffset) | 0x80));
|
|
||||||
lastBlock.rewind();
|
|
||||||
|
|
||||||
for (int index = 0; index < LONGS_PER_BLOCK; ++index) {
|
|
||||||
st[index] ^= lastBlock.getLong();
|
|
||||||
}
|
|
||||||
|
|
||||||
keccakf(st, KECCAK_ROUNDS);
|
|
||||||
|
|
||||||
ByteBuffer result = ByteBuffer.allocate(32);
|
|
||||||
result.slice().order(ByteOrder.LITTLE_ENDIAN).asLongBuffer().put(st, 0, 4);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MoneroBase58 {
|
|
||||||
|
|
||||||
private static final String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
||||||
private static final BigInteger ALPHABET_SIZE = BigInteger.valueOf(ALPHABET.length());
|
|
||||||
private static final int FULL_DECODED_BLOCK_SIZE = 8;
|
|
||||||
private static final int FULL_ENCODED_BLOCK_SIZE = 11;
|
|
||||||
private static final BigInteger UINT64_MAX = new BigInteger("18446744073709551615");
|
|
||||||
private static final Map<Integer, Integer> DECODED_CHUNK_LENGTH = Map.of( 2, 1,
|
|
||||||
3, 2,
|
|
||||||
5, 3,
|
|
||||||
6, 4,
|
|
||||||
7, 5,
|
|
||||||
9, 6,
|
|
||||||
10, 7,
|
|
||||||
11, 8);
|
|
||||||
|
|
||||||
private static void decodeChunk(String input,
|
|
||||||
int inputOffset,
|
|
||||||
int inputLength,
|
|
||||||
byte[] decoded,
|
|
||||||
int decodedOffset,
|
|
||||||
int decodedLength) throws Exception {
|
|
||||||
|
|
||||||
BigInteger result = BigInteger.ZERO;
|
|
||||||
|
|
||||||
BigInteger order = BigInteger.ONE;
|
|
||||||
for (int index = inputOffset + inputLength; index != inputOffset; order = order.multiply(ALPHABET_SIZE)) {
|
|
||||||
char character = input.charAt(--index);
|
|
||||||
int digit = ALPHABET.indexOf(character);
|
|
||||||
if (digit == -1) {
|
|
||||||
throw new Exception("invalid character " + character);
|
|
||||||
}
|
|
||||||
result = result.add(order.multiply(BigInteger.valueOf(digit)));
|
|
||||||
if (result.compareTo(UINT64_MAX) > 0) {
|
|
||||||
throw new Exception("64-bit unsigned integer overflow " + result.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BigInteger maxCapacity = BigInteger.ONE.shiftLeft(8 * decodedLength);
|
|
||||||
if (result.compareTo(maxCapacity) >= 0) {
|
|
||||||
throw new Exception("capacity overflow " + result.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int index = decodedOffset + decodedLength; index != decodedOffset; result = result.shiftRight(8)) {
|
|
||||||
decoded[--index] = result.byteValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] decode(String input) throws Exception {
|
|
||||||
if (input.length() == 0) {
|
|
||||||
return new byte[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
int chunks = input.length() / FULL_ENCODED_BLOCK_SIZE;
|
|
||||||
int lastEncodedSize = input.length() % FULL_ENCODED_BLOCK_SIZE;
|
|
||||||
int lastChunkSize = lastEncodedSize > 0 ? DECODED_CHUNK_LENGTH.get(lastEncodedSize) : 0;
|
|
||||||
|
|
||||||
byte[] result = new byte[chunks * FULL_DECODED_BLOCK_SIZE + lastChunkSize];
|
|
||||||
int inputOffset = 0;
|
|
||||||
int resultOffset = 0;
|
|
||||||
for (int chunk = 0; chunk < chunks; ++chunk,
|
|
||||||
inputOffset += FULL_ENCODED_BLOCK_SIZE,
|
|
||||||
resultOffset += FULL_DECODED_BLOCK_SIZE) {
|
|
||||||
decodeChunk(input, inputOffset, FULL_ENCODED_BLOCK_SIZE, result, resultOffset, FULL_DECODED_BLOCK_SIZE);
|
|
||||||
}
|
|
||||||
if (lastChunkSize > 0) {
|
|
||||||
decodeChunk(input, inputOffset, lastEncodedSize, result, resultOffset, lastChunkSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static long readVarInt(ByteBuffer buffer) {
|
|
||||||
long result = 0;
|
|
||||||
for (int shift = 0; ; shift += 7) {
|
|
||||||
byte current = buffer.get();
|
|
||||||
result += (current & 0x7fL) << shift;
|
|
||||||
if ((current & 0x80L) == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long decodeAddress(String address, boolean validateChecksum) throws Exception {
|
|
||||||
byte[] decoded = decode(address);
|
|
||||||
|
|
||||||
int checksumSize = 4;
|
|
||||||
if (decoded.length < checksumSize) {
|
|
||||||
throw new Exception("invalid length");
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteBuffer decodedAddress = ByteBuffer.wrap(decoded, 0, decoded.length - checksumSize);
|
|
||||||
|
|
||||||
long prefix = readVarInt(decodedAddress.slice());
|
|
||||||
if (!validateChecksum) {
|
|
||||||
return prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteBuffer fastHash = Keccak.keccak1600(decodedAddress.slice());
|
|
||||||
int checksum = fastHash.getInt();
|
|
||||||
int expected = ByteBuffer.wrap(decoded, decoded.length - checksumSize, checksumSize).getInt();
|
|
||||||
if (checksum != expected) {
|
|
||||||
throw new Exception(String.format("invalid checksum %08X, expected %08X", checksum, expected));
|
|
||||||
}
|
|
||||||
|
|
||||||
return prefix;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
247
assets/src/main/java/bisq/asset/CryptoNoteUtils.java
Normal file
247
assets/src/main/java/bisq/asset/CryptoNoteUtils.java
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of Bisq.
|
||||||
|
*
|
||||||
|
* Bisq 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.
|
||||||
|
*
|
||||||
|
* Bisq 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 Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.asset;
|
||||||
|
|
||||||
|
import org.bitcoinj.core.Utils;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class CryptoNoteUtils {
|
||||||
|
public static String convertToRawHex(String address) {
|
||||||
|
try {
|
||||||
|
byte[] decoded = MoneroBase58.decode(address);
|
||||||
|
// omit the type (1st byte) and checksum (last 4 byte)
|
||||||
|
byte[] slice = Arrays.copyOfRange(decoded, 1, decoded.length - 4);
|
||||||
|
return Utils.HEX.encode(slice);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Keccak {
|
||||||
|
private static final int BLOCK_SIZE = 136;
|
||||||
|
private static final int LONGS_PER_BLOCK = BLOCK_SIZE / 8;
|
||||||
|
private static final int KECCAK_ROUNDS = 24;
|
||||||
|
private static final long[] KECCAKF_RNDC = {
|
||||||
|
0x0000000000000001L, 0x0000000000008082L, 0x800000000000808aL,
|
||||||
|
0x8000000080008000L, 0x000000000000808bL, 0x0000000080000001L,
|
||||||
|
0x8000000080008081L, 0x8000000000008009L, 0x000000000000008aL,
|
||||||
|
0x0000000000000088L, 0x0000000080008009L, 0x000000008000000aL,
|
||||||
|
0x000000008000808bL, 0x800000000000008bL, 0x8000000000008089L,
|
||||||
|
0x8000000000008003L, 0x8000000000008002L, 0x8000000000000080L,
|
||||||
|
0x000000000000800aL, 0x800000008000000aL, 0x8000000080008081L,
|
||||||
|
0x8000000000008080L, 0x0000000080000001L, 0x8000000080008008L
|
||||||
|
};
|
||||||
|
private static final int[] KECCAKF_ROTC = {
|
||||||
|
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14,
|
||||||
|
27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44
|
||||||
|
};
|
||||||
|
private static final int[] KECCAKF_PILN = {
|
||||||
|
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4,
|
||||||
|
15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1
|
||||||
|
};
|
||||||
|
|
||||||
|
private static long rotateLeft(long value, int shift) {
|
||||||
|
return (value << shift) | (value >>> (64 - shift));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void keccakf(long[] st, int rounds) {
|
||||||
|
long[] bc = new long[5];
|
||||||
|
|
||||||
|
for (int round = 0; round < rounds; ++round) {
|
||||||
|
for (int i = 0; i < 5; ++i) {
|
||||||
|
bc[i] = st[i] ^ st[i + 5] ^ st[i + 10] ^ st[i + 15] ^ st[i + 20];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
long t = bc[(i + 4) % 5] ^ rotateLeft(bc[(i + 1) % 5], 1);
|
||||||
|
for (int j = 0; j < 25; j += 5) {
|
||||||
|
st[j + i] ^= t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long t = st[1];
|
||||||
|
for (int i = 0; i < 24; ++i) {
|
||||||
|
int j = KECCAKF_PILN[i];
|
||||||
|
bc[0] = st[j];
|
||||||
|
st[j] = rotateLeft(t, KECCAKF_ROTC[i]);
|
||||||
|
t = bc[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < 25; j += 5) {
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
bc[i] = st[j + i];
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
st[j + i] ^= (~bc[(i + 1) % 5]) & bc[(i + 2) % 5];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
st[0] ^= KECCAKF_RNDC[round];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ByteBuffer keccak1600(ByteBuffer input) {
|
||||||
|
input.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
|
||||||
|
int fullBlocks = input.remaining() / BLOCK_SIZE;
|
||||||
|
long[] st = new long[25];
|
||||||
|
for (int block = 0; block < fullBlocks; ++block) {
|
||||||
|
for (int index = 0; index < LONGS_PER_BLOCK; ++index) {
|
||||||
|
st[index] ^= input.getLong();
|
||||||
|
}
|
||||||
|
keccakf(st, KECCAK_ROUNDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer lastBlock = ByteBuffer.allocate(144).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
lastBlock.put(input);
|
||||||
|
lastBlock.put((byte) 1);
|
||||||
|
int paddingOffset = BLOCK_SIZE - 1;
|
||||||
|
lastBlock.put(paddingOffset, (byte) (lastBlock.get(paddingOffset) | 0x80));
|
||||||
|
lastBlock.rewind();
|
||||||
|
|
||||||
|
for (int index = 0; index < LONGS_PER_BLOCK; ++index) {
|
||||||
|
st[index] ^= lastBlock.getLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
keccakf(st, KECCAK_ROUNDS);
|
||||||
|
|
||||||
|
ByteBuffer result = ByteBuffer.allocate(32);
|
||||||
|
result.slice().order(ByteOrder.LITTLE_ENDIAN).asLongBuffer().put(st, 0, 4);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class MoneroBase58 {
|
||||||
|
|
||||||
|
private static final String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||||
|
private static final BigInteger ALPHABET_SIZE = BigInteger.valueOf(ALPHABET.length());
|
||||||
|
private static final int FULL_DECODED_BLOCK_SIZE = 8;
|
||||||
|
private static final int FULL_ENCODED_BLOCK_SIZE = 11;
|
||||||
|
private static final BigInteger UINT64_MAX = new BigInteger("18446744073709551615");
|
||||||
|
private static final Map<Integer, Integer> DECODED_CHUNK_LENGTH = Map.of(2, 1,
|
||||||
|
3, 2,
|
||||||
|
5, 3,
|
||||||
|
6, 4,
|
||||||
|
7, 5,
|
||||||
|
9, 6,
|
||||||
|
10, 7,
|
||||||
|
11, 8);
|
||||||
|
|
||||||
|
private static void decodeChunk(String input,
|
||||||
|
int inputOffset,
|
||||||
|
int inputLength,
|
||||||
|
byte[] decoded,
|
||||||
|
int decodedOffset,
|
||||||
|
int decodedLength) throws Exception {
|
||||||
|
|
||||||
|
BigInteger result = BigInteger.ZERO;
|
||||||
|
|
||||||
|
BigInteger order = BigInteger.ONE;
|
||||||
|
for (int index = inputOffset + inputLength; index != inputOffset; order = order.multiply(ALPHABET_SIZE)) {
|
||||||
|
char character = input.charAt(--index);
|
||||||
|
int digit = ALPHABET.indexOf(character);
|
||||||
|
if (digit == -1) {
|
||||||
|
throw new Exception("invalid character " + character);
|
||||||
|
}
|
||||||
|
result = result.add(order.multiply(BigInteger.valueOf(digit)));
|
||||||
|
if (result.compareTo(UINT64_MAX) > 0) {
|
||||||
|
throw new Exception("64-bit unsigned integer overflow " + result.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BigInteger maxCapacity = BigInteger.ONE.shiftLeft(8 * decodedLength);
|
||||||
|
if (result.compareTo(maxCapacity) >= 0) {
|
||||||
|
throw new Exception("capacity overflow " + result.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int index = decodedOffset + decodedLength; index != decodedOffset; result = result.shiftRight(8)) {
|
||||||
|
decoded[--index] = result.byteValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] decode(String input) throws Exception {
|
||||||
|
if (input.length() == 0) {
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
int chunks = input.length() / FULL_ENCODED_BLOCK_SIZE;
|
||||||
|
int lastEncodedSize = input.length() % FULL_ENCODED_BLOCK_SIZE;
|
||||||
|
int lastChunkSize = lastEncodedSize > 0 ? DECODED_CHUNK_LENGTH.get(lastEncodedSize) : 0;
|
||||||
|
|
||||||
|
byte[] result = new byte[chunks * FULL_DECODED_BLOCK_SIZE + lastChunkSize];
|
||||||
|
int inputOffset = 0;
|
||||||
|
int resultOffset = 0;
|
||||||
|
for (int chunk = 0; chunk < chunks; ++chunk,
|
||||||
|
inputOffset += FULL_ENCODED_BLOCK_SIZE,
|
||||||
|
resultOffset += FULL_DECODED_BLOCK_SIZE) {
|
||||||
|
decodeChunk(input, inputOffset, FULL_ENCODED_BLOCK_SIZE, result, resultOffset, FULL_DECODED_BLOCK_SIZE);
|
||||||
|
}
|
||||||
|
if (lastChunkSize > 0) {
|
||||||
|
decodeChunk(input, inputOffset, lastEncodedSize, result, resultOffset, lastChunkSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long readVarInt(ByteBuffer buffer) {
|
||||||
|
long result = 0;
|
||||||
|
for (int shift = 0; ; shift += 7) {
|
||||||
|
byte current = buffer.get();
|
||||||
|
result += (current & 0x7fL) << shift;
|
||||||
|
if ((current & 0x80L) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long decodeAddress(String address, boolean validateChecksum) throws Exception {
|
||||||
|
byte[] decoded = decode(address);
|
||||||
|
|
||||||
|
int checksumSize = 4;
|
||||||
|
if (decoded.length < checksumSize) {
|
||||||
|
throw new Exception("invalid length");
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer decodedAddress = ByteBuffer.wrap(decoded, 0, decoded.length - checksumSize);
|
||||||
|
|
||||||
|
long prefix = readVarInt(decodedAddress.slice());
|
||||||
|
if (!validateChecksum) {
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer fastHash = Keccak.keccak1600(decodedAddress.slice());
|
||||||
|
int checksum = fastHash.getInt();
|
||||||
|
int expected = ByteBuffer.wrap(decoded, decoded.length - checksumSize, checksumSize).getInt();
|
||||||
|
if (checksum != expected) {
|
||||||
|
throw new Exception(String.format("invalid checksum %08X, expected %08X", checksum, expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,6 +32,7 @@ import javax.annotation.Nullable;
|
|||||||
public abstract class AutoConfirmResult {
|
public abstract class AutoConfirmResult {
|
||||||
|
|
||||||
public static AutoConfirmResult fromCurrencyCode(String currencyCode) {
|
public static AutoConfirmResult fromCurrencyCode(String currencyCode) {
|
||||||
|
//noinspection SwitchStatementWithTooFewBranches
|
||||||
switch (currencyCode) {
|
switch (currencyCode) {
|
||||||
case "XMR":
|
case "XMR":
|
||||||
return new XmrAutoConfirmResult();
|
return new XmrAutoConfirmResult();
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
package bisq.core.trade.autoconf.xmr;
|
package bisq.core.trade.autoconf.xmr;
|
||||||
|
|
||||||
import bisq.asset.CryptoNoteAddressValidator;
|
import bisq.asset.CryptoNoteUtils;
|
||||||
|
|
||||||
import bisq.common.app.DevEnv;
|
import bisq.common.app.DevEnv;
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ public class XmrProofInfo {
|
|||||||
if (jsonAddress == null) {
|
if (jsonAddress == null) {
|
||||||
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.API_INVALID, "Missing address field");
|
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.API_INVALID, "Missing address field");
|
||||||
} else {
|
} else {
|
||||||
String expectedAddressHex = CryptoNoteAddressValidator.convertToRawHex(this.recipientAddress);
|
String expectedAddressHex = CryptoNoteUtils.convertToRawHex(this.recipientAddress);
|
||||||
if (!jsonAddress.getAsString().equalsIgnoreCase(expectedAddressHex)) {
|
if (!jsonAddress.getAsString().equalsIgnoreCase(expectedAddressHex)) {
|
||||||
log.warn("address {}, expected: {}", jsonAddress.getAsString(), expectedAddressHex);
|
log.warn("address {}, expected: {}", jsonAddress.getAsString(), expectedAddressHex);
|
||||||
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.ADDRESS_INVALID, null);
|
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.ADDRESS_INVALID, null);
|
||||||
|
Loading…
Reference in New Issue
Block a user