mirror of
https://github.com/rustfs/rustfs.git
synced 2026-09-10 06:05:52 +00:00
test(scanner): bind G14 case artifacts
Require Scanner/Heal G14 release bundle JSON wrappers to mirror their outer evidence and carry self-contained case artifact provenance. Copy proof-json case artifacts into the generated G14 descriptor bundle so assembled release bundles can validate case file hashes after relocation. Co-Authored-By: heihutu <heihutu@gmail.com> Co-Authored-By: zhi22915 <qiuzgang@gmail.com>
This commit is contained in:
@@ -1961,6 +1961,24 @@ def release_bundle_json_artifact_mirrored_fields(gate: str, field: str) -> tuple
|
||||
"grace_outcomes_retained",
|
||||
),
|
||||
}[field])
|
||||
if gate == "G14":
|
||||
fields.append("case_evidence")
|
||||
if field == "same_window_field_evidence":
|
||||
fields.append("same_window_fields")
|
||||
if field == "ec8_4_evidence":
|
||||
fields.append("topology")
|
||||
if field == "multi_set_evidence":
|
||||
fields.append("sets")
|
||||
if field == "multi_pool_evidence":
|
||||
fields.append("pools")
|
||||
if field == "distributed_segment_invalidation_evidence":
|
||||
fields.extend((
|
||||
"invalidation_domain",
|
||||
"distributed_ec_invalidation",
|
||||
"peer_count",
|
||||
"same_window_remote_proof",
|
||||
"all_peers_bound_to_generation_window",
|
||||
))
|
||||
if (gate, field) in SCANNER_HEAL_RELEASE_MRF_DURABLE_REPLAY_FIELDS:
|
||||
fields.extend(("replayed_records", "responsibility_anchor_retained", "successor_snapshot_published"))
|
||||
if gate == "P4" and field == "retained_responsibility_evidence":
|
||||
@@ -2050,9 +2068,57 @@ def validate_profile_json_artifact_payload(bundle_path: Path, path: Path, payloa
|
||||
evidence_integer(payload.get(metric), f"{prefix}.{metric}", 1, 2**63 - 1)
|
||||
|
||||
|
||||
def validate_g14_json_artifact_payload(bundle_path: Path, payload: dict[str, object],
|
||||
evidence: dict[str, object] | None, gate: str, field: str,
|
||||
source_revision: str, window_id: str) -> None:
|
||||
prefix = f"{gate}.{field}"
|
||||
require(evidence is not None, f"{prefix} JSON artifact missing outer evidence context")
|
||||
for mirror_field in release_bundle_json_artifact_mirrored_fields(gate, field):
|
||||
require(payload.get(mirror_field) == evidence.get(mirror_field),
|
||||
f"{prefix} JSON artifact {mirror_field} mismatch")
|
||||
cases = payload.get("case_evidence")
|
||||
require(isinstance(cases, list) and cases, f"{prefix} JSON artifact missing case_evidence")
|
||||
seen_cases = set()
|
||||
for index, item in enumerate(cases):
|
||||
require(isinstance(item, dict), f"{prefix} JSON artifact case_evidence[{index}] must be an object")
|
||||
case = evidence_string(
|
||||
item.get("case"),
|
||||
f"{prefix} JSON artifact case_evidence[{index}].case",
|
||||
r"[A-Za-z0-9][A-Za-z0-9._:-]{1,127}",
|
||||
)
|
||||
require(case not in seen_cases, f"{prefix} JSON artifact case_evidence has duplicate case: {case}")
|
||||
seen_cases.add(case)
|
||||
artifact = release_bundle_artifact_path(
|
||||
bundle_path,
|
||||
item.get("artifact"),
|
||||
gate,
|
||||
f"{field}.case_evidence.{case}",
|
||||
)
|
||||
require(sha(item.get("sha256")) and digest(artifact) == item["sha256"],
|
||||
f"{prefix} JSON artifact case_evidence[{index}] artifact hash mismatch")
|
||||
require(item.get("source_revision") == source_revision,
|
||||
f"{prefix} JSON artifact case_evidence[{index}].source_revision mismatch")
|
||||
require(item.get("measurement_window_id") == window_id,
|
||||
f"{prefix} JSON artifact case_evidence[{index}].measurement_window_id mismatch")
|
||||
case_payload = read_json(artifact)
|
||||
require(isinstance(case_payload, dict), f"{prefix} JSON artifact case_evidence[{index}] artifact must be a JSON object")
|
||||
for marker in ("fixture", "fixture_only", "dry_run", "synthetic"):
|
||||
require(case_payload.get(marker) is not True,
|
||||
f"{prefix} JSON artifact case_evidence[{index}] artifact is {marker}")
|
||||
require(case_payload.get("case") == case,
|
||||
f"{prefix} JSON artifact case_evidence[{index}] artifact case mismatch")
|
||||
if "source_revision" in case_payload:
|
||||
require(case_payload["source_revision"] == source_revision,
|
||||
f"{prefix} JSON artifact case_evidence[{index}] artifact source revision mismatch")
|
||||
if "measurement_window_id" in case_payload:
|
||||
require(case_payload["measurement_window_id"] == window_id,
|
||||
f"{prefix} JSON artifact case_evidence[{index}] artifact measurement window mismatch")
|
||||
|
||||
|
||||
def validate_release_bundle_json_artifact_payload(bundle_path: Path, path: Path, source_revision: str, gate: str, field: str,
|
||||
run_id: str, window_id: str,
|
||||
artifact_kind: str | None = None) -> None:
|
||||
artifact_kind: str | None = None,
|
||||
evidence: dict[str, object] | None = None) -> None:
|
||||
payload = read_json(path)
|
||||
prefix = f"{gate}.{field}"
|
||||
for marker in ("fixture", "fixture_only", "dry_run", "synthetic"):
|
||||
@@ -2077,6 +2143,8 @@ def validate_release_bundle_json_artifact_payload(bundle_path: Path, path: Path,
|
||||
if not mirror_fields:
|
||||
return
|
||||
validate_release_bundle_domain_evidence(gate, field, payload)
|
||||
if gate == "G14":
|
||||
validate_g14_json_artifact_payload(bundle_path, payload, evidence, gate, field, source_revision, window_id)
|
||||
if gate in ("G03", "G09", "R-L"):
|
||||
versions = payload.get("versions")
|
||||
require(isinstance(versions, list) and
|
||||
@@ -2610,7 +2678,16 @@ def validate_release_bundle_artifact(bundle_path: Path, source_revision: str, ga
|
||||
artifact = release_bundle_artifact_path(bundle_path, evidence.get("artifact"), gate, field)
|
||||
require(sha(evidence.get("sha256")) and digest(artifact) == evidence["sha256"], f"{gate}.{field} artifact hash mismatch")
|
||||
if is_json_artifact_format(artifact_format):
|
||||
validate_release_bundle_json_artifact_payload(bundle_path, artifact, source_revision, gate, field, run_id, window_id)
|
||||
validate_release_bundle_json_artifact_payload(
|
||||
bundle_path,
|
||||
artifact,
|
||||
source_revision,
|
||||
gate,
|
||||
field,
|
||||
run_id,
|
||||
window_id,
|
||||
evidence=evidence,
|
||||
)
|
||||
summary = evidence.get("summary")
|
||||
require(isinstance(summary, str) and summary.strip(), f"{gate}.{field} missing human summary")
|
||||
if gate.startswith("P"):
|
||||
@@ -3023,6 +3100,41 @@ def copy_release_bundle_profile_raw_artifact(descriptor_path: Path, bundle_dir:
|
||||
item["sha256"] = digest(wrapper)
|
||||
|
||||
|
||||
def copy_release_bundle_g14_case_artifacts(descriptor_path: Path, bundle_dir: Path, evidence: dict[str, object],
|
||||
gate: str, field: str) -> None:
|
||||
wrapper = release_bundle_artifact_path(bundle_dir / "release-evidence.json", evidence.get("artifact"), gate, field)
|
||||
payload = read_json(wrapper)
|
||||
cases = payload.get("case_evidence")
|
||||
require(isinstance(cases, list), f"{gate}.{field} descriptor case_evidence must be a list")
|
||||
artifact_dir = bundle_dir / "artifacts"
|
||||
for index, item in enumerate(cases):
|
||||
require(isinstance(item, dict), f"{gate}.{field} descriptor case_evidence[{index}] must be an object")
|
||||
case = evidence_string(
|
||||
item.get("case"),
|
||||
f"{gate}.{field}.case_evidence[{index}].case",
|
||||
r"[A-Za-z0-9][A-Za-z0-9._:-]{1,127}",
|
||||
)
|
||||
source = release_bundle_descriptor_path(
|
||||
descriptor_path,
|
||||
item.get("artifact"),
|
||||
gate,
|
||||
f"{field}.case_evidence.{case}",
|
||||
)
|
||||
source_sha = digest(source)
|
||||
require(sha(item.get("sha256")) and item["sha256"] == source_sha,
|
||||
f"{gate}.{field} descriptor case_evidence[{index}] artifact hash mismatch")
|
||||
safe_case = re.sub(r"[^A-Za-z0-9._-]", "-", case)
|
||||
target = artifact_dir / f"{gate}-{field}-case-{safe_case}{release_bundle_artifact_suffix(source)}"
|
||||
require(not target.exists(), f"{gate}.{field} duplicate assembled case evidence path")
|
||||
shutil.copyfile(source, target)
|
||||
require(digest(target) == source_sha, f"{gate}.{field} assembled case evidence hash mismatch")
|
||||
item["artifact"] = target.relative_to(bundle_dir).as_posix()
|
||||
item["sha256"] = source_sha
|
||||
write_json(wrapper, payload)
|
||||
evidence["case_evidence"] = payload["case_evidence"]
|
||||
evidence["sha256"] = digest(wrapper)
|
||||
|
||||
|
||||
def assemble_scanner_heal_release_bundle_descriptors(
|
||||
root: Path,
|
||||
descriptor_paths: list[Path],
|
||||
@@ -3076,6 +3188,8 @@ def assemble_scanner_heal_release_bundle_descriptors(
|
||||
evidence = json.loads(json.dumps(raw_evidence))
|
||||
reject_release_bundle_markers(evidence, f"{gate}.{field} descriptor evidence")
|
||||
copy_release_bundle_artifact(descriptor_path, bundle_dir, evidence, gate, field, f"{gate}-{field}")
|
||||
if gate == "G14":
|
||||
copy_release_bundle_g14_case_artifacts(descriptor_path, bundle_dir, evidence, gate, field)
|
||||
profile_artifacts = evidence.get("profile_artifacts")
|
||||
if profile_artifacts is not None:
|
||||
require(isinstance(profile_artifacts, dict), f"{gate}.{field} profile artifacts must be an object")
|
||||
@@ -3801,6 +3915,34 @@ class SelfTests(unittest.TestCase):
|
||||
evidence["peer_count"] = 3
|
||||
evidence["same_window_remote_proof"] = True
|
||||
evidence["all_peers_bound_to_generation_window"] = True
|
||||
if gate == "G14":
|
||||
case_id = "ec84-multiset-multipool"
|
||||
case_artifact = artifact_dir / "cases" / f"{gate}-{field}-{case_id}.json"
|
||||
case_artifact.parent.mkdir(parents=True, exist_ok=True)
|
||||
write_json(case_artifact, {
|
||||
"schema": 1,
|
||||
"case": case_id,
|
||||
"evidence_type": "measured",
|
||||
"source_revision": source_revision,
|
||||
"measurement_window_id": window_id,
|
||||
"topology": {"erasure": "EC8+4", "nodes": 3, "drives_per_node": 4},
|
||||
"sets": 2,
|
||||
"pools": 2,
|
||||
"invalidation_domain": "distributed-ec",
|
||||
"distributed_ec_invalidation": True,
|
||||
"peer_count": 3,
|
||||
"same_window_remote_proof": True,
|
||||
"all_peers_bound_to_generation_window": True,
|
||||
})
|
||||
evidence["case_evidence"] = [
|
||||
{
|
||||
"case": case_id,
|
||||
"artifact": case_artifact.relative_to(bundle_dir).as_posix(),
|
||||
"sha256": digest(case_artifact),
|
||||
"source_revision": source_revision,
|
||||
"measurement_window_id": window_id,
|
||||
},
|
||||
]
|
||||
if field == "segment_activation_preflight":
|
||||
evidence["production_activation"] = False
|
||||
evidence["scanner_segment_reuse_activated"] = False
|
||||
@@ -4146,7 +4288,7 @@ class SelfTests(unittest.TestCase):
|
||||
|
||||
def test_scanner_heal_release_bundle_enforces_topology_duration_profile_and_versions(self) -> None:
|
||||
for fault, gate, field, mutation, expected in (
|
||||
("topology", "G14", "ec8_4_evidence", lambda item: item.update({"topology": {"erasure": "EC4+2", "nodes": 2, "drives_per_node": 3}}), "EC8+4"),
|
||||
("topology", "G14", "ec8_4_evidence", lambda item: item.update({"topology": {"erasure": "EC4+2", "nodes": 2, "drives_per_node": 3}}), "JSON artifact topology mismatch"),
|
||||
("missing-duration", "P1", "cold_walk_share_measurement", lambda item: item.pop("duration_seconds"), "duration_seconds"),
|
||||
("duration", "P3", "two_hour_pressure_measurement", lambda item: item.update({"duration_seconds": 7199}), "two hours"),
|
||||
("mrf-cleanup-soak-duration", "P4", "mrf_cleanup_gc_soak_evidence", lambda item: item.update({"duration_seconds": 7199}), "two hours"),
|
||||
@@ -4304,7 +4446,7 @@ class SelfTests(unittest.TestCase):
|
||||
lambda item: item.update({"stale_journals_after_gc": 1}),
|
||||
"zero stale journals",
|
||||
),
|
||||
("same-window-fields", "G14", "same_window_field_evidence", lambda item: item.update({"same_window_fields": ["ec8_4_evidence", "multi_set_evidence"]}), "missing fields"),
|
||||
("same-window-fields", "G14", "same_window_field_evidence", lambda item: item.update({"same_window_fields": ["ec8_4_evidence", "multi_set_evidence"]}), "JSON artifact same_window_fields mismatch"),
|
||||
("activation-enabled", "G11", "segment_activation_preflight", lambda item: item.update({"production_activation": True}), "production activation disabled"),
|
||||
(
|
||||
"activation-missing-fail-closed",
|
||||
@@ -4409,14 +4551,14 @@ class SelfTests(unittest.TestCase):
|
||||
"G14",
|
||||
"distributed_segment_invalidation_evidence",
|
||||
lambda item: item.update({"distributed_ec_invalidation": False}),
|
||||
"peer invalidation proof",
|
||||
"JSON artifact distributed_ec_invalidation mismatch",
|
||||
),
|
||||
(
|
||||
"distributed-peer-window",
|
||||
"G14",
|
||||
"distributed_segment_invalidation_evidence",
|
||||
lambda item: item.update({"all_peers_bound_to_generation_window": False}),
|
||||
"peer generation-window binding",
|
||||
"JSON artifact all_peers_bound_to_generation_window mismatch",
|
||||
),
|
||||
(
|
||||
"post-stop-work-multiple",
|
||||
@@ -4666,6 +4808,18 @@ class SelfTests(unittest.TestCase):
|
||||
("R-L", "migration_gap_evidence"),
|
||||
"migration_gap_closed",
|
||||
),
|
||||
(
|
||||
"g14-topology-mirror",
|
||||
lambda payload: payload.update({"topology": {"erasure": "EC8+4", "nodes": 4, "drives_per_node": 4}}),
|
||||
("G14", "ec8_4_evidence"),
|
||||
"JSON artifact topology mismatch",
|
||||
),
|
||||
(
|
||||
"g14-case-source",
|
||||
lambda payload: payload["case_evidence"][0].update({"source_revision": "c" * 40}),
|
||||
("G14", "multi_pool_evidence"),
|
||||
"JSON artifact case_evidence mismatch",
|
||||
),
|
||||
):
|
||||
with self.subTest(fault=fault), tempfile.TemporaryDirectory() as tmp:
|
||||
root, bundle = self.scanner_heal_release_bundle_fixture(Path(tmp))
|
||||
@@ -4713,7 +4867,12 @@ class SelfTests(unittest.TestCase):
|
||||
status = scanner_heal_release_bundle_status(root, bundle)
|
||||
self.assertEqual(status["decision"], "blocked")
|
||||
self.assertFalse(status["release_approved"])
|
||||
self.assertTrue(any("must share one measurement window" in error
|
||||
expected = (
|
||||
"case_evidence[0].measurement_window_id mismatch"
|
||||
if gate == "G14"
|
||||
else "must share one measurement window"
|
||||
)
|
||||
self.assertTrue(any(expected in error
|
||||
for error in status["rejected_gates"][gate]))
|
||||
|
||||
def test_scanner_heal_release_bundle_requires_domain_evidence(self) -> None:
|
||||
|
||||
@@ -56,6 +56,20 @@ def parse_case_dir_arg(value: str) -> tuple[str | None, Path]:
|
||||
return None, Path(value).expanduser().resolve()
|
||||
|
||||
|
||||
def proof_case_artifact_path(proof_path: Path, sample: dict[str, Any], index: int) -> Path:
|
||||
raw_artifact = sample.get("artifact")
|
||||
require(isinstance(raw_artifact, str) and raw_artifact.strip(), f"G14 case evidence {index} missing artifact")
|
||||
artifact = Path(raw_artifact)
|
||||
require(not artifact.is_absolute() and ".." not in artifact.parts,
|
||||
f"G14 case evidence {index} artifact path escapes proof directory")
|
||||
resolved = (proof_path.parent / artifact).resolve()
|
||||
require(resolved.is_relative_to(proof_path.parent.resolve()),
|
||||
f"G14 case evidence {index} artifact path escapes proof directory")
|
||||
require(resolved.is_file(), f"G14 case evidence {index} artifact is missing")
|
||||
require(resolved.stat().st_size > 0, f"G14 case evidence {index} artifact is empty")
|
||||
return resolved
|
||||
|
||||
|
||||
def load_proof(path: Path, source_revision: str) -> dict[str, Any]:
|
||||
proof = read_json(path)
|
||||
for marker in ("fixture", "fixture_only", "dry_run", "synthetic"):
|
||||
@@ -88,11 +102,23 @@ def load_proof(path: Path, source_revision: str) -> dict[str, Any]:
|
||||
require(isinstance(sample.get("case"), str) and sample["case"].strip(), f"G14 case evidence {index} missing case")
|
||||
require(isinstance(sample.get("sha256"), str) and re.fullmatch(r"[0-9a-f]{64}", sample["sha256"]),
|
||||
f"G14 case evidence {index} missing sha256")
|
||||
if "source_revision" in sample:
|
||||
require(sample["source_revision"] == source_revision, f"G14 case evidence {index} source revision mismatch")
|
||||
if "measurement_window_id" in sample:
|
||||
require(sample["measurement_window_id"] == proof["measurement_window_id"],
|
||||
f"G14 case evidence {index} measurement window mismatch")
|
||||
artifact = proof_case_artifact_path(path, sample, index)
|
||||
require(digest(artifact) == sample["sha256"], f"G14 case evidence {index} artifact hash mismatch")
|
||||
require(sample.get("source_revision") == source_revision,
|
||||
f"G14 case evidence {index} source revision mismatch")
|
||||
require(sample.get("measurement_window_id") == proof["measurement_window_id"],
|
||||
f"G14 case evidence {index} measurement window mismatch")
|
||||
artifact_payload = read_json(artifact)
|
||||
require(isinstance(artifact_payload, dict), f"G14 case evidence {index} artifact must be a JSON object")
|
||||
for marker in ("fixture", "fixture_only", "dry_run", "synthetic"):
|
||||
require(artifact_payload.get(marker) is not True, f"G14 case evidence {index} artifact is {marker}")
|
||||
require(artifact_payload.get("case") == sample["case"], f"G14 case evidence {index} artifact case mismatch")
|
||||
if "source_revision" in artifact_payload:
|
||||
require(artifact_payload["source_revision"] == source_revision,
|
||||
f"G14 case evidence {index} artifact source revision mismatch")
|
||||
if "measurement_window_id" in artifact_payload:
|
||||
require(artifact_payload["measurement_window_id"] == proof["measurement_window_id"],
|
||||
f"G14 case evidence {index} artifact measurement window mismatch")
|
||||
return proof
|
||||
|
||||
|
||||
@@ -176,6 +202,27 @@ def copy_case_artifacts(out_dir: Path, records: list[dict[str, Any]], window_id:
|
||||
return copied
|
||||
|
||||
|
||||
def copy_proof_case_artifacts(out_dir: Path, proof_path: Path, samples: list[dict[str, Any]], window_id: str,
|
||||
source_revision: str) -> list[dict[str, Any]]:
|
||||
case_dir = out_dir / "artifacts" / "cases"
|
||||
case_dir.mkdir(parents=True, exist_ok=True)
|
||||
copied = []
|
||||
for index, sample in enumerate(samples):
|
||||
source = proof_case_artifact_path(proof_path, sample, index)
|
||||
safe_case = re.sub(r"[^A-Za-z0-9._-]", "-", sample["case"])
|
||||
target = case_dir / f"{index:02d}-{safe_case}{source.suffix or '.json'}"
|
||||
require(not target.exists(), f"G14 case evidence {index} destination already exists")
|
||||
shutil.copyfile(source, target)
|
||||
copied.append({
|
||||
"case": sample["case"],
|
||||
"artifact": target.relative_to(out_dir).as_posix(),
|
||||
"sha256": digest(target),
|
||||
"source_revision": source_revision,
|
||||
"measurement_window_id": window_id,
|
||||
})
|
||||
return copied
|
||||
|
||||
|
||||
def proof_from_case_directories(raw_values: list[str], out_dir: Path, source_revision: str) -> dict[str, Any]:
|
||||
require(raw_values, "missing G14 case directories")
|
||||
records = [load_case_directory(value, source_revision) for value in raw_values]
|
||||
@@ -230,6 +277,7 @@ def write_field(out_dir: Path, field: str, proof: dict[str, Any], source_revisio
|
||||
"command": proof["command"],
|
||||
"artifact_format": "json",
|
||||
"summary": proof.get("summary") or "Measured G14 same-window EC8+4 multi-set/multi-pool proof.",
|
||||
"case_evidence": proof["case_evidence"],
|
||||
}
|
||||
if field == "same_window_field_evidence":
|
||||
evidence["same_window_fields"] = [item for item in G14_FIELDS if item != field]
|
||||
@@ -275,7 +323,15 @@ def build_descriptor(args: argparse.Namespace) -> Path:
|
||||
source_revision = args.source_revision or git_head()
|
||||
out_dir.mkdir(parents=True)
|
||||
if args.proof_json is not None:
|
||||
proof = load_proof(args.proof_json.resolve(), source_revision)
|
||||
proof_path = args.proof_json.resolve()
|
||||
proof = load_proof(proof_path, source_revision)
|
||||
proof["case_evidence"] = copy_proof_case_artifacts(
|
||||
out_dir,
|
||||
proof_path,
|
||||
proof["case_evidence"],
|
||||
proof["measurement_window_id"],
|
||||
source_revision,
|
||||
)
|
||||
else:
|
||||
proof = proof_from_case_directories(args.case_dir, out_dir, source_revision)
|
||||
fields = {field: write_field(out_dir, field, proof, source_revision) for field in G14_FIELDS}
|
||||
@@ -304,13 +360,35 @@ def build_descriptor(args: argparse.Namespace) -> Path:
|
||||
|
||||
|
||||
def write_self_test_proof(root: Path, source_revision: str) -> Path:
|
||||
window_id = "g14-self-test-window"
|
||||
case_evidence = []
|
||||
for case in (
|
||||
"ec84-target-drive-restart",
|
||||
"multi-set-distributed-invalidation",
|
||||
"multi-pool-distributed-invalidation",
|
||||
):
|
||||
artifact = root / f"{case}.json"
|
||||
write_json(artifact, {
|
||||
"schema": 1,
|
||||
"case": case,
|
||||
"evidence_type": "measured",
|
||||
"source_revision": source_revision,
|
||||
"measurement_window_id": window_id,
|
||||
})
|
||||
case_evidence.append({
|
||||
"case": case,
|
||||
"artifact": artifact.relative_to(root).as_posix(),
|
||||
"sha256": digest(artifact),
|
||||
"source_revision": source_revision,
|
||||
"measurement_window_id": window_id,
|
||||
})
|
||||
proof = root / "proof.json"
|
||||
write_json(proof, {
|
||||
"schema": 1,
|
||||
"evidence_type": "measured",
|
||||
"source_revision": source_revision,
|
||||
"run_id": "g14-self-test-run",
|
||||
"measurement_window_id": "g14-self-test-window",
|
||||
"measurement_window_id": window_id,
|
||||
"started_at": "2026-09-09T00:00:00Z",
|
||||
"finished_at": "2026-09-09T00:30:00Z",
|
||||
"command": ["scripts/run_scanner_heal_g14_multiset_evidence.py", "--proof-json", "proof.json"],
|
||||
@@ -323,11 +401,7 @@ def write_self_test_proof(root: Path, source_revision: str) -> Path:
|
||||
"peer_count": 3,
|
||||
"same_window_remote_proof": True,
|
||||
"all_peers_bound_to_generation_window": True,
|
||||
"case_evidence": [
|
||||
{"case": "ec84-target-drive-restart", "sha256": "a" * 64},
|
||||
{"case": "multi-set-distributed-invalidation", "sha256": "b" * 64},
|
||||
{"case": "multi-pool-distributed-invalidation", "sha256": "c" * 64},
|
||||
],
|
||||
"case_evidence": case_evidence,
|
||||
})
|
||||
return proof
|
||||
|
||||
|
||||
Reference in New Issue
Block a user