Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Grin #2217

Merged
merged 2 commits into from Jan 8, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
106 changes: 106 additions & 0 deletions assets/src/main/java/bisq/asset/GrinAddressValidator.java
@@ -0,0 +1,106 @@
/*
* 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;

/**
* The supported "address" (better wallet URL) format is IP:port or the grinbox format.
*
* Here is the information from a conversation with the Grinbox developer regarding the Grinbox address format.
*
A Grinbox address is of the format: grinbox://<key>@domain.com:port where everything besides <key> is optional.
If no domain is specified, the default relay grinbox.io will be used.

The <key> is a base58check encoded value (like in Bitcoin). For Grin mainnet, the first 2 bytes will be [1, 11] and
the following 33 bytes should be a valid secp256k1 compressed public key.

Some examples of valid addresses are:

gVvRNiuopubvxPrs1BzJdQjVdFAxmkLzMqiVJzUZ7ubznhdtNTGB
gVvUcSafSTD3YTSqgNf9ojEYWkz3zMZNfsjdpdb9en5mxc6gmja6
gVvk7rLBg3r3qoWYL3VsREnBbooT7nynxx5HtDvUWCJUaNCnddvY
grinbox://gVtWzX5NTLCBkyNV19QVdnLXue13heAVRD36sfkGD6xpqy7k7e4a
gVw9TWimGFXRjoDXWhWxeNQbu84ZpLkvnenkKvA5aJeDo31eM5tC@somerelay.com
grinbox://gVwjSsYW5vvHpK4AunJ5piKhhQTV6V3Jb818Uqs6PdC3SsB36AsA@somerelay.com:1220

Some examples of invalid addresses are:

gVuBJDKcWkhueMfBLAbFwV4ax55YXPeinWXdRME1Zi3eiC6sFNye (invalid checksum)
geWGCMQjxZMHG3EtTaRbR7rH9rE4DsmLfpm1iiZEa7HFKjjkgpf2 (wrong version bytes)
gVvddC2jYAfxTxnikcbTEQKLjhJZpqpBg39tXkwAKnD2Pys2mWiK (invalid public key)

We only add the basic validation without checksum, version byte and pubkey validation as that would require much more
effort. Any Grin developer is welcome to add that though!

*/
public class GrinAddressValidator implements AddressValidator {
// A Grin Wallet URL (address is not the correct term) can be in the form IP:port or a grinbox format.
// The grinbox has the format grinbox://<key>@domain.com:port where everything beside the key is optional.


// Regex for IP validation borrowed from https://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses
private static final String IPV4 = "((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])";
private static final String PORT = "((6553[0-5])|(655[0-2][0-9])|(65[0-4][0-9]{2})|(6[0-4][0-9]{3})|([1-5][0-9]{4})|([0-5]{0,5})|([0-9]{1,4}))$";
private static final String DOMAIN = "[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\\.[a-zA-Z]{2,}$";
private static final String KEY = "[a-km-zA-HJ-NP-Z1-9]{52}$";

public GrinAddressValidator() {
}

@Override
public AddressValidationResult validate(String address) {
if (address == null || address.length() == 0)
return AddressValidationResult.invalidAddress("Address may not be empty");

if (address.matches("^" + IPV4 + ":" + PORT))
return AddressValidationResult.validAddress();


// We might have a grinbox address
String key;
String domain = null;
String port = null;
address = address.replace("grinbox://", "");
if (address.contains("@")) {
String[] keyAndDomain = address.split("@");
key = keyAndDomain[0];
if (keyAndDomain.length > 1) {
domain = keyAndDomain[1];
if (domain.contains(":")) {
String[] domainAndPort = domain.split(":");
domain = domainAndPort[0];
if (domainAndPort.length > 1)
port = domainAndPort[1];
}
}
} else {
key = address;
}

if (!key.matches("^" + KEY))
return AddressValidationResult.invalidAddress("Invalid key");

if (domain != null && !domain.matches("^" + DOMAIN))
return AddressValidationResult.invalidAddress("Invalid domain");

if (port != null && !port.matches("^" + PORT))
return AddressValidationResult.invalidAddress("Invalid port");

return AddressValidationResult.validAddress();

}
}
7 changes: 2 additions & 5 deletions assets/src/main/java/bisq/asset/coins/Grin.java
Expand Up @@ -18,14 +18,11 @@
package bisq.asset.coins;

import bisq.asset.Coin;
import bisq.asset.RegexAddressValidator;
import bisq.asset.GrinAddressValidator;

public class Grin extends Coin {
// Borrowed from https://stackoverflow.com/questions/53497/regular-expression-that-matches-valid-ipv6-addresses
private static final String IPV4 = "((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])";
private static final String PORT = ":(([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$)";

public Grin() {
super("Grin", "GRIN", new RegexAddressValidator("^" + IPV4 + PORT));
super("Grin", "GRIN", new GrinAddressValidator());
}
}
17 changes: 16 additions & 1 deletion assets/src/test/java/bisq/asset/coins/GrinTest.java
Expand Up @@ -38,6 +38,14 @@ public void testValidAddresses() {
assertValidAddress("173.194.34.134:11");
assertValidAddress("173.194.34.134:1111");
assertValidAddress("173.194.34.134:65535");

// grinbox
assertValidAddress("gVvk7rLBg3r3qoWYL3VsREnBbooT7nynxx5HtDvUWCJUaNCnddvY");
assertValidAddress("grinbox://gVtWzX5NTLCBkyNV19QVdnLXue13heAVRD36sfkGD6xpqy7k7e4a");
assertValidAddress("gVw9TWimGFXRjoDXWhWxeNQbu84ZpLkvnenkKvA5aJeDo31eM5tC@somerelay.com");
assertValidAddress("gVw9TWimGFXRjoDXWhWxeNQbu84ZpLkvnenkKvA5aJeDo31eM5tC@somerelay.com:1220");
assertValidAddress("grinbox://gVwjSsYW5vvHpK4AunJ5piKhhQTV6V3Jb818Uqs6PdC3SsB36AsA@somerelay.com");
assertValidAddress("grinbox://gVwjSsYW5vvHpK4AunJ5piKhhQTV6V3Jb818Uqs6PdC3SsB36AsA@somerelay.com:1220");
}

@Test
Expand All @@ -54,8 +62,15 @@ public void testInvalidAddresses() {
assertInvalidAddress("http://123.123.123:1222");
assertInvalidAddress("1000.2.3.4:1222");
assertInvalidAddress("999.2.3.4:1222");

// too large port
assertInvalidAddress("173.194.34.134:65536");

assertInvalidAddress("gVvk7rLBg3r3qoWYL3VsREnBbooT7nynxx5HtDvUWCJUaNCnddvY1111");
assertInvalidAddress("grinbox:/gVtWzX5NTLCBkyNV19QVdnLXue13heAVRD36sfkGD6xpqy7k7e4a");
assertInvalidAddress("gVw9TWimGFXRjoDXWhWxeNQbu84ZpLkvnenkKvA5aJeDo31eM5tC@somerelay.com.");
assertInvalidAddress("gVw9TWimGFXRjoDXWhWxeNQbu84ZpLkvnenkKvA5aJeDo31eM5tC@somerelay.com:1220a");
assertInvalidAddress("grinbox://gVwjSsYW5vvHpK4AunJ5piKhhQTV6V3Jb818Uqs6PdC3SsB36AsAsomerelay.com");
assertInvalidAddress("grinbox://gVwjSsYW5vvHpK4AunJ5piKhhQTV6V3Jb818Uqs6PdC3SsB36AsA@somerelay.com1220");

}
}