Skip to content

Commit

Permalink
Add address decoding/encoding for Neo N3
Browse files Browse the repository at this point in the history
NeoAddrEncoder.EncodeKey now accepts the prefix and suffix bytes as parameter to be as generic as possible
Old NeoAddrEncoder class is now NeoLegacyAddrEncoder (NeoLegacyAddrDecoder exists but it's the same of NeoAddrDecoder)
Add NeoN3AddrEncoder class for Neo N3 (NeoN3AddrDecoder exists but it's the same of NeoAddrDecoder)
Add WIF configuration to NEO
  • Loading branch information
ebellocchia committed May 3, 2024
1 parent b8cfe54 commit 2732f83
Show file tree
Hide file tree
Showing 12 changed files with 324 additions and 37 deletions.
3 changes: 2 additions & 1 deletion bip_utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
ErgoNetworkTypes, ErgoP2PKHAddr, ErgoP2PKHAddrDecoder, ErgoP2PKHAddrEncoder, EthAddr, EthAddrDecoder,
EthAddrEncoder, FilSecp256k1Addr, FilSecp256k1AddrDecoder, FilSecp256k1AddrEncoder, IcxAddr, IcxAddrDecoder,
IcxAddrEncoder, InjAddr, InjAddrDecoder, InjAddrEncoder, NanoAddr, NanoAddrDecoder, NanoAddrEncoder, NearAddr,
NearAddrDecoder, NearAddrEncoder, NeoAddr, NeoAddrDecoder, NeoAddrEncoder, NimAddr, NimAddrDecoder, NimAddrEncoder,
NearAddrDecoder, NearAddrEncoder, NeoAddr, NeoAddrDecoder, NeoAddrEncoder, NeoLegacyAddr, NeoLegacyAddrDecoder,
NeoLegacyAddrEncoder, NeoN3Addr, NeoN3AddrDecoder, NeoN3AddrEncoder, NimAddr, NimAddrDecoder, NimAddrEncoder,
OkexAddr, OkexAddrDecoder, OkexAddrEncoder, OneAddr, OneAddrDecoder, OneAddrEncoder, P2PKHAddr, P2PKHAddrDecoder,
P2PKHAddrEncoder, P2PKHPubKeyModes, P2SHAddr, P2SHAddrDecoder, P2SHAddrEncoder, P2TRAddr, P2TRAddrDecoder,
P2TRAddrEncoder, P2WPKHAddr, P2WPKHAddrDecoder, P2WPKHAddrEncoder, SolAddr, SolAddrDecoder, SolAddrEncoder,
Expand Down
2 changes: 2 additions & 0 deletions bip_utils/addr/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
from bip_utils.addr.nano_addr import NanoAddr, NanoAddrDecoder, NanoAddrEncoder
from bip_utils.addr.near_addr import NearAddr, NearAddrDecoder, NearAddrEncoder
from bip_utils.addr.neo_addr import NeoAddr, NeoAddrDecoder, NeoAddrEncoder
from bip_utils.addr.neo_legacy_addr import NeoLegacyAddr, NeoLegacyAddrDecoder, NeoLegacyAddrEncoder
from bip_utils.addr.neo_n3_addr import NeoN3Addr, NeoN3AddrDecoder, NeoN3AddrEncoder
from bip_utils.addr.nim_addr import NimAddr, NimAddrDecoder, NimAddrEncoder
from bip_utils.addr.okex_addr import OkexAddr, OkexAddrDecoder, OkexAddrEncoder
from bip_utils.addr.one_addr import OneAddr, OneAddrDecoder, OneAddrEncoder
Expand Down
19 changes: 6 additions & 13 deletions bip_utils/addr/neo_addr.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,6 @@
from bip_utils.utils.misc import BytesUtils, IntegerUtils


class NeoAddrConst:
"""Class container for NEO address constants."""

# Address prefix byte
PREFIX_BYTE: bytes = b"\x21"
# Address suffix byte
SUFFIX_BYTE: bytes = b"\xac"


class NeoAddrDecoder(IAddrDecoder):
"""
Neo address decoder class.
Expand Down Expand Up @@ -102,7 +93,9 @@ def EncodeKey(pub_key: Union[bytes, IPublicKey],
pub_key (bytes or IPublicKey): Public key bytes or object
Other Parameters:
ver (bytes): Version
ver (bytes) : Version
prefix (bytes): Prefix
suffix (bytes): Suffix
Returns:
str: Address string
Expand All @@ -112,13 +105,13 @@ def EncodeKey(pub_key: Union[bytes, IPublicKey],
TypeError: If the public key is not nist256p1
"""
ver_bytes = kwargs["ver"]
prefix_bytes = kwargs["prefix"]
suffix_bytes = kwargs["suffix"]

pub_key_obj = AddrKeyValidator.ValidateAndGetNist256p1Key(pub_key)

# Get payload
payload_bytes = (NeoAddrConst.PREFIX_BYTE
+ pub_key_obj.RawCompressed().ToBytes()
+ NeoAddrConst.SUFFIX_BYTE)
payload_bytes = prefix_bytes + pub_key_obj.RawCompressed().ToBytes() + suffix_bytes
# Encode to base58
return Base58Encoder.CheckEncode(ver_bytes + Hash160.QuickDigest(payload_bytes))

Expand Down
70 changes: 70 additions & 0 deletions bip_utils/addr/neo_legacy_addr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright (c) 2021 Emanuele Bellocchia
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

"""Module for Neo legacy address encoding/decoding."""

# Imports
from typing import Any, Union

from bip_utils.addr.iaddr_encoder import IAddrEncoder
from bip_utils.addr.neo_addr import NeoAddrDecoder, NeoAddrEncoder
from bip_utils.coin_conf.coins_conf import CoinsConf
from bip_utils.ecc.common.ikeys import IPublicKey


# Same as NeoAddrDecoder
NeoLegacyAddrDecoder = NeoAddrDecoder


class NeoLegacyAddrEncoder(IAddrEncoder):
"""
Neo legacy address encoder class.
It allows the Neo legacy address encoding.
"""

@staticmethod
def EncodeKey(pub_key: Union[bytes, IPublicKey],
**kwargs: Any) -> str:
"""
Encode a public key to Neo legacy address.
Args:
pub_key (bytes or IPublicKey): Public key bytes or object
Other Parameters:
ver (bytes): Version
Returns:
str: Address string
Raises:
ValueError: If the public key is not valid
TypeError: If the public key is not nist256p1
"""
return NeoAddrEncoder.EncodeKey(
pub_key,
ver=kwargs["ver"],
prefix=CoinsConf.Neo.ParamByKey("addr_prefix"),
suffix=CoinsConf.Neo.ParamByKey("addr_suffix")
)


# Deprecated: only for compatibility, Encoder class shall be used instead
NeoLegacyAddr = NeoLegacyAddrEncoder
70 changes: 70 additions & 0 deletions bip_utils/addr/neo_n3_addr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright (c) 2021 Emanuele Bellocchia
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

"""Module for Neo N3 address encoding/decoding."""

# Imports
from typing import Any, Union

from bip_utils.addr.iaddr_encoder import IAddrEncoder
from bip_utils.addr.neo_addr import NeoAddrDecoder, NeoAddrEncoder
from bip_utils.coin_conf.coins_conf import CoinsConf
from bip_utils.ecc.common.ikeys import IPublicKey


# Same as NeoAddrDecoder
NeoN3AddrDecoder = NeoAddrDecoder


class NeoN3AddrEncoder(IAddrEncoder):
"""
Neo N3 address encoder class.
It allows the Neo N3 address encoding.
"""

@staticmethod
def EncodeKey(pub_key: Union[bytes, IPublicKey],
**kwargs: Any) -> str:
"""
Encode a public key to Neo N3 address.
Args:
pub_key (bytes or IPublicKey): Public key bytes or object
Other Parameters:
ver (bytes): Version
Returns:
str: Address string
Raises:
ValueError: If the public key is not valid
TypeError: If the public key is not nist256p1
"""
return NeoAddrEncoder.EncodeKey(
pub_key,
ver=kwargs["ver"],
prefix=CoinsConf.NeoN3.ParamByKey("addr_prefix"),
suffix=CoinsConf.NeoN3.ParamByKey("addr_suffix")
)


# Deprecated: only for compatibility, Encoder class shall be used instead
NeoN3Addr = NeoN3AddrEncoder
8 changes: 4 additions & 4 deletions bip_utils/bip/conf/bip44/bip44_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
AdaByronIcarusAddrEncoder, AlgoAddrEncoder, AptosAddrEncoder, AtomAddrEncoder, AvaxPChainAddrEncoder,
AvaxXChainAddrEncoder, BchP2PKHAddrEncoder, EgldAddrEncoder, EosAddrEncoder, ErgoNetworkTypes, ErgoP2PKHAddrEncoder,
EthAddrEncoder, FilSecp256k1AddrEncoder, IcxAddrEncoder, InjAddrEncoder, NanoAddrEncoder, NearAddrEncoder,
NeoAddrEncoder, NimAddrEncoder, OkexAddrEncoder, OneAddrEncoder, P2PKHAddrEncoder, SolAddrEncoder,
NeoLegacyAddrEncoder, NimAddrEncoder, OkexAddrEncoder, OneAddrEncoder, P2PKHAddrEncoder, SolAddrEncoder,
SubstrateEd25519AddrEncoder, SuiAddrEncoder, TrxAddrEncoder, XlmAddrEncoder, XlmAddrTypes, XmrAddrEncoder,
XrpAddrEncoder, XtzAddrEncoder, XtzAddrPrefixes, ZilAddrEncoder
)
Expand Down Expand Up @@ -914,9 +914,9 @@ class Bip44Conf:
is_testnet=False,
def_path=DER_PATH_NON_HARDENED_FULL,
key_net_ver=_BIP44_BTC_KEY_NET_VER_MAIN,
wif_net_ver=None,
wif_net_ver=CoinsConf.Neo.ParamByKey("wif_net_ver"),
bip32_cls=Bip32Slip10Nist256p1,
addr_cls=NeoAddrEncoder,
addr_cls=NeoLegacyAddrEncoder,
addr_params={
"ver": CoinsConf.Neo.ParamByKey("addr_ver"),
},
Expand Down Expand Up @@ -996,7 +996,7 @@ class Bip44Conf:
key_net_ver=_BIP44_BTC_KEY_NET_VER_MAIN,
wif_net_ver=None,
bip32_cls=Bip32Slip10Nist256p1,
addr_cls=NeoAddrEncoder,
addr_cls=NeoLegacyAddrEncoder,
addr_params={
"ver": CoinsConf.Ontology.ParamByKey("addr_ver"),
},
Expand Down
14 changes: 14 additions & 0 deletions bip_utils/coin_conf/coins_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,20 @@ class CoinsConf:
coin_name=CoinNames("NEO", "NEO"),
params={
"addr_ver": b"\x17",
"addr_prefix": b"\x21",
"addr_suffix": b"\xac",
"wif_net_ver": _BTC_WIF_NET_VER_MN,
},
)

