Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/actions/setup-rust/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: "Setup Rust"

runs:
using: "composite"
steps:
# Install essential system packages required for building Rust projects
- name: Install system packages
run: sudo apt-get update && sudo apt-get install -y build-essential libudev-dev protobuf-compiler libprotobuf-dev # Installs essential packages like GCC and libudev development headers
shell: bash

# Install Rust nightly toolchain and additional components
# Ensure rustfmt and clippy are installed for the nightly toolchain as well
- name: Install Rust Toolchain Components
run: |
rustup install nightly
rustup component add rustfmt clippy
rustup component add rustfmt clippy --toolchain nightly
shell: bash

# Install Cargo Expand for expanding macros in Rust, useful for debugging macro-generated code
- name: Install Cargo Expand
run: cargo install --locked cargo-expand # Installs the cargo-expand tool, using --locked to ensure exact versions from Cargo.lock are used
shell: bash
55 changes: 55 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Lint crates

on:
workflow_dispatch:
pull_request: # Workflow can be triggered by either a manual dispatch or a pull request


jobs:
Lint-Workspace:
name: Lint Workspace
runs-on: ubuntu-20.04
steps:
- run: echo "Starting Lint-Workspace"

lint:
name: Lint
needs: Lint-Workspace
# This job runs on an Ubuntu 20.04 runner
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4 # Checkout the code from the repository

# https://github.com/Swatinem/rust-cache
- name: Cache Rust and its Packages
# Cache Rust dependencies using Swatinem's rust-cache action to speed up builds
uses: Swatinem/rust-cache@v2
with:
prefix-key: "lint" # Using a locally shared cache key
shared-key: "trident-rust-cache" # Use a shared cache key across multiple jobs to reuse cache
cache-directories: "~/.rustup" # Additional non workspace directories to be cached, separated by newlines.

- name: Setup Rust Environment
# Set up the Rust environment (e.g., install nightly, Rust components)
uses: ./.github/actions/setup-rust/

- name: Cargo build
# Build the Trident workspace
run: cargo build --release --all-features
- name: Cargo fmt
# Run cargo fmt to check if the code is formatted correctly
run: cargo fmt --check
- name: Cargo clippy
# Run Clippy to check for code linting issues and fail on warnings
run: cargo clippy -- -D warnings
- name: Cargo test
# Run tests to ensure the project works as expected
run: cargo test


checks:
name: Lint-Workspace (Checks)
needs: lint
runs-on: ubuntu-20.04
steps:
- run: echo "Lint Workspace completed successfully"
181 changes: 168 additions & 13 deletions src/accounts_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub struct AccountsDB {
permanent_accounts: HashMap<Pubkey, AccountSharedData>,
programs: HashMap<Pubkey, AccountSharedData>,
sysvars: HashMap<Pubkey, AccountSharedData>,
sysvar_tracker: SysvarTracker,
}

