SHA256
Зафиксировать все текущие изменения проекта
This commit is contained in:
@@ -14,6 +14,8 @@ const MAGIC: &[u8; 5] = b"SHiNE";
|
||||
const FORMAT_MAJOR: u8 = 1;
|
||||
const FORMAT_MINOR: u8 = 0;
|
||||
const MAX_SYNC_SERVERS: usize = 32;
|
||||
const MAX_SESSIONS: usize = 64;
|
||||
const MAX_SESSION_NAME_LEN: usize = 64;
|
||||
const MAX_AUTO_REALLOC_INCREASE: usize = 10_000;
|
||||
const ZERO_HASH: [u8; 32] = [0; 32];
|
||||
const BLOCK_TYPE_ROOT_KEY: u8 = 1;
|
||||
@@ -22,10 +24,23 @@ const BLOCK_TYPE_BLOCKCHAIN_REGISTRY: u8 = 3;
|
||||
const BLOCK_TYPE_SERVER_PROFILE: u8 = 30;
|
||||
const BLOCK_TYPE_ACCESS_SERVERS: u8 = 40;
|
||||
const BLOCK_TYPE_TRUSTED_STATE: u8 = 50;
|
||||
const BLOCK_TYPE_SESSIONS: u8 = 55;
|
||||
const BLOCK_VERSION_0: u8 = 0;
|
||||
const BLOCKCHAIN_TYPE_MAIN_USER: u8 = 1;
|
||||
const SESSIONS_MODE_MIXED: u8 = 1;
|
||||
const SESSIONS_MODE_PDA_ONLY: u8 = 10;
|
||||
const SESSION_TYPE_USER: u8 = 1;
|
||||
const SESSION_TYPE_SUBSERVER: u8 = 100;
|
||||
const LAST_BLOCK_STATE_PREFIX: &[u8] = b"SHiNE_LAST_BLOCK";
|
||||
|
||||
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SessionRecord {
|
||||
pub session_type: u8,
|
||||
pub session_version: u8,
|
||||
pub session_name: String,
|
||||
pub session_pub_key: Pubkey,
|
||||
}
|
||||
|
||||
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug)]
|
||||
pub struct UserMutableFields {
|
||||
pub device_key: Pubkey,
|
||||
@@ -42,6 +57,8 @@ pub struct UserMutableFields {
|
||||
pub server_address: String,
|
||||
pub sync_servers: Vec<String>,
|
||||
pub access_servers: Vec<String>,
|
||||
pub sessions_mode: u8,
|
||||
pub sessions: Vec<SessionRecord>,
|
||||
pub trusted_count: u8,
|
||||
}
|
||||
|
||||
@@ -83,6 +100,8 @@ pub struct UserRecord {
|
||||
pub server_address: String,
|
||||
pub sync_servers: Vec<String>,
|
||||
pub access_servers: Vec<String>,
|
||||
pub sessions_mode: u8,
|
||||
pub sessions: Vec<SessionRecord>,
|
||||
pub trusted_count: u8,
|
||||
pub signature: [u8; 64],
|
||||
}
|
||||
@@ -242,7 +261,35 @@ pub fn update_users_economy_config(
|
||||
}
|
||||
|
||||
pub fn create_user_pda(ctx: Context<CreateUserPda>, args: CreateUserPdaArgs) -> Result<()> {
|
||||
validate_login(&args.login)?;
|
||||
let CreateUserPdaArgs {
|
||||
login,
|
||||
root_key,
|
||||
created_at_ms,
|
||||
additional_limit,
|
||||
fields,
|
||||
signature,
|
||||
} = args;
|
||||
let UserMutableFields {
|
||||
device_key,
|
||||
blockchain_public_key,
|
||||
blockchain_name,
|
||||
used_bytes,
|
||||
last_block_number,
|
||||
last_block_hash,
|
||||
last_block_signature,
|
||||
arweave_tx_id,
|
||||
is_server,
|
||||
address_format_type,
|
||||
address_format_version,
|
||||
server_address,
|
||||
sync_servers,
|
||||
access_servers,
|
||||
sessions_mode,
|
||||
sessions,
|
||||
trusted_count,
|
||||
} = fields;
|
||||
|
||||
validate_login(&login)?;
|
||||
require_keys_eq!(
|
||||
ctx.accounts.login_guard_program.key(),
|
||||
Pubkey::from_str(settings::SHINE_LOGIN_GUARD_PROGRAM_ID)
|
||||
@@ -252,17 +299,35 @@ pub fn create_user_pda(ctx: Context<CreateUserPda>, args: CreateUserPdaArgs) ->
|
||||
classify_login_or_fail(
|
||||
&ctx.accounts.login_guard_program.to_account_info(),
|
||||
&ctx.accounts.signer,
|
||||
&args.login,
|
||||
&login,
|
||||
)?;
|
||||
validate_fields(&args.fields)?;
|
||||
validate_fields(&UserMutableFields {
|
||||
device_key,
|
||||
blockchain_public_key,
|
||||
blockchain_name: blockchain_name.clone(),
|
||||
used_bytes,
|
||||
last_block_number,
|
||||
last_block_hash: last_block_hash.clone(),
|
||||
last_block_signature: last_block_signature.clone(),
|
||||
arweave_tx_id: arweave_tx_id.clone(),
|
||||
is_server,
|
||||
address_format_type,
|
||||
address_format_version,
|
||||
server_address: server_address.clone(),
|
||||
sync_servers: sync_servers.clone(),
|
||||
access_servers: access_servers.clone(),
|
||||
sessions_mode,
|
||||
sessions: sessions.clone(),
|
||||
trusted_count,
|
||||
})?;
|
||||
validate_inflow_vault(&ctx.accounts.inflow_vault)?;
|
||||
require!(
|
||||
args.additional_limit % settings::LIMIT_STEP == 0,
|
||||
additional_limit % settings::LIMIT_STEP == 0,
|
||||
ErrCode::InvalidLimitIncrement
|
||||
);
|
||||
let economy = read_users_economy_config(&ctx.accounts.users_economy_config_pda)?;
|
||||
|
||||
let login_seed = login_seed_normalized(&args.login);
|
||||
let login_seed = login_seed_normalized(&login);
|
||||
let (expected_pda, bump) = find_user_pda(ctx.program_id, &login_seed);
|
||||
require_keys_eq!(
|
||||
expected_pda,
|
||||
@@ -276,46 +341,50 @@ pub fn create_user_pda(ctx: Context<CreateUserPda>, args: CreateUserPdaArgs) ->
|
||||
|
||||
let start_balance = economy
|
||||
.start_bonus_limit
|
||||
.checked_add(args.additional_limit)
|
||||
.checked_add(additional_limit)
|
||||
.ok_or(error!(ErrCode::MathOverflow))?;
|
||||
|
||||
let mut record = UserRecord {
|
||||
created_at_ms: args.created_at_ms,
|
||||
updated_at_ms: args.created_at_ms,
|
||||
created_at_ms,
|
||||
updated_at_ms: created_at_ms,
|
||||
record_number: 0,
|
||||
prev_record_hash: ZERO_HASH,
|
||||
login: args.login.clone(),
|
||||
root_key: args.root_key,
|
||||
device_key: args.fields.device_key,
|
||||
login,
|
||||
root_key,
|
||||
device_key,
|
||||
blockchain: BlockchainRecord {
|
||||
blockchain_type: BLOCKCHAIN_TYPE_MAIN_USER,
|
||||
blockchain_name: args.fields.blockchain_name.clone(),
|
||||
blockchain_public_key: args.fields.blockchain_public_key,
|
||||
blockchain_name,
|
||||
blockchain_public_key,
|
||||
paid_limit_bytes: start_balance,
|
||||
used_bytes: args.fields.used_bytes,
|
||||
last_block_number: args.fields.last_block_number,
|
||||
last_block_hash: vec_to_hash32(&args.fields.last_block_hash)?,
|
||||
last_block_signature: vec_to_signature(&args.fields.last_block_signature)?,
|
||||
arweave_tx_id: args.fields.arweave_tx_id.clone(),
|
||||
used_bytes,
|
||||
last_block_number,
|
||||
last_block_hash: vec_to_hash32(&last_block_hash)?,
|
||||
last_block_signature: vec_to_signature(&last_block_signature)?,
|
||||
arweave_tx_id,
|
||||
},
|
||||
is_server: args.fields.is_server,
|
||||
address_format_type: args.fields.address_format_type,
|
||||
address_format_version: args.fields.address_format_version,
|
||||
server_address: args.fields.server_address.clone(),
|
||||
sync_servers: args.fields.sync_servers.clone(),
|
||||
access_servers: args.fields.access_servers.clone(),
|
||||
trusted_count: args.fields.trusted_count,
|
||||
is_server,
|
||||
address_format_type,
|
||||
address_format_version,
|
||||
server_address,
|
||||
sync_servers,
|
||||
access_servers,
|
||||
sessions_mode,
|
||||
sessions,
|
||||
trusted_count,
|
||||
signature: [0; 64],
|
||||
};
|
||||
validate_blockchain_limits(&record.blockchain, 0, 0, true)?;
|
||||
verify_last_block_state_signature(&ctx.accounts.instructions, &record)?;
|
||||
|
||||
let unsigned = serialize_unsigned_record(&record)?;
|
||||
record.signature = verify_record_signature(
|
||||
let unsigned_hash = hashv(&[&unsigned]);
|
||||
drop(unsigned);
|
||||
record.signature = verify_record_signature_hash(
|
||||
&ctx.accounts.instructions,
|
||||
&record.root_key,
|
||||
&args.signature,
|
||||
&unsigned,
|
||||
&signature,
|
||||
unsigned_hash.as_ref(),
|
||||
)?;
|
||||
|
||||
let serialized = serialize_full_record(&record)?;
|
||||
@@ -342,10 +411,7 @@ pub fn create_user_pda(ctx: Context<CreateUserPda>, args: CreateUserPdaArgs) ->
|
||||
|
||||
let total_fee = economy
|
||||
.registration_fee_lamports
|
||||
.checked_add(limit_fee_lamports(
|
||||
args.additional_limit,
|
||||
economy.lamports_per_limit_step,
|
||||
)?)
|
||||
.checked_add(limit_fee_lamports(additional_limit, economy.lamports_per_limit_step)?)
|
||||
.ok_or(error!(ErrCode::MathOverflow))?;
|
||||
transfer_lamports(
|
||||
&ctx.accounts.signer,
|
||||
@@ -396,10 +462,11 @@ pub fn update_user_pda(ctx: Context<UpdateUserPda>, args: UpdateUserPdaArgs) ->
|
||||
);
|
||||
let economy = read_users_economy_config(&ctx.accounts.users_economy_config_pda)?;
|
||||
|
||||
let login_seed = login_seed_normalized(&args.login);
|
||||
let (expected_pda, _) = find_user_pda(ctx.program_id, &login_seed);
|
||||
require_keys_eq!(
|
||||
expected_pda,
|
||||
{
|
||||
let normalized_login = login_seed_normalized(&args.login);
|
||||
find_user_pda(ctx.program_id, &normalized_login).0
|
||||
},
|
||||
ctx.accounts.user_pda.key(),
|
||||
ErrCode::InvalidPdaAddress
|
||||
);
|
||||
@@ -410,7 +477,7 @@ pub fn update_user_pda(ctx: Context<UpdateUserPda>, args: UpdateUserPdaArgs) ->
|
||||
|
||||
let raw = safe_read_pda(&ctx.accounts.user_pda);
|
||||
require!(!raw.is_empty(), ErrCode::EmptyPdaData);
|
||||
let old_record = deserialize_record_from_pda(raw.as_slice())?;
|
||||
let old_record = Box::new(deserialize_record_from_pda(raw.as_slice())?);
|
||||
drop(raw);
|
||||
|
||||
require!(
|
||||
@@ -431,24 +498,21 @@ pub fn update_user_pda(ctx: Context<UpdateUserPda>, args: UpdateUserPdaArgs) ->
|
||||
ErrCode::InvalidVersion
|
||||
);
|
||||
|
||||
let expected_prev_hash = hash_unsigned_record(&old_record)?;
|
||||
let provided_prev_hash = vec_to_hash32(&args.prev_hash)?;
|
||||
let provided_prev_hash = Box::new(vec_to_hash32(&args.prev_hash)?);
|
||||
require!(
|
||||
expected_prev_hash == provided_prev_hash,
|
||||
hash_unsigned_record(&old_record)? == *provided_prev_hash,
|
||||
ErrCode::InvalidPrevHash
|
||||
);
|
||||
|
||||
let new_balance = old_record
|
||||
let new_balance = Box::new(old_record
|
||||
.blockchain
|
||||
.paid_limit_bytes
|
||||
.checked_add(args.additional_limit)
|
||||
.ok_or(error!(ErrCode::MathOverflow))?;
|
||||
.ok_or(error!(ErrCode::MathOverflow))?);
|
||||
require!(
|
||||
new_balance >= old_record.blockchain.paid_limit_bytes,
|
||||
*new_balance >= old_record.blockchain.paid_limit_bytes,
|
||||
ErrCode::BalanceDecrease
|
||||
);
|
||||
let old_used_bytes = old_record.blockchain.used_bytes;
|
||||
let old_last_block_number = old_record.blockchain.last_block_number;
|
||||
let blockchain_state_unchanged = old_record.blockchain.used_bytes == args.fields.used_bytes
|
||||
&& old_record.blockchain.last_block_number == args.fields.last_block_number
|
||||
&& old_record.blockchain.last_block_hash.as_slice() == args.fields.last_block_hash.as_slice()
|
||||
@@ -456,60 +520,60 @@ pub fn update_user_pda(ctx: Context<UpdateUserPda>, args: UpdateUserPdaArgs) ->
|
||||
== args.fields.last_block_signature.as_slice()
|
||||
&& old_record.blockchain.arweave_tx_id == args.fields.arweave_tx_id;
|
||||
|
||||
let mut new_record = UserRecord {
|
||||
created_at_ms: old_record.created_at_ms,
|
||||
updated_at_ms: args.updated_at_ms,
|
||||
record_number: args.version,
|
||||
prev_record_hash: provided_prev_hash,
|
||||
login: old_record.login.clone(),
|
||||
root_key: old_record.root_key,
|
||||
device_key: args.fields.device_key,
|
||||
blockchain: BlockchainRecord {
|
||||
blockchain_type: old_record.blockchain.blockchain_type,
|
||||
blockchain_name: args.fields.blockchain_name.clone(),
|
||||
blockchain_public_key: args.fields.blockchain_public_key,
|
||||
paid_limit_bytes: new_balance,
|
||||
used_bytes: args.fields.used_bytes,
|
||||
last_block_number: args.fields.last_block_number,
|
||||
last_block_hash: vec_to_hash32(&args.fields.last_block_hash)?,
|
||||
last_block_signature: vec_to_signature(&args.fields.last_block_signature)?,
|
||||
arweave_tx_id: args.fields.arweave_tx_id.clone(),
|
||||
},
|
||||
is_server: args.fields.is_server,
|
||||
address_format_type: args.fields.address_format_type,
|
||||
address_format_version: args.fields.address_format_version,
|
||||
server_address: args.fields.server_address.clone(),
|
||||
sync_servers: args.fields.sync_servers.clone(),
|
||||
access_servers: args.fields.access_servers.clone(),
|
||||
trusted_count: args.fields.trusted_count,
|
||||
signature: [0; 64],
|
||||
};
|
||||
require!(
|
||||
new_record.blockchain.blockchain_type == old_record.blockchain.blockchain_type
|
||||
&& new_record.blockchain.blockchain_name == old_record.blockchain.blockchain_name
|
||||
&& new_record.blockchain.blockchain_public_key
|
||||
args.fields.blockchain_name == old_record.blockchain.blockchain_name
|
||||
&& args.fields.blockchain_public_key
|
||||
== old_record.blockchain.blockchain_public_key,
|
||||
ErrCode::ImmutableFieldChanged
|
||||
);
|
||||
validate_blockchain_limits(
|
||||
&new_record.blockchain,
|
||||
old_used_bytes,
|
||||
old_last_block_number,
|
||||
false,
|
||||
)?;
|
||||
drop(old_record);
|
||||
{
|
||||
let candidate_blockchain =
|
||||
build_candidate_blockchain_for_update(&old_record, *new_balance, &args.fields)?;
|
||||
validate_blockchain_limits(
|
||||
&candidate_blockchain,
|
||||
old_record.blockchain.used_bytes,
|
||||
old_record.blockchain.last_block_number,
|
||||
false,
|
||||
)?;
|
||||
}
|
||||
if !blockchain_state_unchanged {
|
||||
verify_last_block_state_signature(&ctx.accounts.instructions, &new_record)?;
|
||||
verify_last_block_state_signature_candidate(
|
||||
&ctx.accounts.instructions,
|
||||
&old_record,
|
||||
args.updated_at_ms,
|
||||
args.version,
|
||||
*provided_prev_hash,
|
||||
*new_balance,
|
||||
&args.fields,
|
||||
)?;
|
||||
}
|
||||
|
||||
let unsigned = serialize_unsigned_record(&new_record)?;
|
||||
new_record.signature = verify_record_signature(
|
||||
&ctx.accounts.instructions,
|
||||
&new_record.root_key,
|
||||
&args.signature,
|
||||
&unsigned,
|
||||
let unsigned = serialize_unsigned_update_candidate(
|
||||
&old_record,
|
||||
args.updated_at_ms,
|
||||
args.version,
|
||||
*provided_prev_hash,
|
||||
*new_balance,
|
||||
&args.fields,
|
||||
)?;
|
||||
let unsigned_hash = hashv(&[&unsigned]);
|
||||
drop(unsigned);
|
||||
let verified_signature = Box::new(verify_record_signature_hash(
|
||||
&ctx.accounts.instructions,
|
||||
&old_record.root_key,
|
||||
&args.signature,
|
||||
unsigned_hash.as_ref(),
|
||||
)?);
|
||||
let mut new_record = build_update_record(
|
||||
&old_record,
|
||||
args.updated_at_ms,
|
||||
args.version,
|
||||
*provided_prev_hash,
|
||||
*new_balance,
|
||||
&args.fields,
|
||||
)?;
|
||||
new_record.signature = *verified_signature;
|
||||
drop(old_record);
|
||||
|
||||
let serialized = serialize_full_record(&new_record)?;
|
||||
ensure_pda_size_and_rent(
|
||||
@@ -533,6 +597,97 @@ pub fn update_user_pda(ctx: Context<UpdateUserPda>, args: UpdateUserPdaArgs) ->
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn build_candidate_blockchain_for_update(
|
||||
old_record: &UserRecord,
|
||||
new_balance: u64,
|
||||
fields: &UserMutableFields,
|
||||
) -> Result<BlockchainRecord> {
|
||||
Ok(BlockchainRecord {
|
||||
blockchain_type: old_record.blockchain.blockchain_type,
|
||||
blockchain_name: fields.blockchain_name.clone(),
|
||||
blockchain_public_key: fields.blockchain_public_key,
|
||||
paid_limit_bytes: new_balance,
|
||||
used_bytes: fields.used_bytes,
|
||||
last_block_number: fields.last_block_number,
|
||||
last_block_hash: vec_to_hash32(&fields.last_block_hash)?,
|
||||
last_block_signature: vec_to_signature(&fields.last_block_signature)?,
|
||||
arweave_tx_id: fields.arweave_tx_id.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn build_update_record(
|
||||
old_record: &UserRecord,
|
||||
updated_at_ms: u64,
|
||||
version: u32,
|
||||
prev_record_hash: [u8; 32],
|
||||
new_balance: u64,
|
||||
fields: &UserMutableFields,
|
||||
) -> Result<UserRecord> {
|
||||
Ok(UserRecord {
|
||||
created_at_ms: old_record.created_at_ms,
|
||||
updated_at_ms,
|
||||
record_number: version,
|
||||
prev_record_hash,
|
||||
login: old_record.login.clone(),
|
||||
root_key: old_record.root_key,
|
||||
device_key: fields.device_key,
|
||||
blockchain: build_candidate_blockchain_for_update(old_record, new_balance, fields)?,
|
||||
is_server: fields.is_server,
|
||||
address_format_type: fields.address_format_type,
|
||||
address_format_version: fields.address_format_version,
|
||||
server_address: fields.server_address.clone(),
|
||||
sync_servers: fields.sync_servers.clone(),
|
||||
access_servers: fields.access_servers.clone(),
|
||||
sessions_mode: fields.sessions_mode,
|
||||
sessions: fields.sessions.clone(),
|
||||
trusted_count: fields.trusted_count,
|
||||
signature: [0; 64],
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn verify_last_block_state_signature_candidate(
|
||||
instructions_sysvar: &AccountInfo,
|
||||
old_record: &UserRecord,
|
||||
updated_at_ms: u64,
|
||||
version: u32,
|
||||
prev_record_hash: [u8; 32],
|
||||
new_balance: u64,
|
||||
fields: &UserMutableFields,
|
||||
) -> Result<()> {
|
||||
let candidate = build_update_record(
|
||||
old_record,
|
||||
updated_at_ms,
|
||||
version,
|
||||
prev_record_hash,
|
||||
new_balance,
|
||||
fields,
|
||||
)?;
|
||||
verify_last_block_state_signature(instructions_sysvar, &candidate)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn serialize_unsigned_update_candidate(
|
||||
old_record: &UserRecord,
|
||||
updated_at_ms: u64,
|
||||
version: u32,
|
||||
prev_record_hash: [u8; 32],
|
||||
new_balance: u64,
|
||||
fields: &UserMutableFields,
|
||||
) -> Result<Vec<u8>> {
|
||||
let candidate = build_update_record(
|
||||
old_record,
|
||||
updated_at_ms,
|
||||
version,
|
||||
prev_record_hash,
|
||||
new_balance,
|
||||
fields,
|
||||
)?;
|
||||
serialize_unsigned_record(&candidate)
|
||||
}
|
||||
|
||||
fn serialize_unsigned_record(record: &UserRecord) -> Result<Vec<u8>> {
|
||||
let login_bytes = record.login.as_bytes();
|
||||
require!(login_bytes.len() <= u8::MAX as usize, ErrCode::InvalidLogin);
|
||||
@@ -551,7 +706,7 @@ fn serialize_unsigned_record(record: &UserRecord) -> Result<Vec<u8>> {
|
||||
out.push(login_bytes.len() as u8);
|
||||
out.extend_from_slice(login_bytes);
|
||||
|
||||
let blocks_count = if record.is_server { 6 } else { 5 };
|
||||
let blocks_count = if record.is_server { 7 } else { 6 };
|
||||
out.push(blocks_count);
|
||||
write_root_key_block(&mut out, record);
|
||||
write_device_key_block(&mut out, record);
|
||||
@@ -560,6 +715,7 @@ fn serialize_unsigned_record(record: &UserRecord) -> Result<Vec<u8>> {
|
||||
write_server_profile_block(&mut out, record)?;
|
||||
}
|
||||
write_access_servers_block(&mut out, record)?;
|
||||
write_sessions_block(&mut out, record)?;
|
||||
write_trusted_state_block(&mut out, record);
|
||||
|
||||
let record_len = out
|
||||
@@ -650,6 +806,29 @@ fn write_access_servers_block(out: &mut Vec<u8>, record: &UserRecord) -> Result<
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_sessions_block(out: &mut Vec<u8>, record: &UserRecord) -> Result<()> {
|
||||
out.push(BLOCK_TYPE_SESSIONS);
|
||||
out.push(BLOCK_VERSION_0);
|
||||
out.push(record.sessions_mode);
|
||||
require!(
|
||||
record.sessions.len() <= MAX_SESSIONS,
|
||||
ErrCode::InvalidRecordData
|
||||
);
|
||||
out.push(record.sessions.len() as u8);
|
||||
for session in &record.sessions {
|
||||
write_session_record(out, session)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_session_record(out: &mut Vec<u8>, session: &SessionRecord) -> Result<()> {
|
||||
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);
|
||||
@@ -696,6 +875,19 @@ fn read_blockchain_record(data: &[u8], cursor: &mut usize) -> Result<BlockchainR
|
||||
})
|
||||
}
|
||||
|
||||
fn read_session_record(data: &[u8], cursor: &mut usize) -> Result<SessionRecord> {
|
||||
let session_type = read_u8(data, cursor)?;
|
||||
let session_version = read_u8(data, cursor)?;
|
||||
let session_name = read_len_prefixed_string(data, cursor)?;
|
||||
let session_pub_key = Pubkey::new_from_array(read_fixed_32(data, cursor)?);
|
||||
Ok(SessionRecord {
|
||||
session_type,
|
||||
session_version,
|
||||
session_name,
|
||||
session_pub_key,
|
||||
})
|
||||
}
|
||||
|
||||
fn deserialize_record_from_pda(raw: &[u8]) -> Result<UserRecord> {
|
||||
require!(raw.len() >= 9, ErrCode::InvalidRecordData);
|
||||
require!(&raw[0..5] == MAGIC, ErrCode::InvalidRecordMagic);
|
||||
@@ -727,6 +919,8 @@ fn deserialize_record_from_pda(raw: &[u8]) -> Result<UserRecord> {
|
||||
let mut server_address = String::new();
|
||||
let mut sync_servers = Vec::new();
|
||||
let mut access_servers = Vec::new();
|
||||
let mut sessions_mode = SESSIONS_MODE_MIXED;
|
||||
let mut sessions = Vec::new();
|
||||
let mut trusted_count = 0u8;
|
||||
|
||||
for _ in 0..blocks_count {
|
||||
@@ -771,6 +965,15 @@ fn deserialize_record_from_pda(raw: &[u8]) -> Result<UserRecord> {
|
||||
access_servers.push(read_len_prefixed_string(useful, &mut cursor)?);
|
||||
}
|
||||
}
|
||||
BLOCK_TYPE_SESSIONS => {
|
||||
require!(sessions.is_empty(), ErrCode::InvalidRecordData);
|
||||
sessions_mode = read_u8(useful, &mut cursor)?;
|
||||
let sessions_count = read_u8(useful, &mut cursor)? as usize;
|
||||
require!(sessions_count <= MAX_SESSIONS, ErrCode::InvalidRecordData);
|
||||
for _ in 0..sessions_count {
|
||||
sessions.push(read_session_record(useful, &mut cursor)?);
|
||||
}
|
||||
}
|
||||
BLOCK_TYPE_TRUSTED_STATE => {
|
||||
trusted_count = read_u8(useful, &mut cursor)?;
|
||||
}
|
||||
@@ -778,6 +981,7 @@ fn deserialize_record_from_pda(raw: &[u8]) -> Result<UserRecord> {
|
||||
}
|
||||
}
|
||||
|
||||
validate_sessions_fields(sessions_mode, &sessions)?;
|
||||
let signature = read_fixed_64(useful, &mut cursor)?;
|
||||
require!(cursor == useful.len(), ErrCode::InvalidRecordLength);
|
||||
|
||||
@@ -796,6 +1000,8 @@ fn deserialize_record_from_pda(raw: &[u8]) -> Result<UserRecord> {
|
||||
server_address,
|
||||
sync_servers,
|
||||
access_servers,
|
||||
sessions_mode,
|
||||
sessions,
|
||||
trusted_count,
|
||||
signature,
|
||||
})
|
||||
@@ -809,20 +1015,19 @@ fn hash_unsigned_record(record: &UserRecord) -> Result<[u8; 32]> {
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
fn verify_record_signature(
|
||||
fn verify_record_signature_hash(
|
||||
instructions_sysvar: &AccountInfo,
|
||||
root_key: &Pubkey,
|
||||
signature: &[u8],
|
||||
unsigned: &[u8],
|
||||
message_hash: &[u8],
|
||||
) -> Result<[u8; 64]> {
|
||||
let provided_sig = vec_to_signature(signature)?;
|
||||
let msg_hash = hashv(&[unsigned]);
|
||||
verify_ed25519_signature_instruction(
|
||||
instructions_sysvar,
|
||||
-2,
|
||||
root_key,
|
||||
&provided_sig,
|
||||
msg_hash.as_ref(),
|
||||
message_hash,
|
||||
)?;
|
||||
Ok(provided_sig)
|
||||
}
|
||||
@@ -1007,6 +1212,46 @@ fn validate_fields(fields: &UserMutableFields) -> Result<()> {
|
||||
ErrCode::InvalidRecordData
|
||||
);
|
||||
}
|
||||
validate_sessions_fields(fields.sessions_mode, &fields.sessions)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_sessions_fields(mode: u8, sessions: &[SessionRecord]) -> Result<()> {
|
||||
require!(
|
||||
mode == SESSIONS_MODE_MIXED || mode == SESSIONS_MODE_PDA_ONLY,
|
||||
ErrCode::InvalidRecordData
|
||||
);
|
||||
require!(sessions.len() <= MAX_SESSIONS, ErrCode::InvalidRecordData);
|
||||
|
||||
for i in 0..sessions.len() {
|
||||
validate_session_record(&sessions[i])?;
|
||||
for j in (i + 1)..sessions.len() {
|
||||
require!(
|
||||
sessions[i].session_name != sessions[j].session_name,
|
||||
ErrCode::InvalidRecordData
|
||||
);
|
||||
require!(
|
||||
sessions[i].session_pub_key != sessions[j].session_pub_key,
|
||||
ErrCode::InvalidRecordData
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_session_record(session: &SessionRecord) -> Result<()> {
|
||||
require!(
|
||||
session.session_type == SESSION_TYPE_USER || session.session_type == SESSION_TYPE_SUBSERVER,
|
||||
ErrCode::InvalidRecordData
|
||||
);
|
||||
require!(session.session_version == 1, ErrCode::InvalidRecordData);
|
||||
let bytes = session.session_name.as_bytes();
|
||||
require!(!bytes.is_empty(), ErrCode::InvalidRecordData);
|
||||
require!(bytes.len() <= MAX_SESSION_NAME_LEN, ErrCode::InvalidRecordData);
|
||||
for &b in bytes {
|
||||
let ok = b.is_ascii_alphanumeric() || b == b'_';
|
||||
require!(ok, ErrCode::InvalidRecordData);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user