# Configuration for Neo N3
NeoN3: CoinConf = CoinConf(
coin_name=CoinNames("NEO", "NEO"),
params={
"addr_ver": b"\x35",
"addr_prefix": b"\x0c\x21",
"addr_suffix": b"\x41\x56\xe7\xb3\x27",
"wif_net_ver": _BTC_WIF_NET_VER_MN,
},
)

Expand Down
40 changes: 30 additions & 10 deletions readme/utility/addr.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,20 +290,40 @@ The address library allows encoding/decoding addresses for all the supported coi
# Public key bytes or a public key object can be used
pub_key = binascii.unhexlify(b"038ea003d38b3f2043e681f06f56b3864d28d73b4f243aee90ed04a28dbc058c5b")
pub_key = Nist256p1PublicKey.FromBytes(
binascii.unhexlify(b"038ea003d38b3f2043e681f06f56b3864d28d73b4f243aee90ed04a28dbc058c5b"))
binascii.unhexlify(b"038ea003d38b3f2043e681f06f56b3864d28d73b4f243aee90ed04a28dbc058c5b")
)

# NEO address with parameters from generic configuration
# NEO generic address
addr = NeoAddrEncoder.EncodeKey(pub_key,
ver=CoinsConf.Neo.ParamByKey("addr_ver"))
ver=b"\x17",
prefix=b"\x21",
suffix=b"\xac")
pub_key_hash = NeoAddrDecoder.DecodeAddr(addr,
ver=b"\x17")

