mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-11-30 16:36:36 -05:00
support Cardano (ADA)
This commit is contained in:
parent
7298a6373a
commit
0dc67f06c4
7 changed files with 182 additions and 0 deletions
106
assets/src/main/java/haveno/asset/CardanoAddressValidator.java
Normal file
106
assets/src/main/java/haveno/asset/CardanoAddressValidator.java
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* This file is part of Haveno.
|
||||
*
|
||||
* Haveno 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.
|
||||
*
|
||||
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package haveno.asset;
|
||||
|
||||
/**
|
||||
* Validates a Shelley-era mainnet Cardano address.
|
||||
*/
|
||||
public class CardanoAddressValidator extends RegexAddressValidator {
|
||||
|
||||
private static final String CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
||||
private static final int BECH32_CONST = 1;
|
||||
private static final int BECH32M_CONST = 0x2bc830a3;
|
||||
private static final int MAX_LEN = 104; // bech32 / bech32m max for Cardano
|
||||
|
||||
public CardanoAddressValidator() {
|
||||
super("^addr1[0-9a-z]{20,98}$");
|
||||
}
|
||||
|
||||
public CardanoAddressValidator(String errorMessageI18nKey) {
|
||||
super("^addr1[0-9a-z]{20,98}$", errorMessageI18nKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressValidationResult validate(String address) {
|
||||
if (!isValidShelleyMainnet(address)) {
|
||||
return AddressValidationResult.invalidStructure();
|
||||
}
|
||||
return super.validate(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given address is a valid Shelley-era mainnet Cardano address.
|
||||
*
|
||||
* This code is AI-generated and has been tested with a variety of addresses.
|
||||
*
|
||||
* @param addr the address to validate
|
||||
* @return true if the address is valid, false otherwise
|
||||
*/
|
||||
private static boolean isValidShelleyMainnet(String addr) {
|
||||
if (addr == null) return false;
|
||||
String lower = addr.toLowerCase();
|
||||
|
||||
// must start addr1 and not be absurdly long
|
||||
if (!lower.startsWith("addr1") || lower.length() > MAX_LEN) return false;
|
||||
|
||||
int sep = lower.lastIndexOf('1');
|
||||
if (sep < 1) return false; // no separator or empty HRP
|
||||
String hrp = lower.substring(0, sep);
|
||||
if (!"addr".equals(hrp)) return false; // mainnet only
|
||||
|
||||
String dataPart = lower.substring(sep + 1);
|
||||
if (dataPart.length() < 6) return false; // checksum is 6 chars minimum
|
||||
|
||||
int[] data = new int[dataPart.length()];
|
||||
for (int i = 0; i < dataPart.length(); i++) {
|
||||
int v = CHARSET.indexOf(dataPart.charAt(i));
|
||||
if (v == -1) return false;
|
||||
data[i] = v;
|
||||
}
|
||||
|
||||
int[] hrpExp = hrpExpand(hrp);
|
||||
int[] combined = new int[hrpExp.length + data.length];
|
||||
System.arraycopy(hrpExp, 0, combined, 0, hrpExp.length);
|
||||
System.arraycopy(data, 0, combined, hrpExp.length, data.length);
|
||||
|
||||
int chk = polymod(combined);
|
||||
return chk == BECH32_CONST || chk == BECH32M_CONST; // accept either legacy Bech32 (1) or Bech32m (0x2bc830a3)
|
||||
}
|
||||
|
||||
private static int[] hrpExpand(String hrp) {
|
||||
int[] ret = new int[hrp.length() * 2 + 1];
|
||||
int idx = 0;
|
||||
for (char c : hrp.toCharArray()) ret[idx++] = c >> 5;
|
||||
ret[idx++] = 0;
|
||||
for (char c : hrp.toCharArray()) ret[idx++] = c & 31;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static int polymod(int[] values) {
|
||||
int chk = 1;
|
||||
int[] GEN = {0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3};
|
||||
for (int v : values) {
|
||||
int b = chk >>> 25;
|
||||
chk = ((chk & 0x1ffffff) << 5) ^ v;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (((b >>> i) & 1) != 0) chk ^= GEN[i];
|
||||
}
|
||||
}
|
||||
return chk;
|
||||
}
|
||||
}
|
||||
28
assets/src/main/java/haveno/asset/coins/Cardano.java
Normal file
28
assets/src/main/java/haveno/asset/coins/Cardano.java
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* This file is part of Haveno.
|
||||
*
|
||||
* Haveno 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.
|
||||
*
|
||||
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package haveno.asset.coins;
|
||||
|
||||
import haveno.asset.CardanoAddressValidator;
|
||||
import haveno.asset.Coin;
|
||||
|
||||
public class Cardano extends Coin {
|
||||
|
||||
public Cardano() {
|
||||
super("Cardano", "ADA", new CardanoAddressValidator());
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
# See https://haveno.exchange/list-asset for complete instructions.
|
||||
haveno.asset.coins.Bitcoin$Mainnet
|
||||
haveno.asset.coins.BitcoinCash
|
||||
haveno.asset.coins.Cardano
|
||||
haveno.asset.coins.Ether
|
||||
haveno.asset.coins.Litecoin
|
||||
haveno.asset.coins.Monero
|
||||
|
|
|
|||
42
assets/src/test/java/haveno/asset/coins/CardanoTest.java
Normal file
42
assets/src/test/java/haveno/asset/coins/CardanoTest.java
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* This file is part of Haveno.
|
||||
*
|
||||
* Haveno 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.
|
||||
*
|
||||
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package haveno.asset.coins;
|
||||
|
||||
import haveno.asset.AbstractAssetTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class CardanoTest extends AbstractAssetTest {
|
||||
|
||||
public CardanoTest() {
|
||||
super(new Cardano());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidAddresses() {
|
||||
assertValidAddress("addr1vpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5eg0yu80w");
|
||||
assertValidAddress("addr1q8gg2r3vf9zggn48g7m8vx62rwf6warcs4k7ej8mdzmqmesj30jz7psduyk6n4n2qrud2xlv9fgj53n6ds3t8cs4fvzs05yzmz");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidAddresses() {
|
||||
assertInvalidAddress("addr1Q9r4y0gx0m4hd5s2u3pnj7ufc4s0ghqzj7u6czxyfks5cty5k5yq5qp6gmw5v7uqvx2g4kw6zjhx4l6fnhcey9lg9nys6v2mpu");
|
||||
assertInvalidAddress("addr2q9r4y0gx0m4hd5s2u3pnj7ufc4s0ghqzj7u6czxyfks5cty5k5yq5qp6gmw5v7uqvx2g4kw6zjhx4l6fnhcey9lg9nys6v2mpu");
|
||||
assertInvalidAddress("addr2vpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5eg0yu80w");
|
||||
assertInvalidAddress("Ae2tdPwUPEYxkYw5GrFyqb4Z9TzXo8f1WnWpPZP1sXrEn1pz2VU3CkJ8aTQ");
|
||||
}
|
||||
}
|
||||
|
|
@ -201,6 +201,7 @@ public class CurrencyUtil {
|
|||
result.add(new CryptoCurrency("ETH", "Ether"));
|
||||
result.add(new CryptoCurrency("LTC", "Litecoin"));
|
||||
result.add(new CryptoCurrency("XRP", "Ripple"));
|
||||
result.add(new CryptoCurrency("ADA", "Cardano"));
|
||||
result.add(new CryptoCurrency("DAI-ERC20", "Dai Stablecoin"));
|
||||
result.add(new CryptoCurrency("USDT-ERC20", "Tether USD"));
|
||||
result.add(new CryptoCurrency("USDT-TRC20", "Tether USD"));
|
||||
|
|
|
|||
|
|
@ -357,6 +357,10 @@
|
|||
-fx-image: url("../../images/xrp_logo.png");
|
||||
}
|
||||
|
||||
#image-ada-logo {
|
||||
-fx-image: url("../../images/ada_logo.png");
|
||||
}
|
||||
|
||||
#image-dark-mode-toggle {
|
||||
-fx-image: url("../../images/dark_mode_toggle.png");
|
||||
}
|
||||
|
|
|
|||
BIN
desktop/src/main/resources/images/ada_logo.png
Normal file
BIN
desktop/src/main/resources/images/ada_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Loading…
Add table
Add a link
Reference in a new issue