Skip to content

Commit 28593fb

Browse files
committed
mirror_worker: address lbaquerofierro review on PR 273
- Return the Display form of a malformed reference-checkpoint parse error, matching add-checkpoint, so the Rust type name stays out of the 400 body. - Reuse tlog_witness::MAX_REQUEST_BODY_SIZE for the sign-subtree body cap instead of duplicating the constant the parser already enforces. - Log the previously-silent rejections (unknown origin 404, subtree bounds 400s, consistency-proof 422) with the subtree range and checkpoint size, so operators can tell which check failed. - Map the unexpected verify errors (MismatchedVerifier/AmbiguousKey, unreachable over a one-element verifier list) to 500 rather than blaming the client with 400.
1 parent c07e423 commit 28593fb

1 file changed

Lines changed: 24 additions & 15 deletions

File tree

crates/mirror_worker/src/frontend_worker.rs

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ use signed_note::{NoteError, NoteVerifier, VerifierList};
4545
use tlog_checkpoint::{CheckpointSigner as _, CheckpointText};
4646
use tlog_core::{Subtree, verify_subtree_consistency_proof};
4747
use tlog_witness::{
48-
AddCheckpointRequest, CONTENT_TYPE_TLOG_SIZE, SignSubtreeRequest, parse_add_checkpoint_request,
49-
parse_sign_subtree_request, serialize_sign_subtree_response,
48+
AddCheckpointRequest, CONTENT_TYPE_TLOG_SIZE, MAX_REQUEST_BODY_SIZE, SignSubtreeRequest,
49+
parse_add_checkpoint_request, parse_sign_subtree_request, serialize_sign_subtree_response,
5050
};
5151
use tower_service::Service as _;
5252
#[allow(clippy::wildcard_imports)]
@@ -87,7 +87,7 @@ async fn fetch(
8787
)
8888
.route(
8989
"/sign-subtree",
90-
post(sign_subtree).layer(DefaultBodyLimit::max(MAX_SIGN_SUBTREE_BODY_SIZE)),
90+
post(sign_subtree).layer(DefaultBodyLimit::max(MAX_REQUEST_BODY_SIZE)),
9191
)
9292
.route("/metadata", get(metadata))
9393
.route("/", get(root))
@@ -386,25 +386,33 @@ async fn sign_subtree(State(env): State<Env>, body: Bytes) -> ApiResult<axum::re
386386
Ok(t) => t,
387387
Err(e) => {
388388
log::warn!("sign-subtree: malformed checkpoint text: {e:?}");
389-
return Err(AppError::BadRequest(format!("{e:?}")));
389+
return Err(AppError::BadRequest(e.to_string()));
390390
}
391391
};
392392
if subtree_end > cp_text.size() {
393+
log::info!(
394+
"sign-subtree: subtree end {subtree_end} exceeds checkpoint size {}",
395+
cp_text.size()
396+
);
393397
return Err(AppError::BadRequest(format!(
394398
"subtree end {subtree_end} > checkpoint size {}",
395399
cp_text.size()
396400
)));
397401
}
398402
let subtree = match Subtree::new(subtree_start, subtree_end) {
399403
Ok(s) => s,
400-
Err(e) => return Err(AppError::BadRequest(format!("invalid subtree: {e:?}"))),
404+
Err(e) => {
405+
log::info!("sign-subtree: invalid subtree [{subtree_start}, {subtree_end}): {e:?}");
406+
return Err(AppError::BadRequest(format!("invalid subtree: {e:?}")));
407+
}
401408
};
402409

403410
// Look up the log by its origin. Subtree DoS-protection cosignatures
404411
// in the request are ignored: this implementation applies no
405412
// pre-screening policy, as the spec leaves their use to the operator.
406413
let origin = cp_text.origin();
407414
if log_verifiers(origin).is_none() {
415+
log::info!("sign-subtree: unknown log origin {origin:?}");
408416
return Err(AppError::UnknownLogOrigin);
409417
}
410418

@@ -420,9 +428,13 @@ async fn sign_subtree(State(env): State<Env>, body: Bytes) -> ApiResult<axum::re
420428
log::info!("sign-subtree: reference checkpoint not cosigned by this mirror: {e:?}");
421429
return Err(AppError::ReferenceCheckpointNotCosignedByThisMirror);
422430
}
431+
// `MismatchedVerifier`/`AmbiguousKey` mean the verifier list
432+
// we built is malformed, not that the client sent a bad
433+
// request; over a one-element list they are unreachable
434+
// today. Surface them as 500 rather than blaming the client.
423435
_ => {
424-
log::warn!("sign-subtree: checkpoint verify failed: {e:?}");
425-
return Err(AppError::BadRequest(e.to_string()));
436+
log::error!("sign-subtree: checkpoint verify failed unexpectedly: {e:?}");
437+
return Err(AppError::InternalServerError(e.to_string()));
426438
}
427439
}
428440
}
@@ -438,6 +450,11 @@ async fn sign_subtree(State(env): State<Env>, body: Bytes) -> ApiResult<axum::re
438450
)
439451
.is_err()
440452
{
453+
log::info!(
454+
"sign-subtree: consistency proof failed for subtree [{subtree_start}, {subtree_end}) \
455+
against checkpoint size {}",
456+
cp_text.size()
457+
);
441458
return Err(AppError::UnprocessableEntity(
442459
"subtree consistency proof failed".to_owned(),
443460
));
@@ -458,14 +475,6 @@ async fn sign_subtree(State(env): State<Env>, body: Bytes) -> ApiResult<axum::re
458475
// Helpers
459476
// ---------------------------------------------------------------------------
460477

461-
/// Maximum `sign-subtree` request body, enforced by the route's
462-
/// [`DefaultBodyLimit`] layer. A well-formed request is a `subtree` range
463-
/// line, a base64 hash line, up to 8 subtree cosignature lines (~3.3 KiB
464-
/// each for ML-DSA-44), up to 63 base64 consistency-proof hash lines, and
465-
/// a reference checkpoint of up to `signed_note::MAX_NOTE_SIZE` (1 MiB);
466-
/// 1 MiB plus 64 KiB of headroom covers that.
467-
const MAX_SIGN_SUBTREE_BODY_SIZE: usize = 1_024 * 1_024 + 64 * 1_024;
468-
469478
/// Maximum `add-checkpoint` request body, enforced by the route's
470479
/// [`DefaultBodyLimit`] layer. A well-formed request is an `old <N>`
471480
/// line, up to 63 base64 hash lines, and a checkpoint note of up to

0 commit comments

Comments
 (0)