# NEO legacy address with parameters from generic configuration
addr = NeoLegacyAddrEncoder.EncodeKey(pub_key,
ver=CoinsConf.Neo.ParamByKey("addr_ver"))
# Or with custom parameters
addr = NeoAddrEncoder.EncodeKey(pub_key,
ver=b"\x10")
addr = NeoLegacyAddrEncoder.EncodeKey(pub_key,
ver=b"\x17")
# Or with the default parameters from BIP configuration:
addr = NeoAddrEncoder.EncodeKey(pub_key,
**Bip44Conf.Neo.AddrParams())
# Same as before for decoding
pub_key_hash = NeoAddrDecoder.DecodeAddr(addr,
ver=CoinsConf.Neo.ParamByKey("addr_ver"))
addr = NeoLegacyAddrEncoder.EncodeKey(pub_key,
**Bip44Conf.Neo.AddrParams())
# Same of NeoAddrDecoder
pub_key_hash = NeoLegacyAddrDecoder.DecodeAddr(addr,
ver=CoinsConf.Neo.ParamByKey("addr_ver"))

# NEO N3 address with parameters from generic configuration
addr = NeoN3AddrEncoder.EncodeKey(pub_key,
ver=CoinsConf.NeoN3.ParamByKey("addr_ver"))
# Or with custom parameters
addr = NeoN3AddrEncoder.EncodeKey(pub_key,
ver=b"\x35")
# Same of NeoAddrDecoder
pub_key_hash = NeoN3AddrDecoder.DecodeAddr(addr,
ver=CoinsConf.NeoN3.ParamByKey("addr_ver"))


