diff --git a/Cargo.lock b/Cargo.lock
index e4c21d437..02ec99e95 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3875,6 +3875,8 @@ dependencies = [
"solana-program 3.0.0",
"solana-signature 2.3.0",
"solana-signature 3.3.0",
+ "solana-sysvar 3.1.1",
+ "solana-sysvar-id 3.1.0",
]
[[package]]
@@ -6266,7 +6268,7 @@ dependencies = [
[[package]]
name = "solana-account"
version = "3.4.0"
-source = "git+https://github.com/magicblock-labs/magicblock-svm.git?rev=190146af2ac0890848b50e8fc9d6c926c8205b5e#190146af2ac0890848b50e8fc9d6c926c8205b5e"
+source = "git+https://github.com/magicblock-labs/magicblock-svm.git?rev=ab02436a458bf83aade6a8b9276f6384077c78fc#ab02436a458bf83aade6a8b9276f6384077c78fc"
dependencies = [
"bincode",
"qualifier_attr",
@@ -7846,7 +7848,7 @@ dependencies = [
[[package]]
name = "solana-program-runtime"
version = "4.0.0"
-source = "git+https://github.com/magicblock-labs/magicblock-svm.git?rev=190146af2ac0890848b50e8fc9d6c926c8205b5e#190146af2ac0890848b50e8fc9d6c926c8205b5e"
+source = "git+https://github.com/magicblock-labs/magicblock-svm.git?rev=ab02436a458bf83aade6a8b9276f6384077c78fc#ab02436a458bf83aade6a8b9276f6384077c78fc"
dependencies = [
"base64 0.22.1",
"bincode",
@@ -8542,7 +8544,7 @@ dependencies = [
[[package]]
name = "solana-svm"
version = "4.0.0"
-source = "git+https://github.com/magicblock-labs/magicblock-svm.git?rev=190146af2ac0890848b50e8fc9d6c926c8205b5e#190146af2ac0890848b50e8fc9d6c926c8205b5e"
+source = "git+https://github.com/magicblock-labs/magicblock-svm.git?rev=ab02436a458bf83aade6a8b9276f6384077c78fc#ab02436a458bf83aade6a8b9276f6384077c78fc"
dependencies = [
"ahash 0.8.12",
"log",
@@ -8858,7 +8860,7 @@ dependencies = [
[[package]]
name = "solana-transaction-context"
version = "4.0.0"
-source = "git+https://github.com/magicblock-labs/magicblock-svm.git?rev=190146af2ac0890848b50e8fc9d6c926c8205b5e#190146af2ac0890848b50e8fc9d6c926c8205b5e"
+source = "git+https://github.com/magicblock-labs/magicblock-svm.git?rev=ab02436a458bf83aade6a8b9276f6384077c78fc#ab02436a458bf83aade6a8b9276f6384077c78fc"
dependencies = [
"bincode",
"qualifier_attr",
diff --git a/Cargo.toml b/Cargo.toml
index 451715d7d..1682d3c6b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -140,8 +140,9 @@ paste = "1.0"
prometheus = "0.13.4"
# Keep in sync with `solana-storage-proto` codegen.
+# TODO: Update to the merged version when ready
-solana-transaction-context = { git = "https://github.com/magicblock-labs/magicblock-svm.git", rev = "190146af2ac0890848b50e8fc9d6c926c8205b5e", features = [
+solana-transaction-context = { git = "https://github.com/magicblock-labs/magicblock-svm.git", rev = "ab02436a458bf83aade6a8b9276f6384077c78fc", features = [
"dev-context-only-utils",
] }
solana-transaction-error = { version = "3.0" }
@@ -194,7 +195,8 @@ serde_json = "1.0"
serde_with = "3.16"
serial_test = "3.2"
sha3 = "0.10.8"
-solana-account = { git = "https://github.com/magicblock-labs/magicblock-svm.git", rev = "190146af2ac0890848b50e8fc9d6c926c8205b5e" }
+# TODO: Update to the merged version when ready
+solana-account = { git = "https://github.com/magicblock-labs/magicblock-svm.git", rev = "ab02436a458bf83aade6a8b9276f6384077c78fc" }
solana-account-decoder = { version = "4.0" }
solana-account-decoder-client-types = { version = "4.0" }
solana-account-info = { version = "3.1" }
@@ -246,7 +248,8 @@ solana-program = "3.0"
solana-program-error = { version = "3.0" }
solana-program-option = { version = "3.0" }
solana-program-pack = { version = "3.0" }
-solana-program-runtime = { git = "https://github.com/magicblock-labs/magicblock-svm.git", rev = "190146af2ac0890848b50e8fc9d6c926c8205b5e" }
+# TODO: Update to the merged version when ready
+solana-program-runtime = { git = "https://github.com/magicblock-labs/magicblock-svm.git", rev = "ab02436a458bf83aade6a8b9276f6384077c78fc" }
solana-pubkey = { version = "4.1" }
solana-pubsub-client = { version = "4.0" }
solana-rent = { version = "3.0" }
@@ -270,22 +273,28 @@ solana-system-program = { version = "=4.0.0", features = [
] }
solana-system-transaction = { version = "3.0" }
solana-sysvar = { version = "3.0" }
+solana-sysvar-id = { version = "3.0" }
solana-timings = { package = "solana-svm-timings", version = "=4.0.0", features = [
"agave-unstable-api",
] }
solana-transaction = { version = "3.0" }
+# TODO: Update to the merged version when ready
[workspace.dependencies.solana-svm]
features = ["dev-context-only-utils"]
git = "https://github.com/magicblock-labs/magicblock-svm.git"
-rev = "190146af2ac0890848b50e8fc9d6c926c8205b5e"
+rev = "ab02436a458bf83aade6a8b9276f6384077c78fc"
[patch.crates-io]
-solana-account = { git = "https://github.com/magicblock-labs/magicblock-svm.git", rev = "190146af2ac0890848b50e8fc9d6c926c8205b5e" }
-solana-program-runtime = { git = "https://github.com/magicblock-labs/magicblock-svm.git", rev = "190146af2ac0890848b50e8fc9d6c926c8205b5e" }
+# TODO: Update to the merged version when ready
+solana-account = { git = "https://github.com/magicblock-labs/magicblock-svm.git", rev = "ab02436a458bf83aade6a8b9276f6384077c78fc" }
+# TODO: Update to the merged version when ready
+solana-program-runtime = { git = "https://github.com/magicblock-labs/magicblock-svm.git", rev = "ab02436a458bf83aade6a8b9276f6384077c78fc" }
solana-storage-proto = { path = "storage-proto" }
-solana-svm = { git = "https://github.com/magicblock-labs/magicblock-svm.git", rev = "190146af2ac0890848b50e8fc9d6c926c8205b5e" }
-solana-transaction-context = { git = "https://github.com/magicblock-labs/magicblock-svm.git", rev = "190146af2ac0890848b50e8fc9d6c926c8205b5e" }
+# TODO: Update to the merged version when ready
+solana-svm = { git = "https://github.com/magicblock-labs/magicblock-svm.git", rev = "ab02436a458bf83aade6a8b9276f6384077c78fc" }
+# TODO: Update to the merged version when ready
+solana-transaction-context = { git = "https://github.com/magicblock-labs/magicblock-svm.git", rev = "ab02436a458bf83aade6a8b9276f6384077c78fc" }
# Fork is used to enable `disable_manual_compaction` usage
# Fork is based on commit d4e9e16 of rocksdb (parent commit of 0.23.0 release)
# without patching update isn't possible due to conflict with solana deps
diff --git a/magicblock-core/src/link/replication.rs b/magicblock-core/src/link/replication.rs
index 2825bc1ab..41d3fc948 100644
--- a/magicblock-core/src/link/replication.rs
+++ b/magicblock-core/src/link/replication.rs
@@ -77,8 +77,10 @@ pub struct Block {
pub slot: Slot,
/// Blockhash for this slot.
pub hash: Hash,
- /// Unix timestamp (seconds).
- pub timestamp: i64,
+ /// Unix timestamp in milliseconds. The whole-second `Clock::unix_timestamp`
+ /// is derived from this; the sub-second remainder is the precision exposed
+ /// via the `HighPrecisionClock` sysvar.
+ pub timestamp_millis: i64,
}
/// Periodic checkpoint for state verification and catch-up.
diff --git a/magicblock-ledger/src/blockstore_processor/mod.rs b/magicblock-ledger/src/blockstore_processor/mod.rs
index 9e60bf1a4..ec2c807e7 100644
--- a/magicblock-ledger/src/blockstore_processor/mod.rs
+++ b/magicblock-ledger/src/blockstore_processor/mod.rs
@@ -110,15 +110,22 @@ async fn replay_blocks(
transactions: successfull_txs,
};
- let Some(timestamp) = block.block_time else {
+ if block.block_time.is_none() {
return Err(LedgerError::BlockStoreProcessor(format!(
"Block has no timestamp, {block:?}",
)));
- };
+ }
{
- let block =
- LatestBlockInner::new(block.slot, block.blockhash, timestamp);
- ledger.latest_block().store(block);
+ // Restore the millisecond precision so the HighPrecisionClock
+ // sysvar is reproduced exactly during replay.
+ let latest = LatestBlockInner::new_with_millis(
+ block.slot,
+ block.blockhash,
+ ledger
+ .get_block_time_millis(block.slot)?
+ .unwrap_or_default(),
+ );
+ ledger.latest_block().store(latest);
}
// Transactions are stored in the ledger ordered by most recent to latest
// such to replay them in the order they executed we need to reverse them
diff --git a/magicblock-ledger/src/database/columns.rs b/magicblock-ledger/src/database/columns.rs
index 984172a2e..a829f01a0 100644
--- a/magicblock-ledger/src/database/columns.rs
+++ b/magicblock-ledger/src/database/columns.rs
@@ -56,8 +56,14 @@ pub struct SlotSignatures;
/// The block time column
///
+/// Stores the block timestamp as **milliseconds** since the Unix epoch. The
+/// sub-second remainder preserves the precision exposed via the
+/// `HighPrecisionClock` sysvar so it can be reproduced deterministically during
+/// ledger replay; [`Ledger::get_block_time`] rounds it down to whole seconds
+/// for consumers that expect a [`UnixTimestamp`] (RPC `getBlockTime`, etc.).
+///
/// * index type: `u64` (see [`SlotColumn`])
-/// * value type: [`UnixTimestamp`]
+/// * value type: [`UnixTimestamp`] (milliseconds, despite the type name)
pub struct Blocktime;
/// The block hash column
diff --git a/magicblock-ledger/src/lib.rs b/magicblock-ledger/src/lib.rs
index 77b9e4210..a6f417421 100644
--- a/magicblock-ledger/src/lib.rs
+++ b/magicblock-ledger/src/lib.rs
@@ -16,6 +16,7 @@ pub struct LatestBlockInner {
pub slot: u64,
pub blockhash: Hash,
pub clock: Clock,
+ pub timestamp_millis: i64,
}
/// Atomically updated, shared, latest block information
@@ -36,16 +37,29 @@ pub struct LatestBlock {
}
impl LatestBlockInner {
+ /// Creates a block from a whole-second timestamp. The millisecond timestamp
+ /// is that value with a zero sub-second component.
pub fn new(slot: u64, blockhash: Hash, timestamp: i64) -> Self {
+ Self::new_with_millis(slot, blockhash, timestamp * 1000)
+ }
+
+ /// Creates a block from a millisecond-accurate timestamp, deriving the
+ /// whole-second `Clock::unix_timestamp` from it.
+ pub fn new_with_millis(
+ slot: u64,
+ blockhash: Hash,
+ timestamp_millis: i64,
+ ) -> Self {
let clock = Clock {
slot: slot + 1,
- unix_timestamp: timestamp,
+ unix_timestamp: timestamp_millis.div_euclid(1000),
..Default::default()
};
Self {
slot,
blockhash,
clock,
+ timestamp_millis,
}
}
}
diff --git a/magicblock-ledger/src/store/api.rs b/magicblock-ledger/src/store/api.rs
index 7809cbd23..e831becb5 100644
--- a/magicblock-ledger/src/store/api.rs
+++ b/magicblock-ledger/src/store/api.rs
@@ -167,8 +167,13 @@ impl Ledger {
latest_block,
};
let (slot, blockhash) = ledger.get_max_blockhash()?;
- let time = ledger.get_block_time(slot)?.unwrap_or_default();
- let block = LatestBlockInner::new(slot, blockhash, time);
+ let timestamp_millis =
+ ledger.get_block_time_millis(slot)?.unwrap_or_default();
+ let block = LatestBlockInner::new_with_millis(
+ slot,
+ blockhash,
+ timestamp_millis,
+ );
ledger.latest_block.store(block);
ledger.initialize_lowest_cleanup_slot()?;
@@ -279,6 +284,19 @@ impl Ledger {
&self,
slot: Slot,
) -> LedgerResult