20260408_2 增加CIR sequence,未验证drop analysis,遇到问题是static pool保存太慢,拖慢整体录制,待解决
This commit is contained in:
230
tests/test_cir_delta_export_m1.rs
Normal file
230
tests/test_cir_delta_export_m1.rs
Normal file
@@ -0,0 +1,230 @@
|
||||
use std::collections::BTreeSet;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
use rpki::ccr::{encode_content_info, CcrContentInfo, CcrDigestAlgorithm, RpkiCanonicalCacheRepresentation, TrustAnchorState};
|
||||
use rpki::cir::{encode_cir, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, CIR_VERSION_V1};
|
||||
|
||||
#[test]
|
||||
fn cir_full_and_delta_pair_reuses_shared_static_pool() {
|
||||
let script = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("scripts/cir/run_cir_record_full_delta.sh");
|
||||
let out_dir = tempfile::tempdir().expect("tempdir");
|
||||
let out = out_dir.path().join("cir-pair");
|
||||
let fixture_root = out_dir.path().join("fixture");
|
||||
std::fs::create_dir_all(&fixture_root).unwrap();
|
||||
let static_payload_root = fixture_root.join("payloads");
|
||||
std::fs::create_dir_all(&static_payload_root).unwrap();
|
||||
let base_locks = fixture_root.join("base-locks.json");
|
||||
let delta_locks = fixture_root.join("locks-delta.json");
|
||||
std::fs::write(
|
||||
&base_locks,
|
||||
br#"{"validationTime":"2026-03-16T11:49:15Z"}"#,
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::write(
|
||||
&delta_locks,
|
||||
br#"{"validationTime":"2026-03-16T11:50:15Z"}"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let full_obj_hash = {
|
||||
use sha2::{Digest, Sha256};
|
||||
hex::encode(Sha256::digest(b"full-object"))
|
||||
};
|
||||
let delta_obj_hash = {
|
||||
use sha2::{Digest, Sha256};
|
||||
hex::encode(Sha256::digest(b"delta-object"))
|
||||
};
|
||||
|
||||
let full_cir = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: time::OffsetDateTime::parse(
|
||||
"2026-03-16T11:49:15Z",
|
||||
&time::format_description::well_known::Rfc3339,
|
||||
)
|
||||
.unwrap(),
|
||||
objects: vec![CirObject {
|
||||
rsync_uri: "rsync://example.net/repo/full.roa".to_string(),
|
||||
sha256: hex::decode(&full_obj_hash).unwrap(),
|
||||
}],
|
||||
tals: vec![CirTal {
|
||||
tal_uri: "https://rpki.apnic.net/tal/apnic-rfc7730-https.tal".to_string(),
|
||||
tal_bytes: b"rsync://example.net/repo/root.cer\nMIIB".to_vec(),
|
||||
}],
|
||||
};
|
||||
let delta_cir = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: time::OffsetDateTime::parse(
|
||||
"2026-03-16T11:50:15Z",
|
||||
&time::format_description::well_known::Rfc3339,
|
||||
)
|
||||
.unwrap(),
|
||||
objects: {
|
||||
let mut objects = vec![
|
||||
CirObject {
|
||||
rsync_uri: "rsync://example.net/repo/full.roa".to_string(),
|
||||
sha256: hex::decode(&full_obj_hash).unwrap(),
|
||||
},
|
||||
CirObject {
|
||||
rsync_uri: "rsync://example.net/repo/delta.roa".to_string(),
|
||||
sha256: hex::decode(&delta_obj_hash).unwrap(),
|
||||
},
|
||||
];
|
||||
objects.sort_by(|a, b| a.rsync_uri.cmp(&b.rsync_uri));
|
||||
objects
|
||||
},
|
||||
tals: full_cir.tals.clone(),
|
||||
};
|
||||
let empty_ccr = CcrContentInfo::new(RpkiCanonicalCacheRepresentation {
|
||||
version: 0,
|
||||
hash_alg: CcrDigestAlgorithm::Sha256,
|
||||
produced_at: full_cir.validation_time,
|
||||
mfts: None,
|
||||
vrps: None,
|
||||
vaps: None,
|
||||
tas: Some(TrustAnchorState { skis: vec![vec![0x11; 20]], hash: vec![0x22; 32] }),
|
||||
rks: None,
|
||||
});
|
||||
let full_cir_path = fixture_root.join("full.cir");
|
||||
let delta_cir_path = fixture_root.join("delta.cir");
|
||||
let full_ccr_path = fixture_root.join("full.ccr");
|
||||
let delta_ccr_path = fixture_root.join("delta.ccr");
|
||||
let full_report_path = fixture_root.join("full-report.json");
|
||||
let delta_report_path = fixture_root.join("delta-report.json");
|
||||
std::fs::write(&full_cir_path, encode_cir(&full_cir).unwrap()).unwrap();
|
||||
std::fs::write(&delta_cir_path, encode_cir(&delta_cir).unwrap()).unwrap();
|
||||
std::fs::write(&full_ccr_path, encode_content_info(&empty_ccr).unwrap()).unwrap();
|
||||
std::fs::write(&delta_ccr_path, encode_content_info(&empty_ccr).unwrap()).unwrap();
|
||||
std::fs::write(&full_report_path, br#"{"format_version":2,"publication_points":[]}"#).unwrap();
|
||||
std::fs::write(&delta_report_path, br#"{"format_version":2,"publication_points":[]}"#).unwrap();
|
||||
|
||||
let stub = out_dir.path().join("stub-rpki.sh");
|
||||
std::fs::write(
|
||||
&stub,
|
||||
format!(
|
||||
r#"#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
MODE=""
|
||||
cir=""
|
||||
ccr=""
|
||||
report=""
|
||||
static_root=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--payload-replay-archive) MODE="full"; shift 2 ;;
|
||||
--payload-base-archive) MODE="delta"; shift 2 ;;
|
||||
--cir-out) cir="$2"; shift 2 ;;
|
||||
--ccr-out) ccr="$2"; shift 2 ;;
|
||||
--report-json) report="$2"; shift 2 ;;
|
||||
--cir-static-root) static_root="$2"; shift 2 ;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
mkdir -p "$(dirname "$cir")" "$(dirname "$ccr")" "$(dirname "$report")" "$static_root/20260316/{{ab,cd,ef}}/00"
|
||||
if [[ "$MODE" == "full" ]]; then
|
||||
cp "{full_cir}" "$cir"
|
||||
cp "{full_ccr}" "$ccr"
|
||||
cp "{full_report}" "$report"
|
||||
install -D -m 0644 "{payload_root}/full-object" "$static_root/20260316/ab/cd/{full_hash}"
|
||||
else
|
||||
cp "{delta_cir}" "$cir"
|
||||
cp "{delta_ccr}" "$ccr"
|
||||
cp "{delta_report}" "$report"
|
||||
install -D -m 0644 "{payload_root}/full-object" "$static_root/20260316/ab/cd/{full_hash}"
|
||||
install -D -m 0644 "{payload_root}/delta-object" "$static_root/20260316/ef/00/{delta_hash}"
|
||||
fi
|
||||
"#,
|
||||
full_cir = full_cir_path.display(),
|
||||
delta_cir = delta_cir_path.display(),
|
||||
full_ccr = full_ccr_path.display(),
|
||||
delta_ccr = delta_ccr_path.display(),
|
||||
full_report = full_report_path.display(),
|
||||
delta_report = delta_report_path.display(),
|
||||
payload_root = static_payload_root.display(),
|
||||
full_hash = full_obj_hash,
|
||||
delta_hash = delta_obj_hash,
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::set_permissions(&stub, std::os::unix::fs::PermissionsExt::from_mode(0o755)).unwrap();
|
||||
std::fs::write(static_payload_root.join("full-object"), b"full-object").unwrap();
|
||||
std::fs::write(static_payload_root.join("delta-object"), b"delta-object").unwrap();
|
||||
|
||||
let proc = Command::new(script)
|
||||
.args([
|
||||
"--out-dir",
|
||||
out.to_string_lossy().as_ref(),
|
||||
"--tal-path",
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("tests/fixtures/tal/apnic-rfc7730-https.tal")
|
||||
.to_string_lossy()
|
||||
.as_ref(),
|
||||
"--ta-path",
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("tests/fixtures/ta/apnic-ta.cer")
|
||||
.to_string_lossy()
|
||||
.as_ref(),
|
||||
"--cir-tal-uri",
|
||||
"https://rpki.apnic.net/tal/apnic-rfc7730-https.tal",
|
||||
"--payload-replay-archive",
|
||||
"/tmp/base-payload-archive",
|
||||
"--payload-replay-locks",
|
||||
base_locks.to_string_lossy().as_ref(),
|
||||
"--payload-base-archive",
|
||||
"/tmp/base-payload-archive",
|
||||
"--payload-base-locks",
|
||||
base_locks.to_string_lossy().as_ref(),
|
||||
"--payload-delta-archive",
|
||||
"/tmp/payload-delta-archive",
|
||||
"--payload-delta-locks",
|
||||
delta_locks.to_string_lossy().as_ref(),
|
||||
"--max-depth",
|
||||
"0",
|
||||
"--max-instances",
|
||||
"1",
|
||||
"--rpki-bin",
|
||||
stub.to_string_lossy().as_ref(),
|
||||
])
|
||||
.output()
|
||||
.expect("run cir record pair");
|
||||
assert!(
|
||||
proc.status.success(),
|
||||
"stderr={}",
|
||||
String::from_utf8_lossy(&proc.stderr)
|
||||
);
|
||||
|
||||
let full_cir = rpki::cir::decode_cir(&std::fs::read(out.join("full").join("input.cir")).unwrap())
|
||||
.expect("decode full cir");
|
||||
let delta_cir =
|
||||
rpki::cir::decode_cir(&std::fs::read(out.join("delta-001").join("input.cir")).unwrap())
|
||||
.expect("decode delta cir");
|
||||
|
||||
let mut hashes = BTreeSet::new();
|
||||
for item in &full_cir.objects {
|
||||
hashes.insert(hex::encode(&item.sha256));
|
||||
}
|
||||
for item in &delta_cir.objects {
|
||||
hashes.insert(hex::encode(&item.sha256));
|
||||
}
|
||||
|
||||
let static_file_count = walk(out.join("static")).len();
|
||||
assert_eq!(static_file_count, hashes.len());
|
||||
|
||||
assert!(out.join("summary.json").is_file());
|
||||
assert!(out.join("full").join("result.ccr").is_file());
|
||||
assert!(out.join("delta-001").join("result.ccr").is_file());
|
||||
}
|
||||
|
||||
fn walk(path: std::path::PathBuf) -> Vec<std::path::PathBuf> {
|
||||
let mut out = Vec::new();
|
||||
if path.is_file() {
|
||||
out.push(path);
|
||||
} else if path.is_dir() {
|
||||
for entry in std::fs::read_dir(path).unwrap() {
|
||||
out.extend(walk(entry.unwrap().path()));
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
111
tests/test_cir_drop_report_m5.rs
Normal file
111
tests/test_cir_drop_report_m5.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
use rpki::ccr::{
|
||||
encode_content_info, CcrContentInfo, CcrDigestAlgorithm, RpkiCanonicalCacheRepresentation,
|
||||
TrustAnchorState,
|
||||
};
|
||||
use rpki::cir::{encode_cir, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, CIR_VERSION_V1};
|
||||
|
||||
#[test]
|
||||
fn cir_drop_report_counts_dropped_roa_objects_and_vrps() {
|
||||
let td = tempfile::tempdir().expect("tempdir");
|
||||
let cir_path = td.path().join("input.cir");
|
||||
let ccr_path = td.path().join("output.ccr");
|
||||
let report_path = td.path().join("report.json");
|
||||
let static_root = td.path().join("static");
|
||||
let json_out = td.path().join("drop.json");
|
||||
let md_out = td.path().join("drop.md");
|
||||
|
||||
let roa_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("tests/fixtures/repository/rpki.cernet.net/repo/cernet/0/AS4538.roa");
|
||||
let roa_bytes = std::fs::read(&roa_path).expect("read roa fixture");
|
||||
let hash = {
|
||||
use sha2::{Digest, Sha256};
|
||||
hex::encode(Sha256::digest(&roa_bytes))
|
||||
};
|
||||
let dir = static_root.join("20260409").join(&hash[0..2]).join(&hash[2..4]);
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
std::fs::write(dir.join(&hash), &roa_bytes).unwrap();
|
||||
|
||||
let cir = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: time::OffsetDateTime::parse(
|
||||
"2026-04-09T00:00:00Z",
|
||||
&time::format_description::well_known::Rfc3339,
|
||||
)
|
||||
.unwrap(),
|
||||
objects: vec![CirObject {
|
||||
rsync_uri: "rsync://example.net/repo/AS4538.roa".to_string(),
|
||||
sha256: hex::decode(&hash).unwrap(),
|
||||
}],
|
||||
tals: vec![CirTal {
|
||||
tal_uri: "https://example.test/root.tal".to_string(),
|
||||
tal_bytes: b"rsync://example.net/repo/root.cer\nMIIB".to_vec(),
|
||||
}],
|
||||
};
|
||||
std::fs::write(&cir_path, encode_cir(&cir).unwrap()).unwrap();
|
||||
|
||||
let ccr = CcrContentInfo::new(RpkiCanonicalCacheRepresentation {
|
||||
version: 0,
|
||||
hash_alg: CcrDigestAlgorithm::Sha256,
|
||||
produced_at: cir.validation_time,
|
||||
mfts: None,
|
||||
vrps: None,
|
||||
vaps: None,
|
||||
tas: Some(TrustAnchorState {
|
||||
skis: vec![vec![0x11; 20]],
|
||||
hash: vec![0x22; 32],
|
||||
}),
|
||||
rks: None,
|
||||
});
|
||||
std::fs::write(&ccr_path, encode_content_info(&ccr).unwrap()).unwrap();
|
||||
|
||||
let report = serde_json::json!({
|
||||
"format_version": 2,
|
||||
"publication_points": [
|
||||
{
|
||||
"manifest_rsync_uri": "rsync://example.net/repo/example.mft",
|
||||
"publication_point_rsync_uri": "rsync://example.net/repo/",
|
||||
"objects": [
|
||||
{
|
||||
"rsync_uri": "rsync://example.net/repo/AS4538.roa",
|
||||
"sha256_hex": hash,
|
||||
"kind": "roa",
|
||||
"result": "error",
|
||||
"detail": "policy rejected"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
std::fs::write(&report_path, serde_json::to_vec_pretty(&report).unwrap()).unwrap();
|
||||
|
||||
let bin = env!("CARGO_BIN_EXE_cir_drop_report");
|
||||
let out = Command::new(bin)
|
||||
.args([
|
||||
"--cir",
|
||||
cir_path.to_string_lossy().as_ref(),
|
||||
"--ccr",
|
||||
ccr_path.to_string_lossy().as_ref(),
|
||||
"--report-json",
|
||||
report_path.to_string_lossy().as_ref(),
|
||||
"--static-root",
|
||||
static_root.to_string_lossy().as_ref(),
|
||||
"--json-out",
|
||||
json_out.to_string_lossy().as_ref(),
|
||||
"--md-out",
|
||||
md_out.to_string_lossy().as_ref(),
|
||||
])
|
||||
.output()
|
||||
.expect("run cir_drop_report");
|
||||
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
|
||||
|
||||
let output: serde_json::Value =
|
||||
serde_json::from_slice(&std::fs::read(&json_out).unwrap()).unwrap();
|
||||
assert_eq!(output["summary"]["droppedObjectCount"], 1);
|
||||
assert!(output["summary"]["droppedVrpCount"].as_u64().unwrap_or(0) >= 1);
|
||||
assert_eq!(output["summary"]["droppedByKind"]["roa"], 1);
|
||||
assert!(std::fs::read_to_string(&md_out).unwrap().contains("Dropped By Reason"));
|
||||
}
|
||||
212
tests/test_cir_sequence_m2.rs
Normal file
212
tests/test_cir_sequence_m2.rs
Normal file
@@ -0,0 +1,212 @@
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
use rpki::ccr::{encode_content_info, CcrContentInfo, CcrDigestAlgorithm, RpkiCanonicalCacheRepresentation, TrustAnchorState};
|
||||
use rpki::cir::{encode_cir, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, CIR_VERSION_V1};
|
||||
|
||||
#[test]
|
||||
fn cir_offline_sequence_writes_parseable_sequence_json_and_steps() {
|
||||
let out_dir = tempfile::tempdir().expect("tempdir");
|
||||
let out = out_dir.path().join("cir-sequence");
|
||||
let script =
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("scripts/cir/run_cir_record_sequence_offline.sh");
|
||||
let fixture_root = out_dir.path().join("fixture");
|
||||
let static_payload_root = fixture_root.join("payloads");
|
||||
std::fs::create_dir_all(&static_payload_root).unwrap();
|
||||
let base_locks = fixture_root.join("base-locks.json");
|
||||
let delta_locks = fixture_root.join("locks-delta.json");
|
||||
std::fs::write(&base_locks, br#"{"validationTime":"2026-03-16T11:49:15Z"}"#).unwrap();
|
||||
std::fs::write(&delta_locks, br#"{"validationTime":"2026-03-16T11:50:15Z"}"#).unwrap();
|
||||
|
||||
let mk_cir = |uri: &str, hash_hex: &str, vt: &str| CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: time::OffsetDateTime::parse(vt, &time::format_description::well_known::Rfc3339).unwrap(),
|
||||
objects: vec![CirObject {
|
||||
rsync_uri: uri.to_string(),
|
||||
sha256: hex::decode(hash_hex).unwrap(),
|
||||
}],
|
||||
tals: vec![CirTal {
|
||||
tal_uri: "https://rpki.apnic.net/tal/apnic-rfc7730-https.tal".to_string(),
|
||||
tal_bytes: b"rsync://example.net/repo/root.cer\nMIIB".to_vec(),
|
||||
}],
|
||||
};
|
||||
let full_hash = {
|
||||
use sha2::{Digest, Sha256};
|
||||
hex::encode(Sha256::digest(b"full-object"))
|
||||
};
|
||||
let delta_hash = {
|
||||
use sha2::{Digest, Sha256};
|
||||
hex::encode(Sha256::digest(b"delta-object"))
|
||||
};
|
||||
let full_cir = mk_cir("rsync://example.net/repo/full.roa", &full_hash, "2026-03-16T11:49:15Z");
|
||||
let delta_cir = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: time::OffsetDateTime::parse(
|
||||
"2026-03-16T11:50:15Z",
|
||||
&time::format_description::well_known::Rfc3339,
|
||||
)
|
||||
.unwrap(),
|
||||
objects: {
|
||||
let mut objects = vec![
|
||||
full_cir.objects[0].clone(),
|
||||
CirObject {
|
||||
rsync_uri: "rsync://example.net/repo/delta.roa".to_string(),
|
||||
sha256: hex::decode(&delta_hash).unwrap(),
|
||||
},
|
||||
];
|
||||
objects.sort_by(|a, b| a.rsync_uri.cmp(&b.rsync_uri));
|
||||
objects
|
||||
},
|
||||
tals: full_cir.tals.clone(),
|
||||
};
|
||||
let empty_ccr = CcrContentInfo::new(RpkiCanonicalCacheRepresentation {
|
||||
version: 0,
|
||||
hash_alg: CcrDigestAlgorithm::Sha256,
|
||||
produced_at: full_cir.validation_time,
|
||||
mfts: None,
|
||||
vrps: None,
|
||||
vaps: None,
|
||||
tas: Some(TrustAnchorState { skis: vec![vec![0x11; 20]], hash: vec![0x22; 32] }),
|
||||
rks: None,
|
||||
});
|
||||
let full_cir_path = fixture_root.join("full.cir");
|
||||
let delta_cir_path = fixture_root.join("delta.cir");
|
||||
let full_ccr_path = fixture_root.join("full.ccr");
|
||||
let delta_ccr_path = fixture_root.join("delta.ccr");
|
||||
let full_report_path = fixture_root.join("full-report.json");
|
||||
let delta_report_path = fixture_root.join("delta-report.json");
|
||||
std::fs::create_dir_all(&fixture_root).unwrap();
|
||||
std::fs::write(&full_cir_path, encode_cir(&full_cir).unwrap()).unwrap();
|
||||
std::fs::write(&delta_cir_path, encode_cir(&delta_cir).unwrap()).unwrap();
|
||||
std::fs::write(&full_ccr_path, encode_content_info(&empty_ccr).unwrap()).unwrap();
|
||||
std::fs::write(&delta_ccr_path, encode_content_info(&empty_ccr).unwrap()).unwrap();
|
||||
std::fs::write(&full_report_path, br#"{"format_version":2,"publication_points":[]}"#).unwrap();
|
||||
std::fs::write(&delta_report_path, br#"{"format_version":2,"publication_points":[]}"#).unwrap();
|
||||
std::fs::write(static_payload_root.join("full-object"), b"full-object").unwrap();
|
||||
std::fs::write(static_payload_root.join("delta-object"), b"delta-object").unwrap();
|
||||
|
||||
let stub = out_dir.path().join("stub-rpki.sh");
|
||||
std::fs::write(
|
||||
&stub,
|
||||
format!(
|
||||
r#"#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
MODE=""
|
||||
cir=""
|
||||
ccr=""
|
||||
report=""
|
||||
static_root=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--payload-replay-archive) MODE="full"; shift 2 ;;
|
||||
--payload-base-archive) MODE="delta"; shift 2 ;;
|
||||
--cir-out) cir="$2"; shift 2 ;;
|
||||
--ccr-out) ccr="$2"; shift 2 ;;
|
||||
--report-json) report="$2"; shift 2 ;;
|
||||
--cir-static-root) static_root="$2"; shift 2 ;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
mkdir -p "$(dirname "$cir")" "$(dirname "$ccr")" "$(dirname "$report")" "$static_root/20260316/{{ab,cd,ef}}/00"
|
||||
if [[ "$MODE" == "full" ]]; then
|
||||
cp "{full_cir}" "$cir"
|
||||
cp "{full_ccr}" "$ccr"
|
||||
cp "{full_report}" "$report"
|
||||
install -D -m 0644 "{payload_root}/full-object" "$static_root/20260316/ab/cd/{full_hash}"
|
||||
else
|
||||
if [[ "$cir" == *delta-001* ]]; then
|
||||
cp "{delta_cir}" "$cir"
|
||||
cp "{delta_ccr}" "$ccr"
|
||||
cp "{delta_report}" "$report"
|
||||
else
|
||||
cp "{delta_cir}" "$cir"
|
||||
cp "{delta_ccr}" "$ccr"
|
||||
cp "{delta_report}" "$report"
|
||||
fi
|
||||
install -D -m 0644 "{payload_root}/full-object" "$static_root/20260316/ab/cd/{full_hash}"
|
||||
install -D -m 0644 "{payload_root}/delta-object" "$static_root/20260316/ef/00/{delta_hash}"
|
||||
fi
|
||||
"#,
|
||||
full_cir = full_cir_path.display(),
|
||||
delta_cir = delta_cir_path.display(),
|
||||
full_ccr = full_ccr_path.display(),
|
||||
delta_ccr = delta_ccr_path.display(),
|
||||
full_report = full_report_path.display(),
|
||||
delta_report = delta_report_path.display(),
|
||||
payload_root = static_payload_root.display(),
|
||||
full_hash = full_hash,
|
||||
delta_hash = delta_hash,
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
std::fs::set_permissions(&stub, std::os::unix::fs::PermissionsExt::from_mode(0o755)).unwrap();
|
||||
|
||||
let proc = Command::new(script)
|
||||
.args([
|
||||
"--out-dir",
|
||||
out.to_string_lossy().as_ref(),
|
||||
"--tal-path",
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("tests/fixtures/tal/apnic-rfc7730-https.tal")
|
||||
.to_string_lossy()
|
||||
.as_ref(),
|
||||
"--ta-path",
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("tests/fixtures/ta/apnic-ta.cer")
|
||||
.to_string_lossy()
|
||||
.as_ref(),
|
||||
"--cir-tal-uri",
|
||||
"https://rpki.apnic.net/tal/apnic-rfc7730-https.tal",
|
||||
"--payload-replay-archive",
|
||||
"/tmp/base-payload-archive",
|
||||
"--payload-replay-locks",
|
||||
base_locks.to_string_lossy().as_ref(),
|
||||
"--payload-base-archive",
|
||||
"/tmp/base-payload-archive",
|
||||
"--payload-base-locks",
|
||||
base_locks.to_string_lossy().as_ref(),
|
||||
"--payload-delta-archive",
|
||||
"/tmp/payload-delta-archive",
|
||||
"--payload-delta-locks",
|
||||
delta_locks.to_string_lossy().as_ref(),
|
||||
"--delta-count",
|
||||
"2",
|
||||
"--max-depth",
|
||||
"0",
|
||||
"--max-instances",
|
||||
"1",
|
||||
"--rpki-bin",
|
||||
stub.to_string_lossy().as_ref(),
|
||||
])
|
||||
.output()
|
||||
.expect("run cir sequence script");
|
||||
assert!(
|
||||
proc.status.success(),
|
||||
"stderr={}",
|
||||
String::from_utf8_lossy(&proc.stderr)
|
||||
);
|
||||
|
||||
let sequence_json: serde_json::Value =
|
||||
serde_json::from_slice(&std::fs::read(out.join("sequence.json")).unwrap()).unwrap();
|
||||
let steps = sequence_json["steps"].as_array().expect("steps");
|
||||
assert_eq!(steps.len(), 3);
|
||||
assert_eq!(steps[0]["stepId"], "full");
|
||||
assert_eq!(steps[1]["stepId"], "delta-001");
|
||||
assert_eq!(steps[2]["stepId"], "delta-002");
|
||||
|
||||
for rel in [
|
||||
"full/input.cir",
|
||||
"full/result.ccr",
|
||||
"full/report.json",
|
||||
"delta-001/input.cir",
|
||||
"delta-001/result.ccr",
|
||||
"delta-001/report.json",
|
||||
"delta-002/input.cir",
|
||||
"delta-002/result.ccr",
|
||||
"delta-002/report.json",
|
||||
] {
|
||||
assert!(out.join(rel).is_file(), "missing {}", rel);
|
||||
}
|
||||
}
|
||||
176
tests/test_cir_sequence_peer_replay_m4.rs
Normal file
176
tests/test_cir_sequence_peer_replay_m4.rs
Normal file
@@ -0,0 +1,176 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
use rpki::cir::{encode_cir, materialize_cir, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, CIR_VERSION_V1};
|
||||
|
||||
fn apnic_tal_path() -> PathBuf {
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/tal/apnic-rfc7730-https.tal")
|
||||
}
|
||||
|
||||
fn apnic_ta_path() -> PathBuf {
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/ta/apnic-ta.cer")
|
||||
}
|
||||
|
||||
fn build_ta_only_cir() -> (CanonicalInputRepresentation, Vec<u8>) {
|
||||
let tal_bytes = std::fs::read(apnic_tal_path()).expect("read tal");
|
||||
let ta_bytes = std::fs::read(apnic_ta_path()).expect("read ta");
|
||||
let tal = rpki::data_model::tal::Tal::decode_bytes(&tal_bytes).expect("decode tal");
|
||||
let ta_rsync_uri = tal
|
||||
.ta_uris
|
||||
.iter()
|
||||
.find(|uri| uri.scheme() == "rsync")
|
||||
.expect("tal has rsync uri")
|
||||
.as_str()
|
||||
.to_string();
|
||||
let ta_hash = {
|
||||
use sha2::{Digest, Sha256};
|
||||
Sha256::digest(&ta_bytes).to_vec()
|
||||
};
|
||||
(
|
||||
CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: time::OffsetDateTime::parse(
|
||||
"2026-04-07T00:00:00Z",
|
||||
&time::format_description::well_known::Rfc3339,
|
||||
)
|
||||
.unwrap(),
|
||||
objects: vec![CirObject {
|
||||
rsync_uri: ta_rsync_uri,
|
||||
sha256: ta_hash,
|
||||
}],
|
||||
tals: vec![CirTal {
|
||||
tal_uri: "https://example.test/root.tal".to_string(),
|
||||
tal_bytes,
|
||||
}],
|
||||
},
|
||||
ta_bytes,
|
||||
)
|
||||
}
|
||||
|
||||
fn write_static(root: &Path, date: &str, bytes: &[u8]) {
|
||||
use sha2::{Digest, Sha256};
|
||||
let hash = hex::encode(Sha256::digest(bytes));
|
||||
let dir = root.join(date).join(&hash[0..2]).join(&hash[2..4]);
|
||||
std::fs::create_dir_all(&dir).expect("mkdir static");
|
||||
std::fs::write(dir.join(hash), bytes).expect("write static object");
|
||||
}
|
||||
|
||||
fn prepare_reference_ccr(work: &Path, cir: &CanonicalInputRepresentation, mirror_root: &Path) -> PathBuf {
|
||||
let reference_ccr = work.join("reference.ccr");
|
||||
let rpki_bin = env!("CARGO_BIN_EXE_rpki");
|
||||
let wrapper = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("scripts/cir/cir-rsync-wrapper");
|
||||
let tal_path = apnic_tal_path();
|
||||
let ta_path = apnic_ta_path();
|
||||
let out = Command::new(rpki_bin)
|
||||
.env("REAL_RSYNC_BIN", "/usr/bin/rsync")
|
||||
.env("CIR_MIRROR_ROOT", mirror_root)
|
||||
.env("CIR_LOCAL_LINK_MODE", "1")
|
||||
.args([
|
||||
"--db",
|
||||
work.join("reference-db").to_string_lossy().as_ref(),
|
||||
"--tal-path",
|
||||
tal_path.to_string_lossy().as_ref(),
|
||||
"--ta-path",
|
||||
ta_path.to_string_lossy().as_ref(),
|
||||
"--disable-rrdp",
|
||||
"--rsync-command",
|
||||
wrapper.to_string_lossy().as_ref(),
|
||||
"--validation-time",
|
||||
&cir.validation_time.format(&time::format_description::well_known::Rfc3339).unwrap(),
|
||||
"--max-depth",
|
||||
"0",
|
||||
"--max-instances",
|
||||
"1",
|
||||
"--ccr-out",
|
||||
reference_ccr.to_string_lossy().as_ref(),
|
||||
])
|
||||
.output()
|
||||
.expect("run reference rpki");
|
||||
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
|
||||
reference_ccr
|
||||
}
|
||||
|
||||
fn prepare_sequence_root(td: &Path) -> PathBuf {
|
||||
let sequence_root = td.join("sequence");
|
||||
let static_root = sequence_root.join("static");
|
||||
let mirror_root = td.join("mirror");
|
||||
std::fs::create_dir_all(sequence_root.join("full")).unwrap();
|
||||
std::fs::create_dir_all(sequence_root.join("delta-001")).unwrap();
|
||||
std::fs::create_dir_all(sequence_root.join("delta-002")).unwrap();
|
||||
|
||||
let (cir, ta_bytes) = build_ta_only_cir();
|
||||
let cir_bytes = encode_cir(&cir).expect("encode cir");
|
||||
std::fs::write(sequence_root.join("full").join("input.cir"), &cir_bytes).unwrap();
|
||||
std::fs::write(sequence_root.join("delta-001").join("input.cir"), &cir_bytes).unwrap();
|
||||
std::fs::write(sequence_root.join("delta-002").join("input.cir"), &cir_bytes).unwrap();
|
||||
write_static(&static_root, "20260407", &ta_bytes);
|
||||
materialize_cir(&cir, &static_root, &mirror_root, true).unwrap();
|
||||
let reference = prepare_reference_ccr(td, &cir, &mirror_root);
|
||||
std::fs::copy(&reference, sequence_root.join("full").join("result.ccr")).unwrap();
|
||||
std::fs::copy(&reference, sequence_root.join("delta-001").join("result.ccr")).unwrap();
|
||||
std::fs::copy(&reference, sequence_root.join("delta-002").join("result.ccr")).unwrap();
|
||||
std::fs::write(sequence_root.join("full").join("report.json"), b"{}").unwrap();
|
||||
std::fs::write(sequence_root.join("delta-001").join("report.json"), b"{}").unwrap();
|
||||
std::fs::write(sequence_root.join("delta-002").join("report.json"), b"{}").unwrap();
|
||||
let sequence = serde_json::json!({
|
||||
"version": 1,
|
||||
"staticRoot": "static",
|
||||
"steps": [
|
||||
{"stepId":"full","kind":"full","validationTime":"2026-04-07T00:00:00Z","cirPath":"full/input.cir","ccrPath":"full/result.ccr","reportPath":"full/report.json","previousStepId":null},
|
||||
{"stepId":"delta-001","kind":"delta","validationTime":"2026-04-07T00:00:00Z","cirPath":"delta-001/input.cir","ccrPath":"delta-001/result.ccr","reportPath":"delta-001/report.json","previousStepId":"full"},
|
||||
{"stepId":"delta-002","kind":"delta","validationTime":"2026-04-07T00:00:00Z","cirPath":"delta-002/input.cir","ccrPath":"delta-002/result.ccr","reportPath":"delta-002/report.json","previousStepId":"delta-001"}
|
||||
]
|
||||
});
|
||||
std::fs::write(sequence_root.join("sequence.json"), serde_json::to_vec_pretty(&sequence).unwrap()).unwrap();
|
||||
sequence_root
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn peer_sequence_replay_scripts_replay_all_steps() {
|
||||
if !Path::new("/usr/bin/rsync").exists()
|
||||
|| !Path::new("/home/yuyr/dev/rust_playground/routinator/target/debug/routinator").exists()
|
||||
|| !Path::new("/home/yuyr/dev/rpki-client-9.7/build-m5/src/rpki-client").exists()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let td = tempfile::tempdir().expect("tempdir");
|
||||
let sequence_root = prepare_sequence_root(td.path());
|
||||
|
||||
let routinator_script = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("scripts/cir/run_cir_replay_sequence_routinator.sh");
|
||||
let out = Command::new(routinator_script)
|
||||
.args([
|
||||
"--sequence-root",
|
||||
sequence_root.to_string_lossy().as_ref(),
|
||||
])
|
||||
.output()
|
||||
.expect("run routinator sequence replay");
|
||||
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
|
||||
let r_summary: serde_json::Value = serde_json::from_slice(
|
||||
&std::fs::read(sequence_root.join("sequence-summary-routinator.json")).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(r_summary["stepCount"], 3);
|
||||
assert_eq!(r_summary["allMatch"], true);
|
||||
|
||||
let rpki_client_script = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("scripts/cir/run_cir_replay_sequence_rpki_client.sh");
|
||||
let out = Command::new(rpki_client_script)
|
||||
.args([
|
||||
"--sequence-root",
|
||||
sequence_root.to_string_lossy().as_ref(),
|
||||
"--build-dir",
|
||||
"/home/yuyr/dev/rpki-client-9.7/build-m5",
|
||||
])
|
||||
.output()
|
||||
.expect("run rpki-client sequence replay");
|
||||
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
|
||||
let c_summary: serde_json::Value = serde_json::from_slice(
|
||||
&std::fs::read(sequence_root.join("sequence-summary-rpki-client.json")).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(c_summary["stepCount"], 3);
|
||||
assert_eq!(c_summary["allMatch"], true);
|
||||
}
|
||||
155
tests/test_cir_sequence_replay_m3.rs
Normal file
155
tests/test_cir_sequence_replay_m3.rs
Normal file
@@ -0,0 +1,155 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
use rpki::cir::{encode_cir, materialize_cir, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, CIR_VERSION_V1};
|
||||
|
||||
fn apnic_tal_path() -> PathBuf {
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/tal/apnic-rfc7730-https.tal")
|
||||
}
|
||||
|
||||
fn apnic_ta_path() -> PathBuf {
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/ta/apnic-ta.cer")
|
||||
}
|
||||
|
||||
fn build_ta_only_cir() -> (CanonicalInputRepresentation, Vec<u8>) {
|
||||
let tal_bytes = std::fs::read(apnic_tal_path()).expect("read tal");
|
||||
let ta_bytes = std::fs::read(apnic_ta_path()).expect("read ta");
|
||||
let tal = rpki::data_model::tal::Tal::decode_bytes(&tal_bytes).expect("decode tal");
|
||||
let ta_rsync_uri = tal
|
||||
.ta_uris
|
||||
.iter()
|
||||
.find(|uri| uri.scheme() == "rsync")
|
||||
.expect("tal has rsync uri")
|
||||
.as_str()
|
||||
.to_string();
|
||||
let ta_hash = {
|
||||
use sha2::{Digest, Sha256};
|
||||
Sha256::digest(&ta_bytes).to_vec()
|
||||
};
|
||||
(
|
||||
CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: time::OffsetDateTime::parse(
|
||||
"2026-04-07T00:00:00Z",
|
||||
&time::format_description::well_known::Rfc3339,
|
||||
)
|
||||
.unwrap(),
|
||||
objects: vec![CirObject {
|
||||
rsync_uri: ta_rsync_uri,
|
||||
sha256: ta_hash,
|
||||
}],
|
||||
tals: vec![CirTal {
|
||||
tal_uri: "https://example.test/root.tal".to_string(),
|
||||
tal_bytes,
|
||||
}],
|
||||
},
|
||||
ta_bytes,
|
||||
)
|
||||
}
|
||||
|
||||
fn write_static(root: &Path, date: &str, bytes: &[u8]) {
|
||||
use sha2::{Digest, Sha256};
|
||||
let hash = hex::encode(Sha256::digest(bytes));
|
||||
let dir = root.join(date).join(&hash[0..2]).join(&hash[2..4]);
|
||||
std::fs::create_dir_all(&dir).expect("mkdir static");
|
||||
std::fs::write(dir.join(hash), bytes).expect("write static object");
|
||||
}
|
||||
|
||||
fn prepare_reference_ccr(work: &Path, cir: &CanonicalInputRepresentation, mirror_root: &Path) -> PathBuf {
|
||||
let reference_ccr = work.join("reference.ccr");
|
||||
let rpki_bin = env!("CARGO_BIN_EXE_rpki");
|
||||
let wrapper = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("scripts/cir/cir-rsync-wrapper");
|
||||
let tal_path = apnic_tal_path();
|
||||
let ta_path = apnic_ta_path();
|
||||
let out = Command::new(rpki_bin)
|
||||
.env("REAL_RSYNC_BIN", "/usr/bin/rsync")
|
||||
.env("CIR_MIRROR_ROOT", mirror_root)
|
||||
.env("CIR_LOCAL_LINK_MODE", "1")
|
||||
.args([
|
||||
"--db",
|
||||
work.join("reference-db").to_string_lossy().as_ref(),
|
||||
"--tal-path",
|
||||
tal_path.to_string_lossy().as_ref(),
|
||||
"--ta-path",
|
||||
ta_path.to_string_lossy().as_ref(),
|
||||
"--disable-rrdp",
|
||||
"--rsync-command",
|
||||
wrapper.to_string_lossy().as_ref(),
|
||||
"--validation-time",
|
||||
&cir.validation_time.format(&time::format_description::well_known::Rfc3339).unwrap(),
|
||||
"--max-depth",
|
||||
"0",
|
||||
"--max-instances",
|
||||
"1",
|
||||
"--ccr-out",
|
||||
reference_ccr.to_string_lossy().as_ref(),
|
||||
])
|
||||
.output()
|
||||
.expect("run reference rpki");
|
||||
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
|
||||
reference_ccr
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ours_sequence_replay_script_replays_all_steps() {
|
||||
if !Path::new("/usr/bin/rsync").exists() {
|
||||
return;
|
||||
}
|
||||
let td = tempfile::tempdir().expect("tempdir");
|
||||
let sequence_root = td.path().join("sequence");
|
||||
let static_root = sequence_root.join("static");
|
||||
let mirror_root = td.path().join("mirror");
|
||||
std::fs::create_dir_all(sequence_root.join("full")).unwrap();
|
||||
std::fs::create_dir_all(sequence_root.join("delta-001")).unwrap();
|
||||
std::fs::create_dir_all(sequence_root.join("delta-002")).unwrap();
|
||||
|
||||
let (cir, ta_bytes) = build_ta_only_cir();
|
||||
let cir_bytes = encode_cir(&cir).expect("encode cir");
|
||||
std::fs::write(sequence_root.join("full").join("input.cir"), &cir_bytes).unwrap();
|
||||
std::fs::write(sequence_root.join("delta-001").join("input.cir"), &cir_bytes).unwrap();
|
||||
std::fs::write(sequence_root.join("delta-002").join("input.cir"), &cir_bytes).unwrap();
|
||||
write_static(&static_root, "20260407", &ta_bytes);
|
||||
materialize_cir(&cir, &static_root, &mirror_root, true).unwrap();
|
||||
let reference = prepare_reference_ccr(td.path(), &cir, &mirror_root);
|
||||
std::fs::copy(&reference, sequence_root.join("full").join("result.ccr")).unwrap();
|
||||
std::fs::copy(&reference, sequence_root.join("delta-001").join("result.ccr")).unwrap();
|
||||
std::fs::copy(&reference, sequence_root.join("delta-002").join("result.ccr")).unwrap();
|
||||
std::fs::write(sequence_root.join("full").join("report.json"), b"{}").unwrap();
|
||||
std::fs::write(sequence_root.join("delta-001").join("report.json"), b"{}").unwrap();
|
||||
std::fs::write(sequence_root.join("delta-002").join("report.json"), b"{}").unwrap();
|
||||
|
||||
let sequence = serde_json::json!({
|
||||
"version": 1,
|
||||
"staticRoot": "static",
|
||||
"steps": [
|
||||
{"stepId":"full","kind":"full","validationTime":"2026-04-07T00:00:00Z","cirPath":"full/input.cir","ccrPath":"full/result.ccr","reportPath":"full/report.json","previousStepId":null},
|
||||
{"stepId":"delta-001","kind":"delta","validationTime":"2026-04-07T00:00:00Z","cirPath":"delta-001/input.cir","ccrPath":"delta-001/result.ccr","reportPath":"delta-001/report.json","previousStepId":"full"},
|
||||
{"stepId":"delta-002","kind":"delta","validationTime":"2026-04-07T00:00:00Z","cirPath":"delta-002/input.cir","ccrPath":"delta-002/result.ccr","reportPath":"delta-002/report.json","previousStepId":"delta-001"}
|
||||
]
|
||||
});
|
||||
std::fs::write(
|
||||
sequence_root.join("sequence.json"),
|
||||
serde_json::to_vec_pretty(&sequence).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let script =
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("scripts/cir/run_cir_replay_sequence_ours.sh");
|
||||
let out = Command::new(script)
|
||||
.args([
|
||||
"--sequence-root",
|
||||
sequence_root.to_string_lossy().as_ref(),
|
||||
"--rpki-bin",
|
||||
env!("CARGO_BIN_EXE_rpki"),
|
||||
])
|
||||
.output()
|
||||
.expect("run sequence replay");
|
||||
assert!(out.status.success(), "stderr={}", String::from_utf8_lossy(&out.stderr));
|
||||
|
||||
let summary: serde_json::Value =
|
||||
serde_json::from_slice(&std::fs::read(sequence_root.join("sequence-summary.json")).unwrap())
|
||||
.unwrap();
|
||||
assert_eq!(summary["stepCount"], 3);
|
||||
assert_eq!(summary["allMatch"], true);
|
||||
}
|
||||
Reference in New Issue
Block a user