**Code example (coins based on the sr25519 curve)**

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
# THE SOFTWARE.

# Imports
from bip_utils import CoinsConf, NeoAddr, NeoAddrDecoder, NeoAddrEncoder
from bip_utils import CoinsConf, NeoLegacyAddr, NeoLegacyAddrDecoder, NeoLegacyAddrEncoder
from tests.addr.test_addr_base import AddrBaseTests
from tests.addr.test_addr_const import TEST_NIST256P1_ADDR_INVALID_KEY_TYPES
from tests.ecc.test_ecc import TEST_VECT_NIST256P1_PUB_KEY_INVALID, Nist256p1PublicKey
Expand Down Expand Up @@ -84,19 +84,19 @@
#
# Tests
#
class NeoAddrTests(AddrBaseTests):
class NeoLegacyAddrTests(AddrBaseTests):
# Test encode key
def test_encode_key(self):
self._test_encode_key(NeoAddrEncoder, Nist256p1PublicKey, TEST_VECT)
self._test_encode_key(NeoLegacyAddrEncoder, Nist256p1PublicKey, TEST_VECT)

# Test decode address
def test_decode_addr(self):
self._test_decode_addr(NeoAddrDecoder, TEST_VECT)
self._test_decode_addr(NeoLegacyAddrDecoder, TEST_VECT)

# Test invalid decoding
def test_invalid_dec(self):
self._test_invalid_dec(
NeoAddrDecoder,
NeoLegacyAddrDecoder,
{
"ver": CoinsConf.Neo.ParamByKey("addr_ver"),
},
Expand All @@ -106,12 +106,12 @@ def test_invalid_dec(self):
# Test invalid keys
def test_invalid_keys(self):
self._test_invalid_keys(
NeoAddrEncoder,
NeoLegacyAddrEncoder,
{"ver": b"\x00"},
TEST_NIST256P1_ADDR_INVALID_KEY_TYPES,
TEST_VECT_NIST256P1_PUB_KEY_INVALID
)

# Test old address class
def test_old_addr_cls(self):
self.assertTrue(NeoAddr is NeoAddrEncoder)
self.assertTrue(NeoLegacyAddr is NeoLegacyAddrEncoder)
Loading

0 comments on commit 2732f83

Please sign in to comment.