Обновить формат Solana user PDA

This commit is contained in:
AidarKC
2026-05-24 19:41:13 +03:00
parent 74df7e2645
commit baef264bd0
7 changed files with 861 additions and 411 deletions
@@ -12,19 +12,31 @@ use common::utils::{create_pda, safe_read_pda, write_to_pda, ErrCode};
use std::str::FromStr;
const MAGIC: &[u8; 5] = b"SHiNE";
const FORMAT_MAJOR: u8 = 1;
const FORMAT_MAJOR: u8 = 2;
const FORMAT_MINOR: u8 = 0;
const KEY_STATUS_CREATED: u8 = 0;
const MAX_SYNC_SERVERS: usize = 32;
const MAX_AUTO_REALLOC_INCREASE: usize = 10_000;
const RESERVED_BYTES: [u8; 5] = [0, 0, 0, 0, 0];
const ZERO_HASH: [u8; 32] = [0; 32];
const BLOCK_TYPE_ROOT_KEY: u8 = 1;
const BLOCK_TYPE_DEVICE_KEY: u8 = 2;
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_VERSION_0: u8 = 0;
const BLOCKCHAIN_TYPE_MAIN_USER: u8 = 1;
const LAST_BLOCK_STATE_PREFIX: &[u8] = b"SHiNE_LAST_BLOCK";
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug)]
pub struct UserMutableFields {
pub blockchain_key: Pubkey,
pub device_key: Pubkey,
pub chain_number: u16,
pub blockchain_public_key: Pubkey,
pub blockchain_name: String,
pub used_bytes: u64,
pub last_block_number: u32,
pub last_block_hash: Vec<u8>,
pub last_block_signature: Vec<u8>,
pub arweave_tx_id: String,
pub is_server: bool,
pub server_key: Pubkey,
pub server_address: String,
@@ -59,17 +71,12 @@ pub struct UpdateUserPdaArgs {
pub struct UserRecord {
pub created_at_ms: u64,
pub updated_at_ms: u64,
pub version: u32,
pub prev_hash: [u8; 32],
pub record_number: u32,
pub prev_record_hash: [u8; 32],
pub login: String,
pub root_key_status: u8,
pub root_key: Pubkey,
pub blockchain_key_status: u8,
pub blockchain_key: Pubkey,
pub device_key_status: u8,
pub device_key: Pubkey,
pub chain_number: u16,
pub balance: u64,
pub blockchain: BlockchainRecord,
pub is_server: bool,
pub server_key: Pubkey,
pub server_address: String,
@@ -79,6 +86,18 @@ pub struct UserRecord {
pub signature: [u8; 64],
}
pub struct BlockchainRecord {
pub blockchain_type: u8,
pub blockchain_name: String,
pub blockchain_public_key: Pubkey,
pub paid_limit_bytes: u64,
pub used_bytes: u64,
pub last_block_number: u32,
pub last_block_hash: [u8; 32],
pub last_block_signature: [u8; 64],
pub arweave_tx_id: String,
}
#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug)]
pub struct UsersEconomyConfigState {
pub version: u8,
@@ -192,7 +211,11 @@ pub fn update_users_economy_config(
) -> Result<()> {
let dao_authority =
Pubkey::from_str(settings::DAO_AUTHORITY).map_err(|_| error!(ErrCode::InvalidSigner))?;
require_keys_eq!(dao_authority, ctx.accounts.signer.key(), ErrCode::InvalidSigner);
require_keys_eq!(
dao_authority,
ctx.accounts.signer.key(),
ErrCode::InvalidSigner
);
let (expected_pda, _) = find_users_economy_config_pda(ctx.program_id);
require_keys_eq!(
@@ -258,17 +281,22 @@ pub fn create_user_pda(ctx: Context<CreateUserPda>, args: CreateUserPdaArgs) ->
let mut record = UserRecord {
created_at_ms: args.created_at_ms,
updated_at_ms: args.created_at_ms,
version: 0,
prev_hash: ZERO_HASH,
record_number: 0,
prev_record_hash: ZERO_HASH,
login: args.login.clone(),
root_key_status: KEY_STATUS_CREATED,
root_key: args.root_key,
blockchain_key_status: KEY_STATUS_CREATED,
blockchain_key: args.fields.blockchain_key,
device_key_status: KEY_STATUS_CREATED,
device_key: args.fields.device_key,
chain_number: args.fields.chain_number,
balance: start_balance,
blockchain: BlockchainRecord {
blockchain_type: BLOCKCHAIN_TYPE_MAIN_USER,
blockchain_name: args.fields.blockchain_name.clone(),
blockchain_public_key: args.fields.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(),
},
is_server: args.fields.is_server,
server_key: args.fields.server_key,
server_address: args.fields.server_address.clone(),
@@ -277,6 +305,8 @@ pub fn create_user_pda(ctx: Context<CreateUserPda>, args: CreateUserPdaArgs) ->
trusted_count: args.fields.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(
@@ -310,7 +340,10 @@ 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(
args.additional_limit,
economy.lamports_per_limit_step,
)?)
.ok_or(error!(ErrCode::MathOverflow))?;
transfer_lamports(
&ctx.accounts.signer,
@@ -391,13 +424,7 @@ pub fn update_user_pda(ctx: Context<UpdateUserPda>, args: UpdateUserPdaArgs) ->
ErrCode::ImmutableFieldChanged
);
require!(
old_record.root_key_status == KEY_STATUS_CREATED
&& old_record.blockchain_key_status == KEY_STATUS_CREATED
&& old_record.device_key_status == KEY_STATUS_CREATED,
ErrCode::InvalidRecordData
);
require!(
args.version == old_record.version.saturating_add(1),
args.version == old_record.record_number.saturating_add(1),
ErrCode::InvalidVersion
);
@@ -409,25 +436,34 @@ pub fn update_user_pda(ctx: Context<UpdateUserPda>, args: UpdateUserPdaArgs) ->
);
let new_balance = old_record
.balance
.blockchain
.paid_limit_bytes
.checked_add(args.additional_limit)
.ok_or(error!(ErrCode::MathOverflow))?;
require!(new_balance >= old_record.balance, ErrCode::BalanceDecrease);
require!(
new_balance >= old_record.blockchain.paid_limit_bytes,
ErrCode::BalanceDecrease
);
let mut new_record = UserRecord {
created_at_ms: old_record.created_at_ms,
updated_at_ms: args.updated_at_ms,
version: args.version,
prev_hash: provided_prev_hash,
record_number: args.version,
prev_record_hash: provided_prev_hash,
login: old_record.login.clone(),
root_key_status: old_record.root_key_status,
root_key: old_record.root_key,
blockchain_key_status: old_record.blockchain_key_status,
blockchain_key: args.fields.blockchain_key,
device_key_status: old_record.device_key_status,
device_key: args.fields.device_key,
chain_number: args.fields.chain_number,
balance: new_balance,
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,
server_key: args.fields.server_key,
server_address: args.fields.server_address.clone(),
@@ -436,6 +472,20 @@ pub fn update_user_pda(ctx: Context<UpdateUserPda>, args: UpdateUserPdaArgs) ->
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
== old_record.blockchain.blockchain_public_key,
ErrCode::ImmutableFieldChanged
);
validate_blockchain_limits(
&new_record.blockchain,
old_record.blockchain.used_bytes,
old_record.blockchain.last_block_number,
false,
)?;
verify_last_block_state_signature(&ctx.accounts.instructions, &new_record)?;
let unsigned = serialize_unsigned_record(&new_record)?;
new_record.signature = verify_record_signature(
@@ -471,13 +521,6 @@ 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);
let server_address_bytes = record.server_address.as_bytes();
require!(
server_address_bytes.len() <= u8::MAX as usize,
ErrCode::InvalidRecordData
);
require!(record.access_servers.len() <= u8::MAX as usize, ErrCode::InvalidRecordData);
let mut out = Vec::new();
out.extend_from_slice(MAGIC);
out.push(FORMAT_MAJOR);
@@ -486,50 +529,22 @@ fn serialize_unsigned_record(record: &UserRecord) -> Result<Vec<u8>> {
out.extend_from_slice(&record.created_at_ms.to_le_bytes());
out.extend_from_slice(&record.updated_at_ms.to_le_bytes());
out.extend_from_slice(&record.version.to_le_bytes());
out.extend_from_slice(&record.prev_hash);
out.extend_from_slice(&record.record_number.to_le_bytes());
out.extend_from_slice(&record.prev_record_hash);
out.push(login_bytes.len() as u8);
out.extend_from_slice(login_bytes);
out.push(record.root_key_status);
out.extend_from_slice(record.root_key.as_ref());
out.push(record.blockchain_key_status);
out.extend_from_slice(record.blockchain_key.as_ref());
out.push(record.device_key_status);
out.extend_from_slice(record.device_key.as_ref());
out.extend_from_slice(&record.chain_number.to_le_bytes());
out.extend_from_slice(&record.balance.to_le_bytes());
out.push(if record.is_server { 1 } else { 0 });
let blocks_count = if record.is_server { 6 } else { 5 };
out.push(blocks_count);
write_root_key_block(&mut out, record);
write_device_key_block(&mut out, record);
write_blockchain_registry_block(&mut out, &record.blockchain)?;
if record.is_server {
out.extend_from_slice(record.server_key.as_ref());
out.push(server_address_bytes.len() as u8);
out.extend_from_slice(server_address_bytes);
require!(
record.sync_servers.len() <= MAX_SYNC_SERVERS,
ErrCode::InvalidRecordData
);
out.push(record.sync_servers.len() as u8);
for login in &record.sync_servers {
let bytes = login.as_bytes();
require!(bytes.len() <= u8::MAX as usize, ErrCode::InvalidRecordData);
out.push(bytes.len() as u8);
out.extend_from_slice(bytes);
}
write_server_profile_block(&mut out, record)?;
}
out.push(record.access_servers.len() as u8);
for login in &record.access_servers {
let bytes = login.as_bytes();
require!(bytes.len() <= u8::MAX as usize, ErrCode::InvalidRecordData);
out.push(bytes.len() as u8);
out.extend_from_slice(bytes);
}
out.push(record.trusted_count);
out.extend_from_slice(&RESERVED_BYTES);
write_access_servers_block(&mut out, record)?;
write_trusted_state_block(&mut out, record);
let record_len = out
.len()
@@ -549,6 +564,121 @@ fn serialize_full_record(record: &UserRecord) -> Result<Vec<u8>> {
Ok(out)
}
fn write_root_key_block(out: &mut Vec<u8>, record: &UserRecord) {
out.push(BLOCK_TYPE_ROOT_KEY);
out.push(BLOCK_VERSION_0);
out.extend_from_slice(record.root_key.as_ref());
}
fn write_device_key_block(out: &mut Vec<u8>, record: &UserRecord) {
out.push(BLOCK_TYPE_DEVICE_KEY);
out.push(BLOCK_VERSION_0);
out.extend_from_slice(record.device_key.as_ref());
}
fn write_blockchain_registry_block(out: &mut Vec<u8>, blockchain: &BlockchainRecord) -> Result<()> {
out.push(BLOCK_TYPE_BLOCKCHAIN_REGISTRY);
out.push(BLOCK_VERSION_0);
out.push(1);
write_blockchain_record(out, blockchain)?;
Ok(())
}
fn write_blockchain_record(out: &mut Vec<u8>, blockchain: &BlockchainRecord) -> Result<()> {
out.push(blockchain.blockchain_type);
write_len_prefixed_string(out, &blockchain.blockchain_name)?;
out.extend_from_slice(blockchain.blockchain_public_key.as_ref());
out.extend_from_slice(&blockchain.paid_limit_bytes.to_le_bytes());
out.extend_from_slice(&blockchain.used_bytes.to_le_bytes());
out.extend_from_slice(&blockchain.last_block_number.to_le_bytes());
out.extend_from_slice(&blockchain.last_block_hash);
out.extend_from_slice(&blockchain.last_block_signature);
if blockchain.arweave_tx_id.is_empty() {
out.push(0);
} else {
out.push(1);
write_len_prefixed_string(out, &blockchain.arweave_tx_id)?;
}
Ok(())
}
fn write_server_profile_block(out: &mut Vec<u8>, record: &UserRecord) -> Result<()> {
out.push(BLOCK_TYPE_SERVER_PROFILE);
out.push(BLOCK_VERSION_0);
out.push(1);
out.extend_from_slice(record.server_key.as_ref());
write_len_prefixed_string(out, &record.server_address)?;
require!(
record.sync_servers.len() <= MAX_SYNC_SERVERS,
ErrCode::InvalidRecordData
);
out.push(record.sync_servers.len() as u8);
for login in &record.sync_servers {
write_len_prefixed_string(out, login)?;
}
Ok(())
}
fn write_access_servers_block(out: &mut Vec<u8>, record: &UserRecord) -> Result<()> {
out.push(BLOCK_TYPE_ACCESS_SERVERS);
out.push(BLOCK_VERSION_0);
require!(
record.access_servers.len() <= u8::MAX as usize,
ErrCode::InvalidRecordData
);
out.push(record.access_servers.len() as u8);
for login in &record.access_servers {
write_len_prefixed_string(out, login)?;
}
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_len_prefixed_string(out: &mut Vec<u8>, value: &str) -> Result<()> {
let bytes = value.as_bytes();
require!(bytes.len() <= u8::MAX as usize, ErrCode::InvalidRecordData);
out.push(bytes.len() as u8);
out.extend_from_slice(bytes);
Ok(())
}
fn read_blockchain_record(data: &[u8], cursor: &mut usize) -> Result<BlockchainRecord> {
let blockchain_type = read_u8(data, cursor)?;
require!(
blockchain_type == BLOCKCHAIN_TYPE_MAIN_USER,
ErrCode::InvalidRecordData
);
let blockchain_name = read_len_prefixed_string(data, cursor)?;
let blockchain_public_key = Pubkey::new_from_array(read_fixed_32(data, cursor)?);
let paid_limit_bytes = read_u64(data, cursor)?;
let used_bytes = read_u64(data, cursor)?;
let last_block_number = read_u32(data, cursor)?;
let last_block_hash = read_fixed_32(data, cursor)?;
let last_block_signature = read_fixed_64(data, cursor)?;
let arweave_present = read_u8(data, cursor)?;
let arweave_tx_id = match arweave_present {
0 => String::new(),
1 => read_len_prefixed_string(data, cursor)?,
_ => return Err(error!(ErrCode::InvalidRecordData)),
};
Ok(BlockchainRecord {
blockchain_type,
blockchain_name,
blockchain_public_key,
paid_limit_bytes,
used_bytes,
last_block_number,
last_block_hash,
last_block_signature,
arweave_tx_id,
})
}
fn deserialize_record_from_pda(raw: &[u8]) -> Result<UserRecord> {
require!(raw.len() >= 9, ErrCode::InvalidRecordData);
require!(&raw[0..5] == MAGIC, ErrCode::InvalidRecordMagic);
@@ -566,72 +696,81 @@ fn deserialize_record_from_pda(raw: &[u8]) -> Result<UserRecord> {
let created_at_ms = read_u64(useful, &mut cursor)?;
let updated_at_ms = read_u64(useful, &mut cursor)?;
let version = read_u32(useful, &mut cursor)?;
let prev_hash = read_fixed_32(useful, &mut cursor)?;
let record_number = read_u32(useful, &mut cursor)?;
let prev_record_hash = read_fixed_32(useful, &mut cursor)?;
let login = read_len_prefixed_string(useful, &mut cursor)?;
let root_key_status = read_u8(useful, &mut cursor)?;
let root_key = Pubkey::new_from_array(read_fixed_32(useful, &mut cursor)?);
let blockchain_key_status = read_u8(useful, &mut cursor)?;
let blockchain_key = Pubkey::new_from_array(read_fixed_32(useful, &mut cursor)?);
let device_key_status = read_u8(useful, &mut cursor)?;
let device_key = Pubkey::new_from_array(read_fixed_32(useful, &mut cursor)?);
let blocks_count = read_u8(useful, &mut cursor)? as usize;
let mut root_key: Option<Pubkey> = None;
let mut device_key: Option<Pubkey> = None;
let mut blockchain: Option<BlockchainRecord> = None;
let mut is_server = false;
let mut server_key = Pubkey::default();
let mut server_address = String::new();
let mut sync_servers = Vec::new();
let mut access_servers = Vec::new();
let mut trusted_count = 0u8;
let chain_number = read_u16(useful, &mut cursor)?;
let balance = read_u64(useful, &mut cursor)?;
let is_server = read_u8(useful, &mut cursor)? == 1;
let (server_key, server_address) = if is_server {
(
Pubkey::new_from_array(read_fixed_32(useful, &mut cursor)?),
read_len_prefixed_string(useful, &mut cursor)?,
)
} else {
(Pubkey::default(), String::new())
};
let sync_servers = if is_server {
let sync_count = read_u8(useful, &mut cursor)? as usize;
require!(sync_count <= MAX_SYNC_SERVERS, ErrCode::InvalidRecordData);
let mut out = Vec::with_capacity(sync_count);
for _ in 0..sync_count {
out.push(read_len_prefixed_string(useful, &mut cursor)?);
for _ in 0..blocks_count {
let block_type = read_u8(useful, &mut cursor)?;
let block_version = read_u8(useful, &mut cursor)?;
require!(
block_version == BLOCK_VERSION_0,
ErrCode::InvalidRecordFormat
);
match block_type {
BLOCK_TYPE_ROOT_KEY => {
require!(root_key.is_none(), ErrCode::InvalidRecordData);
root_key = Some(Pubkey::new_from_array(read_fixed_32(useful, &mut cursor)?));
}
BLOCK_TYPE_DEVICE_KEY => {
require!(device_key.is_none(), ErrCode::InvalidRecordData);
device_key = Some(Pubkey::new_from_array(read_fixed_32(useful, &mut cursor)?));
}
BLOCK_TYPE_BLOCKCHAIN_REGISTRY => {
require!(blockchain.is_none(), ErrCode::InvalidRecordData);
let count = read_u8(useful, &mut cursor)?;
require!(count == 1, ErrCode::InvalidRecordData);
blockchain = Some(read_blockchain_record(useful, &mut cursor)?);
}
BLOCK_TYPE_SERVER_PROFILE => {
require!(!is_server, ErrCode::InvalidRecordData);
is_server = read_u8(useful, &mut cursor)? == 1;
require!(is_server, ErrCode::InvalidRecordData);
server_key = Pubkey::new_from_array(read_fixed_32(useful, &mut cursor)?);
server_address = read_len_prefixed_string(useful, &mut cursor)?;
let sync_count = read_u8(useful, &mut cursor)? as usize;
require!(sync_count <= MAX_SYNC_SERVERS, ErrCode::InvalidRecordData);
for _ in 0..sync_count {
sync_servers.push(read_len_prefixed_string(useful, &mut cursor)?);
}
}
BLOCK_TYPE_ACCESS_SERVERS => {
require!(access_servers.is_empty(), ErrCode::InvalidRecordData);
let access_count = read_u8(useful, &mut cursor)? as usize;
for _ in 0..access_count {
access_servers.push(read_len_prefixed_string(useful, &mut cursor)?);
}
}
BLOCK_TYPE_TRUSTED_STATE => {
trusted_count = read_u8(useful, &mut cursor)?;
}
_ => return Err(error!(ErrCode::InvalidRecordFormat)),
}
out
} else {
Vec::new()
};
let access_count = read_u8(useful, &mut cursor)? as usize;
let mut access_servers = Vec::with_capacity(access_count);
for _ in 0..access_count {
access_servers.push(read_len_prefixed_string(useful, &mut cursor)?);
}
let trusted_count = read_u8(useful, &mut cursor)?;
require!(
useful.get(cursor..cursor + 5) == Some(&RESERVED_BYTES),
ErrCode::InvalidRecordData
);
cursor += 5;
let signature = read_fixed_64(useful, &mut cursor)?;
require!(cursor == useful.len(), ErrCode::InvalidRecordLength);
Ok(UserRecord {
created_at_ms,
updated_at_ms,
version,
prev_hash,
record_number,
prev_record_hash,
login,
root_key_status,
root_key,
blockchain_key_status,
blockchain_key,
device_key_status,
device_key,
chain_number,
balance,
root_key: root_key.ok_or(error!(ErrCode::InvalidRecordData))?,
device_key: device_key.ok_or(error!(ErrCode::InvalidRecordData))?,
blockchain: blockchain.ok_or(error!(ErrCode::InvalidRecordData))?,
is_server,
server_key,
server_address,
@@ -656,29 +795,71 @@ fn verify_record_signature(
signature: &[u8],
unsigned: &[u8],
) -> Result<[u8; 64]> {
let provided_sig = vec_to_signature(signature)?;
let msg_hash = hashv(&[unsigned]);
verify_ed25519_signature_instruction(
instructions_sysvar,
root_key,
&provided_sig,
msg_hash.as_ref(),
)?;
Ok(provided_sig)
}
fn verify_last_block_state_signature(
instructions_sysvar: &AccountInfo,
record: &UserRecord,
) -> Result<()> {
let message = serialize_last_block_state(record)?;
let msg_hash = hashv(&[&message]);
verify_ed25519_signature_instruction(
instructions_sysvar,
&record.blockchain.blockchain_public_key,
&record.blockchain.last_block_signature,
msg_hash.as_ref(),
)
}
fn verify_ed25519_signature_instruction(
instructions_sysvar: &AccountInfo,
expected_pubkey: &Pubkey,
expected_signature: &[u8; 64],
expected_message: &[u8],
) -> Result<()> {
require_keys_eq!(
*instructions_sysvar.key,
anchor_lang::solana_program::sysvar::instructions::id(),
ErrCode::InvalidSignature
);
let provided_sig = vec_to_signature(signature)?;
let msg_hash = hashv(&[unsigned]);
let current_ix_index = load_current_index_checked(instructions_sysvar)
.map_err(|_| error!(ErrCode::InvalidSignature))?;
require!(current_ix_index > 0, ErrCode::InvalidSignature);
let ed_ix = load_instruction_at_checked((current_ix_index - 1) as usize, instructions_sysvar)
.map_err(|_| error!(ErrCode::InvalidSignature))?;
for ix_index in 0..current_ix_index {
let ed_ix = load_instruction_at_checked(ix_index as usize, instructions_sysvar)
.map_err(|_| error!(ErrCode::InvalidSignature))?;
if ed_ix.program_id != ed25519_program::id() {
continue;
}
let parsed = parse_ed25519_ix(&ed_ix)?;
if parsed.pubkey == *expected_pubkey
&& parsed.signature == *expected_signature
&& parsed.message == expected_message
{
return Ok(());
}
}
Err(error!(ErrCode::InvalidSignature))
}
let parsed = parse_ed25519_ix(&ed_ix)?;
require_keys_eq!(parsed.pubkey, *root_key, ErrCode::InvalidSignature);
require!(
parsed.message == msg_hash.as_ref(),
ErrCode::InvalidSignature
);
require!(parsed.signature == provided_sig, ErrCode::InvalidSignature);
Ok(parsed.signature)
fn serialize_last_block_state(record: &UserRecord) -> Result<Vec<u8>> {
let mut out = Vec::new();
out.extend_from_slice(LAST_BLOCK_STATE_PREFIX);
write_len_prefixed_string(&mut out, &record.login)?;
write_len_prefixed_string(&mut out, &record.blockchain.blockchain_name)?;
out.extend_from_slice(&record.blockchain.last_block_number.to_le_bytes());
out.extend_from_slice(&record.blockchain.last_block_hash);
out.extend_from_slice(&record.blockchain.used_bytes.to_le_bytes());
Ok(out)
}
struct ParsedEd25519 {
@@ -770,6 +951,22 @@ fn login_seed_normalized(login: &str) -> String {
}
fn validate_fields(fields: &UserMutableFields) -> Result<()> {
require!(
!fields.blockchain_name.is_empty(),
ErrCode::InvalidRecordData
);
require!(
fields.blockchain_name.as_bytes().len() <= u8::MAX as usize,
ErrCode::InvalidRecordData
);
require!(
fields.last_block_hash.len() == 32 && fields.last_block_signature.len() == 64,
ErrCode::InvalidRecordData
);
require!(
fields.arweave_tx_id.as_bytes().len() <= u8::MAX as usize,
ErrCode::InvalidRecordData
);
if fields.is_server {
require!(
!fields.server_address.is_empty(),
@@ -808,6 +1005,30 @@ fn validate_fields(fields: &UserMutableFields) -> Result<()> {
Ok(())
}
fn validate_blockchain_limits(
blockchain: &BlockchainRecord,
old_used_bytes: u64,
old_last_block_number: u32,
is_create: bool,
) -> Result<()> {
require!(
blockchain.blockchain_type == BLOCKCHAIN_TYPE_MAIN_USER,
ErrCode::InvalidRecordData
);
require!(
blockchain.used_bytes <= blockchain.paid_limit_bytes,
ErrCode::InvalidRecordData
);
if !is_create {
require!(
blockchain.used_bytes >= old_used_bytes
&& blockchain.last_block_number >= old_last_block_number,
ErrCode::InvalidRecordData
);
}
Ok(())
}
fn validate_inflow_vault(inflow_vault: &AccountInfo) -> Result<()> {
let payments_program_id = Pubkey::from_str(settings::SHINE_PAYMENTS_PROGRAM_ID)
.map_err(|_| error!(ErrCode::InvalidFeeReceiver))?;
@@ -850,7 +1071,10 @@ fn ensure_pda_size_and_rent<'info>(
let increase = required_len
.checked_sub(current_len)
.ok_or(error!(ErrCode::MathOverflow))?;
require!(increase <= MAX_AUTO_REALLOC_INCREASE, ErrCode::RecordTooLarge);
require!(
increase <= MAX_AUTO_REALLOC_INCREASE,
ErrCode::RecordTooLarge
);
let rent = Rent::get()?;
let required_lamports = rent.minimum_balance(required_len);
@@ -918,17 +1142,6 @@ fn read_u8(data: &[u8], cursor: &mut usize) -> Result<u8> {
Ok(v)
}
fn read_u16(data: &[u8], cursor: &mut usize) -> Result<u16> {
let end = cursor
.checked_add(2)
.ok_or(error!(ErrCode::InvalidRecordData))?;
let slice = data
.get(*cursor..end)
.ok_or(error!(ErrCode::InvalidRecordData))?;
*cursor = end;
Ok(u16::from_le_bytes([slice[0], slice[1]]))
}
fn read_u32(data: &[u8], cursor: &mut usize) -> Result<u32> {
let end = cursor
.checked_add(4)