Skip to content

Commit ece1f99

Browse files
✨ link processor to dispatcher
1 parent 357e274 commit ece1f99

14 files changed

+232
-97
lines changed

src/account/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ mod types;
3535
pub use error::Error;
3636
pub use meta::AccountMeta;
3737
pub use onchain::wallet::Wallet;
38-
pub use transaction::{Accounts, TransactionAccount};
38+
pub use transaction::TransactionAccount;
3939
pub use types::Writable;
4040

4141
/// The result for the accounts module.

src/account/transaction.rs

+1-55
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use super::{AccountMeta, Error, Result, Wallet};
3636

3737
/// Stores all data regarding an account needed by an instruction
3838
/// to allow it to access or modify its data.
39+
#[derive(Clone)]
3940
pub struct TransactionAccount<'a> {
4041
/// The public key of the account
4142
pub key: Pubkey,
@@ -128,61 +129,6 @@ impl<'a> TransactionAccount<'a> {
128129
}
129130
}
130131

131-
/// List of accounts use in an instruction.
132-
pub struct Accounts<'a> {
133-
accounts: &'a [TransactionAccount<'a>],
134-
current: RefCell<usize>,
135-
}
136-
137-
impl<'a> Accounts<'a> {
138-
/// Program utility to access the instruction's accounts.
139-
///
140-
/// # Parameters
141-
/// * `accounts` - the accounts in an instruction,
142-
///
143-
/// # Example
144-
/// ```rust
145-
/// # use bifrost::Error;
146-
/// # use bifrost::{crypto::Keypair, account::{Writable, Wallet, AccountMeta, TransactionAccount, Accounts}};
147-
/// let key1 = Keypair::generate().pubkey();
148-
/// let key2 = Keypair::generate().pubkey();
149-
/// let meta1 = AccountMeta::signing(key1, Writable::Yes)?;
150-
/// let meta2 = AccountMeta::wallet(key2, Writable::Yes)?;
151-
/// let mut wallet1 = Wallet { prisms: 1_000 };
152-
/// let mut wallet2 = Wallet { prisms: 0 };
153-
///
154-
/// let accounts_vec = vec![
155-
/// TransactionAccount::new(&meta1, &mut wallet1),
156-
/// TransactionAccount::new(&meta2, &mut wallet2),
157-
/// ];
158-
/// let accounts = Accounts::new(accounts_vec.as_slice());
159-
/// # Ok::<(), Error>(())
160-
/// ```
161-
#[must_use]
162-
pub const fn new(accounts: &'a [TransactionAccount<'a>]) -> Self {
163-
Self {
164-
accounts,
165-
current: RefCell::new(0),
166-
}
167-
}
168-
169-
/// Get the next account in the list.
170-
///
171-
/// # Errors
172-
/// If there are no next account.
173-
#[must_use]
174-
#[instrument(skip(self), fields(current = *self.current.borrow(), len = self.accounts.len()))]
175-
pub fn next(&self) -> Result<&'a TransactionAccount> {
176-
debug!("getting account");
177-
let res = self
178-
.accounts
179-
.get(*self.current.borrow())
180-
.ok_or(Error::MissingAccounts)?;
181-
*self.current.borrow_mut() += 1;
182-
Ok(res)
183-
}
184-
}
185-
186132
#[cfg(test)]
187133
#[cfg_attr(coverage_nightly, coverage(off))]
188134
mod tests {

src/io/index.rs

+3
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,14 @@ impl Index {
6666
read_from_file(index_path).await
6767
}
6868

69+
#[instrument(skip(self))]
6970
pub async fn load(&self, key: &Pubkey) -> Result<Option<Wallet>> {
7071
let Some(loc) = self.find(key) else {
72+
trace!("account was not found in the index");
7173
return Ok(None);
7274
};
7375

76+
trace!("account was found, reading it from the disk");
7477
Some(loc.read().await).transpose()
7578
}
7679

src/io/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ mod vault;
3636
pub use error::Error;
3737
type Result<T> = core::result::Result<T, Error>;
3838

39-
pub use vault::Vault;
39+
pub use vault::{set_vault_path, Vault};
4040

4141
/// Maximum size for an account file.
4242
#[cfg(test)]

src/io/support.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use borsh::{BorshDeserialize, BorshSerialize};
3232
use memmap2::MmapOptions;
3333
use tokio::{
3434
fs::{self, File, OpenOptions},
35-
io::AsyncWriteExt as _,
35+
io::AsyncWriteExt,
3636
sync::Semaphore,
3737
};
3838
use tracing::{debug, instrument, trace};
@@ -105,7 +105,9 @@ where
105105
.truncate(true)
106106
.open(path.into())
107107
.await?;
108-
Ok(file.write_all(&data).await?)
108+
file.write_all(&data).await?;
109+
file.flush().await?;
110+
Ok(())
109111
}
110112

