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

Added isAddress() validation method #134

Closed
wants to merge 15 commits into from
Closed

Conversation

assafY
Copy link

@assafY assafY commented Jul 14, 2017

While using the Web3j Numeric and Hash libraries to implement a isAddress() method in a local project, I have realised the method hexStringToByteArray() is not only unnecessary (you can just use the native .getBytes() on a string), but also returns an incorrect byte array.

To verify this, the isAddress() method in this PR fails if using the aforementioned method, but works if using the native .toBytes().

I have therefore removed the method content and replaced it simply with .getBytes(), and implemented the isAddress() method in a new class . This method is based on the web3.js implementation and is working perfectly in my own project.

@assafY
Copy link
Author

assafY commented Jul 14, 2017

so I realise now many tests depend on the methods in Numeric.java and Hash.java to remain as they are. Therefore I have kept them as they are, and use the method in Hash.java that accepts a byte array directly.

@codecov-io
Copy link

codecov-io commented Jul 14, 2017

Codecov Report

Merging #134 into master will increase coverage by <.01%.
The diff coverage is 78.94%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master     #134      +/-   ##
============================================
+ Coverage     78.77%   78.77%   +<.01%     
- Complexity     1241     1251      +10     
============================================
  Files           181      182       +1     
  Lines          4339     4358      +19     
  Branches        662      668       +6     
============================================
+ Hits           3418     3433      +15     
- Misses          742      743       +1     
- Partials        179      182       +3
Impacted Files Coverage Δ Complexity Δ
core/src/main/java/org/web3j/utils/Account.java 78.94% <78.94%> (ø) 10 <10> (?)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 41fdc38...bd23fe2. Read the comment docs.

@conor10
Copy link
Contributor

conor10 commented Jul 16, 2017

Hi @assafY, please can you provide a unit test that demonstrates this error you believe exists in hexStringToByteArray?

@assafY
Copy link
Author

assafY commented Jul 17, 2017

Sure, but it would be useful if you told me first whether you intended for hexStringToByteArray to transform a String into a byte array in a similar way that String.getBytes() does? Because the two return different results for the same string, so if that was the intention I can quickly write a unit test that demonstrates this error.

@conor10
Copy link
Contributor

conor10 commented Jul 17, 2017

If you call String.getBytes() on a hex string, you get the ASCII values for each of the characters in the String. This is not the same as a byte array which is a binary encoded array of data.

E.g. Using getBytes:

"123".getBytes() == new byte[] { '1', '2', '3'} 

Or equivalently:

new byte[] { (byte) 49, (byte) 50, (byte) 51 };

Whereas with a byte array (remember the 123 is a hex not decimal value here):

hexStringToByteArray("123") == new byte[] { (byte) 1, (byte) 35 }

@assafY
Copy link
Author

assafY commented Jul 24, 2017

I understand - so I am assuming that the checksum validation for newer Ethereum account addresses, as described here, uses the ASCII value byte representation rather than the one you implemented in hexStringToByteArray.

In this case I think the comparison between the two is irrelevant, and the checksum validation method I added which uses getBytes works independently. Checksum validation is now used by most major ether wallets and I assumed it would be a useful addition to the Web3j library.

@clowdrgn
Copy link
Contributor

Hi, I try to validate a address but cant pass.Maybe this line String hash = Numeric.toHexString(Hash.sha3(address.replace("0x", "").getBytes()));should toLowerCase when get address hash value ?and I can verify success when I change to String hash = Numeric.toHexStringNoPrefix(Hash.sha3(address.replace("0x", "").toLowerCase().getBytes())); Or do I miss some method ?

@assafY
Copy link
Author

assafY commented Jul 26, 2017

That is correct, thanks! You do need toLowerCase() before validation. I added that now.

@@ -38,7 +38,7 @@ public static Boolean isAddress(String address) {

private static Boolean validateChecksumAddress(String address) {
String formattedAddress = address.replace("0x", "").toLowerCase();
String hash = Numeric.toHexString(Hash.sha3(formattedAddress.getBytes()));
String hash = Numeric.toHexStringNoPrefix(Hash.sha3(formattedAddress.getBytes()));
for (int i = 0; i < 40; i++) {
if (Character.isLetter(address.charAt(i))) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you check from the address? This seems to be wrong - address still contains the leading '0x'

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I missed that method. In this case you can also remove the .replace("0x", "") above, which I assumes is what happens in toHexStringNoPrefix?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I misunderstood your question. This is actually the correct way to do it. The hash needs to be computed from the lowercase address without a prefix, but the validation of the hash should happen against the original, untouched address.

ethereum/EIPs#55

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No I don't think that you can remove it - if you remove it it would be used in the sha3 hashing.

Copy link

@Mawster Mawster Aug 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

compare with
https://ethereum.stackexchange.com/questions/1374/how-can-i-check-if-an-ethereum-address-is-valid

especially this line:
address = address.replace('0x','');

using the untouched address with leading '0x' is false

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh you are correct - thank you for pointing that out. The checksum validation should be carried out on the address without the 0x prefix. Fixed that now, thanks!

Copy link

@Mawster Mawster Aug 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now you are ussing the lower cased version also in the comparision - thats not right.

Character.isUpperCase(address.charAt(i)) && charInt <= 7
can never happen

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slow day for me today isn't it? Fixed that now, thanks.

@@ -38,7 +38,7 @@ public static Boolean isAddress(String address) {

private static Boolean validateChecksumAddress(String address) {
String formattedAddress = address.replace("0x", "").toLowerCase();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to validate eth addresses in a current project - I modified your version:

String formattedAddress = address.replace("0x", ""); String lowercaseAddress = formattedAddress.toLowerCase(); String hash = Numeric.toHexStringNoPrefix(Hash.sha3(lowercaseAddress.getBytes())); for (int i = 0; i < 40; i++) { if (Character.isLetter(formattedAddress.charAt(i))) {

@Mawster
Copy link

Mawster commented Aug 30, 2017

Some tests wouldn't be bad

@assafY
Copy link
Author

assafY commented Nov 8, 2017

anything stopping this from being merged?

* @param address given address in HEX
* @return is this a valid address
*/
public static Boolean isAddress(String address) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WalletUtils.isValidAddress achieves similar functionality.

}
}

private static Boolean validateChecksumAddress(String address) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validating checksum addresses is useful functionality.

It might be better to have isChecksumAddress as a method on the Address type, and a toChecksumAddress that operates on an Address or String instance

@conor10
Copy link
Contributor

conor10 commented Nov 9, 2017

See comments. It's also useful to reference the original EIP for checksum addresses ethereum/EIPs#55, and include the corresponding issue in your commits.

I'd recommend you create a brand new PR, as there are a lot of commits in this PR.

Thanks for taking the time to get this far.

@assafY assafY closed this Feb 16, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants