Skip to content

Commit 78d3c5c

Browse files
authored
[message-buffer 14/x] - Message buffer/zero copy fixes (#811)
* test(message-buffer): add rust integration tests add rust-toolchain.toml to pin rust version, add integration tests, update cpi caller auth seeds * refactor(message-buffer): remove unused test ix * chore(message-buffer): clean up * refactor(message-buffer): simple refactor * test(message-buffer): refactor integration test structure * refactor(message-buffer): rename * fix(message-buffer): fix min size check when shrinking msg buffer * chore(message-buffer): cleanup * fix(message-buffer): resize borrow bug fix * test(message-buffer): refactor test util methods into MessageBufferTestContext for less duplication * test(message-buffer): resolve merge conflicts from repo restructure * chore(message-buffer): delete commented out code * feat(message-buffer): use AccountLoader for zero-copy for resize/delete & put_all * chore(message-buffer): clean up * style(message-buffer): clean up * fix(message-buffer): address PR feedback revert to old put_all impl of using bytemuck for deserializing header, update tests add back old check * chore(message-buffer): clean up
1 parent 2e32a22 commit 78d3c5c

File tree

10 files changed

+273
-260
lines changed

10 files changed

+273
-260
lines changed

pythnet/message_buffer/programs/message_buffer/src/instructions/create_buffer.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ pub fn create_buffer<'info>(
7676
}
7777
loader.exit(&crate::ID)?;
7878
} else {
79-
// FIXME: change this to be emit!(Event)
8079
msg!("Buffer account already initialized");
8180
}
8281

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,21 @@
11
use {
2-
crate::{
3-
state::*,
4-
MessageBufferError,
5-
MESSAGE,
6-
},
2+
crate::state::*,
73
anchor_lang::prelude::*,
84
};
95

106
pub fn delete_buffer<'info>(
117
ctx: Context<'_, '_, '_, 'info, DeleteBuffer<'info>>,
128
allowed_program_auth: Pubkey,
13-
base_account_key: Pubkey,
14-
bump: u8,
9+
_base_account_key: Pubkey,
1510
) -> Result<()> {
16-
let message_buffer_account_info = ctx
17-
.remaining_accounts
18-
.first()
19-
.ok_or(MessageBufferError::MessageBufferNotProvided)?;
20-
2111
ctx.accounts
2212
.whitelist
2313
.is_allowed_program_auth(&allowed_program_auth)?;
24-
25-
MessageBuffer::check_discriminator(message_buffer_account_info)?;
26-
27-
let expected_key = Pubkey::create_program_address(
28-
&[
29-
allowed_program_auth.as_ref(),
30-
MESSAGE.as_bytes(),
31-
base_account_key.as_ref(),
32-
&[bump],
33-
],
34-
&crate::ID,
35-
)
36-
.map_err(|_| MessageBufferError::InvalidPDA)?;
37-
38-
require_keys_eq!(
39-
message_buffer_account_info.key(),
40-
expected_key,
41-
MessageBufferError::InvalidPDA
42-
);
43-
let loader = AccountLoader::<MessageBuffer>::try_from(message_buffer_account_info)?;
44-
loader.close(ctx.accounts.admin.to_account_info())?;
4514
Ok(())
4615
}
4716

4817
#[derive(Accounts)]
18+
#[instruction(allowed_program_auth: Pubkey, base_account_key: Pubkey)]
4919
pub struct DeleteBuffer<'info> {
5020
#[account(
5121
seeds = [b"message".as_ref(), b"whitelist".as_ref()],
@@ -57,5 +27,12 @@ pub struct DeleteBuffer<'info> {
5727
// Also the recipient of the lamports from closing the buffer account
5828
#[account(mut)]
5929
pub admin: Signer<'info>,
60-
// remaining_account: - [AccumulatorInput PDA]
30+
31+
#[account(
32+
mut,
33+
close = admin,
34+
seeds = [allowed_program_auth.as_ref(), b"message".as_ref(), base_account_key.as_ref()],
35+
bump = message_buffer.load()?.bump,
36+
)]
37+
pub message_buffer: AccountLoader<'info, MessageBuffer>,
6138
}
Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,40 @@
11
use {
2-
crate::{
3-
state::*,
4-
MessageBufferError,
5-
},
2+
crate::state::*,
63
anchor_lang::prelude::*,
7-
std::mem,
84
};
95

106