111113
#[expect(clippy::unwrap_used)]
@@ -125,6 +127,7 @@ where
125127
let _guard = SEMAPHORE.acquire().await?;
126128
let offset = file.metadata().await?.len();
127129
file.write_all(&data).await?;
130+
file.flush().await?;
128131
Ok((data.len() as u64, offset))
129132
}
130133

src/io/vault.rs

+7
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ use super::{
4343

4444
pub static VAULT_PATH: OnceLock<PathBuf> = OnceLock::new();
4545

46+
/// Sets the path where the vault will be stored on disk.
47+
///
48+
/// # Parameters
49+
/// * `path` - Path to the on-disk vault.
50+
///
51+
/// # Errors
52+
/// if the vault can not be created on the given path.
4653
#[mutants::skip]
4754
#[expect(clippy::unwrap_used)]
4855
pub fn set_vault_path<P>(path: P)

src/program/dispatcher.rs

+21-10
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,32 @@
11
use tracing::{debug, instrument};
22

3-
use crate::{account::Accounts, transaction::Instruction};
3+
use crate::{account::TransactionAccount, crypto::Pubkey};
44

55
use super::{
66
system::{self, SYSTEM_PROGRAM},
77
Error, Result,
88
};
99

10+
/// Dispatches an instruction to the program handling it.
11+
///
12+
/// # Parameters
13+
/// * `instruction` - The instruction to execute,
14+
/// * `accounts` - The accounts referenced by the instruction.
15+
///
16+
/// # Errors
17+
/// If the program is unknown or failed to run.
1018
#[instrument(skip_all)]
11-
fn dispatch<'a>(instruction: &Instruction, accounts: &'a Accounts<'a>) -> Result<()> {
19+
pub fn dispatch<'a>(
20+
program: &Pubkey,
21+
accounts: &[TransactionAccount],
22+
payload: &[u8],
23+
) -> Result<()> {
1224
debug!(
13-
program = %instruction.program(),
25+
%program,
1426
"received new instruction to handle"
1527
);
16-
match *instruction.program() {
17-
SYSTEM_PROGRAM => system::execute_instruction(accounts, instruction.data()),
28+
match *program {
29+
SYSTEM_PROGRAM => system::execute_instruction(accounts, payload),
1830
key => Err(Error::UnknownProgram { key }),
1931
}
2032
}
@@ -27,9 +39,10 @@ mod tests {
2739

2840
use test_log::test;
2941

30-
use crate::account::{AccountMeta, Accounts, TransactionAccount, Wallet, Writable};
42+
use crate::account::{AccountMeta, TransactionAccount, Wallet, Writable};
3143
use crate::crypto::Keypair;
3244
use crate::program::system;
45+
use crate::transaction::Instruction;
3346

3447
// use super::super::Error;
3548
use super::*;
@@ -50,12 +63,11 @@ mod tests {
5063
TransactionAccount::new(&meta1, &mut wallet1),
5164
TransactionAccount::new(&meta2, &mut wallet2),
5265
];
53-
let accounts = Accounts::new(accounts_vec.as_slice());
5466

5567
let instruction = system::instruction::transfer(key1, key2, AMOUNT)?;
5668

5769
// When
58-
dispatch(&instruction, &accounts)?;
70+
dispatch(&SYSTEM_PROGRAM, &accounts_vec, instruction.data())?;
5971

6072
// Then
6173
assert_eq!(wallet1.prisms, 0);
@@ -80,12 +92,11 @@ mod tests {
8092
TransactionAccount::new(&meta1, &mut wallet1),
8193
TransactionAccount::new(&meta2, &mut wallet2),
8294
];
83-
let accounts = Accounts::new(accounts_vec.as_slice());
8495

8596
let instruction = Instruction::new(program, [meta1, meta2], &Vec::<u8>::new());
8697

8798
// When
88-
let res = dispatch(&instruction, &accounts);
99+
let res = dispatch(&program, &accounts_vec, instruction.data());
89100

90101
// Then
91102
assert_matches!(res, Err(err) if matches!(err, Error::UnknownProgram { key } if key == program));

src/program/system.rs

+14-13
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@
2929
use borsh::{BorshDeserialize, BorshSerialize};
3030
use tracing::{debug, instrument};
3131

32-
use crate::{account::Accounts, crypto::Pubkey};
32+
use crate::{account::TransactionAccount, crypto::Pubkey};
3333

3434
use super::{Error, Result};
3535

3636
/// The System's program id (`BifrostSystemProgram111111111111111111111111`)
3737
pub const SYSTEM_PROGRAM: Pubkey = Pubkey::from_bytes(&[
38-
159, 65, 158, 196, 5, 83, 96, 13, 242, 56, 2, 138, 167, 225, 20, 157, 169, 199, 82, 249, 248,
39-
91, 220, 170, 46, 53, 235, 98, 98, 0, 0, 0,
38+
2, 190, 236, 171, 26, 147, 23, 185, 158, 168, 176, 152, 117, 167, 48, 232, 60, 78, 120, 154,
39+
96, 248, 193, 153, 0, 203, 246, 209, 37, 0, 0, 0,
4040
]);
4141

4242
#[derive(Debug, BorshSerialize, BorshDeserialize)]
@@ -53,24 +53,28 @@ enum SystemInstruction {
5353
/// # Errors
5454
/// if the instruction fails to complete (missing accounts, arithmetic overflows, *etc.*).
5555
#[instrument(skip_all)]
56-
pub fn execute_instruction<'a>(accounts: &'a Accounts<'a>, payload: &[u8]) -> Result<()> {
56+
pub fn execute_instruction<'a>(accounts: &'a [TransactionAccount], payload: &[u8]) -> Result<()> {
5757
debug!("received system insruction");
5858
match borsh::from_slice(payload)? {
5959
SystemInstruction::Transfer(amount) => transfer(accounts, amount),
6060
}
6161
}
6262

6363
#[instrument(skip(accounts))]
64-
fn transfer<'a>(accounts: &'a Accounts<'a>, amount: u64) -> Result<()> {
64+
fn transfer<'a>(accounts: &'a [TransactionAccount], amount: u64) -> Result<()> {
6565
debug!("transferring prisms");
66-
let payer = accounts.next()?;
66+
if accounts.len() != 2 {
67+
return Err(Error::Account(crate::account::Error::MissingAccounts));
68+
}
69+
assert!(accounts.len() > 1, "missing accounts");
70+
let payer = &accounts[0];
6771
if !payer.is_signer {
6872
return Err(Error::Custom(format!(
6973
"{} must be a signing account",
7074
payer.key
7175
)));
7276
}
73-
let receiver = accounts.next()?;
77+
let receiver = &accounts[1];
7478
debug!("from {} to {}", payer.key, receiver.key);
7579
payer.sub_prisms(amount)?;
7680
receiver.add_prisms(amount)?;
@@ -140,12 +144,11 @@ mod tests {
140144
TransactionAccount::new(&meta1, &mut wallet1),
141145
TransactionAccount::new(&meta2, &mut wallet2),
142146
];
143-
let accounts = Accounts::new(accounts_vec.as_slice());
144147

145148
let payload = borsh::to_vec(&SystemInstruction::Transfer(100)).unwrap();
146149

147150
// When
148-
execute_instruction(&accounts, &payload)?;
151+
execute_instruction(&accounts_vec, &payload)?;
149152

150153
// Then
151154
assert_eq!(wallet1.prisms, AMOUNT - 100);
@@ -163,13 +166,12 @@ mod tests {
163166
let mut wallet1 = Wallet { prisms: AMOUNT };
164167

165168
let accounts_vec = vec![TransactionAccount::new(&meta1, &mut wallet1)];
166-
let accounts = Accounts::new(accounts_vec.as_slice());
167169

168170
#[expect(clippy::unwrap_used)]
169171
let payload = borsh::to_vec(&SystemInstruction::Transfer(100)).unwrap();
170172

171173
// When
172-
let res = execute_instruction(&accounts, &payload);
174+
let res = execute_instruction(&accounts_vec, &payload);
173175

174176
// Then
175177
assert_matches!(res, Err(error) if matches!(error, Error::Account(_)));
@@ -192,13 +194,12 @@ mod tests {
192194
TransactionAccount::new(&meta1, &mut wallet1),
193195
TransactionAccount::new(&meta2, &mut wallet2),
194196
];
195-
let accounts = Accounts::new(accounts_vec.as_slice());
196197

197198
#[expect(clippy::unwrap_used)]
198199
let payload = borsh::to_vec(&SystemInstruction::Transfer(100)).unwrap();
199200

200201
// When
201-
let res = execute_instruction(&accounts, &payload);
202+
let res = execute_instruction(&accounts_vec, &payload);
202203

203204
// Then
204205
assert_matches!(res, Err(error) if matches!(error, Error::Custom { .. }));

src/transaction/instruction.rs

+14-3
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,15 @@ use borsh::{BorshDeserialize, BorshSerialize};
3030

3131
use crate::{account::AccountMeta, crypto::Pubkey};
3232

33+
/// An instruction compiled and ready to be executed on the blockchain.
3334
#[derive(Clone, Debug, Default, BorshSerialize, BorshDeserialize)]
3435
pub struct CompiledInstruction {
35-
program_account_id: u8,
36-
data: Vec<u8>,
37-
accounts: Vec<u8>,
36+
/// The id in the message's accounts of the program executing this instruction.
37+
pub program_account_id: u8,
38+
/// The payload for the instruction.
39+
pub data: Vec<u8>,
40+
/// The id in the message's accounts of the accounts referenced by the instruction.
41+
pub accounts: Vec<u8>,
3842
}
3943

4044
/// An instruction to execute on the blockchain.
@@ -110,6 +114,13 @@ impl Instruction {
110114
}
111115

112116
impl CompiledInstruction {
117+
/// Creates a new compiled instruction.
118+
///
119+
/// # Parameters
120+
/// * `program_account_id` - The id of the program in the [`Message`]'s accounts.
121+
/// * `data` - The instruction's payload,
122+
/// * `accounts` - The id of the accounts referenced by the instruction in the [`Message`]'s accounts.
123+
#[must_use]
113124
pub const fn new(program_account_id: u8, data: Vec<u8>, accounts: Vec<u8>) -> Self {
114125
Self {
115126
program_account_id,

src/transaction/message.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ pub struct Message {
4444
/// Slot at which the transaction was created
4545
slot: u64,
4646
/// The instruction of a transaction.
47-
instructions: Vec<CompiledInstruction>,
47+
pub instructions: Vec<CompiledInstruction>,
4848
/// List of accounts referenced by the transaction's instructions.
49-
accounts: Vec<AccountMeta>,
49+
pub accounts: Vec<AccountMeta>,
5050
}
5151

5252
impl Message {

src/transaction/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,5 @@ mod transaction;
3434
pub use error::Error;
3535
type Result<T> = core::result::Result<T, Error>;
3636

37-
pub use instruction::Instruction;
37+
pub use instruction::{CompiledInstruction, Instruction};
3838
pub use transaction::Transaction;

src/transaction/transaction.rs

+6
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,12 @@ impl Transaction {
199199
.collect::<Vec<_>>()
200200
}
201201

202+
/// Get the message for the transaction.
203+
#[must_use]
204+
pub const fn message(&self) -> &Message {
205+
&self.message
206+
}
207+
202208
#[instrument(skip_all)]
203209
fn validate_signers(&self, signers: &[Pubkey]) -> Result<()> {
204210
debug!("check that there’s a 1 to 1 match between signatures and signers");

src/validator/error.rs

+9
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ pub enum Error {
3535
/// The transaction's signatures are missing or do not match the expectation.
3636
#[display("the transaction’s signatures are invalid")]
3737
InvalidTransactionSignatures,
38+
/// When the lock on the vault could not be obtained.
39+
#[display("the lock on the vault could not be obtained")]
40+
VaultLock,
41+
/// An error occurred in the vault
42+
#[from]
43+
Io(crate::io::Error),
44+
/// An error occurred while running a program.
45+
#[from]
46+
Program(crate::program::Error),
3847
}
3948

4049
impl core::error::Error for Error {}

0 commit comments

Comments
 (0)