SHA256
Обновить формат Solana user PDA
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user