117
pub fn put_all<'info>(
128
ctx: Context<'_, '_, '_, 'info, PutAll<'info>>,
13-
base_account_key: Pubkey,
9+
_base_account_key: Pubkey,
1410
messages: Vec<Vec<u8>>,
1511
) -> Result<()> {
16-
let cpi_caller_auth = ctx.accounts.whitelist_verifier.is_allowed()?;
17-
let message_buffer_account_info = ctx
18-
.remaining_accounts
19-
.first()
20-
.ok_or(MessageBufferError::MessageBufferNotProvided)?;
12+
ctx.accounts.whitelist_verifier.is_allowed()?;
2113

22-
MessageBuffer::check_discriminator(message_buffer_account_info)?;
23-
24-
let account_data = &mut message_buffer_account_info.try_borrow_mut_data()?;
25-
let header_end_index = mem::size_of::<MessageBuffer>() + 8;
14+
let msg_buffer_ai = ctx.accounts.message_buffer.to_account_info();
15+
let account_data = &mut msg_buffer_ai.try_borrow_mut_data()?;
16+
let header_end_index = MessageBuffer::HEADER_LEN as usize;
2617

2718
let (header_bytes, body_bytes) = account_data.split_at_mut(header_end_index);
2819

2920
let message_buffer: &mut MessageBuffer = bytemuck::from_bytes_mut(&mut header_bytes[8..]);
3021

31-
message_buffer.validate(
32-
message_buffer_account_info.key(),
33-
cpi_caller_auth,
34-
base_account_key,
35-
)?;
36-
3722
message_buffer.refresh_header();
38-
3923
let (num_msgs, num_bytes) = message_buffer.put_all_in_buffer(body_bytes, &messages);
40-
4124
if num_msgs != messages.len() {
4225
msg!("unable to fit all messages in MessageBuffer account. Wrote {}/{} messages and {} bytes", num_msgs, messages.len(), num_bytes);
4326
}
44-
4527
Ok(())
4628
}
4729

4830
#[derive(Accounts)]
49-
#[instruction( base_account_key: Pubkey)]
31+
#[instruction(base_account_key: Pubkey)]
5032
pub struct PutAll<'info> {
5133
pub whitelist_verifier: WhitelistVerifier<'info>,
52-
// remaining_accounts: - [AccumulatorInput PDA]
34+
#[account(
35+
mut,
36+
seeds = [whitelist_verifier.cpi_caller_auth.key().as_ref(), b"message".as_ref(), base_account_key.as_ref()],
37+
bump = message_buffer.load()?.bump,
38+
)]
39+
pub message_buffer: AccountLoader<'info, MessageBuffer>,
5340
}

pythnet/message_buffer/programs/message_buffer/src/instructions/resize_buffer.rs

Lines changed: 24 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -2,112 +2,35 @@ use {
22
crate::{
33
state::*,
44
MessageBufferError,
5-
MESSAGE,
6-
},
7-
anchor_lang::{
8-
prelude::*,
9-
solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE,
10-
system_program::{
11-
self,
12-
Transfer,
13-
},
145
},
6+
anchor_lang::prelude::*,
157
};
168

9+
1710
pub fn resize_buffer<'info>(
1811
ctx: Context<'_, '_, '_, 'info, ResizeBuffer<'info>>,
1912
allowed_program_auth: Pubkey,
20-
base_account_key: Pubkey,
21-
buffer_bump: u8,
13+
_base_account_key: Pubkey,
2214
target_size: u32,
2315
) -> Result<()> {
24-
let message_buffer_account_info = ctx
25-
.remaining_accounts
26-
.first()
27-
.ok_or(MessageBufferError::MessageBufferNotProvided)?;
28-
2916
ctx.accounts
3017
.whitelist
3118
.is_allowed_program_auth(&allowed_program_auth)?;
32-
MessageBuffer::check_discriminator(message_buffer_account_info)?;
33-
3419

35-
let target_size = target_size as usize;
36-
37-
let current_account_size = message_buffer_account_info.data_len();
38-
let target_size_delta = target_size.saturating_sub(current_account_size);
20+
let message_buffer = &ctx.accounts.message_buffer.load()?;
21+
let max_end_offset = message_buffer.end_offsets.iter().max().unwrap();
22+
let minimum_size = max_end_offset + message_buffer.header_len;
3923
require_gte!(
40-
MAX_PERMITTED_DATA_INCREASE,
41-
target_size_delta,
42-
MessageBufferError::TargetSizeDeltaExceeded
43-
);
44-
45-
let expected_key = Pubkey::create_program_address(
46-
&[
47-
allowed_program_auth.as_ref(),
48-
MESSAGE.as_bytes(),
49-
base_account_key.as_ref(),
50-
&[buffer_bump],
51-
],
52-
&crate::ID,
53-
)
54-
.map_err(|_| MessageBufferError::InvalidPDA)?;
55-
56-
require_keys_eq!(
57-
message_buffer_account_info.key(),
58-
expected_key,
59-
MessageBufferError::InvalidPDA
24+
target_size as usize,
25+
minimum_size as usize,
26+
MessageBufferError::MessageBufferTooSmall
6027
);
61-
62-
// allow for target_size == account_size in case Rent requirements have changed
63-
// and additional lamports need to be transferred.
64-
// the realloc step will be a no-op in this case.
65-
if target_size >= current_account_size {
66-
let target_rent = Rent::get()?.minimum_balance(target_size);
67-
if message_buffer_account_info.lamports() < target_rent {
68-
system_program::transfer(
69-
CpiContext::new(
70-
ctx.accounts.system_program.to_account_info(),
71-
Transfer {
72-
from: ctx.accounts.admin.to_account_info(),
73-
to: message_buffer_account_info.to_account_info(),
74-
},
75-
),
76-
target_rent - message_buffer_account_info.lamports(),
77-
)?;
78-
}
79-
message_buffer_account_info
80-
.realloc(target_size, false)
81-
.map_err(|_| MessageBufferError::ReallocFailed)?;
82-
} else {
83-
// Check that account doesn't get resized to smaller than the amount of
84-
// data it is currently holding (if any)
85-
{
86-
let account_data = &message_buffer_account_info.try_borrow_data()?;
87-
let header_end_index = std::mem::size_of::<MessageBuffer>() + 8;
88-
let (header_bytes, _) = account_data.split_at(header_end_index);
89-
let message_buffer: &MessageBuffer = bytemuck::from_bytes(&header_bytes[8..]);
90-
let max_end_offset = message_buffer.end_offsets.iter().max().unwrap();
91-
let minimum_size = max_end_offset + message_buffer.header_len;
92-
require_gte!(
93-
target_size,
94-
minimum_size as usize,
95-
MessageBufferError::MessageBufferTooSmall
96-
);
97-
}
98-
99-
100-
// Not transferring excess lamports back to admin.
101-
// Account will retain more lamports than necessary.
102-
message_buffer_account_info.realloc(target_size, false)?;
103-
}
10428
Ok(())
10529
}
10630

