Validate content in portal protocol.

This commit is contained in:
bhartnett 2024-10-25 14:56:25 +08:00
parent ab475cccdc
commit e881144557
No known key found for this signature in database
GPG Key ID: 076F2830DA6BD535
2 changed files with 56 additions and 33 deletions

View File

@ -90,32 +90,39 @@ proc getContent(
info "Fetched state local content value"
return Opt.some(contentValue)
for i in 0 ..< (1 + n.contentRequestRetries):
let
contentLookupResult = (
await n.portalProtocol.contentLookup(contentKeyBytes, contentId)
).valueOr:
warn "Failed fetching state content from the network"
return Opt.none(V)
contentValueBytes = contentLookupResult.content
let contentValue = V.decode(contentValueBytes).valueOr:
# Define the state content validation closure
proc stateContentValidator(
contentKey: ContentKeyByteList, content: seq[byte]
): bool {.raises: [], gcsafe.} =
let contentValue = V.decode(content).valueOr:
warn "Unable to decode state content value from content lookup"
continue
return false
validateRetrieval(key, contentValue).isOkOr:
if validateRetrieval(key, contentValue).isOk():
true
else:
warn "Validation of retrieved state content failed"
continue
false
info "Fetched valid state content from the network"
n.portalProtocol.storeContent(
contentKeyBytes, contentId, contentValueBytes, cacheContent = true
)
let
contentLookupResult = (
await n.portalProtocol.contentLookup(
contentKeyBytes, contentId, stateContentValidator
)
).valueOr:
warn "Failed fetching state content from the network"
return Opt.none(V)
contentValueBytes = contentLookupResult.content
return Opt.some(contentValue)
info "Fetched valid state content from the network"
n.portalProtocol.storeContent(
contentKeyBytes, contentId, contentValueBytes, cacheContent = true
)
# Content was requested `1 + requestRetries` times and all failed on validation
Opt.none(V)
let contentValue = V.decode(contentValueBytes).valueOr:
raiseAssert("Content should already have been validated")
return Opt.some(contentValue)
proc getAccountTrieNode*(
n: StateNetwork, key: AccountTrieNodeKey

View File

@ -153,16 +153,20 @@ type
DbRadiusHandler* = proc(): UInt256 {.raises: [], gcsafe.}
ContentValidationHandler* = proc(
contentKey: ContentKeyByteList, content: seq[byte]
): bool {.raises: [], gcsafe.}
PortalProtocolId* = array[2, byte]
RadiusCache* = LRUCache[NodeId, UInt256]
ContentCache = LRUCache[ContentId, seq[byte]]
ContentKV* = object
contentKey*: ContentKeyByteList
content*: seq[byte]
ContentCache = LRUCache[ContentId, seq[byte]]
OfferRequestType = enum
Direct
Database
@ -1125,11 +1129,19 @@ proc triggerPoke*(
# Offer queue is full, do not start more offer-accept interactions
return
proc defaultContentValidator(
contentKey: ContentKeyByteList, content: seq[byte]
): bool {.raises: [], gcsafe.} =
true
# TODO ContentLookup and Lookup look almost exactly the same, also lookups in other
# networks will probably be very similar. Extract lookup function to separate module
# and make it more generaic
proc contentLookup*(
p: PortalProtocol, target: ContentKeyByteList, targetId: UInt256
p: PortalProtocol,
target: ContentKeyByteList,
targetId: UInt256,
validationHandler: ContentValidationHandler = defaultContentValidator,
): Future[Opt[ContentLookupResult]] {.async: (raises: [CancelledError]).} =
## Perform a lookup for the given target, return the closest n nodes to the
## target. Maximum value for n is `BUCKET_SIZE`.
@ -1212,17 +1224,21 @@ proc contentLookup*(
if closestNodes.len > BUCKET_SIZE:
closestNodes.del(closestNodes.high())
of Content:
# cancel any pending queries as the content has been found
for f in pendingQueries:
f.cancelSoon()
portal_lookup_content_requests.observe(
requestAmount, labelValues = [$p.protocolId]
)
return Opt.some(
ContentLookupResult.init(
content.content, content.utpTransfer, nodesWithoutContent
if validationHandler(target, content.content):
# cancel any pending queries as the content has been found
for f in pendingQueries:
f.cancelSoon()
portal_lookup_content_requests.observe(
requestAmount, labelValues = [$p.protocolId]
)
)
return Opt.some(
ContentLookupResult.init(
content.content, content.utpTransfer, nodesWithoutContent
)
)
else:
# TODO: What should we do if the validation fails?
discard
else:
# TODO: Should we do something with the node that failed responding our
# query?