Draft: draft-pda-core

Commentary: Based on . 
 I have a high level concern with the architecture here , following up from our meeting on Wednesday related to heavy hitters . TL;DR evaluating a hierarchy at only a subset of levels ( which are specified at query time ) is not optimal for performance if the verification step does not have access to those specified levels . For heavy hitters , we are considering a model where records form a binary tree , where aggregates can be reported at different levels of the tree . There are a few possible ways this could go : every record , verify and evaluate the entire binary tree ( i.e. bit - by - bit evaluation ) every record , verify and evaluate only a subset of the tree ( at the limit , this can look more like Prio which only evaluates the last level of the tree ) 2a . The subset of the tree to evaluate is configured within a particular record at record - creation time 2b . The subset of the tree to evaluate is configured dynamically at collection / aggregation time For our use - case ( discussed in ) , we are interested in ( 2b ) , for a few reasons : ( 1 ) is potentially inefficient from both a performance and accuracy POV in that it requires more rounds , and additionally when used with differential privacy requires splitting a DP budget across all levels . Some prefixes might just not be interesting to the caller so we should n't waste privacy budget / compute on them . ( 2a ) is problematic in two dimensions . Embedding configuration in records may compromise user privacy if the configurations can be set by an adversary colluding with one of the aggregators ( e.g. you can leak  choose k ) bits of information if you allow any subset of k prefixes for an n - bit domain ) . Levels must be pre - specified at record - creation time , which is non - ideal if new information comes up at collection time which informs how aggregation should work ( e.g. realizing you have fewer records than expected so it is better to focus on querying only up to a given prefix ) . However , I think ( 2b ) runs into a few issues with this architecture that separates verification from collection , in that without knowing the specific levels to evaluate , we run the risk of spending unnecessary compute verifying levels that will never end up being used . The protocol is more efficient ( less computation , fewer rounds ) if we can verify multiple levels at once ( i.e. the levels the collector cares about ) . Possible solutions : a step where the collector and leader communicate prior to verification ( but keep verification and collection separate ) tightly couple " verify " and " collect " stages , so that verification can be done based on communication with the collector . In our setting , the " leader " and " collector " roles are somewhat merged which aligns with option ( 1 ) , but it seems like a good idea to make the general architecture robust to this use - case . cc who is working on our C++ implementation of IDPFs . 
 can you please file an issue with your comment ? It does n't seem to apply to this PR , which punts on the aggregate protocol . 
 Ah yes sorry , I included it here because I thought it may impact design for upload / verify . Let me delete and re - upload as an issue . 
  ( Allow an aribtrary number of verify requests 
 includes a detailed specification of how helpers advertise HPKE configs for clients to use when encrypting helper shares . We should consider how key rotation works . 
 Might this just be deployment specific , or should all helpers do the same thing ? What we ( i.e. , Cloudflare ) do for is rotate the HPKE config every hour . To add reliability in case a client has an old key , we keep around a rotating set of keys from the last hour or so . The config i d is used to determine which key was used to encrypt the payload . 
 This seems deployment specific . 
 I agree that the rotation mechanism is deployment specific , but how the protocol reacts when a key rotation event occurs , possibly causing a mismatch between client and helper keys , should be in scope . ( I assumed this issue was meant to address the latter . ) 
 2021/06/16 design call : The servers need to agree , roughly , on how long reports are valid . ( Once a helper rotates its key , reports encrypted under the previous key wo n't be decryptable . ) Should the HPKE config lifetime be a paramater ( e.g. , of PAParam ) ? Does the report need to carry this parameter as well ? 
 Do they need to agree , or should the data just naturally become invalid once rotation occurs ? 
 It would be nice if submissions were rejected if they used invalid keys 
 Clients reports encrypted with a public key that 's too old will indeed be rejected by helper . The validity period we 're talking about is of interest for the collector in a protocol like  where the collector makes multiple , interactive requests to the aggregators : collectors need to have a sense of how long after the verify phase they can expect to be able to make collect requests before helpers are unable to decrypt the reports . i.e. , collectors may not be able to make collect queries over reports that are more than 30 days old . In some cases , a validity period of hours could suffice if the successive queries are being made by an automated system , but maybe the queries are being formulated by a team of human analysts who need a day or so to consider what the n+1th query they want to send is based on the results of the nth query . 
 I think we might be converging on something like this : There 's no explicit notion of report lifetime that 's enforced by the protocol . Perhaps what we need instead is a way for the helper to tell the leader which reports are invalid and should be pruned . ( Note that this is not unrelated to . ) 
 Any solution to will also solve this problem . I 'm going to make a call and say this issue is a duplicate and can be closed . 
 Per , the first step in the upload protocol is a POST to the leader server which returns information about helpers . In some yet to be specified cases , the response could include a challenge to be incorporated into reports . However I suspect that in most cases , the response will be static and so an idempotent , cacheable GET would be a better fit , especially since it would save clients a roundtrip to the helper on the hot path of submitting reports , and would make it possible for helpers to implement uploadstart by putting some static content in a CDN . HTTP GET requests do n't have bodies , so we would have to figure out how to encode all the data in the current into the URI , which could be done as either path fragments in the URI ( i.e. , ) or as query parameters ( i.e. , ) . We would of course need to make sure that do n't preclude proof systems like the one alleged to exist in section 5.2 of " BBCp19 " . 
 Also , because the current protocol uses a POST for uploadstart , the implication is that for every report they submit , clients need to hit and then . If we make idempotent and cacheable , then that request can be skipped in the majority of cases . I suppose the significance of this depends on how we expect clients to use PDA : are they going to be continuously streaming hundreds or thousands of reports into the leader as events of interest occur ( in which case I think eliminating requests is interesting ) or are they expected to accumulate several reports locally before submitting them all in one or a few requests ? 
 I agree this would be a good change , if we can swing it . It 's notable that Prio v2 and HH do n't require a random challenge from the leader . But as discussed , this is useful for other proof systems one might want to implement for Prio . For example , see the data type implemented here : See below for an explanation . There might be other ways to implement the challenge . One possibility is to derive the challenge from the TLS key schedule ( or from whatever state the client and leader share for the secure channel ) . and have both pointed out that adding this dependency on the underlying transport is problematic . Another possibility might be to use some external source of randomness that the leader trusts . comes to mind , though it is somewhat problematic because clients would effectively use the same randomness for some period of time . The security model of BBCp19 would need to be extended to account for this . ( I believe this could be done . ) Some other solution I 'm missing ? _ This data type is used to compute the mean and variance of each integer in a sequence of integers . To do so , the client encodes each integer of its input vector as a pair , where is the representation of as a bit vector and is equal to . To prove validity of its input , the proof needs to do two things : verify that ( a ) is indeed a bit vector , and ( b ) the integer encoded by is the square root of . In order to check these things efficiently , the proof system uses a random challenge generated by the leader . At a very high level , the client wants to prove that the following boolean conjunction holds : is 1 or 0 AND is 1 or 0 AND ... AND encodes the square root of . In Prio , we need to represent this conjunction as an arithmetic circuit , which the random challenge helps us do . In a bit more detail now : Let 's rewrite this expression as w[0 ] AND w[1 ] AND ... AND w[s ] . As an arithmetic circuit , this expression is actually , where if and only if w[i ] is true . If the input is valid , then the expression should evaluate to . But because we 're talking about field elements , a malicious client may try to play games like setting and so that the sum is still , but the statement is false . To detect this , we actually compute the following expression : , where is a randomly chosen field element . If the statement is true , then the expression evaluates to . If the statement is false , then with high probability , the expression will evaluate to something other than . ( This idea comes from Theorem 5.3 ) 
 Thank you for the detailed explanalation Having thought about it more , I suspect an HTTP GET would n't be the right way to support proof systems that need a challenge . Assuming that a unique challenge is needed for every report[^1 ] , then intuitively the leader would have to maintain a mapping of reports to issued challenges so that the leader and helper(s ) can later use the challenge value when evaluating the client 's proof . But now that the leader has to maintain state per - report , I think the upload protocol is missing a notion of a report identifier . The contains a , but IIUC the will be the same across many reports[^2 ] ( e.g. , if a browser is sending daily reports on how often users click a button , they use the same every time they submit ) . So needs to include a report ID , which must be echoed back to the leader in so that the leader can look up the challenge for the report . [ ^1 ] Is this true ? Or would it suffice for each to use the same challenge across multiple reports ? [ ^2 ] I notice that the design doc lacks a strong definition of a report and how it relates to tasks , inputs , measurements : because GET should be " safe " in the sense of not changing any server state , and it seems like the server would have to maintain a mapping of reports to issued challenges so that the leader and helper can later use the challenge value when evaluating the client 's proof . 
 Continuing on the assumption that causes a state change on the leader , POST is then the appropriate HTTP method to use . , though it 's a little unusual . There may be some big drawback to cacheable POST responses , but I think that might let us support proof systems that require live challenges while also eliminating the majority of requests when the proof system is such that the will change very rarely . 
 The requirement for the leader to keep state across upload start and upload finish may be avoidable . Suppose the leader has a long - lived PRF key K. It would could choose a random nonce N , compute R = PRF(K , N ) as the challenge , and hand ( N , R ) to the client in its upload start response . The client would then use R to generate the proof and send N to the leader in its upload finish request . The leader could then re - derive R. and I kicked around the idea of having the client generate a report ID . I think this may end up being useful , but we were n't sure if it was better to have the leader choose it or the client . To conform to the security model of BBCp19 , each report would need its own challenge . It may be possible to relax this , but this would be risky . 
 I like this a lot ! I 'm a big fan of deterministic derivation schemes like this -- besides reintroducing the possibility of an idempotent GET , eliminating storage requirements for challenges makes it much easier to scale up leaders . If the report IDs are big enough ( UUIDs ? ) we could even use them as . I would say the leader , because if you let the client do it , then the leader would have to check for things like collisions with existing report IDs or other malicious ID choices . If the leader chooses it , then all it has to do is generate a few bytes of randomness . I agree . 
 we 'd appreciate your feedback on this issue because it involves security considerations for ZKP systems on secret - shared data [ 1 ] . I 'll quickly sum up the problem so that you do n't need to worry about the conversation above . The problem at hand regards systems like [ 1 , Theorem 5.3 ] that require interaction between the prover and verifier . Specifically , we 're considering a special case of Theorem 5.3 in which the verifier sends the prover a random " challenge " that is used to generate and verify a ( non - interactive ) FLPCP ( call it ): We refer to the prover 's first and second messages as " upload start " and " upload finish " respectively . Each message corresponds to an HTTP request made by a web client to a web server . Ideally , the verifier would be able to handle these requests statelessly , i.e. , without having to store . Consider the following change . Suppose the verifier has a long - term PRF key . In response to an upload start request , it chooses a unique " upload i d " --- maybe this is a 16 - byte integer chosen at random --- and computes and sends to the prover . In the upload finish request , the prover sends so that the verifier can re - derive instead of needing to remember it : The ( potential ) problem with this variant is that a malicious client is able to replay a challenge as many times as it wants . This behavior is n't accounted for in Definition 3.9 , so when you try to " compile " this system into a ZKP on secret - shared data as described in Section 6.2 and consider its security with respect to Definition 6.4 ( Setting I ) , you can no longer appeal to Theorem 6.6 to reason about its security . Our question is therefore : What impact do you expect this change to have on the soundness of the system ? Can you think of an attack that exploits the fact that challenges can be replayed ? My expectation is that the problem is merely theoretical , i.e. , we ought to be able to close the gap with an appropriate refinement of Definition of 3.9 and reproving Theorem 5.3 . [ 1 ] 
 Thanks for looping me in . In the discussion below , I am assuming that the client sends its data vector ( called " x " in the paper [ 1 ] ) at the same time as it sends its proof to the verifier . Even if the client sends its data vector in the first message , it seems that the client can reuse an ( R , N ) pair from upload request 1 to later submit upload request 2 , in which case the client sees ( R , N ) before it chooses its data vector and proof . So I think we can assume that the client 's data vector and proof can depend on the verifier 's random value R. ( I 'll call this value " r " to match the paper . ) For soundness : the client / prover must commit to its data vector before it learns the verifier 's random value r. If the prover can choose its data value and proof in a way that depends on the verifier 's randomness r , the soundness guarantee no longer holds . There is also an efficient attack . So I suspect that the stateless - verifier optimization is unsound . The attack works as follows : given r , the verifier finds a non - satisfying vector x such that \sumi ri C(Ai(x ) ) = 0 , again using the notation from Theorem 5.3 in the paper [ 1 ] . For the circuits C used in most applications , it will be easy to find such a vector x. Then , the prover constructs a proof that x that \sumi ri C(Ai(x ) ) = 0 . The proof is valid , so the verifier will accept always . To remove interaction , the Fiat - Shamir - like technique in Section 6.2.3 is probably the best way to go . We only sketched the idea in the paper ( without formal analysis or proof ) but if it 's going to be important for your application , I 'm happy to sanity - check the protocol you come up with . Another idea — the details of which I have not checked carefully — would be to modify your protocol as follows : prover sends its data vector x to verifiers , split into k shares  , ... , xk — one for each of the k verifiers .   a PRF key ki computes ri = PRF(ki , xi ) and they return r = r1 + ... + rk to the prover . The verifiers store no state . prover computes the proof \pi and sends ( x1 , \pi1 ) , ... , ( xk ,  ) to the verifiers — again , one for each of the k verifiers . Each verifier computes ri = PRF(ki , xi ) , they jointly compute r = r1 + ... + r_k , and check the proof using randomness r. The Fiat - Shamir - like approach seems slightly cleaner to me , since it only requires a single prover - to - verifier message . Either way , please let me know if I misunderstood your question or if anything else here is unclear . [ 1 ] 
 Thanks for the nice explanation ,  . It 's perfectly clear and answers my question . It also points to something that I , for one , missed the importance of : the need for the prover to " commit " to the input shares before generating the r - dependent proof . I appreciate you pointing this out . The only requirement for making upload start not idempotent is so that the randomness r can be sent to the client . It would be nice to do away with this requirement . I think the way forward is using the Fiat - Shamir technique . I 'll get cracking on this and let you know when I have something for you to look at . 
 I believe the change is something like this . Let be a hash function with range . Suppose there are verifiers . The prover proceeds as follows : Splits its inputs into shares . For each , do : Choose blinding factor from at random . Let . Set . Derive field element from ( e.g. , seed a PRNG with and map the output to a field element by rejection sampling on chunks of the output ) . Generate the proof using and . Split into shares . For each send to the -th verifier . 
 Yes , this looks right to me ! 
 As of , the upload start request no longer exists . When we 're ready to specify Prio , we 'll want to include the Fiat - Shamir approach for protocols that use joint randomness . I 've noted this and am closing the issue . 
 ( moving a comment made in to an issue ) I have a high level concern with the architecture here , following up from our meeting on Wednesday related to heavy hitters . TL;DR evaluating a hierarchy at only a subset of levels ( which are specified at query time ) is not optimal for performance if the verification step does not have access to those specified levels . For heavy hitters , we are considering a model where records form a binary tree , where aggregates can be reported at different levels of the tree . There are a few possible ways this could go : For every record , verify and evaluate the entire binary tree ( i.e. bit - by - bit evaluation ) For every record , verify and evaluate only a subset of the tree ( at the limit , this can look more like Prio which only evaluates the last level of the tree ) 2a . The subset of the tree to evaluate is configured within a particular record at record - creation time 2b . The subset of the tree to evaluate is configured dynamically at collection / aggregation time For our use - case ( discussed in ( comment ) ) , we are interested in ( 2b ) , for a few reasons : ( 1 ) is potentially inefficient from both a performance and accuracy POV in that it requires more rounds , and additionally when used with differential privacy requires splitting a DP budget across all levels . Some prefixes might just not be interesting to the caller so we should n't waste privacy budget / compute on them . ( 2a ) is problematic in two dimensions . Embedding configuration in records may compromise user privacy if the configurations can be set by an adversary colluding with one of the aggregators ( e.g. you can leak  choose k ) bits of information if you allow any subset of k prefixes for an n - bit domain ) . Levels must be pre - specified at record - creation time , which is non - ideal if new information comes up at collection time which informs how aggregation should work ( e.g. realizing you have fewer records than expected so it is better to focus on querying only up to a given prefix ) . However , I think ( 2b ) runs into a few issues with this architecture that separates verification from collection , in that without knowing the specific levels to evaluate , we run the risk of spending unnecessary compute verifying levels that will never end up being used . The protocol is more efficient ( less computation , fewer rounds ) if we can verify multiple levels at once ( i.e. the levels the collector cares about ) . Possible solutions : a step where the collector and leader communicate prior to verification ( but keep verification and collection separate ) tightly couple " verify " and " collect " stages , so that verification can be done based on communication with the collector . In our setting , the " leader " and " collector " roles are somewhat merged which aligns with option ( 1 ) , but it seems like a good idea to make the general architecture robust to this use - case . cc who is working on our C++ implementation of IDPFs . 
 Thanks for the feedback I 've finally had a chance today to dig into the heavy hitters paper . I agree that the framework described in is n't going to be feasible for heavy hitters . I think your option ( 2b ) is the only way to go , as I explain below . For context , thinks of PA ( " Private Aggregation " ) protocols as having three distinct " phases " : Upload : Each client uploads its input shares to the aggregators . Verify : The aggregators verify each client 's input . Collect : Each aggregators add up their verified input shares and emits its output share to the collector . In order to fit heavy hitters into his framework , we could take your option ( 1 ) or ( 2a ) . However , these options are only feasible for very small inputs , say , , where is the length of each client input in bits . In order to make the protocol feasible for large inputs , heavy hitters involves multiple rounds of the Verify / Collect phases , where the output of the previous Collect phase is used to decide what to do in the next  phase . So that we 're all on the same page about why interaction is necessary , I 've included a brief overview of the protocol below [ 1 ] . ( Please correct me if I 've misunderstood anything ! ) Accounting for this interaction in the protocol spec should be fairly straightforward . The delta , I think , is that there needs to be a way to re - run the Verify / Collect phases on the same inputs , but using different parameters each time , i.e. , with different sets of prefixes specified by the collector . _ [ 1 ] Here 's a bird 's eye view of the protocol , as I understand it . ( I 'm ignoring DP for now . ) First , ( Upload ) each client generates IDPF shares of its -bit input and sends a share to each aggregator . After all clients have uploaded their IDPF key shares , the collector and aggregators verify and collect the heavy hitters as follows . Let . For each : ( Verify ) For each client upload : a. For each prefix in , each aggregator evaluates its IDPF share on input of . b. The aggregators verify ( in zero - knowledge ) that their IDPF outputs are well - formed , i.e. , that only one of the outputs is and the rest are . ( Collect ) a. For each verified IDPF output , each aggregator adds up its shares and sends the resulting " histogram share " to the collector . b. The collector adds the histogram shares together to get the histogram . This encodes the frequency of each prefix in among the client inputs .  The collector assembles , the next set of candidate prefixes . To do so , it collects the set of prefixes that appear at least times in the histogram . It lets be the set of strings obtained by appending or to each element of . d. Finally , the collector disseminates among the aggregators . What makes this feasible is that the size of each is at most the number of clients divided . 
 this sounds about right to me . Just a small nit but I wanted to point out that your ( Collect ) stage , multiple interactions with the collector are not strictly necessary . What you described is a zero - knowledge protocol but alternatively the aggregators could interact to learn the aggregates at each level and proceed down without interaction with the collector . We discussed this variant a few weeks ago , it trades off aggregator knowledge with protocol simplicity ( e.g. collectors do n't need to follow an interactive protocol ) . 
 Let me see if I understand you correctly . Concretely , the helper would send the leader its share of the aggregate at the end of each round . Next , the leader would use the aggregate shares to compute the next set of prefixes , then send this to the helper for the next round . If my understanding is right , then it seems to me that this is the same protocol as [ 1 ] , but with leader = = collector . What do you think ? Maybe I 'm missing something . I 'll do my best to read the design doc you shared by the end of the week . 
 Yes that 's right . I was outlining a " simpler " communication model where helpers communicated directly with each other but in this architecture it would go through the leader . 
 I wrote up my outstanding questions and comments in distinct issues in the name of unblocking progress here ( , , ) . Otherwise this looks good to me .
Paragraph (version B):
This section specifies a protocol for executing generic PA tasks.
Concrete PA protocols are described in prio and hits.
Paragraph (version B):
Each round of the protocol corresponds to an HTTP request and
response.  The content type of each request is "application/octet-
stream".  We assume that some transport layer security protocol
(e.g., TLS or QUIC) is used between each pair of parties and that the
server is always authenticated.
Paragraph (version B):
The first field, "version" specifies the version of this document.
The second field, "id" is an opaque identifier used by the clients,
aggregators, and collector to uniquely identify the PA task at hand.
We will call it the _task id_ in the remainder.
Paragraph (version B):
The "batch_size" field encodes the _batch size_, the number of input
shares accumulated by each aggregator before emitting its output
share.  The "proto" field identifies the specific PA protocol in use.
The rest of the structure contains any protocol-specific parameters
that are required.
Paragraph (version B):
We call this the helper's _key configation_. The key configuration is
used to set up a base-mode HPKE context to use to derive symmetric
keys for protecting the shares sent to the helper.  The _config id_,
"HpkeConfig.id", is forwarded by the client to the helper, who uses
this value to decide if it knows how to decrypt a share it receives.
Paragraph (version B):
The client, aggregators, and collector are configured with a
specific PA task.
Paragraph (version B):
The client and leader can establish a leader-authenticated secure
channel.
Paragraph (version B):
The leader and each helper can establish a leader-authenticated
secure channel.
Paragraph (version B):
Each helper has chosen an HPKE key pair.
Paragraph (version B):
The aggregators agree on a set of PA tasks, as well as the PA
protocol and parameters used for each task.
Paragraph (version B):
Uploading a report involves two requests to the leader.  In the
_upload start request_, the client discovers the protocol-specific
parameters it needs to generate the report, as well as the endpoint
URL of each helper.  In the _upload finish request_, it uploads its
report to the leader.
Paragraph (version B):
The "task" field corresponds to the PA task for which a report will
be generated.
Paragraph (version B):
The message includes the URL of each helper and any protocol-specific
parameters the client needs to generate its report.  The leader's
response to malformed requests is specified in pa-error-common-
aborts.
Paragraph (version B):
the key config specifies a KEM, KDF, or AEAD algorithm the client
doesn't recognize.
Paragraph (version B):
If the set of supported helpers is empty, then the client aborts and
alerts the leader with "no supported helpers".  Otherwise, for each
supported helper the client issues a POST request to
"[leader]/upload_finish" with a payload constructed as described
below.
Paragraph (version B):
where "pk" is the KEM public key encoded by "HpkeConfig.public_key".
The outputs are the helper's encapsulated context "enc" and the
context "context".
Paragraph (version B):
Next, the client encodes its measurements as an input for the PA
protocol.  It then generates a validity proof for its input and uses
"context" to split the input and proof into a _leader share_ and a
_helper share_, where the latter is protected by the HPKE context.
Note that the details of each of these processing steps -- encode,
prove, split, and encrypt -- are specific to the PA protocol.
Paragraph (version B):
Note that the leader share is sent not encrypted.
Paragraph (version B):
The leader responds to well-formed requests to
"[leader]/upload_finish" with status 200 and an empty body.
Malformed requests are handled as described in pa-error-common-
aborts.
Paragraph (version B):
After the client uploads a report to the leader, the leader and
helper verify in zero knowledge that the proof is well-formed.  The
exact procedure for doing so is protocol specific, but all protocols
have the same basic structure.  In particular, the protocol is
comprised of a sequence of HTTPS requests from the leader to the
helper.  At the end of this phase, the leader and helper will have
decided whether a set of client inputs are valid.  For each valid
input, they proceed as described in pa-collect.
Paragraph (version B):
The "enc" field is the helper's encapsulated HPKE context sent in the
report.  The remainder of the me structure is dedicated to the
protocol-specific helper share and request parameters used for the
current round.
Paragraph (version B):
where "sk" is the secret key corresponding to the HPKE config.  Next,
it computes the body of the "PAVerifyResp" according to the PA
protocol.
Paragraph (version B):
where "task" is the associated PA task (this value is always known)
and "payload" is the message.  When sent by an aggregator in response
to an HTTP request, the response status is 400.  When sent in a
request to an aggregator, the URL is always "[aggregator]/error",
where "[aggregator]" is the URL of the aggregator endpoint.
Paragraph (version B):
The following specify the "boiler-plate" behavior for various error
conditions.
Paragraph (version B):
The message type for the payload of each request and response is
unique for a given URL.  If ever a client, aggregator, or
collector receives a request or response to a request with a
malformed payload, then the receiver aborts and alerts the peer
with "unrecognized message".
Paragraph (version B):
Each POST request to an aggregator contains a "PATask".  If the
aggregator does not recognize the task, i.e., it can't find a
"PAParam" for which "PATask.id == PAParam.task.id", then it aborts
and alerts the peer with "unrecognized task".