SHA256
Архивация в Arweave
This commit is contained in:
@@ -36,6 +36,7 @@ const BLOCK_TYPE_SERVER_PROFILE: u8 = 30;
|
||||
const BLOCK_TYPE_ACCESS_SERVERS: u8 = 40;
|
||||
const BLOCK_TYPE_SESSIONS: u8 = 50;
|
||||
const BLOCK_TYPE_TRUSTED_STATE: u8 = 70;
|
||||
const BLOCK_TYPE_ARCHIVE_HEAD: u8 = 100;
|
||||
const BLOCK_VERSION_0: u8 = 0;
|
||||
const BLOCKCHAIN_TYPE_MAIN_USER: u8 = 1;
|
||||
const SESSIONS_MODE_MIXED: u8 = 1;
|
||||
@@ -163,6 +164,8 @@ pub struct UpdateUserPdaArgs {
|
||||
pub prev_hash: [u8; 32],
|
||||
pub additional_limit: u64,
|
||||
pub fields: UserMutableFields,
|
||||
/// None = legacy instruction, archive head сохранить; Some(None) = очистить; Some(Some) = заменить.
|
||||
pub archive_head_update: Option<Option<ArchiveHeadRecord>>,
|
||||
pub signature: [u8; 64],
|
||||
}
|
||||
|
||||
@@ -210,6 +213,12 @@ pub struct BlockchainRecord {
|
||||
pub arweave_tx_id: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ArchiveHeadRecord {
|
||||
pub arweave_tx_id: [u8; 32],
|
||||
pub archive_hash: [u8; 32],
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UserRecord {
|
||||
pub created_at_ms: u64,
|
||||
@@ -230,6 +239,7 @@ pub struct UserRecord {
|
||||
pub sessions_mode: u8,
|
||||
pub sessions: Vec<SessionRecord>,
|
||||
pub trusted_count: u8,
|
||||
pub archive_head: Option<ArchiveHeadRecord>,
|
||||
pub signature: [u8; 64],
|
||||
}
|
||||
|
||||
@@ -349,17 +359,34 @@ fn parse_create_args(r: &mut Reader<'_>) -> Result<CreateUserPdaArgs, ProgramErr
|
||||
}
|
||||
|
||||
fn parse_update_args(r: &mut Reader<'_>) -> Result<UpdateUserPdaArgs, ProgramError> {
|
||||
let login = r.read_string_u8()?;
|
||||
let recovery_key = r.read_pubkey()?;
|
||||
let root_key = r.read_pubkey()?;
|
||||
let created_at_ms = r.read_u64()?;
|
||||
let updated_at_ms = r.read_u64()?;
|
||||
let version = r.read_u32()?;
|
||||
let prev_hash = r.read_fixed_32()?;
|
||||
let additional_limit = r.read_u64()?;
|
||||
let fields = parse_fields(r)?;
|
||||
|
||||
// Backward compatibility: legacy update после trusted_count содержит ровно 64-byte root signature.
|
||||
let archive_head_update = if r.remaining() > 64 {
|
||||
let present = r.read_u8()?;
|
||||
match present {
|
||||
0 => Some(None),
|
||||
1 => Some(Some(ArchiveHeadRecord {
|
||||
arweave_tx_id: r.read_fixed_32()?,
|
||||
archive_hash: r.read_fixed_32()?,
|
||||
})),
|
||||
_ => return Err(ProgramError::from(ShineUsersError::InvalidInstruction)),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(UpdateUserPdaArgs {
|
||||
login: r.read_string_u8()?,
|
||||
recovery_key: r.read_pubkey()?,
|
||||
root_key: r.read_pubkey()?,
|
||||
created_at_ms: r.read_u64()?,
|
||||
updated_at_ms: r.read_u64()?,
|
||||
version: r.read_u32()?,
|
||||
prev_hash: r.read_fixed_32()?,
|
||||
additional_limit: r.read_u64()?,
|
||||
fields: parse_fields(r)?,
|
||||
signature: r.read_fixed_64()?,
|
||||
login, recovery_key, root_key, created_at_ms, updated_at_ms, version, prev_hash,
|
||||
additional_limit, fields, archive_head_update, signature: r.read_fixed_64()?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -590,6 +617,7 @@ fn process_create_user_pda<'a>(program_id: &Pubkey, accounts: &'a [AccountInfo<'
|
||||
sessions_mode: args.fields.sessions_mode,
|
||||
sessions: args.fields.sessions,
|
||||
trusted_count: args.fields.trusted_count,
|
||||
archive_head: None,
|
||||
signature: [0; 64],
|
||||
};
|
||||
validate_blockchain_limits(&record.blockchain, 0, 0, true)?;
|
||||
@@ -713,6 +741,10 @@ fn build_update_record(old_record: &UserRecord, args: &UpdateUserPdaArgs, new_ba
|
||||
sessions_mode: args.fields.sessions_mode,
|
||||
sessions: args.fields.sessions.clone(),
|
||||
trusted_count: args.fields.trusted_count,
|
||||
archive_head: match &args.archive_head_update {
|
||||
None => old_record.archive_head.clone(),
|
||||
Some(value) => value.clone(),
|
||||
},
|
||||
signature: [0; 64],
|
||||
})
|
||||
}
|
||||
@@ -857,6 +889,7 @@ fn deserialize_record_from_pda(raw: &[u8]) -> Result<UserRecord, ProgramError> {
|
||||
let mut sessions_mode = SESSIONS_MODE_MIXED;
|
||||
let mut sessions = Vec::new();
|
||||
let mut trusted_count = 0u8;
|
||||
let mut archive_head: Option<ArchiveHeadRecord> = None;
|
||||
|
||||
for _ in 0..blocks_count {
|
||||
let block_type = read_u8_from(useful, &mut cursor)?;
|
||||
@@ -907,6 +940,13 @@ fn deserialize_record_from_pda(raw: &[u8]) -> Result<UserRecord, ProgramError> {
|
||||
BLOCK_TYPE_TRUSTED_STATE => {
|
||||
trusted_count = read_u8_from(useful, &mut cursor)?;
|
||||
}
|
||||
BLOCK_TYPE_ARCHIVE_HEAD => {
|
||||
require!(archive_head.is_none(), ShineUsersError::InvalidRecordData);
|
||||
archive_head = Some(ArchiveHeadRecord {
|
||||
arweave_tx_id: read_fixed_32_from(useful, &mut cursor)?,
|
||||
archive_hash: read_fixed_32_from(useful, &mut cursor)?,
|
||||
});
|
||||
}
|
||||
_ => return Err(ProgramError::from(ShineUsersError::InvalidRecordFormat)),
|
||||
}
|
||||
}
|
||||
@@ -933,6 +973,7 @@ fn deserialize_record_from_pda(raw: &[u8]) -> Result<UserRecord, ProgramError> {
|
||||
sessions_mode,
|
||||
sessions,
|
||||
trusted_count,
|
||||
archive_head,
|
||||
signature,
|
||||
})
|
||||
}
|
||||
@@ -986,7 +1027,7 @@ fn serialize_unsigned_record(record: &UserRecord) -> Result<Vec<u8>, ProgramErro
|
||||
out.push(login_bytes.len() as u8);
|
||||
out.extend_from_slice(login_bytes);
|
||||
|
||||
let blocks_count = if record.is_server { 8 } else { 7 };
|
||||
let blocks_count = (if record.is_server { 8 } else { 7 }) + if record.archive_head.is_some() { 1 } else { 0 };
|
||||
out.push(blocks_count);
|
||||
write_recovery_key_block(&mut out, record);
|
||||
write_root_key_block(&mut out, record);
|
||||
@@ -996,6 +1037,7 @@ fn serialize_unsigned_record(record: &UserRecord) -> Result<Vec<u8>, ProgramErro
|
||||
write_access_servers_block(&mut out, record)?;
|
||||
write_sessions_block(&mut out, record)?;
|
||||
write_trusted_state_block(&mut out, record);
|
||||
if let Some(archive_head) = &record.archive_head { write_archive_head_block(&mut out, archive_head); }
|
||||
|
||||
let record_len = out.len().checked_add(64).ok_or(ProgramError::from(ShineUsersError::MathOverflow))?;
|
||||
require!(record_len <= u16::MAX as usize, ShineUsersError::RecordTooLarge);
|
||||
@@ -1045,6 +1087,7 @@ fn write_sessions_block(out: &mut Vec<u8>, record: &UserRecord) -> Result<(), Pr
|
||||
out.push(BLOCK_TYPE_SESSIONS); out.push(BLOCK_VERSION_0); out.push(record.sessions_mode); require!(record.sessions.len() <= MAX_SESSIONS, ShineUsersError::InvalidRecordData); out.push(record.sessions.len() as u8); for session in &record.sessions { out.push(session.session_type); out.push(session.session_version); write_len_prefixed_string(out, &session.session_name)?; out.extend_from_slice(session.session_pub_key.as_ref()); } Ok(())
|
||||
}
|
||||
fn write_trusted_state_block(out: &mut Vec<u8>, record: &UserRecord) { out.push(BLOCK_TYPE_TRUSTED_STATE); out.push(BLOCK_VERSION_0); out.push(record.trusted_count); }
|
||||
fn write_archive_head_block(out: &mut Vec<u8>, archive: &ArchiveHeadRecord) { out.push(BLOCK_TYPE_ARCHIVE_HEAD); out.push(BLOCK_VERSION_0); out.extend_from_slice(&archive.arweave_tx_id); out.extend_from_slice(&archive.archive_hash); }
|
||||
fn write_len_prefixed_string(out: &mut Vec<u8>, value: &str) -> Result<(), ProgramError> { let bytes = value.as_bytes(); require!(bytes.len() <= u8::MAX as usize, ShineUsersError::InvalidRecordData); out.push(bytes.len() as u8); out.extend_from_slice(bytes); Ok(()) }
|
||||
|
||||
fn verify_record_signature_hash(instructions_sysvar: &AccountInfo, root_key: &Pubkey, signature: &[u8; 64], message_hash: &[u8]) -> Result<[u8; 64], ProgramError> {
|
||||
|
||||
Reference in New Issue
Block a user