impl AccountsDB {
Expand Down Expand Up @@ -72,28 +73,182 @@ impl AccountsDB {
{
let account = AccountSharedData::new_data(1, &sysvar, &solana_sdk::sysvar::id()).unwrap();
let _ = self.sysvars.insert(T::id(), account);

if T::id() == Clock::id() {
self.sysvar_tracker.refresh_last_clock_update();
}
}

fn update_clock(&self) {
let mut clock: Clock =
bincode::deserialize(self.sysvars.get(&Clock::id()).unwrap().data()).unwrap();

// TODO: find better way how to do this , time needs to be
// updated regularly between instructions
if clock.unix_timestamp == 0 {
clock.unix_timestamp = std::time::SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards!")
.as_secs() as i64;

// TODO: remove this once we have a proper way to set sysvars
#[allow(mutable_transmutes)]
let mutable_db = unsafe { std::mem::transmute::<&AccountsDB, &mut AccountsDB>(self) };
mutable_db.add_sysvar::<Clock>(&clock);
}
let current_timestamp = std::time::SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards!")
.as_secs();

// current time is always greater than last clock update
let time_since_last_update =
current_timestamp.saturating_sub(self.sysvar_tracker.last_clock_update);
clock.unix_timestamp = clock
.unix_timestamp
.saturating_add(time_since_last_update as i64);

// TODO: remove this once we have a proper way to set sysvars
#[allow(mutable_transmutes)]
let mutable_db = unsafe { std::mem::transmute::<&AccountsDB, &mut AccountsDB>(self) };
mutable_db.add_sysvar::<Clock>(&clock);
mutable_db.sysvar_tracker.refresh_last_clock_update();
}

pub(crate) fn reset_temp(&mut self) {
self.accounts = Default::default();
}

// Helper functions for testing purposes
pub fn forward_in_time(&mut self, seconds: i64) {
let mut clock: Clock = self.get_sysvar();
clock.unix_timestamp = clock.unix_timestamp.saturating_add(seconds);
self.add_sysvar(&clock);
}
pub fn warp_to_timestamp(&mut self, timestamp: i64) {
let mut clock: Clock = self.get_sysvar();
clock.unix_timestamp = timestamp;
self.add_sysvar(&clock);
}
}

#[derive(Default)]
pub struct SysvarTracker {
pub last_clock_update: u64, // unix timestamp as seconds
}

impl SysvarTracker {
pub fn refresh_last_clock_update(&mut self) {
self.last_clock_update = std::time::SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards!")
.as_secs();
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::thread::sleep;
use std::time::Duration;

#[test]
fn test_clock_update() {
let mut db = AccountsDB::default();

// Set initial clock
let initial_clock = Clock::default();
db.add_sysvar(&initial_clock);
let initial_timestamp = db.get_sysvar::<Clock>().unix_timestamp;

// Sleep for 2 seconds
sleep(Duration::from_secs(2));
let updated_clock: Clock = db.get_sysvar();
assert!(
updated_clock.unix_timestamp > initial_timestamp,
"Clock timestamp should have increased"
);
let diff = (updated_clock.unix_timestamp - initial_timestamp) as u64;
assert!(
(1..=3).contains(&diff),
"Clock update difference should be ~2 seconds, got {}",
diff
);
}

#[test]
fn test_sysvar_tracker_updates() {
let mut db = AccountsDB::default();

// Set initial clock and get tracker time
db.add_sysvar(&Clock::default());
let initial_tracker_time = db.sysvar_tracker.last_clock_update;
sleep(Duration::from_secs(1));

// Force clock update
let _: Clock = db.get_sysvar();
assert!(
db.sysvar_tracker.last_clock_update > initial_tracker_time,
"SysvarTracker should have been updated"
);
}

#[test]
fn test_multiple_clock_updates() {
let mut db = AccountsDB::default();

// Set initial clock
let initial_clock = Clock::default();
db.add_sysvar(&initial_clock);

// First update
sleep(Duration::from_secs(1));
let first_update: Clock = db.get_sysvar();
let first_diff = (first_update.unix_timestamp - initial_clock.unix_timestamp) as u64;
assert!(
(1..=2).contains(&first_diff),
"First update difference should be ~1 second"
);

// Second update
sleep(Duration::from_secs(1));
let second_update: Clock = db.get_sysvar();
let second_diff = (second_update.unix_timestamp - first_update.unix_timestamp) as u64;
assert!(
(1..=2).contains(&second_diff),
"Second update difference should be ~1 second"
);

// Verify total elapsed time
let total_diff = (second_update.unix_timestamp - initial_clock.unix_timestamp) as u64;
assert!(
(2..=3).contains(&total_diff),
"Total time difference should be ~2 seconds"
);
}

#[test]
fn test_time_manipulation() {
let mut db = AccountsDB::default();

// Set initial clock
let initial_clock = Clock::default();
db.add_sysvar(&initial_clock);

// Get initial time
let mut clock: Clock = db.get_sysvar();
let initial_time = clock.unix_timestamp;

// Forward 600 seconds
db.forward_in_time(600);
clock = db.get_sysvar();
assert_eq!(
clock.unix_timestamp,
initial_time + 600,
"Clock should advance 600 seconds"
);

// Warp to specific timestamp
db.warp_to_timestamp(500);
clock = db.get_sysvar();
assert_eq!(
clock.unix_timestamp, 500,
"Clock should warp to timestamp 500"
);

// Test negative time forwarding
db.forward_in_time(-300);
clock = db.get_sysvar();
assert_eq!(
clock.unix_timestamp, 200,
"Clock should go back 300 seconds from 500"
);
}
}
Loading