split SSZ and JSON parsing logic in LC handler (#5055)

Use separate functions per format when parsing LC data from REST.
This allows to process events from the eventstream more directly,
as they are always JSON not SSZ. And also makes the code cleaner.
This commit is contained in:
Etan Kissling 2023-06-13 23:28:17 +02:00 committed by GitHub
parent 54cc7bb7a1
commit 6671965fd9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 127 additions and 93 deletions

View File

@ -38,119 +38,153 @@ func checkForkConsistency(
consensusFork: ConsensusFork) {.raises: [RestError].} = consensusFork: ConsensusFork) {.raises: [RestError].} =
obj.checkForkConsistency(cfg, Opt[ConsensusFork].ok(consensusFork)) obj.checkForkConsistency(cfg, Opt[ConsensusFork].ok(consensusFork))
func decodeSszLightClientObject[T: SomeForkedLightClientObject](
x: typedesc[T],
data: openArray[byte],
consensusFork: ConsensusFork,
cfg: RuntimeConfig): T {.raises: [RestError].} =
try:
withLcDataFork(lcDataForkAtConsensusFork(consensusFork)):
when lcDataFork > LightClientDataFork.None:
var obj = T(kind: lcDataFork)
obj.forky(lcDataFork) = SSZ.decode(data, T.Forky(lcDataFork))
obj.checkForkConsistency(cfg, consensusFork)
obj
else:
raiseRestDecodingBytesError(
cstring("Unsupported fork: " & $consensusFork))
except SszError as exc:
raiseRestDecodingBytesError(cstring("Malformed data: " & $exc.msg))
proc decodeJsonLightClientObject[T: SomeForkedLightClientObject](
x: typedesc[T],
data: openArray[byte],
consensusFork: Opt[ConsensusFork],
cfg: RuntimeConfig): T {.raises: [RestError].} =
let objRes = decodeBytes(T, data, Opt.none(ContentTypeData))
if objRes.isErr:
raiseRestDecodingBytesError(objRes.error)
template obj: auto = objRes.get
obj.checkForkConsistency(cfg, consensusFork)
obj
proc decodeHttpLightClientObject*[T: SomeForkedLightClientObject](
x: typedesc[T],
data: openArray[byte],
mediaType: MediaType,
consensusFork: ConsensusFork,
cfg: RuntimeConfig): T {.raises: [RestError].} =
if mediaType == OctetStreamMediaType:
x.decodeSszLightClientObject(data, consensusFork, cfg)
elif mediaType == ApplicationJsonMediaType:
x.decodeJsonLightClientObject(data, Opt.some(consensusFork), cfg)
else:
raise newException(RestError, "Unsupported content-type")
proc decodeHttpLightClientObject[T: SomeForkedLightClientObject]( proc decodeHttpLightClientObject[T: SomeForkedLightClientObject](
x: typedesc[T], x: typedesc[T],
data: seq[byte], data: openArray[byte],
contentType: Opt[ContentTypeData], contentType: Opt[ContentTypeData],
consensusFork: ConsensusFork, consensusFork: ConsensusFork,
cfg: RuntimeConfig): T {.raises: [RestError].} = cfg: RuntimeConfig): T {.raises: [RestError].} =
let mediaTypeRes = decodeMediaType(contentType) let mediaTypeRes = decodeMediaType(contentType)
if mediaTypeRes.isErr: if mediaTypeRes.isErr:
raise newException(RestError, mediaTypeRes.error) raise newException(RestError, mediaTypeRes.error)
template mediaType: auto = mediaTypeRes.get x.decodeHttpLightClientObject(data, mediaTypeRes.get, consensusFork, cfg)
return proc decodeSszLightClientObjects[S: seq[SomeForkedLightClientObject]](
if mediaType == OctetStreamMediaType: x: typedesc[S],
try: data: openArray[byte],
withLcDataFork(lcDataForkAtConsensusFork(consensusFork)): cfg: RuntimeConfig,
when lcDataFork > LightClientDataFork.None: forkDigests: ref ForkDigests): S {.raises: [RestError].} =
var obj = T(kind: lcDataFork) let l = data.len
obj.forky(lcDataFork) = SSZ.decode(data, T.Forky(lcDataFork)) var
obj.checkForkConsistency(cfg, consensusFork) res: S
obj o = 0
else: while l - o != 0:
raiseRestDecodingBytesError( # response_chunk_len
cstring("Unsupported fork: " & $consensusFork)) type chunkLenType = uint64
except SszError as exc: const chunkLenLen = sizeof chunkLenType # 8
raiseRestDecodingBytesError(cstring("Malformed data: " & $exc.msg)) if l - o < chunkLenLen:
raiseRestDecodingBytesError("Malformed data: Incomplete length")
let responseChunkLen = chunkLenType.fromBytesLE(
data.toOpenArray(o, o + chunkLenLen - 1))
o = o + chunkLenLen
elif mediaType == ApplicationJsonMediaType: # response_chunk
let objRes = decodeBytes(T, data, contentType) if responseChunkLen > int.high.chunkLenType:
if objRes.isErr: raiseRestDecodingBytesError("Malformed data: Unsupported length")
raiseRestDecodingBytesError(objRes.error) if l - o < responseChunkLen.int:
template obj: auto = objRes.get raiseRestDecodingBytesError("Malformed data: Incomplete chunk")
obj.checkForkConsistency(cfg, consensusFork) let
obj begin = o
after = o + responseChunkLen.int
o += responseChunkLen.int
else: # context
raise newException(RestError, "Unsupported content-type") const contextLen = sizeof ForkDigest # 4
if responseChunkLen < contextLen.chunkLenType:
raiseRestDecodingBytesError("Malformed data: Incomplete context")
let
context = ForkDigest [
data[begin + 0], data[begin + 1], data[begin + 2], data[begin + 3]]
consensusFork = forkDigests[].consensusForkForDigest(context).valueOr:
raiseRestDecodingBytesError("Malformed data: Invalid context")
# payload
try:
withLcDataFork(lcDataForkAtConsensusFork(consensusFork)):
when lcDataFork > LightClientDataFork.None:
type T = typeof(res[0])
var obj = T(kind: lcDataFork)
obj.forky(lcDataFork) = SSZ.decode(
data.toOpenArray(begin + contextLen, after - 1),
T.Forky(lcDataFork))
obj.checkForkConsistency(cfg, consensusFork)
res.add obj
else:
raiseRestDecodingBytesError(
cstring("Unsupported fork: " & $consensusFork))
except SszError as exc:
raiseRestDecodingBytesError(cstring("Malformed data: " & $exc.msg))
res
proc decodeJsonLightClientObjects[S: seq[SomeForkedLightClientObject]](
x: typedesc[S],
data: openArray[byte],
cfg: RuntimeConfig,
forkDigests: ref ForkDigests): S {.raises: [RestError].} =
let objsRes = decodeBytes(S, data, Opt.none(ContentTypeData))
if objsRes.isErr:
raiseRestDecodingBytesError(objsRes.error)
template objs: auto = objsRes.get
for obj in objs:
obj.checkForkConsistency(cfg)
objs
proc decodeHttpLightClientObjects*[S: seq[SomeForkedLightClientObject]](
x: typedesc[S],
data: openArray[byte],
mediaType: MediaType,
cfg: RuntimeConfig,
forkDigests: ref ForkDigests): S {.raises: [RestError].} =
if mediaType == OctetStreamMediaType:
x.decodeSszLightClientObjects(data, cfg, forkDigests)
elif mediaType == ApplicationJsonMediaType:
x.decodeJsonLightClientObjects(data, cfg, forkDigests)
else:
raise newException(RestError, "Unsupported content-type")
proc decodeHttpLightClientObjects[S: seq[SomeForkedLightClientObject]]( proc decodeHttpLightClientObjects[S: seq[SomeForkedLightClientObject]](
x: typedesc[S], x: typedesc[S],
data: seq[byte], data: openArray[byte],
contentType: Opt[ContentTypeData], contentType: Opt[ContentTypeData],
cfg: RuntimeConfig, cfg: RuntimeConfig,
forkDigests: ref ForkDigests): S {.raises: [RestError].} = forkDigests: ref ForkDigests): S {.raises: [RestError].} =
let mediaTypeRes = decodeMediaType(contentType) let mediaTypeRes = decodeMediaType(contentType)
if mediaTypeRes.isErr: if mediaTypeRes.isErr:
raise newException(RestError, mediaTypeRes.error) raise newException(RestError, mediaTypeRes.error)
template mediaType: auto = mediaTypeRes.get x.decodeHttpLightClientObjects(data, mediaTypeRes.get, cfg, forkDigests)
return
if mediaType == OctetStreamMediaType:
let l = data.len
var
res: S
o = 0
while l - o != 0:
# response_chunk_len
type chunkLenType = uint64
const chunkLenLen = sizeof chunkLenType # 8
if l - o < chunkLenLen:
raiseRestDecodingBytesError("Malformed data: Incomplete length")
let responseChunkLen = chunkLenType.fromBytesLE(
data.toOpenArray(o, o + chunkLenLen - 1))
o = o + chunkLenLen
# response_chunk
if responseChunkLen > int.high.chunkLenType:
raiseRestDecodingBytesError("Malformed data: Unsupported length")
if l - o < responseChunkLen.int:
raiseRestDecodingBytesError("Malformed data: Incomplete chunk")
let
begin = o
after = o + responseChunkLen.int
o += responseChunkLen.int
# context
const contextLen = sizeof ForkDigest # 4
if responseChunkLen < contextLen.chunkLenType:
raiseRestDecodingBytesError("Malformed data: Incomplete context")
let
context = ForkDigest [
data[begin + 0], data[begin + 1], data[begin + 2], data[begin + 3]]
consensusFork = forkDigests[].consensusForkForDigest(context).valueOr:
raiseRestDecodingBytesError("Malformed data: Invalid context")
# payload
try:
withLcDataFork(lcDataForkAtConsensusFork(consensusFork)):
when lcDataFork > LightClientDataFork.None:
type T = typeof(res[0])
var obj = T(kind: lcDataFork)
obj.forky(lcDataFork) = SSZ.decode(
data.toOpenArray(begin + contextLen, after - 1),
T.Forky(lcDataFork))
obj.checkForkConsistency(cfg, consensusFork)
res.add obj
else:
raiseRestDecodingBytesError(
cstring("Unsupported fork: " & $consensusFork))
except SszError as exc:
raiseRestDecodingBytesError(cstring("Malformed data: " & $exc.msg))
res
elif mediaType == ApplicationJsonMediaType:
let objsRes = decodeBytes(S, data, contentType)
if objsRes.isErr:
raiseRestDecodingBytesError(objsRes.error)
template objs: auto = objsRes.get
for obj in objs:
obj.checkForkConsistency(cfg)
objs
else:
raise newException(RestError, "Unsupported content-type")
proc getLightClientBootstrapPlain( proc getLightClientBootstrapPlain(
block_root: Eth2Digest): RestHttpResponseRef {. block_root: Eth2Digest): RestHttpResponseRef {.