diff --git a/eth_keys.nimble b/eth_keys.nimble index da4a650..b19bb9d 100644 --- a/eth_keys.nimble +++ b/eth_keys.nimble @@ -26,11 +26,6 @@ task test_cpp, "Run all tests - C++ only & libsecp256k1 backend": test "all_tests", "cpp" task test, "Run all tests - C and C++ & libsecp256k1 backend": - ## TODO: This only runs the C++ tests ... - # block: - # test "all_tests" - # block: - # test "all_tests", "cpp" exec "nimble test_c" exec "rm ./nimcache/*" exec "nimble test_cpp" diff --git a/src/datatypes.nim b/src/datatypes.nim index 1c21890..9b722f7 100644 --- a/src/datatypes.nim +++ b/src/datatypes.nim @@ -13,23 +13,52 @@ import ./private/conversion_bytes # See private field access issue: https://github.com/nim-lang/Nim/issues/7390 type PublicKey* = object - Fraw_key*: array[64, byte] + Fraw_key: array[64, byte] PrivateKey* = object - Fraw_key*: array[32, byte] - Fpublic_key*: PublicKey - + Fraw_key: array[32, byte] + Fpublic_key: PublicKey # This is exported publicly through public_key type - Scalar256 = array[32, byte] + Scalar256 = distinct array[32, byte] # Secp256k1 makes the signature an opaque "implementation dependent". # - # Scalar256 is opaque/distinct too as in practice, they are uint256 - # and by default we don't load any. + # Scalar256 is opaque/distinct too as in practice it's uint256 + # and by default we don't load any uint256 library. # See implementation details in datatypes.md. Signature* {.packed.}= object - Fr*: Scalar256 - Fs*: Scalar256 - Fv*: range[0.byte .. 1.byte] # This should be 27..28 as per Ethereum but it's 0..1 in eth-keys ... + Fr: Scalar256 + Fs: Scalar256 + Fv: range[0.byte .. 1.byte] # This should be 27..28 as per Ethereum but it's 0..1 in eth-keys ... +# Hide the private fields and generate accessors. +# This is needed to: +# - Be able to not store the public_key in PrivateKey in the future and replace it by +# an on-the-fly computation +# - Signature: have different data representation + +template genAccessors(name: untyped, fieldType, objType: typedesc): untyped = + # Access + proc name*(obj: objType): fieldType {.noSideEffect, inline, noInit.} = + obj.`F name` + + # Assignement + proc `name=`*(obj: var objType, value: fieldType) {.noSideEffect, inline.} = + obj.`F name` = value + + # Mutable + proc `name`*(obj: var objType): var fieldType {.noSideEffect, inline.} = + obj.`F name` + +genAccessors(raw_key, array[64, byte], PublicKey) +genAccessors(raw_key, array[32, byte], PrivateKey) +genAccessors(public_key, PublicKey, PrivateKey) + + +## If we hide the fields we need to provide a custom `==` proc +## Because Nim `==` template will not be able to access the fields + +proc `==`*(x, y: Scalar256): bool {.noSideEffect, inline, borrow.} +proc `==`*(x, y: PublicKey or PrivateKey or Signature): bool {.noSideEffect, inline.} = + system.`==`(x, y) diff --git a/src/datatypes_interface.nim b/src/datatypes_interface.nim index 6f822e4..4c9e2ca 100644 --- a/src/datatypes_interface.nim +++ b/src/datatypes_interface.nim @@ -31,8 +31,8 @@ else: # Initialization proc initPrivateKey*(hexString: string): PrivateKey {.noInit.} = - hexToByteArrayBE(hexString, result.Fraw_key) - result.Fpublic_key = private_key_to_public_key(result) + hexToByteArrayBE(hexString, result.raw_key) + result.public_key = private_key_to_public_key(result) proc initPublicKey*(hexString: string): PublicKey {.noInit.} = var b: array[65, byte] @@ -68,4 +68,4 @@ proc sign_msg*(key: PrivateKey, message_hash: Hash[256]): Signature {.inline.} = ecdsa_sign(key, message_hash) proc `$`*(key: PrivateKey): string {.inline.} = - key.Fraw_key.toHex() + key.raw_key.toHex() diff --git a/src/eth_keys.nim b/src/eth_keys.nim index 417d787..78af68e 100644 --- a/src/eth_keys.nim +++ b/src/eth_keys.nim @@ -8,7 +8,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import ./datatypes -export PublicKey, PrivateKey, Signature +export PublicKey, PrivateKey, Signature, public_key, `==` import ./datatypes_interface export datatypes_interface diff --git a/tests/test_key_and_signature_datastructures.nim b/tests/test_key_and_signature_datastructures.nim index 640fa9f..e25215a 100644 --- a/tests/test_key_and_signature_datastructures.nim +++ b/tests/test_key_and_signature_datastructures.nim @@ -14,41 +14,13 @@ import unittest suite "Test key and signature data structure": - # TODO: For now due to needing hex <-> uint256 conversion - # Testing r, s, v direct access is disabled - # - # test "Signing from private key object": - # for person in [alice, bob, eve]: - # let - # pk = initPrivateKey(person.privkey) - # signature = pk.sign_msg(MSG) - - # check: - # signature.Fv == person.raw_sig.v - # signature.Fr == hexToByteArrayBE[32](person.raw_sig.r) - # signature.Fs == hexToByteArrayBE[32](person.raw_sig.s) - test "Signing from private key object (ported from official eth-keys)": for person in [alice, bob, eve]: let pk = initPrivateKey(person.privkey) signature = pk.sign_msg(MSG) - check: verify_msg(pk.Fpublic_key, MSG, signature) - - # TODO: For now due to needing hex <-> uint256 conversion - # Testing r, s, v direct access is disabled - # - # test "Hash signing from private key object": - # for person in [alice, bob, eve]: - # let - # pk = initPrivateKey(person.privkey) - # signature = pk.sign_msg(MSG) - - # check: - # signature.Fv == person.raw_sig.v - # signature.Fr == hexToByteArrayBE[32](person.raw_sig.r) - # signature.Fs == hexToByteArrayBE[32](person.raw_sig.s) + check: verify_msg(pk.public_key, MSG, signature) test "Hash signing from private key object (ported from official eth-keys)": for person in [alice, bob, eve]: @@ -56,7 +28,7 @@ suite "Test key and signature data structure": pk = initPrivateKey(person.privkey) signature = pk.sign_msg(MSGHASH) - check: verify_msg(pk.Fpublic_key, MSGHASH, signature) + check: verify_msg(pk.public_key, MSGHASH, signature) test "Recover public key from message": for person in [alice, bob, eve]: @@ -66,7 +38,7 @@ suite "Test key and signature data structure": recovered_pubkey = recover_pubkey_from_msg(MSG, signature) - check: pk.Fpublic_key == recovered_pubkey + check: pk.public_key == recovered_pubkey test "Recover public key from message hash": for person in [alice, bob, eve]: @@ -76,7 +48,7 @@ suite "Test key and signature data structure": recovered_pubkey = recover_pubkey_from_msg(MSGHASH, signature) - check: pk.Fpublic_key == recovered_pubkey + check: pk.public_key == recovered_pubkey test "Signature serialization and deserialization": for person in [alice, bob, eve]: diff --git a/tests/test_private_public_key_consistency.nim b/tests/test_private_public_key_consistency.nim index cb1945f..4cfcca1 100644 --- a/tests/test_private_public_key_consistency.nim +++ b/tests/test_private_public_key_consistency.nim @@ -17,6 +17,6 @@ suite "Testing private -> public key conversion": for person in [alice, bob, eve]: let privkey = initPrivateKey(person.privkey) - let computed_pubkey = $privkey.Fpublic_key + let computed_pubkey = $privkey.public_key check: computed_pubkey == person.pubkey