montana/Montana-Protocol/Code/crates/montana-node/src/commands/fastsync.rs
2026-05-28 23:22:19 +03:00

163 lines
5.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use mt_net::{FastSyncResponseChunk, TableId};
use mt_sync::{FastSyncChunk, FastSyncTableId};
#[derive(Debug, Eq, PartialEq)]
pub enum WireChunkError {
RecordCountZero,
EmptyRecords,
RecordsIndivisible { len: usize, count: usize },
}
// Преобразование сетевого чанка fast-sync в чанк mt-sync: разбивает плоскую
// конкатенацию records на record_count равных записей. Канонический размер
// записи на таблицу не дублируется здесь — Snapshot::add_record проверяет его
// сам (единый источник истины по размеру). Маппинг TableId тотальный; таблица
// Proposals доходит до клиента и отклоняется там (ProposalsNotImplementedYet).
pub fn wire_chunk_to_sync(wire: FastSyncResponseChunk) -> Result<FastSyncChunk, WireChunkError> {
let table_id = match wire.table_id {
TableId::Account => FastSyncTableId::Account,
TableId::Node => FastSyncTableId::Node,
TableId::Candidate => FastSyncTableId::Candidate,
TableId::Proposals => FastSyncTableId::Proposals,
};
let count = wire.record_count as usize;
if count == 0 {
return Err(WireChunkError::RecordCountZero);
}
if wire.records.is_empty() {
return Err(WireChunkError::EmptyRecords);
}
if wire.records.len() % count != 0 {
return Err(WireChunkError::RecordsIndivisible {
len: wire.records.len(),
count,
});
}
let rec_size = wire.records.len() / count;
let records: Vec<Vec<u8>> = wire
.records
.chunks_exact(rec_size)
.map(<[u8]>::to_vec)
.collect();
Ok(FastSyncChunk {
chunk_index: wire.chunk_index,
total_chunks: wire.total_chunks,
table_id,
records,
})
}
#[cfg(test)]
mod tests {
use super::*;
use mt_codec::CanonicalEncode;
use mt_crypto::PUBLIC_KEY_SIZE;
use mt_state::{AccountRecord, ACCOUNT_RECORD_SIZE};
fn acct(seed: u8) -> Vec<u8> {
let rec = AccountRecord {
account_id: [seed; 32],
balance: 1000,
suite_id: 1,
is_node_operator: false,
frontier_hash: [seed; 32],
op_height: 0,
account_chain_length: 0,
account_chain_length_snapshot: 0,
current_pubkey: [seed; PUBLIC_KEY_SIZE],
creation_window: 0,
last_op_window: 0,
last_activation_window: 0,
};
let mut buf = Vec::with_capacity(ACCOUNT_RECORD_SIZE);
rec.encode(&mut buf);
buf
}
fn flat(records: &[Vec<u8>]) -> Vec<u8> {
let mut f = Vec::new();
for r in records {
f.extend_from_slice(r);
}
f
}
#[test]
fn splits_flat_records_preserving_bytes() {
let recs = vec![acct(0x11), acct(0x22), acct(0x33)];
let wire = FastSyncResponseChunk {
chunk_index: 2,
total_chunks: 5,
table_id: TableId::Account,
record_count: 3,
records: flat(&recs),
};
let out = wire_chunk_to_sync(wire).expect("convert");
assert_eq!(out.chunk_index, 2);
assert_eq!(out.total_chunks, 5);
assert_eq!(out.table_id, FastSyncTableId::Account);
assert_eq!(out.records, recs);
}
#[test]
fn maps_every_table_id() {
for (net, sync) in [
(TableId::Account, FastSyncTableId::Account),
(TableId::Node, FastSyncTableId::Node),
(TableId::Candidate, FastSyncTableId::Candidate),
(TableId::Proposals, FastSyncTableId::Proposals),
] {
let wire = FastSyncResponseChunk {
chunk_index: 0,
total_chunks: 1,
table_id: net,
record_count: 1,
records: vec![0u8; 8],
};
assert_eq!(wire_chunk_to_sync(wire).unwrap().table_id, sync);
}
}
#[test]
fn rejects_zero_record_count() {
let wire = FastSyncResponseChunk {
chunk_index: 0,
total_chunks: 1,
table_id: TableId::Account,
record_count: 0,
records: vec![0u8; 10],
};
assert_eq!(
wire_chunk_to_sync(wire),
Err(WireChunkError::RecordCountZero)
);
}
#[test]
fn rejects_empty_records() {
let wire = FastSyncResponseChunk {
chunk_index: 0,
total_chunks: 1,
table_id: TableId::Account,
record_count: 2,
records: Vec::new(),
};
assert_eq!(wire_chunk_to_sync(wire), Err(WireChunkError::EmptyRecords));
}
#[test]
fn rejects_indivisible_records() {
let wire = FastSyncResponseChunk {
chunk_index: 0,
total_chunks: 1,
table_id: TableId::Account,
record_count: 3,
records: vec![0u8; 10],
};
assert_eq!(
wire_chunk_to_sync(wire),
Err(WireChunkError::RecordsIndivisible { len: 10, count: 3 })
);
}
}