Don't use exceptions for enr get call (#453)

The ENR code used to be solely exception based, and these
exceptions where a left-over of that. They are useless as later
calls use Result anyhow.

Additionally, they cause quite the performance loss because they
are used in the "common path" for the toTypedRecord call, e.g.
when reading the fields of ip6, tcp6 and udp6.
This commit is contained in:
Kim De Mey 2021-12-21 15:09:32 +01:00 committed by GitHub
parent 2c236f6495
commit 8b7d41f596
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 34 additions and 29 deletions

View File

@ -12,10 +12,10 @@
import
std/[strutils, macros, algorithm, options],
stew/shims/net, stew/base64, nimcrypto,
stew/shims/net, stew/[base64, results], nimcrypto,
".."/../[rlp, keys]
export options
export options, results, keys
const
maxEnrSize = 300 ## Maximum size of an encoded node record, in bytes.
@ -207,43 +207,47 @@ proc getField(r: Record, name: string, field: var Field): bool =
field = v
return true
proc requireKind(f: Field, kind: FieldKind) {.raises: [ValueError].} =
proc requireKind(f: Field, kind: FieldKind): EnrResult[void] =
if f.kind != kind:
raise newException(ValueError, "Wrong field kind")
err("Wrong field kind")
else:
ok()
proc get*(r: Record, key: string, T: type): T {.raises: [ValueError, Defect].} =
proc get*(r: Record, key: string, T: type): EnrResult[T] =
## Get the value from the provided key.
## Throw `KeyError` if key does not exist.
## Throw `ValueError` if the value is invalid according to type `T`.
var f: Field
if r.getField(key, f):
when T is SomeInteger:
requireKind(f, kNum)
return T(f.num)
? requireKind(f, kNum)
ok(T(f.num))
elif T is seq[byte]:
requireKind(f, kBytes)
return f.bytes
? requireKind(f, kBytes)
ok(f.bytes)
elif T is string:
requireKind(f, kString)
return f.str
? requireKind(f, kString)
ok(f.str)
elif T is PublicKey:
requireKind(f, kBytes)
? requireKind(f, kBytes)
let pk = PublicKey.fromRaw(f.bytes)
if pk.isErr:
raise newException(ValueError, "Invalid public key")
return pk[]
err("Invalid public key")
else:
ok(pk[])
elif T is array:
when type(result[0]) is byte:
requireKind(f, kBytes)
if f.bytes.len != result.len:
raise newException(ValueError, "Invalid byte blob length")
copyMem(addr result[0], addr f.bytes[0], result.len)
when type(default(T)[low(T)]) is byte:
? requireKind(f, kBytes)
if f.bytes.len != T.len:
err("Invalid byte blob length")
else:
var res: T
copyMem(addr res[0], addr f.bytes[0], res.len)
ok(res)
else:
{.fatal: "Unsupported output type in enr.get".}
else:
{.fatal: "Unsupported output type in enr.get".}
else:
raise newException(KeyError, "Key not found in ENR: " & key)
err("Key not found in ENR")
proc get*(r: Record, T: type PublicKey): Option[T] =
## Get the `PublicKey` from provided `Record`. Return `none` when there is
@ -328,10 +332,11 @@ proc tryGet*(r: Record, key: string, T: type): Option[T] =
## Get the value from the provided key.
## Return `none` if the key does not exist or if the value is invalid
## according to type `T`.
try:
return some get(r, key, T)
except ValueError:
discard
let val = get(r, key, T)
if val.isOk():
some(val.get())
else:
none(T)
proc toTypedRecord*(r: Record): EnrResult[TypedRecord] =
let id = r.tryGet("id", string)

View File

@ -123,14 +123,14 @@ suite "ENR":
let updated = r.update(pk, [newField])
check updated.isOk()
check:
r.get("test", uint) == 123
r.get("test", uint).get() == 123
r.seqNum == 2
block: # Insert same k:v pair, no update of seqNum should occur.
let updated = r.update(pk, [newField])
check updated.isOk()
check:
r.get("test", uint) == 123
r.get("test", uint).get() == 123
r.seqNum == 2
block: # Insert k:v pair with changed value, update of seqNum should occur.
@ -138,7 +138,7 @@ suite "ENR":
let updated = r.update(pk, [updatedField])
check updated.isOk()
check:
r.get("test", uint) == 1234
r.get("test", uint).get() == 1234
r.seqNum == 3
test "ENR update sorted":