From 25c6867529f7d8ec6b358845fa166ade89621565 Mon Sep 17 00:00:00 2001 From: Alex Barnsley <8069294+alexbarnsley@users.noreply.github.com> Date: Fri, 19 Jul 2024 14:02:27 +0100 Subject: [PATCH] feat: keccak addresses (#130) --- crypto/identity/address.py | 89 ++++++++++++++++------------------ tests/identity/conftest.py | 3 +- tests/identity/test_address.py | 6 +-- 3 files changed, 45 insertions(+), 53 deletions(-) diff --git a/crypto/identity/address.py b/crypto/identity/address.py index 9805c06..6b7a11a 100644 --- a/crypto/identity/address.py +++ b/crypto/identity/address.py @@ -1,55 +1,73 @@ import hashlib from binascii import unhexlify -from base58 import b58decode_check, b58encode_check +from crypto.identity.private_key import PrivateKey -from binary.unsigned_integer.writer import write_bit8 +from Cryptodome.Hash import RIPEMD160, keccak +from coincurve import PrivateKey, PublicKey -from crypto.configuration.network import get_network -from crypto.identity.private_key import PrivateKey +def get_checksum_address(address): + """Get checksum address + + Args: + address (str): address to get checksum + + Returns: + str: checksum address + """ + address = address.lower() -from Cryptodome.Hash import RIPEMD160 + chars = list(address[2:]) -def address_from_public_key(public_key, network_version=None): + expanded = bytearray(40) + for i in range(40): + expanded[i] = ord(chars[i]) + + hashed = keccak.new(data=bytes(expanded), digest_bits=256).digest() + + for i in range(0, 40, 2): + if (hashed[i >> 1] >> 4) >= 8: + chars[i] = chars[i].upper() + if (hashed[i >> 1] & 0x0F) >= 8: + chars[i + 1] = chars[i + 1].upper() + + return "0x" + ''.join(chars) + +def address_from_public_key(public_key): """Get an address from a public key Args: public_key (str): - network_version (int, optional): Returns: - bytes: + str: address """ - if not network_version: - network = get_network() - network_version = network['version'] - ripemd160 = RIPEMD160.new(data=unhexlify(public_key.encode())) - seed = write_bit8(network_version) + ripemd160.digest() - return b58encode_check(seed).decode() + public_key = PublicKey(bytes.fromhex(public_key)).format(compressed=False)[1:] + + keccak_hash = keccak.new( + data=bytearray.fromhex(public_key.hex()), + digest_bits=256, + ) + return get_checksum_address(unhexlify(keccak_hash.hexdigest()[22:]).hex()) -def address_from_private_key(private_key, network_version=None): + +def address_from_private_key(private_key): """Get an address from private key Args: private_key (string): - network_version (int, optional): Returns: TYPE: Description """ - if not network_version: - network = get_network() - network_version = network['version'] - private_key = PrivateKey.from_hex(private_key) - ripemd160 = RIPEMD160.new(data=unhexlify(private_key.public_key)) - seed = write_bit8(network_version) + ripemd160.digest() - return b58encode_check(seed).decode() + + return address_from_public_key(private_key.public_key.format(compressed=False).hex()) -def address_from_passphrase(passphrase, network_version=None): +def address_from_passphrase(passphrase): """Get an address from passphrase Args: @@ -59,27 +77,6 @@ def address_from_passphrase(passphrase, network_version=None): Returns: string: address """ - if not network_version: - network = get_network() - network_version = network['version'] - private_key = hashlib.sha256(passphrase.encode()).hexdigest() - address = address_from_private_key(private_key, network_version) + address = address_from_private_key(private_key) return address - - -def validate_address(address, network_version=None): - """Validate a given address - - Args: - address (str): address you wish to validate - network_version (None, optional): integer, version of the network - - Returns: - bool: - """ - if not network_version: - network = get_network() - network_version = network['version'] - - return network_version == b58decode_check(address)[0] diff --git a/tests/identity/conftest.py b/tests/identity/conftest.py index fae17f1..90e09c5 100644 --- a/tests/identity/conftest.py +++ b/tests/identity/conftest.py @@ -1,6 +1,5 @@ import pytest - @pytest.fixture def identity(): """Identity fixture @@ -9,7 +8,7 @@ def identity(): 'data': { 'private_key': 'd8839c2432bfd0a67ef10a804ba991eabba19f154a3d707917681d45822a5712', 'public_key': '034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192', - 'address': 'D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib', + 'address': '0xb0FF9213f7226bBB72b84dE16af86e56f1f38B01', 'wif': 'SGq4xLgZKCGxs7bjmwnBrWcT4C1ADFEermj846KC97FSv1WFD1dA' }, 'passphrase': 'this is a top secret passphrase' diff --git a/tests/identity/test_address.py b/tests/identity/test_address.py index f29814c..95070d5 100644 --- a/tests/identity/test_address.py +++ b/tests/identity/test_address.py @@ -1,6 +1,6 @@ import pytest from crypto.identity.address import ( - address_from_passphrase, address_from_private_key, address_from_public_key, validate_address + address_from_passphrase, address_from_private_key, address_from_public_key ) def test_address_from_public_key(identity): @@ -16,7 +16,3 @@ def test_address_from_private_key(identity): def test_address_from_passphrase(identity): address = address_from_passphrase(identity['passphrase']) assert address == identity['data']['address'] - - -def test_validate_address(identity): - assert validate_address(identity['data']['address']) is True