10731
#[derive(Accounts)]
10832
#[instruction(
109-
allowed_program_auth: Pubkey, base_account_key: Pubkey,
110-
buffer_bump: u8, target_size: u32
33+
allowed_program_auth: Pubkey, base_account_key: Pubkey, target_size: u32
11134
)]
11235
pub struct ResizeBuffer<'info> {
11336
#[account(
@@ -122,5 +45,18 @@ pub struct ResizeBuffer<'info> {
12245
pub admin: Signer<'info>,
12346

12447
pub system_program: Program<'info, System>,
125-
// remaining_accounts: - [AccumulatorInput PDA]
48+
49+
/// If decreasing, Anchor will automatically check
50+
/// if target_size is too small and if so,then load() will fail.
51+
/// If increasing, Anchor also automatically checks if target_size delta
52+
/// exceeds MAX_PERMITTED_DATA_INCREASE
53+
#[account(
54+
mut,
55+
realloc = target_size as usize,
56+
realloc::zero = false,
57+
realloc::payer = admin,
58+
seeds = [allowed_program_auth.as_ref(), b"message".as_ref(), base_account_key.as_ref()],
59+
bump = message_buffer.load()?.bump,
60+
)]
61+
pub message_buffer: AccountLoader<'info, MessageBuffer>,
12662
}

pythnet/message_buffer/programs/message_buffer/src/lib.rs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -112,21 +112,13 @@ pub mod message_buffer {
112112
/// *`target_size` - Size to re-allocate for the
113113
/// `MessageBuffer` PDA. If increasing the size,
114114
/// max delta of current_size & target_size is 10240
115-
/// *`buffer_bump` - Bump seed for the `MessageBuffer` PDA
116115
pub fn resize_buffer<'info>(
117116
ctx: Context<'_, '_, '_, 'info, ResizeBuffer<'info>>,
118117
allowed_program_auth: Pubkey,
119118
base_account_key: Pubkey,
120-
buffer_bump: u8,
121119
target_size: u32,
122120
) -> Result<()> {
123-
instructions::resize_buffer(
124-
ctx,
125-
allowed_program_auth,
126-
base_account_key,
127-
buffer_bump,
128-
target_size,
129-
)
121+
instructions::resize_buffer(ctx, allowed_program_auth, base_account_key, target_size)
130122
}
131123

132124
/// Closes the buffer account and transfers the remaining lamports to the
@@ -138,14 +130,12 @@ pub mod message_buffer {
138130
/// * `base_account_key` - Pubkey of the original account the
139131
/// `MessageBuffer` is derived from
140132
/// (e.g. pyth price account)
141-
/// *`buffer_bump` - Bump seed for the `MessageBuffer` PDA
142133
pub fn delete_buffer<'info>(
143134
ctx: Context<'_, '_, '_, 'info, DeleteBuffer<'info>>,
144135
allowed_program_auth: Pubkey,
145136
base_account_key: Pubkey,
146-
buffer_bump: u8,
147137
) -> Result<()> {
148-
instructions::delete_buffer(ctx, allowed_program_auth, base_account_key, buffer_bump)
138+
instructions::delete_buffer(ctx, allowed_program_auth, base_account_key)
149139
}
150140
}
151141

0 commit comments

Comments
 (0)